
I'm geting the exception <strong>Unable to cast the type 'MySomeTypeThatImplementsISomeInterfaceAndIsPassedAs[T]ToTheClass' to type 'ISomeInterface'. LINQ to Entities only supports casting Entity Data Model primitive types.</strong>
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>
<strong>ParcelId is Int32</strong> 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 <strong>TParcel</strong> and not from <strong>IParcel</strong>.
Already tried (with no luck):
p => ((TParcel)p).ParcelId
Thanks
Answer1:
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 <strong>class</strong> and it will not make the explicit cast that it would do for primitive EDM and enum types.
Answer2:
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));
}