Linq: как использовать спецификации для связанных объектов - PullRequest
5 голосов
/ 08 ноября 2011

Я использую спецификации в таком виде:

public static Expression<Func<User, bool>> IsSuperhero
{
  get
  {
    return x => x.CanFly && x.CanShootLasersFromEyes;
  }
}

Теперь я могу использовать эту спецификацию в виде:

var superHeroes = workspace.GetDataSource<User>().Where(UserSpecifications.IsSuperhero);

Но я не уверен, как использовать спецификацию для связанного объекта, подобного этому:

var loginsBySuperheroes = workspace.GetDataSource<Login>().Where(x => x.User [ ??? ]);

Есть ли способ сделать это, или мне нужно переосмыслить мою реализацию спецификаций?

Ответы [ 4 ]

4 голосов
/ 08 ноября 2011

По сути, вам нужно создать Expression<Func<Login, bool>>, который собирает ассоциированного пользователя из логина и затем применяет существующий предикат IsSuperhero к этому пользователю.Канонический способ сделать это - использовать Expression.Invoke в выражении «в составе» (в данном случае IsSuperHero), заменив его параметры соответствующими аргументами.

К сожалению, такой подход довольно сложен длярука.Хуже того, многим поставщикам LINQ, таким как LINQ to Entities, вообще не нравится такой подход «выражение внутри выражения».Чтобы обойти это, нужно «встроить» вызванное выражение в большее выражение, чтобы оно выглядело как single , гигантское дерево выражений.

К счастью, есть удобныйбиблиотека LINQKit , которая может помочь с этим:

#region LINQKit Magic

Expression<Func<Login, bool>> predicate = login => IsSuperHero.Invoke(login.User);
var expandedPredicate = predicate.Expand(); 

#endregion LINQKit Magic

var loginsBySuperheroes = workspace.GetDataSource<Login>().Where(expandedPredicate);
2 голосов
/ 08 ноября 2011

Очевидно:

var loginsBySuperheroes = workspace.GetDataSource<User>()
  .Where(UserSpecifications.IsSuperhero)
  .SelectMany(x => x.Logins);

Это может быть весело:

var secretBillionaires = workspace.GetDataSource<User>()
   .Where(UserSpecifications.IsSuperhero)
   .SelectMany(user => user.Logins)
   .Where(LoginSpecifications.IsSecretIdentity)
   .Select(login => login.DayJob)
   .Where(DayJobSpecifications.IsBillionaire)
1 голос
/ 08 ноября 2011

Я считаю, что вам нужно скомпилировать, а затем вызвать выражение:

var loginsBySuperheroes = GetLogins().Where(l => IsSuperhero.Compile().Invoke(l.User));

Альтернативой может быть предварительная компиляция выражения:

var f = IsSuperhero.Compile();
var loginsBySuperheroes = GetLogins().Where(l => f(l.User));
0 голосов
/ 08 ноября 2011

Вы можете создать свой собственный QueryProvider, как подробно описано здесь: http://msdn.microsoft.com/en-us/library/bb546158.aspx

...