簡體   English   中英

實體框架與 LINQ 聚合連接字符串?

[英]Entity Framework with LINQ aggregate to concatenate string?

這對我來說在 TSQL 中很容易執行,但我只是坐在這里用頭撞桌子試圖讓它在 EF4 中工作!

我有一張表,我們稱它為 TestData。 它有字段,比如說:DataTypeID、Name、DataValue。

DataTypeID, Name, DataValue
1,"Data 1","Value1"
1,"Data 1","Value2"
2,"Data 1","Value3"
3,"Data 1","Value4"

我想對 DataID/Name 進行分組,並將 DataValue 連接成一個 CSV 字符串。 期望的結果應該包含 -

DataTypeID, Name, DataValues
1,"Data 1","Value1,Value2"
2,"Data 1","Value3"
3,"Data 1","Value4"

現在,這就是我正在嘗試的方式 -

var query = (from t in context.TestData
  group h by new { DataTypeID = h.DataTypeID, Name = h.Name } into g
  select new
 {
   DataTypeID = g.Key.DataTypeID,
   Name = g.Key.Name,
   DataValues = (string)g.Aggregate("", (a, b) => (a != "" ? "," : "") + b.DataValue),
 }).ToList()

問題是 LINQ to Entities 不知道如何將其轉換為 SQL。 這是 3 個 LINQ 查詢聯合的一部分,我真的很希望它保持這種狀態。 我想我可以檢索數據,然后再執行聚合。 出於性能原因,這對我的應用程序不起作用。 我還考慮過使用 SQL 服務器函數。 但這在 EF4 世界中似乎並不“正確”。

有人願意嘗試一下嗎?

如果ToList()是您原始查詢的一部分,而不只是為這個示例添加的,則使用 LINQ to Objects 在結果列表上進行聚合:

var query = (from t in context.TestData
            group t by new { DataTypeID = t.DataTypeID, Name = t.Name } into g 
            select new { DataTypeID = g.Key.DataTypeID, Name = g.Key.Name, Data = g.AsEnumerable()})
            .ToList()
            .Select (q => new { DataTypeID = q.DataTypeID, Name = q.Name, DataValues = q.Data.Aggregate ("", (acc, t) => (acc == "" ? "" : acc + ",") + t.DataValue) });

在 LINQPad 中測試並產生以下結果:

替代文字

一些答案建議調用 ToList(),然后將計算作為 LINQ to OBJECT 執行。 這對於少量數據來說很好,但是如果我有大量數據不想過早加載到內存中怎么辦,那么 ToList() 可能不是一個選項。

因此,更好的想法是在表示層處理/格式化數據,讓數據訪問層只加載或保存 SQL 喜歡的原始數據。 此外,在您的表示層中,很可能您正在通過分頁過濾數據,或者您可能在詳細信息頁面中顯示一行,因此,您將加載到內存中的數據可能小於您從數據庫中加載的數據. (您的情況/架構可能不同,..但我是說,很有可能)。

我有一個類似的要求。 我的問題是從實體框架對象中獲取項目列表並創建一個格式化字符串(逗號分隔值)

  1. 我在我的視圖模型中創建了一個屬性,它將保存存儲庫中的原始數據,並且在填充該屬性時,LINQ 查詢不會成為問題,因為您只是查詢 SQL 理解的內容。

  2. 然后,我在我的 ViewModel 中創建了一個只讀屬性,它讀取原始實體屬性並在顯示之前格式化數據。

     public class MyViewModel { public IEnumerable<Entity> RawChildItems { get; set; } public string FormattedData { get { if (this.RawChildItems == null) return string.Empty; string[] theItems = this.RawChildItems.ToArray(); return theItems.Length > 0? string.Format("{0} ( {1} )", this.AnotherRegularProperty, String.Join(", ", theItems.Select(z => z.Substring(0, 1)))): string.Empty; } } }

好的,通過這種方式,我無需調用 ToList() 即可輕松地將數據從 LINQ to Entity 加載到此視圖模型。

例子:

IQueryable<MyEntity> myEntities = _myRepository.GetData();

IQueryable<MyViewModel> viewModels = myEntities.Select(x => new MyViewModel() { RawChildItems = x.MyChildren })

現在,我可以在需要時隨時調用 MyViewModel 的 FormattedData 屬性,並且只有在調用該屬性時才會執行 Getter,這是此模式(惰性處理)的另一個好處。

架構建議:我強烈建議讓數據訪問層遠離所有格式或視圖邏輯或 SQL 不理解的任何內容。

您的實體框架類應該是簡單的 POCO,無需任何特殊映射器即可直接映射到數據庫列。 您的數據訪問層(比如使用 LINQ to SQL 從 DbContext 獲取數據的存儲庫)應該只獲取直接存儲在數據庫中的數據。 沒有多余的邏輯。

然后,您應該為您的表示層(例如 ViewModel)設置一組專用的類,其中將包含用於格式化用戶喜歡看到的數據的所有邏輯。 這樣,您就不必為 Entity Framework LINQ 的限制而苦惱。 我永遠不會將我的實體框架模型直接傳遞給視圖。 也不,我會讓我的數據訪問層為我創建 ViewModel。 創建 ViewModel 可以委托給您的域服務層或應用層,它比您的數據訪問層上層。

感謝 moi_meme 的回答。 我希望使用 LINQ to Entities 做的事情是不可能的。 正如其他人所建議的那樣,您必須使用 LINQ to Objects 才能訪問字符串操作方法。

有關詳細信息,請參閱 moi_meme 發布的鏈接。

更新 8/27/2018 - 更新鏈接(再次) - https://web.archive.org/web/20141106094131/http://www.mythos-rini.com/blog/archives/4510

而且由於我對 8 年前的僅鏈接答案持懷疑態度,所以我會澄清以防存檔副本有一天消失。 它的基本要點是您不能在 EF 查詢中訪問 string.join。 您必須創建 LINQ 查詢,然后調用 ToList() 才能對數據庫執行查詢。 然后您將數據保存在內存中(又名 LINQ to Objects),因此您可以訪問 string.join。

上面引用鏈接中的建議代碼如下 -

var result1 = (from a in users
                b in roles
           where (a.RoleCollection.Any(x => x.RoleId = b.RoleId))
           select new 
           {
              UserName = a.UserName,
              RoleNames = b.RoleName)                 
           });

var result2 = (from a in result1.ToList()
           group a by a.UserName into userGroup
           select new 
           {
             UserName = userGroup.FirstOrDefault().UserName,
             RoleNames = String.Join(", ", (userGroup.Select(x => x.RoleNames)).ToArray())
           });

作者進一步建議用 aggregate 替換 string.join 以獲得更好的性能,就像這樣 -

RoleNames = (userGroup.Select(x => x.RoleNames)).Aggregate((a,b) => (a + ", " + b))

你已經非常接近了。 試試這個:

var query = (from t in context.TestData
  group h by new { DataTypeID = h.DataTypeID, Name = h.Name } into g
  select new
 {
   DataTypeID = g.Key.DataTypeID,
   Name = g.Key.Name,
   DataValues = String.Join(",", g),
 }).ToList()

或者,如果 EF 不允許String.Join (Linq-to-SQL 允許),您可以這樣做:

var qs = (from t in context.TestData
  group h by new { DataTypeID = h.DataTypeID, Name = h.Name } into g
  select new
 {
   DataTypeID = g.Key.DataTypeID,
   Name = g.Key.Name,
   DataValues = g
 }).ToArray();

var query = (from q in qs
            select new
            {
                q.DataTypeID,
                q.Name,
                DataValues = String.Join(",", q.DataValues),
            }).ToList();

也許在數據庫上為此創建一個視圖(為您連接字段)然后讓 EF 使用此視圖而不是原始表是個好主意?

我很確定這在 LINQ 語句或映射詳細信息中是不可能的。

暫無
暫無

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

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