-
Notifications
You must be signed in to change notification settings - Fork 131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Provide a CustomSortProvider similar to the CustomFilterCompilers #378
Comments
We introduced .Where(c => c.Collection.Any(itemCondition)); Would you please provide an example of an |
I've got the same problem as I've got a string property which must be sorted as an Interger (like: "1" < "2" < "10" < "20"), but keeped as String in result. So a CustomSortCompilers could be useful with parameters :
|
Yes, it's a good implementation but don't forget to pass Type of the model. Or use : |
My case is a snowball from other issues I have worked around with the library and discussed earlier - projections. It also affects grouping, but that is another matter. Basically, I have a table - lets say product, but I project it to a productDto before I return it (because this is just good practice, and because I need to shape the data, excluding some massive text field for registers). The benefit of the Dto projection is that it reduces the data by to less than 1% its original size. The problem is that it must happen before the datasourceloader filtering, sorting etc. My solution to this is to run it as two queries - one on the product base object that ONLY returns the ID of the selected objects. Then I run a separate query that updates the loader.data with the productDto projection. This way I never query the large data from the server. The problem with this is that the consumer only sees the Dto, so they expect to be able to operate on those properties. For example, if the product table has a catName that is a projection of category.categoryName. The user will specify something like
The initial sort needs to be on category.categoryName to get the right Ids. Then after completing my second query, I need to sort the result on catName. At the moment I have a static dictionary that does a substitution based on type. I have yet to figure out the grouping. It would similarly benefit from an interceptor. Here is my projection code, including the substitution.
|
Looks like the same story as in #367, with the addition of updating If this approach works properly for your app, then it doesn't seem like you need any new sorting API. |
Ok, I'll close this as a dup of #367. Would you have other additions, please post them to the original ticket comments. |
Hi Aleksey Can we please reopen this? As #367 is not addressing this, it becomes a pretty big issue for me in managing projections - even when I am using my own workarounds. Where a user specifies a sort that operates on a projected column, I will need to catch this before the projection, and create an equivalent sort method. For example: If I have a DateClosed column on the data source, and a projected column Status which is set to "Closed" or "Open" depending on whether the DateClosed column has a value or is null, I need to intercept the Sort the same way I can with the filter. I will detect a request to sort by Status, and then depending on direction, compile a sort filter something like
|
In this specific case of
You can also invert The code snippet is not clear to me. The Here's my take on how this can be done without any sorting interventions: [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public enum OrderStatus {
[EnumMember(Value = "Open")]
Open,
[EnumMember(Value = "Closed")]
Closed
}
public class OrderDTO {
public int ID { get; set; }
public OrderStatus Status { get; set; }
public decimal Freight { get; set; }
}
[HttpGet("orders")]
public async Task<IActionResult> Orders(DataSourceLoadOptions loadOptions) {
var tempConfig = new MapperConfiguration(cfg => cfg.CreateMap<Order, OrderDTO>()
.ForMember(d => d.ID, opt => opt.MapFrom(c => c.OrderId))
.ForMember(d => d.Status, opt => opt.MapFrom(c => c.ShippedDate == null ? OrderStatus.Open : OrderStatus.Closed))
.ForMember(d => d.Freight, opt => opt.MapFrom(c => c.Freight))
);
var tempMapper = tempConfig.CreateMapper();
loadOptions.RemoteGrouping = false; // against 'could not be translated and will be evaluated locally'
var source = _nwind.Orders.ProjectTo<OrderDTO>(tempMapper.ConfigurationProvider);
return Json(await DataSourceLoader.LoadAsync(source, loadOptions));
} Translated by EF Core info: SELECT COALESCE([dtoOrder].[Freight], 0.0) AS [Freight], [dtoOrder].[OrderID] AS [ID], CASE
WHEN [dtoOrder].[ShippedDate] IS NULL
THEN 0 ELSE 1
END AS [Status]
FROM [Orders] AS [dtoOrder]
ORDER BY [Status], [ID] Instead of |
Sorry typo - pseudocode should be
but yes, you are right, you could do what you are suggesting in this simple case. That is a lot of plumbing for one property on one entity though - and like you point out, this is a trivial example. I have far more sophisticated ones that would need a dynamic enum which I don't think is even possible. The exact same model used for the filter, but used for the sort would fix this without any issues. |
I completely forgot that we have another (currently hidden) feature |
Hello, I have a similar problem.
And my Filter control can create a filter for all persons that have a card. With the 'RegisterBinaryExpressionCompiler' I can create easily an Any() filter.
Mirrored to the database this creates a cartesian product.
So at this point I would need a way to create a custom sorting expression like it is done for the filter. |
@Franki1986 Try a custom accessor: public class Startup {
public void Configure(IApplicationBuilder app) {
DevExtreme.AspNet.Data.Helpers.CustomAccessorCompilers.Register((expr, accessorText) => {
if(expr.Type == typeof(Category) && accessorText == "ProductCount") {
var products = Expression.Property(expr, "Products");
return Expression.Call(typeof(Enumerable), "Count", products.Type.GetGenericArguments(), products);
}
return null;
});
// ...
}
} DataSourceLoader.Load(
_nwind.Categories.Include(c => c.Products),
new DataSourceLoadOptions {
Sort = new[] {
new SortingInfo { Selector = "ProductCount" }
}
}
); The |
If I use the syntax '[Propertyname]', or in words, if the accessorText is in brackets, it will be seen as collection type. So:
Works!! |
In Version 2.3.0 you introduced CustomFilterCompilers to allow us to provide complex filters. This has been very much appreciated as it lets us deal with child collections.
Would it be possible to apply the same logic for complex sorting? This is especially necessary when working with projections. I am hoping it is kind of trivial :)
The text was updated successfully, but these errors were encountered: