簡體   English   中英

在LINQ-to-Entities中鍵入成員支持?

[英]Type member support in LINQ-to-Entities?

我有一個使用Entity Framework模型的MVC3項目,我在其中標記了這樣一個類:

public partial class Product
{
    public bool IsShipped
    {
        get { /* do stuff */ }
    }
}

我想在LINQ表達式中使用它:

db.Products.Where(x => x.IsShipped).Select(...);

但是,我收到以下錯誤:

用戶代碼未處理System.NotSupportedException消息= LINQ to Entities中不支持指定的類型成員'IsShipped'。 僅支持初始值設定項,實體成員和實體導航屬性。 來源= System.Data.Entity的

我用谷歌搜索但沒有發現任何關於這種用法的確定性我嘗試過:

public partial class Product
{
    public bool IsShipped()
    {
        /* do stuff */
    }
}

db.Products.Where(x => x.IsShipped()).Select(...);

但后來我得到:

System.NotSupportedException未由用戶代碼處理Message = LINQ to Entities無法識別方法'Boolean IsShipped()'方法,並且此方法無法轉換為商店表達式。
來源= System.Data.Entity的

那里有功能,我不想構建LINQ查詢本身...什么是處理這個的好方法?

*更新*

Darin提出了有效的觀點,即在IsShipped的實現中所做的任何事情都需要轉換為SQL查詢,並且編譯器可能不知道如何執行它,因此將所有對象檢索到內存中似乎是唯一的選擇(除非直接查詢到數據庫)。 我試過這樣的:

IEnumerable<Product> xp = db.Quizes
    .ToList()
    .Where(x => !x.IsShipped)
    .Select(x => x.Component.Product);

但它會生成此錯誤:

發生了關系多重性約束違規:EntityReference只能有一個相關對象,但查詢返回了多個相關對象。 這是一個不可恢復的錯誤。

雖然好奇但這有效:

IEnumerable<Product> xp = db.Quizes
    .ToList()
    .Where(x => x.Skill.Id == 3)
    .Select(x => x.Component.Product);

為什么會這樣?

*更新II *

對不起,最后一句話也不起作用......

*更新III *

我正在關閉這個問題,轉而采用這里建議的解決方案,將我的邏輯壓縮成一個查詢 - 討論將轉移到這個新帖子 將整個原始查詢檢索到內存中的第二種方法可能是不可接受的,但第三種方法是將邏輯實現為對數據庫的直接查詢,仍有待探索。

感謝大家的寶貴意見。

使這個“DRY”(避免再次在Where子句中重復IsShipped中的邏輯)並避免在應用過濾器之前將所有數據加載到內存中的唯一方法是將IsShipped的內容提取到表達式中。 然后,您可以將此表達式用作WhereIsShipped參數。 例:

public partial class Product
{
    public int ProductId { get; set; }           // <- mapped to DB
    public DateTime? ShippingDate { get; set; }  // <- mapped to DB
    public int ShippedQuantity { get; set; }     // <- mapped to DB

    // Static expression which must be understood
    // by LINQ to Entities, i.e. translatable into SQL
    public static Expression<Func<Product, bool>> IsShippedExpression
    {
        get { return p => p.ShippingDate.HasValue && p.ShippedQuantity > 0; }
    }

    public bool IsShipped // <- not mapped to DB because readonly
    {
        // Compile expression into delegate Func<Product, bool>
        // and execute delegate
        get { return Product.IsShippedExpression.Compile()(this); }
    }
}

您可以像這樣執行查詢:

var result = db.Products.Where(Product.IsShippedExpression).Select(...).ToList();

在這里,您只有一個地方可以將邏輯放入( IsShippedExpression ),然后將其用於數據庫查詢和IsShipped屬性。

我會這樣做嗎? 在大多數情況下可能沒有,因為編譯表達式很慢。 除非邏輯非常復雜,否則可能會發生變化,而且我處於使用IsShipped的性能無關緊要的情況下,我會重復邏輯。 始終可以將常用的過濾器提取到擴展方法中:

public static class MyQueryExtensions
{
    public static IQueryable<Product> WhereIsShipped(
        this IQueryable<Product> query)
    {
        return query.Where(p => p.ShippingDate.HasValue && p.ShippedQuantity >0);
    }
}

然后以這種方式使用它:

var result = db.Products.WhereIsShipped().Select(...).ToList();

雖然維護邏輯有兩個地方: IsShipped屬性和擴展方法,但是你可以重用它。

我猜IsShipped沒有映射到數據庫中的字段? 這可以解釋為什么Linq to Entities會抱怨 - 它無法構建基於此屬性的sql語句。

是你/* do stuff */根據在數據庫領域內的財產? 如果是這樣,您可以在.Where()使用該邏輯。

您可以先通過調用.ToList()使用結果,然后在客戶端執行過濾:

var result = db.Products.ToList().Where(x => x.IsShipped).Select(...);

當然,您應該意識到,通過這樣做,您可能會降低應用程序的性能,因為數據庫正在做得最好。

那里有功能,我不想構建LINQ查詢本身...什么是處理這個的好方法?

我假設您的意思是要執行與DB無關的查詢。 但是你的代碼與你的意圖不符。 看看這一行:

db.Products.Where(x => x.IsShipped()).Select(...);

db.Products的部分意味着您要查詢數據庫。

要解決此問題,請先在內存中設置實體。 然后你可以使用Linq來對象:

List<Product> products = db.Products
    .Where(x => x.SomeDbField == someValue)
    .ToList();

// Todo: Since the DB doesn't know about IsShipped, set that info here

// ...

var shippedProducts = products
    .Where(x => x.IsShipped())
    .Select(...);

.ToList()完成初始數據庫查詢,並為您提供內存表示,以便根據您的喜好進行操作和修改。 在那之后,您可以使用非數據庫屬性。

請注意,如果您在ToList之后執行進一步的數據庫操作(例如編輯實體上的數據庫屬性,查詢導航屬性等),那么您將返回Linq to Entities land並且將無法再對Linq執行操作操作。 你不能直接混合兩者。

請注意,如果public bool IsShipped()讀取或寫入數據庫屬性或導航屬性,如果您不小心,可能會再次使用Linq to Entities。

暫無
暫無

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

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