【问题标题】:Entity Framework relation not loaded from database实体框架关系未从数据库加载
【发布时间】:2017-09-03 02:12:35
【问题描述】:

我正在使用 Entity Framework 6 Code First 并遇到问题,即多对多关系未正确同步到我的“内部”对象。

我有以下课程:

public class ClassA
{
    public String AId { get; set; }
    public ICollection<ClassB> Bs { get; set; }
    public ICollection<ClassC> Cs { get; set; }
}
public class ClassB
{
    public int BId { get; set; }
    ICollection<ClassA> As { get; set; }
}
public class ClassC
{
    public int CId { get; set; }
    public ClassA A { get; set; }
}

A 和 B 具有多对多关系,而 A 和 C 具有一对多关系。我使用 Fluent API 来构建映射:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<ClassA>().ToTable("ClassA");
    modelBuilder.Entity<ClassA>().HasKey(a => a.AId);
    modelBuilder.Entity<ClassA>().HasManyClassB>(a => a.Bs).WithMany(b => b.As).Map(
            a =>
            {
                a.MapLeftKey("AId");
                a.MapRightKey("BId");
                a.ToTable("ClassAClassB");
            });

    modelBuilder.Entity<ClassA>().Property(a => a.OrderId).IsVariableLength().HasColumnName("AId");
    modelBuilder.Entity<ClassB>().ToTable("ClassB");
    modelBuilder.Entity<ClassB>().HasKey(b => b.BId);
    modelBuilder.Entity<ClassB>().Property(b => b.BId)
         .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
         .HasColumnName("BId");
    modelBuilder.Entity<ClassC>().ToTable("ClassC");
    modelBuilder.Entity<ClassC>().HasKey(c => c.CId);
    modelBuilder.Entity<ClassC>().HasRequired(c => c.A).WithMany(a => a.Bs).Map(m => m.MapKey("AId"));
    modelBuilder.Entity<ClassC>().Property(c => c.CId)    
         .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
         .HasColumnName("CId");
}

如果我创建 ClassA、ClassB 和 ClassC 类的对象,请将它们添加到我的数据库上下文并调用 SaveChanges()。在我看来,这些值正确地存储在数据库中:

MariaDB [Test]> select * from ClassA;
+----------+
|   BId    |
+----------+
| 2017-002 |
+----------+

MariaDB [Test]> select * from ClassB;
+--------+
|   BId  |
+--------+
|    2   |
+--------+

MariaDB [OMSData]> select * from ClassAClassB;
+----------+-----+
|    AId   | BId |
+----------+-----+
| 2017-002 |  2  |
+----------+-----+

MariaDB [Test]> select * from classC;
+----+-----------+
| CId |    AId   |
+-----+----------+
|  1  | 2017-002 |
+-----+----------+

但是,如果我再次执行我的测试程序,则无法正确恢复关系。

这意味着:

  • ClassA 中 B 的集合不包含任何对象(反之亦然)。
  • 如果我事先通过我的数据库上下文访问了这个对象,我只能通过 ClassA 访问 ClassC 的对象。

我必须手动调用同步还是模型定义有错误?

提前致谢!

【问题讨论】:

  • 用于从数据库中具体化对象的代码在这里可能很有用。根据上下文配置,使用 .Include() 可能是个问题。

标签: c# database entity-framework ado.net entity-framework-6


【解决方案1】:

对于与我面临同样问题的任何人,我的问题是我在相关实体中缺少virtual 这个词。据我所知,启用延迟加载时需要virtual,这有助于实体框架正确加载导航属性。希望对您有所帮助。

【讨论】:

    【解决方案2】:

    你没有正确地构建你的课程。从逻辑上思考,您需要将多对多绑定存储在某个地方,这意味着您需要一个存储多对多关系的第三个实体类。

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    

    让你的类如下所示:

    public class ClassA
    {
        [Index(IsUnique = true)]
        [MaxLength(200)]
        public String AId { get; set; }
        public virtual List<AB> AB { get; set; }
        public List<ClassC> C { get; set; }
    }
    public class ClassB
    {
        public int BId { get; set; }
        public virtual List<AB> AB { get; set; }
    }
    public class AB
    {
        public int Id { get; set; }
        [Required]
        public virtual A A { get; set; }
        [Required]
        public virtual B B { get; set; }
        [MaxLength(200)]
        public string AId { get; set; }
        public int BId { get; set; }
    }
    public class ClassC
    {
        public int CId { get; set; }
        public ClassA A { get; set; }
        public int AId { get; set; }
    }
    

    创建代码优先迁移,实体框架将理解这一点并相应地勾画出您的数据库。仅将 A 和 B 的 ID 存储在第三个实体 AB 中。

    【讨论】:

    • 在我的基于对象的模型中真的需要这样的链接类吗?由于数据库中的结果正确,我认为实体框架会在内部创建这样一个链接类,因为 OnModelCreating 方法中的“HasMany()..WithMany()”规范。我认为我不必在基于对象的模型中明确定义此类链接类,而 Entity Framework 透明地处理此类。
    • 相信我,我刚开始时也是这么想的,不幸的是,事实并非如此。这实际上是正确的方法。
    • 好的。如果我使用这种方法,数据会加载到内存中。但是仍然存在延迟加载的问题。 EF 仅将条目添加到我已使用上下文的 DbSet 手动访问的 ClassA 和 ClassB 的 AB 列表中。是否有类似的命令:“将所有内容加载到内存中”?
    • 我没有在我的上下文配置中将条目指定为虚拟并禁用延迟加载
    • 您可能还想检查Include 方法。建议你看看我的回答here
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多