jokulmorder jokulmorder - 22 days ago 8
C# Question

Type error trying to call Any() using Expression<Func<..>> from method

I am creating a reusable mapping expression from my EF entity to my BLL entity:

private Expression<Func<Person, bool>> GetNameFilter(string name)
{
return person => person.Profile.UserName == name;
}

internal Expression<Func<Ef.Perk, Bll.Perk>> MapPerkPerk(string name)
{
return perk => new Bll.Perk
{
Id = perk.PerkId,
Name = perk.PerkName,
Description = perk.PerkDescription,

//Compilation Error
Owned = perk.Players.Any(GetNameFilter(name))
};
}


And I am getting a compilation error on the noted line. The error reads:


ICollection does not contain a definition for 'Any' and the best extension method overload 'Queryable.Any(IQueryable, Expression>)' requires a receiver of type 'IQueryable'


But this does not happen when I push this expression in directly:

internal Expression<Func<Ef.Perk, Bll.Perk>> MapPerkPerk(string name)
{
return perk => new Bll.Perk
{
Id = perk.PerkId,
Name = perk.PerkName,
Description = perk.PerkDescription,

//No Compilation Error
Owned = perk.Players.Any(person => person.Profile.UserName == name)
};
}


Why is this happening? The type of both expressions is the same.

Using the LinqExpression solution found below, I am now getting the following error at runtime:


An exception of type 'System.InvalidCastException' occurred in LinqKit.dll but was not handled in user code

Additional information: Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpressionN' to type 'System.Linq.Expressions.LambdaExpression'.


internal FutureQuery<Perk> GetPlayerInfoPerks(string username)
{
return Master
.Perks
.VisiblePerks
.Select(Master.Perks.MapPerkPerk(username))
.Future();
}


Is this due to the use of the EntityFramework.Future library?

Answer

Why is this happening? The type of both expressions is the same.

The type of both expressions is not the same - they just look visually the same. The following is valid:

dbContext.Persons.Any(GetNameFilter(name))

and this is not:

perk.Players.Any(GetNameFilter(name))

Why? Because the first expects Expression<Func<...>> while the second - just Func<..> (the typical difference between IQueryable<T> and IEnumerable<T> methods with the same name). Hope you see the difference. When you type it directly, the C# compiler does its magic to emit one or the another, but when you do that manually, you are supposed to use the correct one.

The problem is addressed by LinqKit package with Invoke / Expand custom extension methods (and more generally with AsExpandable).

For your concrete example, the LinqKit solution could be like this:

using LinqKit;

...

internal Expression<Func<Ef.Perk, Bll.Perk>> MapPerkPerk(string name)
{
    // LinqKit requires expressions to be in variables
    var nameFilter = GetNameFilter(name);
    return Linq.Expr((Ef.Perk perk) => new Bll.Perk
    {
        Id = perk.PerkId,
        Name = perk.PerkName,
        Description = perk.PerkDescription,
        Owned = perk.Players.Any(p => nameFilter.Invoke(p)) 
    }).Expand();
}