C# 无法从外部重新加载相关实体以进行新更改?
这里的场景是每个屏幕(视图)后面都有一个ViewModel。为了获得最佳(或推荐)实践,我们应该为每个ViewModel使用一个long-aliveC# 无法从外部重新加载相关实体以进行新更改?,c#,entity-framework,mvvm,desktop-application,C#,Entity Framework,Mvvm,Desktop Application,这里的场景是每个屏幕(视图)后面都有一个ViewModel。为了获得最佳(或推荐)实践,我们应该为每个ViewModel使用一个long-aliveDbContext 因此,如果在另一个ViewModel中进行了一些更改(新添加/删除的实体),则需要重新加载相关实体 以下是解决此问题的一些解决方案: 发布一些事件或发送一些消息以通知更改,订阅者ViewModels可以: 相应地添加/删除添加/删除的实体,而无需重新加载实体,这看起来像是在ViewModels之间同步数据。它有其自身的复杂性,
DbContext
因此,如果在另一个ViewModel
中进行了一些更改(新添加/删除的实体),则需要重新加载相关实体
以下是解决此问题的一些解决方案:
- 发布一些事件或发送一些消息以通知更改,订阅者
s可以:ViewModel
- 相应地添加/删除添加/删除的实体,而无需重新加载实体,这看起来像是在ViewModels之间同步数据。它有其自身的复杂性,因为此处添加/删除的实体不应该有状态跟踪(意味着状态应该是
未更改的
未
添加的
或
删除的
,因为这些更改已经更新到数据库中)。此外,代理实体不能添加到多个DBContext。。。这里的问题太多了
- 重新加载所有相关实体。EF自然不支持这一点
- 相应地添加/删除添加/删除的实体,而无需重新加载实体,这看起来像是在ViewModels之间同步数据。它有其自身的复杂性,因为此处添加/删除的实体不应该有状态跟踪(意味着状态应该是
- 只需在切换屏幕时重新加载整个ViewModel(这意味着ViewModel不会在应用程序的整个生命周期内保留)。这在某些情况下可能适用,但实际上它不够灵活,无法在任何情况下使用(例如,可能会从应用程序外部(另一个应用程序)进行一些更改,通常我们只需要在当前视图上单击一个刷新按钮来刷新数据,因此重新加载整个ViewModel将不必要地影响当前视图,并可能导致一些不良的视觉效果,…)
DbContext
,这意味着创建并使用一个新的ViewModel(请注意,我使用依赖注入将DbContext注入到ViewModel中,因此DbContext的生存期实际上与ViewModel的相同)
我可以在谷歌上找到一些hacky代码来在实体框架中重新加载实体,但我真的不喜欢hacky的东西。因此,如果可能的话,请与我分享你的方法,你对这个问题的解决方案,甚至说服我说hacky的东西很好
我们应该为每个ViewModel使用一个长活动DbContext
我不会说这是真的。您可以并且可能应该为每个加载/更新操作创建新的DbContext实例 使用不同的DbContext实例可以异步执行查询。
对于Windows应用程序(Winforms,WPF),异步数据库访问在加载时间上有了巨大的改进,同时应用程序保持响应。
使用一个DbContext,这并不容易 与其注入DbContext,不如创建DbContext工厂并将其注入viewmodel,然后
using (var context = _contextFactory.Create<MyDbContext>())
{
var orders = await context.Orders.ToListAsync();
return orders.Select(order => order.ToOrderDto());
}
例如,当您需要重新加载订单行时
this.OrderLinesViewModels = await _dataAccess.GetOrderLines(id);
使用瞬态DbContext实例只会让问题迎刃而解。您的ViewModel的某些实体数据可能已过期。但也可能有未保存的更改。您只需逐个ViewModel决定如何处理这些更改 在桌面应用程序中,ViewModel是工作单元,仍然是DbContext的适当范围
public interface OrderDataAccess
{
Task<Order> GetOrder(Guide id);
Task<IEnumerable<OrderLine>> GetOrderLines(Guide orderId);
}
如果您决定要重新加载一个被跟踪的实体,或一个DbContext实例的所有被跟踪的实体,这应该不是问题。例如:
void ReloadAllTrackedEntities()
{
foreach (var entry in ChangeTracker.Entries())
{
entry.Reload();
}
}
另一方面,由于您正在构建一个桌面应用程序,您知道如何进行更改跟踪吗?我们应该为每个ViewModel使用一个long-alive DbContext-我不会说这是真的。另一个“最佳”练习在每次发送sql查询时创建新的DbContext。为每个sql查询创建自己的DbContext将使您能够异步加载/更新数据,这对windows应用程序非常重要。同意。我认为您应该使用上下文填充视图模型,然后放弃它。请为ach CRUD操作。这还可能需要刷新视图模型的某些部分。这种做法是从Microsoft的某个网站上读到的,但适用于Winforms(它表示类似于表单的每个DbContext)。但我不确定每个表单是否应该保持不变(或者它没有说明这一点).我刚在谷歌上搜索了一下,发现也许最好为视图保留一个DbContext,但应该创建一个新的DbContext(意味着新的ViewModel)在显示视图的开始部分。我们在这里得到的好处是更改跟踪,这仅适用于桌面应用程序。是的,您会丢失更改跟踪。但是,更改跟踪看起来比它更有吸引力。首先,由于包含“许多”,上下文很快变得臃肿被跟踪的实体和所有东西都会变得像糖蜜一样慢。其次,你总是在处理陈旧的数据。可能最好是获得新的“真实”数据实体来填充编辑窗口,然后在编辑操作期间,从更改跟踪中获益,并使断开连接的视图模型与编辑的实体保持同步。是的,我非常喜欢使用DbContext作为UoW,尽可能晚打开并尽快关闭。但这里的折衷是失去更改跟踪的好处。如果eature甚至不受支持或推荐,我本应该尝试编写自己的帮助程序库来处理此问题,但它存在,并且混淆了我们选择的方式。使用contextFactory的想法非常有趣,谢谢,+1,但请等待其他一些答案。此问题有多种可能的解决方案、方法,不仅仅是为了寻求最好的结果。感谢使用瞬态DbContext实例只会让问题迎刃而解-您能举一个瞬态DbContext可以“踢”的例子吗?您仍然有识别在其他视图模型中更改的实体的问题
void ReloadAllTrackedEntities()
{
foreach (var entry in ChangeTracker.Entries())
{
entry.Reload();
}
}