簡體   English   中英

基於接口生成的表達式

[英]Expression generated based on interface

收到異常無法將類型“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 

謝謝

似乎將通用約束從where TAccount : IAccountwhere TAccount : class, IAccount告訴實體框架表達式包含一個類,並且它不會對原始 EDM 和枚舉類型進行顯式轉換。

恐怕您不能這樣做,因為從根本上說,您正在訪問接口中聲明的屬性。 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));
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM