【问题标题】:EF Core. How to extract (left) joined table as collection?EF 核心。如何提取(左)连接表作为集合?
【发布时间】:2020-07-22 13:50:32
【问题描述】:

我需要帮助将连接表提取为上下文 linq-syntax 表达式中的集合。

public class Computer
{
   public int Id { get; set; }
   public string Name { get; set; }
}

public class Component
{
   public int Id { get; set; }
   public string Name { get; set; }

   public int ComputerId { get; set; } //FK
   public Computer Computer { get; set; } //HasOne(Computer).WithMany().HasFK(ComputerId)
}

计算机没有组件的反向引用

需要通过Linq-syntax选择有相关组件的电脑。

(from computer in db.Computers
join component in db.Components on computer.Id equals component.ComputerId into components //|  JOINED
from component in components.DefaultIfEmpty()                                              //|  TABLE
select new ComputerFullData
{
   Computer = computer,
   Components = components // <-- collection
})
........other code

此 linq 不起作用并生成 NullRefException

但是,如果 Computer 具有 ICollection backref 属性,那么我可以通过调用 Include(x = > x.Components) 实际上是 revert-JOIN 就像我的例子一样。在这种情况下,我可能无法访问组件集合。

我怎样才能通过 Linq 达到同样的效果?

【问题讨论】:

    标签: c# .net entity-framework linq entity-framework-core


    【解决方案1】:

    这在 EF Core 中非常适合我:

    public class Computer
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    
    public class Component
    {
        public int Id { get; set; }
        public string Name { get; set; }
    
        public int ComputerId { get; set; }
        public Computer Computer { get; set; }
    }
    
    public class Db : DbContext
    {
        public static readonly ILoggerFactory MyLoggerFactory
            = LoggerFactory.Create(builder => builder.AddConsole());
    
        public DbSet<Component> Components { get; set; }
    
        public DbSet<Computer> Computers { get; set; }
    
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseLoggerFactory(MyLoggerFactory)
                .UseSqlServer(@"Server=(localdb)\mssqllocaldb; Database=demo;Integrated Security=True");
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            using (var db = new Db())
            {
                db.Database.EnsureCreated();
    
                db.Components.Add(
                    new Component
                    {
                        Computer = new Computer
                        {
                            Name = "Fred"
                        },
                        Name = "Fred component"
                    });
    
                db.SaveChanges();
            }
    
            using (var db = new Db())
            {
                var computersWithComponents =
                    (from c in db.Computers
                     select new
                     {
                         Computer = c,
                         Components = (from cp in db.Components
                                       where cp.ComputerId == c.Id
                                       select cp).ToList()
                     }).ToList();
            }
        }
    }
    

    这很像您可能用 SQL 编写的内容。我很高兴地发现在这样的子查询中引用上下文 (db) 是 EF 查询提供程序可以接受的,并且生成的 SQL 类似于我可能手写的内容:

    SELECT [c].[Id], [c].[Name], [c0].[Id], [c0].[ComputerId], [c0].[Name]
           FROM [Computers] AS [c]
           LEFT JOIN [Components] AS [c0] ON [c].[Id] = [c0].[ComputerId]
           ORDER BY [c].[Id], [c0].[Id]
    

    出于兴趣——我知道这不是你问题的直接答案——你为什么拒绝在你的模型上使用Components 的导航属性?我经常在 EF 中发现,只做有效的事情而不是过分努力地获得我想要的模型是有帮助的。 (如果我对我需要的对象的形状有非常具体的要求,我可能有两个类层次结构 - 一个对 EF 惯用的 EF 数据库模型,以及我的首选模型,以及在两者之间转换的方法。)无论如何,在在这种情况下,得到你想要的东西并不难,所以在这里可能会有点矫枉过正。

    【讨论】:

    • 感谢您的努力。我在示例中使用了 EF Core 3。事实上,问题不在于请求本身,而在于接下来发生的事情。接下来使用了AutoMapper,没有正确配置。停止使用它后,我注意到请求正在正确构建和执行。
    猜你喜欢
    • 1970-01-01
    • 2017-05-22
    • 2019-09-15
    • 2021-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-17
    相关资源
    最近更新 更多