Question

I have a method in my data layer in which I pass in a set of search parameters, dynamically build up the required 'where' clause using the PredicateBuilder class from LINQKit and then execute the LINQ query to return a list of objects.

In some cases the search criteria may contain a category name which doesn't exist in the Library table, I then need to join to this table to perform the necessary query. The reason it's been done this way is because there are potentially hundreds of categories per book and for optimisation reasons I only want the join to be performed if actually required.

Now my question is, is it possible to dynamically append this join to the LINQ query?

After a few attempts at trying to get this to work I've unfortunately had to resort to the 'cut-and-paste' pattern which I dislike immensely but needed something that worked for it to get 'out-the-door'.

The code below is an extract with variables renamed of what I've currently got (i.e. this isn't really a library application!):

public IEnumerable<ILibrarySearchResultsDTO> SearchLibrary(ISearchLibrary searchValues)
{
    var whereStatement = PredicateBuilder.True<Library>();

    bool categorySearch = false;

    if (!string.IsNullOrEmpty(searchValues.AuthorFirstName))
        whereStatement = whereStatement.And(q => q.AuthorFirstName == searchValues.AuthorFirstName);

    if (!string.IsNullOrEmpty(searchValues.AuthorLastName))
        whereStatement = whereStatement.And(q => q.AuthorLastName == searchValues.AuthorLastName);

    if (!string.IsNullOrEmpty(searchValues.CategoryName))
        categorySearch = true;

    var libraryObjectSet = Context.CreateObjectSet<Library>();
    LibraryObjectSet.MergeOption = MergeOption.NoTracking;

    var categoriesObjectSet = Context.CreateObjectSet<Categories>();
    categoriesObjectSet.MergeOption = MergeOption.NoTracking;

    if (!categorySearch)
    {
        var query = from lib in libraryObjectSet
                    .Where(whereStatement)
                    .Take(ConfigHelper.MaxQueryRecords)
                    .AsExpandable()
                    select new LibrarySearchResultsDTO()
                    {
                        BookName = lib.BookName,
                        AuthorFirstName = lib.AuthorFirstName,
                        AuthorLastName = lib.AuthorLastName,
                        ISBN = lib.ISBN
                    };
    }
    else
    {
        var query = from lib in LibraryObjectSet
                    .Where(whereStatement)
                    .Take(ConfigHelper.MaxQueryRecords)
                    .AsExpandable()
                    join categories_LKP in categoriesObjectSet on new { CategoryID = lib.CategoryID, CategoryName = searchValues.CategoryName } 
                        equals new { CategoryID = categories_LKP.CategoryID, CategoryName = categories_LKP.CategoryName }
                    select new LibrarySearchResultsDTO()
                    {
                        BookName = lib.BookName,
                        AuthorFirstName = lib.AuthorFirstName,
                        AuthorLastName = lib.AuthorLastName,
                        ISBN = lib.ISBN
                    };      
    }

    return query.ToList();
}

I've had to create the sample code in Notepad++ and because it's a contrived example I haven't been able to check if it compiles. Should do though (I hope!).

Était-ce utile?

La solution

If there is a navigation property from Library to Category you can just dynamically add another predicate:

if (!string.IsNullOrEmpty(searchValues.CategoryName))
{
   whereStatement = whereStatement
                   .And(q => q.Categories
                        .Any(c => c.CategoryName == searchValues.CategoryName));
}

If the navigation property is not present, you can still use a join in the predicate without having to duplicate the whole query. But it could be a good reason to add a navigation property.

Autres conseils

You may use Reflection API like a following generic function...which compiles a dynamic query with a unknown type...

 IQueryable<T> getQuery<T>(T myTableEntity, string[] arrayOfQueryTerms, Expression<Func<T, bool>> predicate)
 { 
    var fieldOrProperty = getMemberInfo(predicate);
 }

 MemberInfo getmemberInfo<T>(Expression<Func<T,bool> expr)
 { 
     var memberExpr = expr as MemberExpression;
     if (memberExpr != null) return memberExpr.Member;
         throw new ArgumentException();
 }

var q = getQuery<FooTable>(foo, new[]{"Bar","Baz"}, x=>x.FieldName);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top