Entity Framework 刷新上下文?

121

我该如何刷新我的上下文?我有基于数据库视图的实体,当我对一个具有导航属性到视图的表实体进行更新时,实体被更新了,但视图没有根据新的更新进行刷新...我只想从数据库中再次获取数据。 谢谢!

7个回答

102

刷新上下文实体的最佳方法是释放当前上下文,然后创建一个新的上下文。

如果您确实需要刷新某个实体,并且正在使用 DbContext 类的 Code First 方法,则可以使用

    public static void ReloadEntity<TEntity>(
        this DbContext context, 
        TEntity entity)
        where TEntity : class
    {
        context.Entry(entity).Reload();
    }
重新加载集合导航属性,可以使用:
    public static void ReloadNavigationProperty<TEntity, TElement>(
        this DbContext context, 
        TEntity entity, 
        Expression<Func<TEntity, ICollection<TElement>>> navigationProperty)
        where TEntity : class
        where TElement : class
    {
        context.Entry(entity).Collection<TElement>(navigationProperty).Query();
    }

参考: https://msdn.microsoft.com/zh-cn/library/system.data.entity.infrastructure.dbentityentry.reload(v=vs.113).aspx#M:System.Data.Entity.Infrastructure.DbEntityEntry.Reload


4
我无法让这个重新加载子导航属性的功能正常工作。 - Paul
@David 如果你有 class Parent { ICollection<Child> Children; },你可以使用 context.ReloadNavigationProperty(parent, p => p.Children); - Jinjinov
1
在 EF Core 中,您可以使用 Query().Load(),例如 context.Entry(order).Collection(o => o.NavigationProperty).Query().Load(); - Rubenisme
4
我不明白为什么这个解决方案获得如此高的评价。context.Entry(entity).Collection<TElement>(navigationProperty).Query()不会重新加载子集合。它只是提供一个IQueryable,代表用于获取该集合的查询。它实际上什么也没做。 - statler
请解释为什么应该优先使用Dispose上下文并创建新上下文,而不是Reload? - BornToCode
1
正如@Rubenisme所说:您需要在末尾使用.Load()。 - Teddy

88
yourContext.Entry(yourEntity).Reload();

6
谢谢你提供简单的解决方案。我认为将其封装成像RX_DID_RX那样的扩展方法并不必要。 - Thomas
29
请注意,这不会重新加载集合导航属性,仅重新加载实体本身。 - James Wilkins

42

如果您想使用DbContextApi重新加载特定实体,则RX_DID_RX已经给出了答案。

如果您想重新加载/刷新您加载的所有实体:

如果您正在使用Entity Framework 4.1+ (EF5或EF6),请使用DbContext API:

public void RefreshAll()
{
     var entitiesList = ctx.ChangeTracker.Entries().ToList();
     foreach (var entity in entitiesList)
     {
           entity.Reload();
     }
}

如果您正在使用entityFramework 4(ObjectContext API):

public void RefreshAll()
{
     // Get all objects in statemanager with entityKey
     // (context.Refresh will throw an exception otherwise)
     var refreshableObjects = (from entry in context.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted
                                               | EntityState.Modified
                                               | EntityState.Unchanged)
                                      where entry.EntityKey != null
                                      select entry.Entity);
 
     context.Refresh(RefreshMode.StoreWins, refreshableObjects);
}

无论如何最好的建议是,尝试使用“短生命周期上下文”,这样就可以避免此类问题。

我写了几篇关于此事的文章:

https://christianarg.wordpress.com/2013/06/13/entityframework-refreshall-loaded-entities-from-database/


3
为了避免出现“Collection was modified; enumeration operation may not execute.”错误,我不得不将foreach更改为for循环。var entities = _context.ChangeTracker.Entries().ToArray(); for (int i = 0; i < entities.Length; i++) { entities[i].Reload(); } - Muflix
@Muflix 这对我来说是救命稻草。感谢您发布您的修复! - Scott Uphus

17
使用 Refresh 方法:
context.Refresh(RefreshMode.StoreWins, yourEntity);

或者选择处理当前上下文并创建一个新的上下文。


@JMK 这里到底有什么问题?对我来说似乎运行良好(EF 6.1.1)。 - Sebastian Krysmanski
@SebastianKrysmanski 我差不多一年前进行了评论,也许现在已经修复了? - JMK
6
我认为它只适用于ObjectContext而不是DbContext。它们之间需要进行交互。 - Emil
5
可以通过((IObjectContextAdapter)dbContext).ObjectContext简单地实现。 - Daniel Z.
4
这句话没有完全表述清楚,略显不完整。 - user441521

6

在MVC 4,EF 5中,context.Reload()对我不起作用,因此我做了以下操作。

context.Entry(entity).State = EntityState.Detached;
entity = context.Find(entity.ID);

它很好地运行了。


3

EF 6

在我的场景中,Entity Framework 没有获取到新更新的数据。原因可能是数据在其范围之外被更新了。获取数据后刷新解决了我的问题。

private void RefreshData(DBEntity entity)
{
    if (entity == null) return;

    ((IObjectContextAdapter)DbContext).ObjectContext.RefreshAsync(RefreshMode.StoreWins, entity);
}

private void RefreshData(List<DBEntity> entities)
{
    if (entities == null || entities.Count == 0) return;

    ((IObjectContextAdapter)DbContext).ObjectContext.RefreshAsync(RefreshMode.StoreWins, entities);
}

1
我使用EF6。为什么这比_context.Entry(entity).Reload();更好? - Csaba Toth
据我所知,EF6中没有提供.Reload()方法。@CsabaToth - Mahbubur Rahman

1

使用 Reload 刷新数据库上下文并不是推荐的方法,因为会导致性能损失。最好的做法是在执行每个操作之前初始化一个新的 dbcontext 实例。这样可以为每个操作提供一个更新的、最新的上下文。

using (YourContext ctx = new YourContext())
{
   //Your operations
}

3
这是一个糟糕的想法,因为它会影响编写单元测试的能力。如果你的代码启动了一个新的上下文,那在单元测试期间如何进行工作呢? - victor
8
如果您能展示一些样品而不是批评,那对我和其他人都会很有帮助。 - aog
1
对于小型网站来说,这是可以的。 - alikuli
1
嗨@aog,为了帮助你,我认为批评的原因是如果可以的话最好使用依赖注入。另外,如果需要一个新的DbContext,可以使用DbContextFactory。 - Jess

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接