views:

42

answers:

1

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 tryed

p => ((TParcel)p).ParcelId but with no luck.

Thanks

A: 

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
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
@Davi: I’ve updated the answer.
Timwi
@Davi: Also, I noticed a mistake I made in the first part. Corrected.
Timwi