简体   繁体   English

Linq:如何针对关联对象使用规范

[英]Linq: how to use specifications against associated objects

I'm using specifications in this kind of form: 我正在使用这种形式的规格:

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

Now I can use this specification in the form: 现在我可以在表单中使用此规范:

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

But I'm not sure how to use the specification against an associated object like this: 但是我不确定如何对这样的关联对象使用规范:

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

Is there a way to do this, or do I need to rethink my implementation of specifications? 有没有办法做到这一点,还是我需要重新考虑我的规范实施?

Essentially, you need to create an Expression<Func<Login, bool>> that collects the associated User from a Login and then applies the existing IsSuperhero predicate on that user. 实际上,您需要创建一个Expression<Func<Login, bool>> ,它从Login中收集关联的User,然后在该用户上应用现有的IsSuperhero谓词。 The canonical way to accomplish this is to use Expression.Invoke on the 'contained' expression ( IsSuperHero in this case), replacing its parameters with appropriate arguments. 实现此目的的规范方法是在“包含”表达式(本例中为IsSuperHero )上使用Expression.Invoke ,用适当的参数替换其参数。

Unfortunately, this approach is quite messy to do by hand. 不幸的是,这种方法手工操作相当混乱。 Worse, many LINQ providers, such as LINQ to Entities, don't like this sort of 'expression inside an expression' approach at all. 更糟糕的是,许多LINQ提供程序,例如LINQ to Entities,根本不喜欢这种“表达式内部表达”方法。 The way around this is to 'inline' the 'invoked' expression into the bigger expression so that it all looks like a single , giant, expression-tree. 解决这个问题的办法是“内联”的“调用”表达成更大的表达,使这一切看起来像一个单一的 ,巨大的,表达树。

Fortuantely, there's the handy library LINQKit that can help out with this: Fortuantely,有一个方便的库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);

Obviously: 明显:

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

This can be fun: 这很有趣:

var secretBillionaires = workspace.GetDataSource<User>()
   .Where(UserSpecifications.IsSuperhero)
   .SelectMany(user => user.Logins)
   .Where(LoginSpecifications.IsSecretIdentity)
   .Select(login => login.DayJob)
   .Where(DayJobSpecifications.IsBillionaire)

I believe you need to compile and then invoke the expression: 我相信你需要编译然后调用表达式:

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

An alternative might be to pre-compile the expression: 另一种方法是预编译表达式:

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

您可以创建自己的自定义QueryProvider,如下所述: http//msdn.microsoft.com/en-us/library/bb546158.aspx

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM