Skip to content
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

How to implement dynamic linq for Query.OrderBy and Query.Where #154

Closed
dcadlereii opened this issue Sep 15, 2021 · 8 comments
Closed

How to implement dynamic linq for Query.OrderBy and Query.Where #154

dcadlereii opened this issue Sep 15, 2021 · 8 comments

Comments

@dcadlereii
Copy link

dcadlereii commented Sep 15, 2021

I would like to be able to pass strings that represent table fields from my client into my API where I have the ardalis.Specification nuget installed. @ardalis had recommended that I review suggestions in a StackOverflow post here: https://stackoverflow.com/questions/2728340/how-can-i-do-an-orderby-with-a-dynamic-string-parameter and
reviewing the SO post led me to the System.Linq.Dynamic.Core nuget package that looked like it would allow me to achieve my goal with some extension methods.

I installed the System.Linq.Dynamic.Core nuget on my API and added the using statement in my Specification class, but then when I try to pass a string into the Query.OrderBy(stringVariable) I get the following error;

CS1929: 'ISpecificationBuilder' does not contain a definition for 'OrderBy' and the best extension method overload 'DynamicQueryableExtensions.OrderBy(IQueryable, string, params object[])' requires a receiver of type IQueryable'

Here is my current spec class;

using System.Linq;
using System.Linq.Dynamic.Core;  //<--  added
using Ardalis.Specification;
using CD.Admin.Core.Aggregates.Organizations.Entities;
using CD.Admin.Core.Aggregates.Organizations.Specifications.Filters;
using CD.SharedKernal.Helpers;

namespace CD.Admin.Core.Aggregates.Organizations.Specifications
{
    public class OrganizationListSpec : Specification<Organization>
    {
        public OrganizationListSpec(OrganizationFilter spec)
        {
            if (!string.IsNullOrEmpty(spec.OrderBy))
            {
                Query.OrderBy(spec.OrderBy); // <-- spec.OrderBy is a string that represents the field name. 
            }

            if (spec.IsPagingEnabled)
            {
                Query.Skip(PaginationHelper.CalculateSkip(spec))
                     .Take(PaginationHelper.CalculateTake(spec));
            }

            if (!string.IsNullOrEmpty(spec.Filter))
            {
                Query.Where(x => x.Name.Contains(spec.Filter) || x.OrganizationId.Contains(spec.Filter));  //<-- I would also like to use dynamics here, if possible.
            }
        }
    }
}


In my use case, the client for my API will be a Blazor WASM that uses a SyncFusion Blazor Grid. The grid allows the potential for the user to sort and filter on every column. I would like to support this flexibility in my API but I don't want to have to code each possible individual table field for every table that will have its paged data exposed in a Grid component. Being able to simply pass the string representation of a table field would be a big benefit toward my desired use case if I can figure out how to correctly apply it to my spec.

Can anyone tell me if this is actually possible with the ardalis.Specification nuget and if so, recommend a way to make this work correctly and/or provide an example?

@ardalis
Copy link
Owner

ardalis commented Sep 15, 2021

@fiseni have you done dynamic order-by at all? Seems like something that might be a common request for specifications. Thoughts?

@fiseni
Copy link
Collaborator

fiseni commented Sep 15, 2021

I provided an extension in the following issue here.
We can include it OOTB. But, there is always "but" :). How about if you use value objects in your entities, then you should care for scanning nested types. I have something for that, that I use for my own projects. But, it becomes too specific.

@dcadlereii
Copy link
Author

Thanks @fiseni and @ardalis. I implemented the extension method provided by @lankymart in issue #53 and it seems to be doing exactly what I wanted.

@ardalis
Copy link
Owner

ardalis commented Sep 16, 2021

This does seem to be almost a duplicate of #53. I'll add an issue for us to document this in our docs.

@davidhenley
Copy link
Contributor

I was just about to ask the same question! You guys are on top of it!

@davidhenley
Copy link
Contributor

davidhenley commented Sep 16, 2021

Would also love a way to add whether the order should be ascending or descending like in DynamicLINQ

@dcadlereii
Copy link
Author

@davidhenley the solution that @lankymart listed in issue #53 uses a comma separated string value for the list of OrderBy fields and if you prefix a - in front of the string field name, for example; "Name, -City", it does the OrderBy(Name) and then OrderByDescending(City). Seems to work well for me, so far.

@davidhenley
Copy link
Contributor

davidhenley commented Sep 16, 2021

Thanks @dcadlereii, I am using his extension and it is working well.

Only thing that I could use now is a dictionary that maps the strings to lambda functions.

Example: "customerType": x => x.CustomerType.Name

Would that work with something like OrderBy("CustomerType.Name") ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants