LINQ to Entities only supports casting EDM primitive or enumeration types (unique new post)

entity-framework entity-framework-6 expression-trees

Question

Preface

Regarding this specific problem, I counted around 20 questions, but none of them seemed to apply. By writing my expressions programmatically rather than using literal lambda syntax, I'm doing something new. This might be exposing more issues, in my opinion.

The code that follows makes use of both my own UnitOfWork class and a few of my helpers. I won't go into all of it here for the sake of conciseness, but I will share any further code upon request.

the query

I'm attempting to provideIQueryable activities on aSelectMany . When I type the phrases out literally, it works. However, I'm not able to utilize literal expressions since I need to be able to generate everything dynamically.

For comments, see the code below.

Unable to cast the type 'IQueryable`1[[SystemAssociateModel]]' to type 'IEnumerable`1[[SystemAssociateModel]]'.
LINQ to Entities only supports casting EDM primitive or enumeration types.

☺

public static void SelectManyTest()
{
    int id = 14690;

    var p = Expression.Parameter(typeof(SystemEntitlement));

    var projector = ExpressionHelpers.GetLambda<SystemAssociate, SystemAssociateModel>(x => new SystemAssociateModel
    {
        role = x.Role.Name,
        id = x.Associate.Id,
        order = x.Order
    });

    var memberPath = ExpressionHelpers.GetLambda<SystemEntitlement, ICollection<SystemAssociate>>(
        x => x.System.Associates).Body.AsMemberExpression().ReplaceInstance(p);

    //These two calls equate to this:  x.System.Associates.AsQueryable().Select(projector)
    var call_asQueryable = Expression.Call(ExpressionHelpers.GetMethodInfo(() => Queryable.AsQueryable<SystemAssociate>(null)), memberPath);
    Expression call_select = Expression.Call(ExpressionHelpers.GetMethodInfo(
        () => Queryable.Select(default(IQueryable<SystemAssociate>), default(Expression<Func<SystemAssociate, SystemAssociateModel>>))),
        call_asQueryable, projector);

    //I use this in an attempt to cast my `Select` into `IEnumerable` for `SelectMany`.  This
    //is most likely where the issue is occurring.  It makes sense that only specific types of
    //casts would be supported within an expression.
    //call_select = Expression.Convert(call_select, typeof(IEnumerable<SystemAssociateModel>));

    //I have to use the uncommented line since that's the only one suitable for `.SelectMany`.
    //This is the reason for the cast above, to convert `Select`'s `IQueryable` into an
    //`IEnumerable` for `SelectMany`.  If I don't use the explicit cast, it will attempt an
    //implicit cast and still fail.
    //var selector = (Expression<Func<SystemEntitlement, IQueryable<SystemAssociateModel>>>)Expression.Lambda(call_select, masterp);
    var selector = (Expression<Func<SystemEntitlement, IEnumerable<SystemAssociateModel>>>)Expression.Lambda(call_select, p);

    //This works so long as I write the `.SelectMany` expression literally.  I seems that the compiler is somehow
    //able to handle the implicit cast from `ICollection` to `IEnumerable`.
    var associates1 = UnitOfWork.UseWith(work => work.GetRepo<SystemEntitlement>().AsQueryable()
    .Where(x => x.Id == id)
    .SelectMany(x => x.System.Associates.AsQueryable().Select(projector))
    .Where(x => x.order == 1)
    .ToArray());

    //This throws the error in question.
    var associates2 = UnitOfWork.UseWith(work => work.GetRepo<SystemEntitlement>().AsQueryable()
    .Where(pred)
    .SelectMany(selector)
    .Where(x => x.order == 1)
    .ToArray());
}
1
1
11/22/2017 12:34:01 AM

Accepted Answer

Such casts are not supported by EF, therefore noExpression.Convert must be applied.

The real issue is thatExpression.Lambda(call_select, p) returns Expression<Func<T, IQueryable<R>>> which you're attempting to castExpression<Func<T, IEnumerable<R>>> . ButExpression Because variance is not supported by any C# class, the cast fails as predicted.

Utilizing is the answer isExpression.Lambda method overloads with a result-type parameter that let you select your preference Your example may look something like this:

var selector = Expression.Lambda<Func<SystemEntitlement, IEnumerable<SystemAssociateModel>>>(
    call_select, p);

Once you do those steps, the issue will be resolved.

As an aside, there is no need for performing transformations while creating expressions dynamically.AsQueryable employing this technique, the C# compiler is tricked into allowing the usage of collection navigation properties.Expression<Func<...>> a variable included in what you term literal expressions in anticipate methodsFunc<...> When you utterExpression.Call You might just dial the relevant number manuallyEnumerable method:

var call_select = Expression.Call(ExpressionHelpers.GetMethodInfo(
    () => Enumerable.Select(default(IEnumerable<SystemAssociate>), default(Func<SystemAssociate, SystemAssociateModel>))),
    memberPath, projector);

I'm assuming the anticipated arguments of yours here.ExpressionHelpers.GetMethodInfo . Personally, I refer to such general techniques as:

var call_select = Expression.Call(
    typeof(Enumerable), "Select", new[] { typeof(SystemAssociate), typeof(SystemAssociateModel) },
    memberPath, projector);

No doubtAsQueryable It not damage EF6, however EF Core does not support it since it is redundant.

2
11/22/2017 10:41:15 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow