【问题标题】:LINQ: Multi table/ join + Group byLINQ:多表/连接+分组
【发布时间】:2021-02-25 23:22:12
【问题描述】:

我有一组非常不寻常的表,我需要为其编写一个 Linq 查询并返回一个特定的自定义模型,以下是实体及其关系

    public class CustomerAccount
    {
        public Guid Id { get; set; }
        public virtual Shop Shop { get; set; }
        public Guid ShopId { get; set; }
        public virtual Account Account { get; set; }
        public Guid AccountId { get; set; }
        public string MyAreaName { get; set; }
        public virtual ICollection<CustomerAccountTag> CustomerAccountTags { get; set; }

    }

    public class Shop
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
    }

    public class Account
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
    }

    public class CustomerAccountTag
    {
        public Guid CustomerAccountId { get; set; }
        public string TagId { get; set; }
        public virtual CustomerAccount CustomerAccount { get; set; }
        public virtual Tag Tag { get; set; }
    }

    public class Tag
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public virtual ICollection<CustomerAccountTag> CustomerAccountTags { get; set; }
    }


CustomerAccount 包含客户列表以及他们的商店和帐户详细信息,以及一些元数据。

它将包含如下记录

{
  "CustomerAccount": [
    {
      "Id": "1",
      "ShopId": "Shop1",
      "AccountId": "AccountA"
    },
    {
      "Id": "2",
      "ShopId": "Shop1",
      "AccountId": "AccountB"
    },
    {
      "Id": "3",
      "ShopId": "Shop2",
      "AccountId": "AccountC"
    }
  ]
}

CustomerAccountTag 表只是 CustomerAccount 和 Tags 表之间的链接表,标签是分配给每个 CustomerAccount 行的字符串。

我需要以下输出, 按商店 ID 分组(因此上面的示例将有两行数据,一行用于 shop1,另一行用于 shop2) 每行将有以下作为数据


{
  ShopName
  ShopId
  Accounts = "custom list of accounts per shop, with Account Id, Account Name, AreaName and a list of related Tags"
}


以下是我如何完成它并且它可以工作,但仅适用于 LINQ lambda,问题是我不能“分组”,除非我将连接的表作为列表返回,然后我可以处理它。否则它会抱怨它无法在服务器上运行查询,需要在客户端上进行评估!


        var queryResults = (from e in _applicationDbContext.CustomerAccount
                           .Include(g => g.Shop)
                           .Include(y => y.Account)
                           .Include(a => a.CustomerAccountTag)
                           .ThenInclude(t => t.Tag)
                           select e).ToList();

            var result = queryResults.GroupBy(x => x.Shop)
                         .Select(y => new CustomAccountResponse
                         {
                             ShopId = y.Key.Id,
                             ShopName = y.Key.Name,
                             Accounts = y.Select(x => new { y.Account.Name, y.Account.Id, y.AreaName, string.Join(",", y.CustomerAccountTags.Select(x => x.Tag.Name)) })
                         });


想知道我是否可以用 Linq 做同样的事情,但在数据库上运行所有内容,而不是一半在客户端,一半在客户端!

更新:

我认为下面的示例代码可以解释这个问题,也许 EFCore 不支持我的要求


using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp2
{

    public static class Program
    {
        public static void Main()
        {
            using (var context = new ApplicationDbContext())
            {
                context.Database.EnsureDeleted();
                context.Database.EnsureCreated();

                var shop1 = new Shop { Name = "Shop1" };
                var shop2 = new Shop { Name = "Shop2" };
                var shop3 = new Shop { Name = "Shop3" };
                var shop4 = new Shop { Name = "Shop4" };
                var shop5 = new Shop { Name = "Shop3" };

                List<Shop> shop = new List<Shop>() { shop1, shop2, shop3, shop4, shop5 };

                context.Shops.AddRange(shop);
                context.SaveChanges();
            }

            using (var context = new ApplicationDbContext())
            {

                var query = from shop in context.Shops
                            group shop by shop.Name into grouped
                            select new
                            {
                                Id = grouped.Key,
                                Count = grouped.Count(),
                                Items = grouped
                            };
            }

            /*
            Processing of the LINQ expression 'GroupByShaperExpression:
            KeySelector: s.Name, 
            ElementSelector:EntityShaperExpression: 
            EntityType: Shop
            ValueBufferExpression: 
            ProjectionBindingExpression: EmptyProjectionMember
            IsNullable: False
            ' by 'RelationalProjectionBindingExpressionVisitor' failed. This may indicate either a bug or a limitation in Entity Framework. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.
            */
            
            Console.ReadLine();

        }
    }
    public class Shop
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
    }

    public class ApplicationDbContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=testdb;Trusted_Connection=True;MultipleActiveResultSets=true");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
        }

        public DbSet<Shop> Shops { get; set; }

    }

}


我真的可以在我的查询中获得分组项目的列表吗? 这正是发生以下异常的地方


            /*
            Processing of the LINQ expression 'GroupByShaperExpression:
            KeySelector: s.Name, 
            ElementSelector:EntityShaperExpression: 
            EntityType: Shop
            ValueBufferExpression: 
            ProjectionBindingExpression: EmptyProjectionMember
            IsNullable: False
            ' by 'RelationalProjectionBindingExpressionVisitor' failed. This may indicate either a bug or a limitation in Entity Framework. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.
            */

【问题讨论】:

    标签: c# sql linq linq-to-sql entity-framework-core


    【解决方案1】:

    您已创建正确的查询。没有简单的方法可以使用 EF 在服务器上完全运行此查询。您可以在此处执行的操作 - 限制从服务器检索的数据并强制 EF 生成最佳查询。

    var query = 
       from a in _applicationDbContext.CustomerAccount
       from t in a.CustomerAccountTags
       select new 
       {
          Shop = new { a.Shop.Id, a.Shop.Name },
          Account = new { a.Account.Name, a.Account.Id, a.AreaName },
          TagName = t.Tag.Name
       };
    
    var result = 
       from q in query.AsEnumerable()
       group q by q.Shop into g
       select new CustomAccountResponse
       {
           ShopId = g.Key.Id,
           ShopName = g.Key.Name,
           Accounts = g.GroupBy(x => x.Account).Select(x => new 
              { 
                 x.Key.Account.Name, 
                 x.Key.Account.Id, 
                 x.Key.Account.AreaName, 
                 Tags = string.Join(",", x.Select(y => y.TagName))
              }).ToList()
       };
     
    

    【讨论】:

    • 非常感谢,是的,您的方法有效。也许我对 EFCore 支持的内容感到困惑,我已经用示例代码更新了我的帖子,请您看看是否能解释这个问题?
    • 你不能那样做。项目无法选择stackoverflow.com/questions/64377955/…
    猜你喜欢
    • 2017-09-19
    • 2010-11-21
    • 1970-01-01
    • 1970-01-01
    • 2010-11-24
    • 1970-01-01
    • 2013-01-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多