Expression generated based on interface

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)); }

人吐槽 人点赞

Recommend

Comment

用户名: 密码:
验证码: 匿名发表

你可以使用这些语言

查看评论:Expression generated based on interface