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. 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.
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. 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:
#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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With