5

I'm geting the exception Unable to cast the type 'MySomeTypeThatImplementsISomeInterfaceAndIsPassedAs[T]ToTheClass' to type 'ISomeInterface'. LINQ to Entities only supports casting Entity Data Model primitive types.

my repository looks like

public interface IRepository<T>
{
    IQueryable<T> Get(System.Linq.Expressions.Expression<System.Func<T, bool>> Query);
}

Also, I have the service class

public abstract class FinanceServiceBase<TAccount, TParcel, TPayment>
    where TAccount : IAccount
    where TParcel : IParcel
    where TPayment : IPayment
 {
     //...
     IRepository<TPayment> ParcelRepository {get; private set;}         

     public bool MakePayment(TPayment payment)
     {
         //...
         ParcelRepository.Get(p => p.ParcelId == 2);
         // here my exception is thrown
         // **p.ParcelId is in IParcel**
         //...
     }
 }
 //...

With this class I can control many things about finances without rewrite code for other programs. I've did the class with 3 generic parameters because I couldn't use IRepository because my repository can't be <out T>

ParcelId is Int32
TParcel is typeof(ParcelToReceive) that is an entity who implement IParcel, and was generated with codeonly

The problem occurs when I call Get and the resultant lambda looks like

(**(ISomeInterface)**$p).SomeInterfaceMember == 

instead of

($p.SomeInterfaceMember)

so, entity framework try do the cast and throws the exception. What I want to know is: is there anyway to tell linq that the lambda field p.ParcelId is from TParcel and not from IParcel.

Already tried (with no luck):

p => ((TParcel)p).ParcelId 

Thanks

Rui Jarimba
  • 11,166
  • 11
  • 56
  • 86
Davi Fiamenghi
  • 1,237
  • 12
  • 28

2 Answers2

6

It seems that setting the generic constraints from where TAccount : IAccount to something like where TAccount : class, IAccount tells entity framework that the expression contains a class and it will not make the explicit cast that it would do for primitive EDM and enum types.

Davi Fiamenghi
  • 1,237
  • 12
  • 28
3

I’m afraid you can’t do this because fundamentally you are accessing the property that is declared in the interface. LINQ-to-Entities doesn’t seem to support that; you need to call the property in the real entity type.

One way you could solve this is by passing the parcelId property as an expression tree in a parameter, and then construct a lambda expression dynamically at runtime using the property in that parameter:

public bool MakePayment(TPayment payment,
                        Expression<Func<TParcel, int>> parcelIdExpr)
{
    // You can use any expression involving parcelId here
    Expression<Func<int, bool>> expr = parcelId => parcelId == 2;

    // This is the parameter of the new lambda we’re creating
    var parameter = Expression.Parameter(typeof(TParcel));

    // This constructs the lambda expression “p => expr(p.ParcelId)”,
    // where “expr” is the lambda expression declared above
    var lambda = Expression.Lambda(Expression.Invoke(expr,
        Expression.Invoke(parcelIdExpr, parameter)), parameter);

    ParcelRepository.Get((Expression<Func<TParcel, bool>>) lambda);
}

[...]

myFinanceService.MakePayment(myPayment, p => p.ParcelId);

If you don’t want to have to pass this extra parameter every time you call MakePayment, then you could in theory retrieve the property by name with a string literal; however, this is unsafe because it doesn’t ensure that it’s the right property which implements the interface. Also, this is a very roundabout way of doing it, so no guarantees:

public bool MakePayment(TPayment payment)
{
    Expression<Func<int, bool>> expr = parcelId => parcelId == 2;

    var parameter = Expression.Parameter(typeof(TParcel));

    // This is the expression “p.ParcelId”, where “p” is the parameter
    var propertyExpression = Expression.Property(parameter, "ParcelId");

    var lambda = Expression.Lambda(Expression.Invoke(expr, propertyExpression),
                                   parameter);

    ParcelRepository.Get((Expression<Func<TParcel, bool>>) lambda);
}

You can factor this out into a generic utility method:

public static class Utils
{
    public static Expression<Func<TParameter, TResult>>
        CombineLambdas<TParameter, T, TResult>(
            Expression<Func<TParameter, T>> lambda1,
            Expression<Func<T, TResult>> lambda2
        )
    {
        var parameter = Expression.Parameter(typeof(TParameter));
        var lambda = Expression.Lambda(Expression.Invoke(lambda2,
            Expression.Invoke(lambda1, parameter)), parameter);
        return (Expression<Func<TParameter, TResult>>) lambda;
    }
}

public bool MakePayment(TPayment payment,
                        Expression<Func<TParcel, int>> parcelIdExpr)
{
    ParcelRepository.Get(Utils.CombineLambdas(
        parcelIdExpr, parcelId => parcelId == 2));
}
Timwi
  • 65,159
  • 33
  • 165
  • 230
  • Thanks, sad but helpfull. so, how do you do to reuse this type of code? I made a repository that have a method called GetAllFromTheParcel(int parcelId) But that isn't enough since i use lambda to a bunch of other things – Davi Fiamenghi Sep 01 '10 at 01:18
  • @Davi: Also, I noticed a mistake I made in the first part. Corrected. – Timwi Sep 01 '10 at 10:47
  • I just want to clearify that this approach is NOT working when EF Core is trying to translate it to SQL. – Teneko Jul 16 '20 at 06:33