Динамически добавить 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);