Динамически добавить LINQ Join

У меня есть метод в моем слое данных, в котором я передаю набор параметров поиска, динамически создаю требуемое предложение 'where', используя класс PredicateBuilder из LINQKit, а затем выполняю запрос LINQ, чтобы вернуть список объектов.

В некоторых случаях критерии поиска могут содержать имя категории, которого нет в таблице библиотеки, поэтому мне нужно присоединиться к этой таблице, чтобы выполнить необходимый запрос. Причина, по которой это было сделано таким образом, заключается в том, что в каждой книге может быть несколько сотен категорий, и по причинам оптимизации я хочу, чтобы объединение выполнялось только в случае необходимости.

Теперь мой вопрос: возможно ли динамически добавить это объединение в запрос LINQ?

После нескольких попыток заставить это работать, мне, к сожалению, пришлось прибегнуть к шаблону "вырезать и вставить", который мне очень не нравится, но мне нужно что-то, что сработало бы, чтобы "выйти на улицу".

Приведенный ниже код является выдержкой с переменными, переименованными в то, что у меня есть в данный момент (то есть это на самом деле не приложение библиотеки!):

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();
}

Мне пришлось создать пример кода в Notepad++, и потому что это надуманный пример, я не смог проверить, компилируется ли он. Должен сделать хотя (я надеюсь!).

2 ответа

Решение

Если есть свойство навигации от Library в Category Вы можете просто динамически добавить другой предикат:

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

Если свойство навигации отсутствует, вы все равно можете использовать соединение в предикате, не дублируя весь запрос. Но это может быть хорошей причиной для добавления свойства навигации.

Вы можете использовать Reflection 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);
Другие вопросы по тегам