【问题标题】:C# EF6 mapping not applied on selectC# EF6 映射未应用于选择
【发布时间】:2018-04-28 00:11:39
【问题描述】:

我遇到了 EF6 中的 Fluent API 映射问题。一切都设置好了,但由于某种原因,每当我选择一个对象时,它都会丢失子对象。

让我们从DbContext开始:

public partial class PMSContext : DbContext
{
    public PMSContext() : base(nameOrConnectionString: "PmsDb")
    {
        this.Configuration.ProxyCreationEnabled = false;
        this.Configuration.LazyLoadingEnabled = false;
    }

    public DbSet<Employee> employees { get; set; }
    public DbSet<Project> projects { get; set; }
    public DbSet<ProjectStep> projectSteps { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

        modelBuilder.Entity<Employee>().HasMany(e => e.Projects).WithMany(t => t.EmployeesWorkingOnProject).Map(m =>
        {
            m.MapLeftKey("EmployeeId");
            m.MapRightKey("ProjectId");
            m.ToTable("employee_project");
        });

        modelBuilder.Entity<ProjectStep>().HasRequired(p => p.Project).WithMany(s => s.ProjectSteps).Map(m => m.MapKey("Project")).WillCascadeOnDelete(true);
        //modelBuilder.Entity<Project>().HasMany(p => p.ProjectSteps).WithRequired(ps => ps.Project).WillCascadeOnDelete(true);

        modelBuilder.Entity<Employee>().HasOptional<Project>(e => e.LeaderOfProject).WithOptionalPrincipal(p => p.ProjectLeader).Map(m => m.MapKey("ProjectLeader"));

    }

    public Project FindProjectById(int id)
    {
        return this.projects.Find(id);
    }
}

这几乎是我的问题所需要的一切。

我一共设置了3个模型类:

[DataContract(Namespace = "Shared")]
public class Employee
{
    public Employee()
    {
        this.Projects = new List<Project>();
    }

    [DataMember]
    public int ID { get; set; }

    [DataMember]
    public String Name { get; set; }

    [DataMember]
    public String JobDescription { get; set; }

    [DataMember]
    public String Department { get; set; }

    [DataMember]
    public String DirectDialing { get; set; }

    [DataMember]
    public bool Status { get; set; }

    public virtual Project LeaderOfProject { get; set; }

    [DataMember]
    public virtual List<Project> Projects { get; set; }
}

[DataContract(Namespace = "Shared")]
public class Project
{

    public Project()
    {
        this.EmployeesWorkingOnProject = new List<Employee>();
        this.ProjectSteps = new List<ProjectStep>();
    }

    [DataMember]
    public int ID { get; set; }

    [DataMember]
    public String Titel { get; set; }

    [DataMember]
    public DateTime StartDate { get; set; }

    [DataMember]
    public DateTime EndDate { get; set; }

    [DataMember]
    public String Description { get; set; }

    [DataMember]
    public Employee ProjectLeader { get; set; }

    [DataMember]
    public bool Status { get; set; }

    [DataMember]
    public virtual List<Employee> EmployeesWorkingOnProject { get; set; }

    [DataMember]
    public virtual List<ProjectStep> ProjectSteps { get; set; }
}

[DataContract(Namespace = "Shared")]
[Table("project_step")]
public class ProjectStep
{
    [DataMember]
    public int ID { get; set; }

    [DataMember]
    public String Description { get; set; }

    [DataMember]
    public DateTime StartDate { get; set; }

    [DataMember]
    public DateTime EndDate { get; set; }

    [DataMember]
    public Project Project { get; set; }
}

以及对应的数据库设置:

现在我的问题。每当我执行FindProjectById 方法时,它都会返回正确的对象,但它缺少对子对象的任何引用。这意味着

  • ProjectSteps
  • EmployeesWorkingOnProject
  • ProjectLeader

未设置。这也会导致我的删除方法出现问题。我认为这是我的 OnModelCreating 方法中的错误,但我不是 100% 确定。

谁能告诉我在获取子对象时我还缺少什么?

【问题讨论】:

  • 数据库中是否也创建了外键约束?
  • 您已禁用延迟加载和代理,但如果您不使用 .Include() 子实体,则 EF 不知道加载它们。要使用 .Include(),您需要使用 .SingleOrDefault() 而不是 Find。否则,您将需要转到上下文以加载子集合/引用。
  • @StevePy 我需要 Include() 并启用代理 = false 和延迟加载吗?我删除了this.Configuration.LazyLoadingEnabled = false; 行,但现在它仍然没有加载子实体。我也尝试过启用代理,但这会导致错误提示 Connection was closed by remote host
  • 您可以在没有代理的情况下使用 .Include(),您的 FindProjectById() 看起来像: var project = this.Projects.Include(x=>x.ProjectLeader).Include(x=>x .EmployeesWorkingOnProject).Include(x=>x.ProjectSteps).SingleOrDefault(x=>x.ID == id);返回项目;通常,您不希望返回 DbContext 范围之外的实体,因为延迟加载代理将不起作用,因此您未预加载的任何内容都将是 #null。我通常依赖延迟执行,返回 IQueryable 然后 .Select() 我关心的各种位到 DTO/ViewModel POCO 类中。
  • @StevePy 非常感谢。它工作得很好。如果您愿意,可以将您的 cmets 转换为答案,我会接受它

标签: c# .net entity-framework orm entity-framework-6


【解决方案1】:

您已禁用延迟加载和代理,但如果您不使用 .Include() 子实体,则 EF 不知道加载它们。要使用 .Include(),您需要使用 .SingleOrDefault() 而不是 Find。否则,您将需要转到上下文以加载子集合/引用。

您的 FindProjectById() 看起来像:

var project = this.Projects.Include(x=>x.ProjectLeader)
  .Include(x=>x.EmployeesWorkingOnProject)
  .Include(x=>x.ProjectSteps)
  .SingleOrDefault(x=>x.ID == id); 
return project; 

使用 SingleOrDefault 与 Find 的一个警告是 Find 将搜索本地存储然后转到 DB,Single/First/ 等将转到 DB。这意味着每次 Find 可能在本地内存存储中找到实​​体时都会执行查询。如果您将记录插入到数据库上下文中(并且在保存更改之前),搜索该实体将不会返回本地存储中但尚未提交到数据库的实体。 (在 SaveChanges() 之前)

通常您不希望返回 DbContext 范围之外的实体,因为延迟加载代理将不起作用,因此您未预加载的任何内容都将是 #null。我通常依赖延迟执行,然后将 IQueryable 返回 .Select() 我关心的各个位到 DTO/ViewModel POCO 类中。

【讨论】:

    【解决方案2】:

    如果要自动加载子对象,可以尝试将延迟加载改为true

    this.Configuration.LazyLoadingEnabled = true;

    如果延迟加载为假,则需要在访问之前加载引用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-23
      相关资源
      最近更新 更多