实体框架自动外键人口

时间:2015-11-24 22:13:46

标签: c# entity-framework

有没有办法强制实体框架在将实体添加到上下文时立即填充外键,而不是将其推迟到上下文发生其他情况?使用数据绑定显示引用的实体时,此默认行为不是很有用。

只需引用上下文中的任何DbSet即可强制EF填充已添加的Parent的{​​{1}}和Parent_Name。但是,Children似乎迫使EF填充SaveChangesReference

我真的很想用Reference_Name ttribute标记Reference_Name,因此它在数据库中会[Required],但如果我这样做,那么当我这样做时,我会收到验证错误尝试拨打Not Null,除非我已明确设置SaveChanges,即使设置了参考,Reference_Name本身也会正确填充SaveChanges

我真的希望能够设置Reference_NameReference并能够立即使用另一个。同样,我希望能够在添加Reference_Name对象后立即使用ParentParent_Name,而无需先使用从上下文中访问其他元素的kludge

任何人都可以帮助我理解为什么EF会延迟这些事情,以及我如何强制它填充外键属性或外键列,最好是立即但至少不必调用Child?当EF要正确填充它们时,我真的不想完全填充所有属性。

SaveChanges

1 个答案:

答案 0 :(得分:2)

  

有没有办法强制Entity Framework在将实体添加到上下文时立即填充外键,而不是在上下文发生其他事件之前将其延迟?

选项1:

如果您只想修复实体之间的关系,而不将其保存到数据库,因此调用DbContext.SaveChanges(),然后只需调用DbContext.ChangeTracker.DetectChanges()

选项2:

EF可以自动修复entites之间的关系,而无需调用DbContext.SaveChanges()DbContext.ChangeTracker.DetectChanges()。这些entites称为代理类。代理类是动态生成的派生类型,充当实体的代理。此代理会覆盖实体的某些虚拟属性,以插入用于在访问属性时自动执行操作的挂钩。默认情况下会为DbContext启用代理创建,除非您通过调用DbContext.Configuration.ProxyEnabled = false;将其停用。您不需要添加该行代码,因为您需要启用代理创建。

无论如何,在使用此功能之前,您需要修改课程中的一些内容:

  • 所有属性(标量,导航,集合)必须标记为虚拟
  • 所有导航集合必须声明为ICollection<T>
  • 所有实体实例必须使用DbContext.DbSet<T>.Create()方法完成。
  • 不能将所有集合实例化初始化为构造函数。代理类将负责实例化,如果你不遵循这一点,将抛出异常。

按照这些步骤,您的entites类必须如下所示:

public class Parent
{
    [Key, MaxLength(30)]
    public virtual string Name { get; set; }

    [InverseProperty("Parent")]
    public virtual ICollection<Child> Children { get; set; }
}

public class Child
{
    [Key, Column(Order = 1), MaxLength(30)]
    public virtual string Parent_Name { get; set; }

    [Key, Column(Order = 2), MaxLength(30)]
    public virtual string Name { get; set; }

    public virtual string Reference_Name { get; set; }

    [ForeignKey("Parent_Name")]
    public virtual Parent Parent { get; set; }

    [ForeignKey("Reference_Name")]
    public virtual Reference Reference { get; set; }

    public Child Clone()
    {
        return new Child
        {
            Parent_Name = this.Parent_Name,
            Name = this.Name,
            Reference_Name = this.Reference_Name,
            Parent = this.Parent,
            Reference = this.Reference
        };
    }
}

public class Reference
{
    [Key, MaxLength(30)]
    public virtual string Name { get; set; }
}

public class SomethingElse
{
    [Key, MaxLength(30)]
    public virtual string Name { get; set; }
}

您的点击事件处理程序impelmentation将如下所示:

Reference reference = context.References.Create();
reference.Name = "Reference";
context.References.Add(reference);

Parent alpha = context.Parents.Create();
alpha.Name = "Alpha"; 
context.Parents.Add(alpha);

Child alphaOne = context.Children.Create();
alphaOne.Name = "AlphaOne";

Child alphatwo = context.Children.Create();
alphatwo.Name = "AlphaTwo";
alphatwo.Reference = reference; // Notice we use the navigational property.

alpha.Children.Add(alphaOne);
alpha.Children.Add(alphatwo);
alphaOne.Reference = reference;

var list = (
        from child in alpha.Children
        select new
        {
            Time = "Before referencing SomethingElses.Local",
            Child = child.Clone()
        }
    ).ToList();

var x = context.SomethingElses.Local;

list.AddRange(
        from child in alpha.Children
        select new
        {
            Time = "After referencing SomethingElses.Local",
            Child = child.Clone()
        }
    );

list.AddRange(
        from parent in context.Parents.Local
        from child in parent.Children
        select new
        {
            Time = "Before SaveChanges",
            Child = child.Clone()
        }
    );

context.SaveChanges();

list.AddRange(
        from parent in context.Parents.Local
        from child in parent.Children
        select new
        {
            Time = "After SaveChanges",
            Child = child.Clone()
        }
    );

foreach (var item in list)
{
    Console.WriteLine("{0}:\r\n\tName = '{1}'\r\n\tParent = '{2}' ({3})\r\n\tReference = '{4}' ({5})",
        item.Time, item.Child.Name, item.Child.Parent_Name, item.Child.Parent, item.Child.Reference_Name, item.Child.Reference);
}

此实施中有两个明显的变化:

  • 如上所述,您必须使用DbContext.DbSet.Create来获取生成的T代理的实例,而不是使用通过代理传递的默认构造函数。
  • 关于延迟加载的一件事是检查是否加载了导航属性。如果没有,则检查数据库以加载实体。在您的情况下,所有实体都处于已添加状态,然后执行Reference_Name = "Reference"将无法帮助您的上下文延迟加载导航属性Refererence。这就是为什么而不是alphatwo.Reference_Name = "Reference";我执行alphatwo.Reference = reference;因为reference处于Added状态,而Lazy Load将在数据库中找不到任何内容。