2011-06-02 111 views
4

實體框架並沒有認識到,路由對象轉換爲字符串specifix我的ToString方法:x.ToString()不被實體框架支持!

public IEnumerable<Route> GetRoutes(string prefix, string searchPattern, string code) 
{ 
    return Routes. 
     Where(x => code == null || x.Code == code). 
     Where(x => searchPattern == null || x.ToString().Contains(searchPattern)). 
     Where(x => prefix == null || x.ToString().StartsWith(prefix)); 
} 

這裏是我的路線實體:

public class Route : IDataEntity, ISoftDeletable 
    { 
     public virtual long Id { get; set; } 
     public virtual string Code { get; set; } 
     public virtual bool IsDeleted { get; set; } 
     public virtual Guid CompanyId { get; set; } 
     public virtual IList<LocationInRoute> Locations { get; set; } 

     public override string ToString() 
     { 
      StringBuilder str = new StringBuilder(); 
      foreach (LocationInRoute loc in Locations) 
      { 
       if (str.Length > 0) 
       { 
        str.Append(" > "); 
       } 
       str.Append(loc.ToString()); 
      } 
      return str.ToString(); 
     } 
    } 

所有x.ToString()拋出不支持的異常linq to entities .. 任何解決方法?

+2

請定義'Route'類您需要將它與「searchPattern」或「prefix」進行比較的屬性 – 2011-06-02 10:05:43

+0

可能有助於提供有關Route的更多信息。 – tofutim 2011-06-02 10:05:44

+7

請記住,您在實體查詢中鍵入的C#代碼永遠不會執行!相反,表達式樹會被評估並轉換爲SQL語句。 ToString()如何轉換爲SQL? – MattDavey 2011-06-02 10:06:09

回答

2

你不能做到這一點在客戶端(ToString)。

您必須創建SQL函數來完成您的評估 - 它可以只執行字符串連接(我相信它是相關的實體),也可以在自定義函數中執行整個測試。然後,您可以將該功能導入您的實體模型(EDMX)並創建EdmFunctionAttribute來調用它 - MSDN

+0

我明白你的想法,但是我的問題與EdmFuncitons的代碼是不正確的地方。它應該在Route類中,而不是在SQL服務器或描述edmx的xml上。將所有「有問題」的功能放在db或xml上的方法有問題。 – Naor 2011-06-02 10:35:53

+2

@Naor:我理解你的擔憂,但不幸的是這是不可能的。您必須決定是否希望在數據庫或應用程序服務器上執行您的邏輯 - 您不能執​​行組合執行。執行不能通過它的物理邊界=>或者使用我描述的數據庫方法,或者加載所有的路由並使用你的方法使用linq-to-objects。 – 2011-06-02 10:38:52

+1

@Naor:即使你能夠將查詢重寫爲純粹的LINQ到實體,你也可以確定這個邏輯不會在你的實體中,而是在查詢本身中。 – 2011-06-02 10:40:36

1

您需要指定Route類的哪個屬性需要與searchPatternprefix進行比較。您不能在您的方案中隱式執行.ToString()

+0

但我需要比較噸字符串..你是否建議添加一個屬性,存儲ToString到實體?? – Naor 2011-06-02 10:17:27

+0

在路由類我重寫ToString。 – Naor 2011-06-02 10:18:02

+0

@Naor,你可以做一個getter而不是重寫'ToString'嗎? – 2011-06-02 10:39:02

1

我認爲你必須有一個名爲preperty要與searchPattern,然後比較IDName使用:

.Where(x => searchPattern == null || x.Name.Contains(searchPattern)); 

因爲我asuuming是x表明一個實體,所以,怎麼辦你想比較一下它的名字本身和搜索模式嗎?

編輯:

看到的問題的變化之後,它仍然無法使用ToString()方法在此查詢,因爲它不能轉換爲SQL語句。

現在,你有兩種選擇:

第一種:(我不知道是否適用取決於可疑數據大小),嘗試調用Where之前加載使用ToList()擴展數據庫中的所有記錄延期。 這應該很好,但它可能會導致大表的問題。

第二個:您必須創建一個存儲過程並將邏輯移動到數據庫。

+0

與searchPattern的比較應該是ToString()的結果。 – Naor 2011-06-02 10:19:18

+0

@Naor:我編輯了我的答案。 – Homam 2011-06-02 10:22:40

+0

改爲ToList我可以使用AsEnumerablt,但正如您所寫,這可能會將許多對象加載到.Net。 scont選項並不是更好,因爲我只是將應用程序轉換爲使用EF4,而我想要保留所有存儲過程的東西 - 存儲過程的邏輯是糟糕的設計。 – Naor 2011-06-02 10:30:54

2

實體框架無法識別將路由對象轉換爲特定字符串的ToString方法。

沒錯。實體框架無法將您在c#中編寫的方法轉換爲sql。

實體框架將表達式轉換爲sql。有時這些表達式代表對方法的調用(如Queryable.Where),Entity Framework知道那些特定的.net框架方法以及如何將它們轉換爲sql。

你如何看待數據庫新增了一個StringBuilder?


PS:這或查詢中的標準是一種可怕的方式。你不應該構造一個查詢來統一它們。取而代之的是,有條件地構造查詢:

IQueryable<Route> query = Routes 

if (code != null) 
{ 
    query = query.Where(x => x.Code == code) 
} 
if (searchPattern != null) 
{ 
    query = query.Where(x => x.Locations.Any(loc => loc.Name.Contains(searchPattern))) 
} 
if (prefix != null) 
{ 
    query = query.Where(x => x.Locations.First().Name.StartsWith(prefix)); 
} 
+1

我知道這個問題!我不希望數據庫與StringBuilder打交道,但我希望有些東西可以解決這些問題。你有答案嗎? – Naor 2011-06-02 10:32:11

0

This postthis post可能會提供一些幫助。請注意,第二個鏈接中的建議方法不適用於LINQ to Entities,因爲它使用了ToString。爲了使工作與更換CreateLike方法:

private static Expression<Func<T, bool>> CreateLike<T>(PropertyInfo prop, string value) 
{ 
    var parameter = Expression.Parameter(typeof(T)); 
    Expression instanceExpression = Expression.MakeMemberAccess(parameter, prop);    
    if(prop.PropertyType != typeof(System.String)) 
    { 
     var cast = Expression.Convert(instanceExpression, typeof(double?)); 
     MethodInfo toString = typeof(SqlFunctions).GetMethods().First(m => m.Name == "StringConvert" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(double?)); 
     instanceExpression = Expression.Call(toString, cast);     
    } 
    var like = Expression.Call(instanceExpression, "Contains", null, Expression.Constant(value, typeof(string))); 
    return Expression.Lambda<Func<T, bool>>(like, parameter); 
} 
+0

因此,他們在兩年後成功解決問題? :) – Naor 2013-07-09 17:35:23

+0

我不知道是否解決方案,只是解決方法。但是,當我在帖子發佈兩年後遇到同樣的問題時,他們幫助我。 :) – acarlon 2013-07-11 03:00:13

1

ToString()被(從6.1版本起)實體框架的新版本支持

見EF 6.1 Release Notes

什麼在EF6.1中

EF6.1增加了以下新功能:
[...]

  • 支持的ToString,String.Concat和LINQ查詢枚舉HasFlags。

(好吧,這個問題已經是幾年的歷史,但也許這信息可能幫助別人...... 也見相關問題How to use ToString SelectListItem with Entity Framework?