1

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!).

4

2 回答 2

1

如果有一个导航属性 from LibrarytoCategory你可以动态添加另一个谓词:

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

如果导航属性不存在,您仍然可以在谓词中使用连接,而无需复制整个查询。但这可能是添加导航属性的好理由。

于 2013-08-02T22:02:56.697 回答
0

您可以像以下通用函数一样使用反射 API……它编译具有未知类型的动态查询……

 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);
于 2013-08-02T17:09:58.070 回答