实体框架自动对外重点人群 [英] Entity Framework automatic foreign key population

查看:112
本文介绍了实体框架自动对外重点人群的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有什么办法来强制实体框架立即填充外键当一个实体加入到环境中,而不是拖延,直到别的东西的背景下发生的?使用数据绑定来显示引用的实体时,这个默认行为是不是非常有帮助。



只是引用任何 DbSet 从背景是足以迫使EF来填充 PARENT_NAME 添加孩子<中/ code>。但不折不扣的的SaveChanges 似乎迫使EF来填充参考 Reference_Name



我真的很想来标记 Reference_Name [必填] ttribute所以这将是不为空在数据库中,但如果我这样做,我得到验证错误,当我试图调用的SaveChanges ,除非我明确地设置 Reference_Name ,虽然的SaveChanges 本身会正确填充 Reference_Name ,如果参考设置。



我真的很想能够设置为参考 Reference_Name 并能够立即使用其他。同样,我希望能够立即使用 PARENT_NAME 添加后儿童对象,而不必使用访问从上下文第一一些其他元素的杂牌组装电脑。



谁能帮助我理解了为什么EF延缓这些的事情,我怎么能迫使它立即首选,但填充外键的属性或外键列,至少无需调用的SaveChanges ?我真的不喜欢要充分填充时明确EF是要正确地填充它们反正所有属性。

 公开类OracleContext:的DbContext 
{
公共虚拟DbSet<家长和GT;家长{搞定;组; }
公共虚拟DbSet<儿童>儿童{搞定;组; }
公共虚拟DbSet<参考>参考{搞定;组; }
公共虚拟DbSet< SomethingElse> SomethingElses {搞定;组; }
}

公共类父
{
[键,最大长度(30)]
公共字符串名称{;组; }

[InverseProperty(母公司)]
公共虚拟目录<儿童>儿童{搞定;组; } =新的List<儿童>();
}

公共类儿童
{
[键,列(订单= 1),最大长度(30)]
公共字符串PARENT_NAME {搞定;组; }

[键,列(顺序= 2),最大长度(30)]
公共字符串名称{;组; }

公共字符串Reference_Name {搞定;组; }

[ForeignKey的(PARENT_NAME)]
公共虚拟父父{搞定;组; }

[ForeignKey的(Reference_Name)]
公共虚拟参考参考{搞定;组; }

公共儿童的clone()
{
返回新儿童
{
PARENT_NAME = this.Parent_Name,
名称= this.Name ,
Reference_Name = this.Reference_Name,
父= this.Parent,
参考= this.Reference
};
}
}

公共类参考
{
[键,最大长度(30)]
公共字符串名称{;组; }
}

公共类SomethingElse
{
[键,最大长度(30)]
公共字符串名称{;组; }
}

私人无效的button1_Click(对象发件人,EventArgs五)
{
OracleContext背景=新OracleContext();

参考参考= context.References.Add(新参考{名称=参考});

父阿尔法= context.Parents.Add(新父{名称=阿尔法});

儿童alphaOne =新的儿童{名称=AlphaOne};
儿童alphatwo =新的儿童{名称=AlphaTwo,Reference_Name =参考};
alpha.Children.AddRange(新名单<儿童> {alphaOne,alphatwo});
alphaOne.Reference =参考;从小孩

变种名单=(
在alpha.Children
选择新的
{
时间=引用SomethingElses.Local之前,$ B $的b子= child.Clone()
}
).ToList();

变种X = context.SomethingElses.Local;

list.AddRange(从小孩
在alpha.Children
选择新的
{
时间=引用SomethingElses.Local后,$ B $的b子= child.Clone()
}
);

list.AddRange(从父
从孩子parent.Children
context.Parents.Local
选择新的
{
时间=的SaveChanges之前,
=儿童child.Clone()
}
);

context.SaveChanges();

list.AddRange(从父
从孩子parent.Children
context.Parents.Local
选择新的
{
时间=的SaveChanges后,
=儿童child.Clone()
}
);

的foreach(在列表VAR项)
{
Console.WriteLine({0}:\r\\\
\tName ='{1}'\ r\\\
\tParent ={2}({3})\r\\\
\tReference ='{4}({5}),
item.Time,项目。 Child.Name,item.Child.Parent_Name,item.Child.Parent,item.Child.Reference_Name,item.Child.Reference);
}
}

引用SomethingElses.Local前:
名称='AlphaOne'
父=''()
参考=' '(WindowsFormsApplication2.Reference)
引用SomethingElses.Local前:
名称='AlphaTwo'
父=''()
参考='参考'()

名称='AlphaOne'
父='阿尔法'(WindowsFormsApplication2.Parent)
参考=''(WindowsFormsApplication2.Reference)
引用SomethingElses后:引用SomethingElses.Local后。本地:
名称='AlphaTwo'
父='阿尔法'(WindowsFormsApplication2.Parent)
参考='参考'()
的SaveChanges之前:
名称=' AlphaOne'
父='阿尔法'(WindowsFormsApplication2.Parent)
参考=''(WindowsFormsApplication2.Reference)
的SaveChanges之前:
名称='AlphaTwo'
父='阿尔法'(WindowsFormsApplication2.Parent)
参考='参考'()
后的SaveChanges:
名称='AlphaOne'
父='阿尔法'(WindowsFormsApplication2.Parent)
参考='参考'(WindowsFormsApplication2.Reference)
后的SaveChanges:
名称='AlphaTwo'
父='阿尔法'(WindowsFormsApplication2.Parent)
参考= 参考(WindowsFormsApplication2.Reference)


解决方案

有没有什么办法来强制实体框架立即填充外键当一个实体加入到环境中,而不是拖延,直到别的东西的背景下发生的?




选项1:



如果你只是想解决实体之间的relationalship而不保存到数据库调用,因此 DbContext.SaveChanges()然后就叫 DbContext.ChangeTracker.DetectChanges()



选项2:



EF能的entites之间自动修正relationalship没有caling DbContext.SaveChanges() DbContext.ChangeTracker.DetectChanges()。这些entites的被称为代理类。代理类是动态生成的派生类型充当实体的代理。此代理覆盖插入挂钩时访问属性会自动执行操作的实体的一些虚拟财产。代理的创建默认情况下启用的的DbContext 除非你禁用它通过调用 DbContext.Configuration.ProxyEnabled = FALSE; 。你不需要,因为你需要代理的创建启用,以添加一行代码。



反正你需要利用这个特性的优势之前,修改你的类的一些东西:




  • 所有属性(标量,导航,集合)必须标记为虚拟

  • 所有导航集合必须声明为的ICollection< T>

  • 所有实体instatiation,必须通过使用 DbContext.DbSet<完成>
  • 所有收集实例不能被初始化为构造函数。代理类会照顾实例,并会抛出一个异常,如果你不遵循这一点。



在此之后的步骤你的entites类必须是这样的:

 公共类父
{
[键,最大长度(30)]
公共虚拟字符串名称{;组; }

[InverseProperty(母公司)]
公共虚拟的ICollection<儿童>儿童{搞定;组; }
}

公共类儿童
{
[键,列(订单= 1),最大长度(30)]
公共虚拟字符串PARENT_NAME {得到;组; }

[键,列(顺序= 2),最大长度(30)]
公共虚拟字符串名称{;组; }

公共虚拟字符串Reference_Name {搞定;组; }

[ForeignKey的(PARENT_NAME)]
公共虚拟父父{搞定;组; }

[ForeignKey的(Reference_Name)]
公共虚拟参考参考{搞定;组; }

公共儿童的clone()
{
返回新儿童
{
PARENT_NAME = this.Parent_Name,
名称= this.Name ,
Reference_Name = this.Reference_Name,
父= this.Parent,
参考= this.Reference
};
}
}

公共类参考
{
[键,最大长度(30)]
公共虚拟字符串名称{;组; }
}

公共类SomethingElse
{
[键,最大长度(30)]
公共虚拟字符串名称{;组; }
}

您单击事件处理程序impelmentation会是这样的:

 参考参考= context.References.Create(); 
reference.Name =参考;
context.References.Add(参考);

父阿尔法= context.Parents.Create();
alpha.Name =阿尔法;
context.Parents.Add(阿尔法);

儿童alphaOne = context.Children.Create();
alphaOne.Name =AlphaOne;

儿童alphatwo = context.Children.Create();
alphatwo.Name =AlphaTwo;
alphatwo.Reference =参考; //注意,我们使用的导航属性。

alpha.Children.Add(alphaOne);
alpha.Children.Add(alphatwo);
alphaOne.Reference =参考;从小孩

变种名单=(
在alpha.Children
选择新的
{
时间=引用SomethingElses.Local之前,$ B $的b子= child.Clone()
}
).ToList();

变种X = context.SomethingElses.Local;

list.AddRange(从小孩
在alpha.Children
选择新的
{
时间=引用SomethingElses.Local后,$ B $的b子= child.Clone()
}
);

list.AddRange(从父
从孩子parent.Children
context.Parents.Local
选择新的
{
时间=的SaveChanges之前,
=儿童child.Clone()
}
);

context.SaveChanges();

list.AddRange(从父
从孩子parent.Children
context.Parents.Local
选择新的
{
时间=的SaveChanges后,
=儿童child.Clone()
}
);

的foreach(在列表VAR项)
{
Console.WriteLine({0}:\r\\\
\tName ='{1}'\ r\\\
\tParent ={2}({3})\r\\\
\tReference ='{4}({5}),
item.Time,项目。 Child.Name,item.Child.Parent_Name,item.Child.Parent,item.Child.Reference_Name,item.Child.Reference);
}

有在此实施两个由显着的变化:




  • 为说你必须使用DbContext.DbSet.Create来获得T的生成的代理的一个实例,而不是使用默认的构造函数路过的代理权。

  • 有一点了解的延迟加载的是,它检查导航属性加载。如果没有,那么它检查数据库加载实体。在你的情况下,所有的实体都在随后加入国家做 Reference_Name =引用不会帮助你的背景下,以延迟加载导航属性 Refererence 。这是做 alphatwo.Reference_Name =参考而不是原因; 我做了 alphatwo.Reference =参考; ,因为参考添加状态,延迟加载会发现数据库一无所获。


Is there any way to force Entity Framework to populate the foreign keys immediately when an entity is added to the context, rather than delaying it until something else happens with the context? This default behavior is not very helpful when using data binding to display referenced entities.

Just referencing any DbSet from the context is enough to force EF to populate the Parent and Parent_Name of the added Children. But nothing short of SaveChanges seems to force EF to populate the Reference or Reference_Name.

I'd really like to mark Reference_Name with the [Required] ttribute so it will be Not Null in the database, but if I do that, I get validation errors when I try to call SaveChanges unless I've explicitly set the Reference_Name, even though SaveChanges itself will correctly populate Reference_Name if Reference is set.

I'd really like to be able to set either Reference or Reference_Name and be able to immediately use the other. Likewise, I'd like to be able to immediately use Parent or Parent_Name after adding the Child object, without having to use the kludge of accessing some other element from the context first.

Can anyone help me understand why EF delays these things, and how I can force it to populate the foreign key properties or the foreign key columns, preferably immediately but at least without having to call SaveChanges? I'd really prefer not to have to fully populate all the properties explicitly when EF is going to correctly populate them anyway.

public class OracleContext : DbContext
{
    public virtual DbSet<Parent> Parents { get; set; }
    public virtual DbSet<Child> Children { get; set; }
    public virtual DbSet<Reference> References { get; set; }
    public virtual DbSet<SomethingElse> SomethingElses { get; set; }
}

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

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

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

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

    public 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 string Name { get; set; }
}

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

private void button1_Click(object sender, EventArgs e)
{
    OracleContext context = new OracleContext();

    Reference reference = context.References.Add(new Reference { Name = "Reference" });

    Parent alpha = context.Parents.Add(new Parent { Name = "Alpha" });

    Child alphaOne = new Child { Name = "AlphaOne" };
    Child alphatwo = new Child { Name = "AlphaTwo", Reference_Name = "Reference" };
    alpha.Children.AddRange(new List<Child> { alphaOne, 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);
    }
}

Before referencing SomethingElses.Local:
    Name = 'AlphaOne'
    Parent = '' ()
    Reference = '' (WindowsFormsApplication2.Reference)
Before referencing SomethingElses.Local:
    Name = 'AlphaTwo'
    Parent = '' ()
    Reference = 'Reference' ()
After referencing SomethingElses.Local:
    Name = 'AlphaOne'
    Parent = 'Alpha' (WindowsFormsApplication2.Parent)
    Reference = '' (WindowsFormsApplication2.Reference)
After referencing SomethingElses.Local:
    Name = 'AlphaTwo'
    Parent = 'Alpha' (WindowsFormsApplication2.Parent)
    Reference = 'Reference' ()
Before SaveChanges:
    Name = 'AlphaOne'
    Parent = 'Alpha' (WindowsFormsApplication2.Parent)
    Reference = '' (WindowsFormsApplication2.Reference)
Before SaveChanges:
    Name = 'AlphaTwo'
    Parent = 'Alpha' (WindowsFormsApplication2.Parent)
    Reference = 'Reference' ()
After SaveChanges:
    Name = 'AlphaOne'
    Parent = 'Alpha' (WindowsFormsApplication2.Parent)
    Reference = 'Reference' (WindowsFormsApplication2.Reference)
After SaveChanges:
    Name = 'AlphaTwo'
    Parent = 'Alpha' (WindowsFormsApplication2.Parent)
    Reference = 'Reference' (WindowsFormsApplication2.Reference)

解决方案

Is there any way to force Entity Framework to populate the foreign keys immediately when an entity is added to the context, rather than delaying it until something else happens with the context?

Option 1:

If you just want to fix the relationalship between entities without saving them to the database hence calling DbContext.SaveChanges() then just call DbContext.ChangeTracker.DetectChanges().

Option 2:

EF can automatically fixup relationalship between entites without caling DbContext.SaveChanges() or DbContext.ChangeTracker.DetectChanges(). Those entites are called Proxy classes. A proxy class is a dynamically generated derived type that acts as a proxy for the entity. This proxy overrides some virtual properties of the entity to insert hooks for performing actions automatically when the property is accessed. Proxy creation is enabled by default for your DbContext unless you disabled it by calling DbContext.Configuration.ProxyEnabled = false;. You don't need to add that line of code because you need proxy creation to be enabled.

Anyway you need to modify some things on your classes before taking advantage of this feature:

  • all properties (scalar, navigational, collection) must be marked as virtual
  • all navigational collections must be declared as ICollection<T>
  • all entity instatiation must be done by using DbContext.DbSet<T>.Create() method.
  • all collection instantiation must not be initialized into a constructor. Proxy class will take care of the instantiation and will throw an exception if you don't follow this point.

Following this steps your entites classes must look like this:

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; }
}

Your click event handler impelmentation will look like this:

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);
}

There are two noticeable changes in this implementation:

  • as said you must use DbContext.DbSet.Create to get an instance of the generated proxy of T instead of using the default constructor which by pass the proxy.
  • one thing to know about Lazy Loading is that it check if the navigational property is loaded. If not then it check the database to load the entity. In your case all your entities are on Added state then doing Reference_Name = "Reference" will not help your context to lazy load the navigational property Refererence. This is why instead of doing alphatwo.Reference_Name = "Reference"; I did alphatwo.Reference = reference; because reference is on Added state, Lazy Load will find nothing on database.

这篇关于实体框架自动对外重点人群的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆