2012-04-09 161 views
59

我有這樣的LINQ查詢:LINQ到實體無法識別方法 'System.String格式(System.String,System.Object的,System.Object的)'

private void GetReceivedInvoiceTasks(User user, List<Task> tasks) 
{ 
    var areaIds = user.Areas.Select(x => x.AreaId).ToArray(); 

    var taskList = from i in _db.Invoices 
        join a in _db.Areas on i.AreaId equals a.AreaId 
        where i.Status == InvoiceStatuses.Received && areaIds.Contains(a.AreaId) 
        select new Task { 
         LinkText = string.Format(Invoice {0} has been received from {1}, i.InvoiceNumber, i.Organisation.Name), 
         Link = Views.Edit 
        }; 
} 

它有問題,雖然。我正在嘗試創建任務。對於每個新任務,當我將鏈接文本設置爲像「Hello」這樣的常量字符串時,這很好。然而,上面我試圖使用發票的屬性來構建屬性linktext。

我得到這個錯誤:

base {System.SystemException} = {"LINQ to Entities does not recognize the method 'System.String Format(System.String, System.Object, System.Object)' method, and this method cannot be translated into a store expression."}

任何人都知道爲什麼嗎?任何人都知道這樣做的另一種方式,使其工作?

+0

是,錯過了這一點原本 – AnonyMouse 2012-04-09 21:21:58

+0

可能重複[LINQ到實體無法識別方法「System.String的ToString()」方法(http://stackoverflow.com/questions/4121863/linq- to-entities-does-not-recognized-the-method-system-string-tostring-method) – 2013-12-12 13:32:23

回答

115

實體框架試圖在SQL端執行您的投影,其中沒有等效於string.Format。使用AsEnumerable()來強制用Linq對對象評估該部分。

基於on the previous answer我已經給了你,我會調整你的查詢是這樣的:

int statusReceived = (int)InvoiceStatuses.Received; 
var areaIds = user.Areas.Select(x=> x.AreaId).ToArray(); 

var taskList = (from i in _db.Invoices 
       where i.Status == statusReceived && areaIds.Contains(i.AreaId) 
       select i) 
       .AsEnumerable() 
       .Select(x => new Task() 
       { 
        LinkText = string.Format("Invoice {0} has been received from {1}", x.InvoiceNumber, x.Organisation.Name), 
        Link = Views.Edit 
       }); 

而且我看到你在查詢中使用相關實體(Organisation.Name),請確保您添加適當的Include您的查詢,或具體兌現以備以後使用這些屬性,即:

var taskList = (from i in _db.Invoices 
       where i.Status == statusReceived && areaIds.Contains(i.AreaId) 
       select new { i.InvoiceNumber, OrganisationName = i.Organisation.Name}) 
       .AsEnumerable() 
       .Select(x => new Task() 
       { 
        LinkText = string.Format("Invoice {0} has been received from {1}", x.InvoiceNumber, x.OrganisationName), 
        Link = Views.Edit 
       }); 
+0

除了由於表達式樹翻譯而導致select-new-task部分不能發生服務器端的事實之外,它也應該是指出這樣做是不可取的。據推測,你想要創建客戶端的任務。因此,查詢和創建任務的分離可能更加明確。 – Tormod 2012-04-09 21:54:06

+3

我還建議選擇一個匿名類型,它只需要其中的InvoiceNumber和Organisation.Name。如果發票實體很大,那麼使用隨後的AsEnumerable選擇我將拉回每一列,即使您只使用兩個。 – Devin 2012-04-10 21:45:07

+1

@Devin:是的,我同意 - 事實上,這正是第二個查詢示例正在做的事情。 – BrokenGlass 2012-04-10 21:47:47

14

IQueriable自IEnumerable派生,主要的相似之處是,當你讓你的查詢其發佈到數據庫引擎在它的郎在這個時刻,你可以告訴C#處理服務器上的數據(而不是客戶端)或告訴SQL處理數據。

所以基本上當你說IEnumerable.ToString()時,C#獲取數據集合並在對象上調用ToString()。 但是,當你說IQueriable.ToString()C#告訴SQL調用對象的ToString(),但SQL中沒有這樣的方法。

缺點是,當您在C#中處理數據時,您正在查看的整個集合必須在C#應用篩選器之前在內存中構建。

最有效的方法是使用您可以應用的所有過濾器將查詢設置爲IQueriable。

然後在內存中構建它並在C#中進行數據格式化。

IQueryable<Customer> dataQuery = Customers.Where(c => c.ID < 100 && c.ZIP == 12345 && c.Name == "John Doe"); 

var inMemCollection = dataQuery.AsEnumerable().Select(c => new 
                { 
                c.ID 
                c.Name, 
                c.ZIP, 
                c.DateRegisterred.ToString("dd,MMM,yyyy") 
                }); 
相關問題