繁体   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