2010-08-31 80 views
2

我正在創建例外 無法強制類型'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

產生當我打電話找並將所得拉姆達看起來像

(**(ISomeInterface)**$p).SomeInterfaceMember == 
出現的問題

而不是

($p.SomeInterfaceMember) 

所以,實體框架試着做投並拋出異常。我想知道的是:無論如何告訴linq lambda字段p.ParcelId是從TParcel而不是從IParcel

已經嘗試過(有沒有運氣):

p => ((TParcel)p).ParcelId 

感謝

+0

你的問題很混亂,措辭不當。請重新說明。 – jeroenh 2010-08-31 22:52:21

+0

我認爲這個問題很有意義,並且已經足夠清晰。 – Timwi 2010-08-31 23:00:55

回答

4

似乎從where TAccount : IAccount設置泛型約束像where TAccount : class, IAccount告訴實體框架該表達式包含並且它不會對原始EDM和枚舉類型進行顯式強制轉換。

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

謝謝,傷心但有幫助。 那麼,你怎麼做才能重用這種類型的代碼?我創建了一個名爲GetAllFromTheParcel(int parcelId)的方法庫但是這還不夠,因爲我使用lambda來處理其他一些事情 – 2010-09-01 01:18:12

+0

@Davi:我已經更新了答案。 – Timwi 2010-09-01 10:44:53

+0

@Davi:另外,我注意到我在第一部分中犯的一個錯誤。糾正。 – Timwi 2010-09-01 10:47:25