【问题标题】:Expression generated based on interface基于接口生成的表达式
【发布时间】:2011-04-06 12:51:23
【问题描述】:

我遇到了异常 无法将类型“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,因为我的存储库不能是 &lt;out T&gt;

ParcelId 是 Int32
TParcel 是 typeof(ParcelToReceive) 是一个实现IParcel的实体,是用codeonly生成的

当我调用 Get 时出现问题,生成的 lambda 看起来像

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

而不是

($p.SomeInterfaceMember)

所以,实体框架尝试进行强制转换并抛出异常。我想知道的是:无论如何告诉linq lambda 字段p.ParcelId 来自TParcel 而不是来自IParcel

已经尝试过(没有运气):

p => ((TParcel)p).ParcelId 

谢谢

【问题讨论】:

  • 您的问题令人困惑且措辞不佳。请改写。
  • 我认为这个问题有道理并且足够清楚。

标签: c# entity-framework lambda code-reuse


【解决方案1】:

恐怕你不能这样做,因为从根本上说你正在访问接口中声明的属性。 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));
}

【讨论】:

  • 谢谢,可悲但有帮助。那么,你如何重用这种类型的代码呢?我创建了一个存储库,其中有一个名为 GetAllFromTheParcel(int parcelId) 的方法,但这还不够,因为我将 lambda 用于一堆其他东西
  • @Davi:另外,我注意到我在第一部分中犯了一个错误。已更正。
  • 我只是想澄清一下,当 EF Core 尝试将其转换为 SQL 时,这种方法不起作用。
【解决方案2】:

似乎将通用约束从where TAccount : IAccount 设置为where TAccount : class, IAccount 之类的东西会告诉实体框架该表达式包含一个,并且它不会像原始类型那样进行显式转换EDM 和枚举类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-21
    • 2013-03-29
    • 1970-01-01
    • 2012-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多