datatables / editor-net Goto Github PK
View Code? Open in Web Editor NEW.NET Framework and .NET Core server-side libraries for Editor
License: Other
.NET Framework and .NET Core server-side libraries for Editor
License: Other
Hi @AllanJard !
First off, thanks for DataTables and deciding to open source it and the server-side libraries!
However, I can't seem to find a License file detailing the license used by the server-side libraries.
Can you please point me where it is?
Thanks!
cc @TomeCirun @tino097
This is maybe a bug with the server side stuff.
This does not work:
.Field(new Field("InventoryServer.UpdatedByDisplayName")
.Set(Field.SetType.Create | Field.SetType.Edit) // <--------------------
.SetValue("TODO"))
I think theres a lot of value here. Otherwise I have to do this in an event.
When you apply a top level filter on a table, this is not taken into account by the options presented by SearchBuilderOptions or SearchPaneOptions.
Using the current example download for Editor. Showing this with SearchPanes because it is easier to see in the browser:
Controllers\SearchPanesController.cs
to add a top level filter on site using the Where clausevar response = new Editor(db, "users")
.Model<UploadManyModel>()
.Where("site", "1", "=")
/examples/extensions/searchPanes.html
The resulting table shows the correct data set:
However, the SearchPanes options still show the options from the full data set:
This includes the other options like Name:
Selecting one of the pre-filtered names results in zero records found, while the options show there should be one.
Pre-filtered items are hidden from the SearchPane/SearchBuilder options.
This could result in data leaking, or weird results from logically deleted records.
For example Validate Async?
IE:
var editor = new Editor(db, "FieldDefinition", "Id")
.ValidatorAsync(async (editor, action, data) =>
{
// ...
var hasPermission = await policyServerRuntimeClient.HasPermissionAsync(this.User, "WriteInventory");
})
In the samples SearchBuilderController.cs
adding addition joins show up in that search builder query:
.Field(new Field("users.site")
.Options(new Options()
.Table("sites")
.Value("id")
.Label("name")
)
)
.Field(new Field("sites.name")
.SearchBuilderOptions(new SearchBuilderOptions()
.Label("sites.name")
.Value("sites.name")
.LeftJoin("sites", "sites.id", "=", "users.site")
)
)
.LeftJoin("sites", "sites.id", "=", "users.site")
.LeftJoin("user_dept", "users.id", "=", "user_dept.user_id")
SELECT sites.name as 'value', sites.name as 'label'
FROM users
LEFT JOIN sites ON sites.id = users.site
LEFT JOIN user_dept ON users.id = user_dept.user_id
GROUP BY sites.name
Maybe im approaching this wrong but I have a global validator that needs to do some special custom checks.
Because my global validator runs first im duplicating a ton of field validators:
// The entire purpose of this validator is to check if the FieldDefinition already exists associated to the same entities
// For example a field definition for Name cannot exist twice both added to InventoryServers
.Validator((editor, action, data) =>
{
if (action == DtRequest.RequestTypes.DataTablesGet)
{
return string.Empty;
}
foreach (var pair in data.Data)
{
var values = pair.Value as Dictionary<string, object>;
if (!values.TryGetValue("FieldDefinition", out var fieldDefinitionData))
{
// DUPLICATED FIELD VALIDATOR
return string.Empty;
}
else
{
if (action is DtRequest.RequestTypes.EditorEdit
or DtRequest.RequestTypes.EditorCreate)
{
var fieldDefinitionDictionary = fieldDefinitionData as Dictionary<string, object>;
if (!fieldDefinitionDictionary.TryGetValue("Name", out var fieldDefinitionNameData))
{
// DUPLICATED FIELD VALIDATOR
// An edit does not have to result in a name change (they didnt send Name field).
// A create Name is required, so another validator will catch this
return string.Empty;
}
var fieldDefinitionName = fieldDefinitionNameData.ToString();
if (string.IsNullOrWhiteSpace(fieldDefinitionName))
{
// DUPLICATED FIELD VALIDATOR
// Field validator will catch it
return string.Empty;
}
if (!values.TryGetValue("FieldDefinitionEntity", out var entities))
{
// DUPLICATED FIELD VALIDATOR
return string.Empty;
}
if (action is DtRequest.RequestTypes.EditorEdit)
{
var existingFieldDefinition = context.WuitFieldDefinition
.Include(x => x.Entities)
.FirstOrDefault(x => x.Id == Convert.ToInt32(pair.Key, CultureInfo.InvariantCulture));
if (fieldDefinitionName == existingFieldDefinition.Name)
{
// Name did not change, no need to check if it exists
continue;
}
}
var entitiesDict = entities as Dictionary<string, object>;
var entityIds = entitiesDict.Values
.Select(x => x as Dictionary<string, object>)
.Select(x => (int)x["Id"])
.ToArray();
if (entityIds.Count == 0)
{
// DUPLICATED FIELD VALIDATOR
// Field validator for the MJoin below will check that they provided a min of 1
return string.Empty;
}
var existingRecord = IsFieldDefinitionInUse(fieldDefinitionName, entityIds);
if (existingRecord is null)
{
return string.Empty;
}
return $"Field Definition already exists for field name {fieldDefinitionName} ({existingRecord.Id}) on entities {string.Join(", ", existingRecord.Entities.Select(x => x.Name))}";
}
}
}
return string.Empty;
})
Now this would be my code if I could trust that field validators all ran and that it was safe to access my dictionary:
.Validator((editor, action, data) =>
{
if (action == DtRequest.RequestTypes.DataTablesGet)
{
return string.Empty;
}
// Create/Edit can accept multiple records at once
foreach (var pair in data.Data)
{
var values = pair.Value as Dictionary<string, object>;
if (!values.TryGetValue("FieldDefinition", out var fieldDefinitionData))
{
return string.Empty;
}
else
{
if (action is DtRequest.RequestTypes.EditorEdit or DtRequest.RequestTypes.EditorCreate)
{
var fieldDefinitionDictionary = fieldDefinitionData as Dictionary<string, object>;
var fieldDefinitionName = fieldDefinitionDictionary["Name"].ToString();
var entities = values["FieldDefinitionEntity"] as Dictionary<string, object>;
var entityIds = entities.Values
.Select(x => x as Dictionary<string, object>)
.Select(x => (int)x["Id"])
.ToArray();
var existingFieldDefinition = context.WuitFieldDefinition
.Include(x => x.Entities)
.FirstOrDefault(x => x.Id == Convert.ToInt32(pair.Key, CultureInfo.InvariantCulture) &&
x.Name == fieldDefinitionName);
if (existingFieldDefinition is not null)
{
// Name did not change, no need for further checks
continue;
}
var existingRecord = IsFieldDefinitionInUse(fieldDefinitionName, entityIds);
if (existingRecord is null)
{
return string.Empty;
}
return $"Field Definition already exists for field name {fieldDefinitionName} ({existingRecord.Id}) on entities {string.Join(", ", existingRecord.Entities.Select(x => x.Name))}";
}
}
}
return string.Empty;
})
Looking at the code looks like PreX events run before field validation too... so those are not a workaround for me either.
Hello, this is more of a feature request
I've stumbled upon this packages through the datatables.net site and saw it mostly supports relational Databases.
Any chance this gets support for Cosmos Db? It could leverages the OData functionality to create complex filters?
RE: https://datatables.net/forums/discussion/78882/editor-net-mjoin-leftjoin#latest
I think this is a legit bug.
// ...
.MJoin(new MJoin("InventoryServerWarranty")
.Set(false)
.Link("InventoryServer.Id", "InventoryServerInventoryServerWarrantyLine.InventoryServerId")
.Link("InventoryServerWarranty.Id", "InventoryServerInventoryServerWarrantyLine.InventoryServerWarrantyId")
.Order("InventoryServerWarranty.CertificateNumber")
.Model<Warranty>()
.LeftJoin("InventoryServerWarrantyProvider", "InventoryServerWarrantyProvider.Id", "=", "InventoryServerWarranty.InventoryServerWarrantyProviderId")
)
// ...
The Editor generated SQL looks like so:
SELECT
DISTINCT [InventoryServer].[Id] as 'dteditor_pkey',
[InventoryServerWarranty].[CertificateNumber] as 'CertificateNumber',
[InventoryServerWarranty].[RegistrationID] as 'RegistrationID',
[InventoryServerWarranty].[Notes] as 'Notes',
[InventoryServerWarranty].[DeliveryDate] as 'DeliveryDate',
[InventoryServerWarranty].[ExpirationDate] as 'ExpirationDate',
[InventoryServerWarranty].[InventoryServerWarrantyProviderId] as 'InventoryServerWarrantyProviderId',
[InventoryServerWarranty].[RetiredIncidentNumber] as 'RetiredIncidentNumber',
[InventoryServerWarranty].[RetiredOn] as 'RetiredOn'
FROM
[InventoryServer]
LEFT JOIN [InventoryServerWarrantyProvider] ON [InventoryServerWarrantyProvider].[Id] = [InventoryServerWarranty].[InventoryServerWarrantyProviderId]
JOIN [InventoryServerInventoryServerWarrantyLine] ON [InventoryServer].[Id] = [InventoryServerInventoryServerWarrantyLine].[InventoryServerId]
JOIN [InventoryServerWarranty] ON [InventoryServerWarranty].[Id] = [InventoryServerInventoryServerWarrantyLine].[InventoryServerWarrantyId]
WHERE
[InventoryServer].[Id] IN (2)
ORDER BY
[InventoryServerWarranty].[CertificateNumber]
Note how that left join is FIRST. That will not work. Only by pulling that down to the be the final join, does the code work.
https://github.com/KonstaMF/Math-Driver-Extend
Hello, is it possible to implement this in code for a Windows system or .NET Framework ? I need to help.
Any plans to port to Go?
Sample project JoinArrayController
says:
/// This example shows a very simple join using the `LeftJoin` method.
/// Of particular note in this example is that the `JoinModel` defines two
/// nested classes that obtain the data required from the two tables
JoinModelUsers and JoinModelSites are not nested classes:
using DataTables;
namespace EditorNetCoreDemo.Models
{
public class JoinModelUsers
{
public string first_name { get; set; }
public string last_name { get; set; }
public string phone { get; set; }
public int site { get; set; }
public int manager { get; set; }
}
public class JoinModelSites
{
public string name { get; set; }
}
}
I am trying to mirror the examples and get my code working and getting an "Object is not set to an instance of an object" error with no-stack trace on a simple LeftJoin and am struggling.
https://github.com/DataTables/Editor-NET/blob/master/DataTables-Editor-Server/MJoin.cs#L750
VS
https://github.com/DataTables/Editor-NET/blob/master/DataTables-Editor-Server/Editor.cs#L1394
Issue:
https://datatables.net/forums/discussion/78941/editor-net-mjoin-leftjoin-data#latest
This forces us to define the nested fields manually via:
.MJoin(new MJoin("InventoryServerWarranty")
.Model<InventoryServerWarranty>()
.Field(new Field("InventoryServerWarrantyProvider.Name"))
.Field(new Field("InventoryServerWarrantyProvider.Description"))
Modify the sample to the following code
.Field(new Field("sites.name")
.SearchBuilderOptions(new SearchBuilderOptions()
.Value("sites.id")
.Label("sites.name")
.LeftJoin("sites", "sites.id", "=", "users.site")
)
)
This works on SQLite not SqlServer/MSSQL.
With SqlServer, you will get "Column '[sites].[name]' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause."
Finally, "fixing" this is:
.Field(new Field("sites.name")
.SearchBuilderOptions(new SearchBuilderOptions()
.Value("sites.id")
.Label("MAX(sites.name)")
.LeftJoin("sites", "sites.id", "=", "users.site")
)
)
Not sure if that is the appropriate fix though...
I get this error when I click a column sort 3 times. It sorts ASC on first click, the DESC, then error.
The generated SQL is:
SELECT
[InventoryServer].[Id] as 'InventoryServer.Id',
[InventoryServer].[Name] as 'InventoryServer.Name',
[InventoryServer].[DockDate] as 'InventoryServer.DockDate',
[InventoryServer].[BillingStartDate] as 'InventoryServer.BillingStartDate',
[PatchPartyDowntime].[Name] as 'PatchPartyDowntime.Name',
[VirtualEnvironment].[Description] as 'VirtualEnvironment.Description'
-- Many more redacted
FROM
[InventoryServer]
LEFT JOIN [ContactGroup] ON [ContactGroup].[Id] = [InventoryServer].[ContactGroupId]
LEFT JOIN [DisasterRecoveryTeir] ON [DisasterRecoveryTeir].[Id] = [InventoryServer].[DisasterRecoveryTeirId]
LEFT JOIN [Domain] ON [Domain].[Id] = [InventoryServer].[DomainId]
-- Many more redacted
WHERE
(1 = 1) OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
It appears the order by is ripped away as a part of removing the sort on my third click.
Let me know if you need more code.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.