我正在寻找例外 无法将类型'MySomeTypeThatImplementsISomeInterfaceAndIsPassedAs [T] ToTheClass'强制转换为'ISomeInterface'。 LINQ to Entities仅支持转换实体数据模型基元类型。
我的存储库看起来像
public interface IRepository<T>
{
IQueryable<T> Get(System.Linq.Expressions.Expression<System.Func<T, bool>> Query);
}
另外,我有服务类
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**
//...
}
}
//...
通过这门课,我可以控制很多关于财务的事情而无需重写其他程序的代码。我用3个通用参数做了这个类,因为我无法使用IRepository,因为我的存储库不能是<out T>
ParcelId是Int32
TParcel是typeof( ParcelToReceive ),是实现 IParcel 的实体,并且是使用codeonly生成的
当我调用Get并且生成的lambda看起来像
时,会出现问题(**(ISomeInterface)**$p).SomeInterfaceMember ==
而不是
($p.SomeInterfaceMember)
所以,实体框架尝试执行转换并抛出异常。我想知道的是:无论如何告诉linq lambda字段p.ParcelId来自 TParcel 而不是来自 IParcel 。
已经尝试过(没有运气):
p => ((TParcel)p).ParcelId
由于
答案 0 :(得分:4)
似乎将where TAccount : IAccount
中的通用约束设置为像where TAccount : class, IAccount
这样的东西告诉实体框架该表达式包含类,并且它不会使显式转换为它可以用于原始EDM和枚举类型。
答案 1 :(得分:2)
我担心你不能这样做,因为从根本上说你正在访问界面中声明的属性。 LINQ-to-Entities似乎不支持这一点;你需要在真实实体类型中调用属性。
解决此问题的一种方法是将parcelId
属性作为表达式树传递给参数,然后使用该参数中的属性在运行时动态构造lambda表达式:
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);
如果您不希望每次调用MakePayment
时都必须传递此额外参数,那么理论上您可以使用字符串文字按名称检索属性;但是,这是不安全的,因为它不能确保它是实现接口的正确属性。此外,这是一种非常迂回的方式,所以不能保证:
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);
}
您可以将其分解为通用实用程序方法:
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));
}