重新加载实体和所有导航属性关联 - DbSet 实体框架

问题描述 投票:0回答:5

我在实体关联刷新方面遇到问题。当我得到这样的实体时:

MyContext context = new MyContext();

Person myPerson = context.PersonSet.FirstOrDefault();
String myPersonName = myPerson.Name;
Address myPersonAddress = myPerson.Address;

我有一个人,有一个名为 Address 的关联和一个名为 Name 的属性。如果我手动修改数据库中的数据(例如属性名称),我必须使用以下代码来重新加载我的实体:

context.Entry(myPerson).Reload();

我有了新的名称值。但如果我对地址做同样的事情,它就不起作用。我认为这是因为地址是关联属性。我需要刷新它。

如何强制重新加载 Address 关联(以及 Person 类中的所有其他关联)?

编辑:

同一情况下,一个人可以拥有多个地址。

MyContext context = new MyContext();

Person myPerson = context.PersonSet.FirstOrDefault();
String myPersonName = myPerson.Name;
List<Address> myPersonAddresses = myPerson.Addresses;

在这种情况下,它不是参考:

context.Entry(myPerson).Reference(p => p.Address).Load();
// Address will be populated with only the new address
// this isn't required because I use lazy loading

但是一个集合:

context.Entry(myPerson).Collection(p => p.Addresses).Load();
// Address will be populated with old value and new value

我需要用它来工作:

context.Entry(myPerson).Collection(p => p.Addresses).CurrentValue.Clear();
context.Entry(myPerson).Collection(p => p.Addresses).Load();

但是对我的所有导航属性执行此操作似乎不是一个好的解决方案!

c# entity-framework ado.net entity-framework-4.1
5个回答
33
投票

如果您不使用延迟加载,则可以显式加载新的

Address
(因为您必须在最初加载
Include
时显式加载它(例如,使用
Person
)):

context.Entry(myPerson).Reload();
// If the person refers to another Address in the DB
// myPerson.Address will be null now

if (myPerson.Address == null)
    context.Entry(myPerson).Reference(p => p.Address).Load();
    // myPerson.Address will be populated with the new Address now

如果使用延迟加载,则不需要第二个代码块。尽管如此,一旦访问新的

myPerson.Address
的属性,您就会得到一个新的数据库查询(就像上面第二个代码块中的新查询一样),因为第一行会将导航属性标记为未加载(如果person 指的是数据库中的新地址。

此行为不取决于您是否在模型类中公开了外键。

似乎没有一种方法可以调用某个单一的魔法

Reload
方法,该方法可以在一次调用中重新加载和更新整个对象图(类似于没有单个
Include
来急切加载完整的对象图)。


13
投票

如果它获得了一项,则只有您的 .Load() 方法有帮助。

context.Entry(myPerson).Collection(p => p.Addresses).Load();

如果p.Addresses丢失一项,可以通过

刷新
((IObjectContextAdapter)CurrentContext(context)).ObjectContext.Refresh(RefreshMode.StoreWins, p.Addresses);

2
投票

您需要使用 Query() 扩展来修改 LINQ 表达式。这是一个基于我个人代码的示例。在此代码中,我使用 myPerson 对象的相关 AddressType 导航属性重新加载 Addresses 集合,并将结果放入 SomeList 中:

_DbContext.Entry<Person>(myPerson)
          .Collection(i => i.Adresses) // navigation property for Person
          .Query()
          .Include("AddressType")        // navigation property for Address
          .OrderBy(i => i.Name)
          .ThenBy(i => i.AddressType.AddressTypeName) // just an example
          .Select(i => new someClass
          {
              SoomeField1 = i.SomeField1,
              ...
          })
          .ToList()
          .ForEach(i => SomeList.Add(i)); // SomeList is a List<T>

0
投票

使用 Entity Framework 6 和经典 .NET(不是 .NET Core),我需要在通过存储过程调用更新记录后重新加载导航属性集合。

有效的方法(无需使用命令敲击数据库,就像迭代集合并为每个元素调用

context.Entity(e).Reload()
那样)是迭代集合并为
Entity(e).State
分配
EntityState.Detached
状态,然后使用
context.Entity(p).Collection(o => o.Navigations).Load() 
从数据库重新加载集合。

public void ReloadFooThings(Foo foo)
{
    foreach (var thing in foo.Things.ToList())
    {
        context_.Entry(thing).State = EntityState.Detached;
    }

    context_.Entry(foo).Collection(o => o.Things).Load();
}

请注意,在这种情况下,您对实体所做的任何更改都将被覆盖,因此必须在从另一个上下文或直接 SQL 访问进行更改之前保存它们。

应该可以使用泛型来定义它,以实现对任何导航集合的库调用,但我没有时间使用导航属性

Expression<Func<TEntity, ICollection<TElement>>>
来计算循环来访问底层集合以迭代分离。


-3
投票

我已经通过在从 dbContext 读取对象之前使用 Detach 解决了这个问题。 此方法允许我刷新对象的所有导航属性。 我在这里描述了我的场景和解决方案的详细信息 实体框架:重新加载新创建的对象/重新加载导航属性

© www.soinside.com 2019 - 2024. All rights reserved.