我们的WinForms应用程序有各种对话框和&屏幕用自己的DbContext打开。我们通常希望屏幕重新加载其数据或部分数据,这些数据是使用不同的DbContext在不同的表单上修改的,只需重新执行对数据库的查询即可。默认情况下,MergeOption = PreserveChanges因此数据库更改不会反映在DbContext中。
我们考虑的选项:
为相关对象调用Refresh(RefreshMode.StoreWins)
ObjectContext.Refresh(RefreshMode.StoreWins, models)
这是丰富的复杂模型结构和集合,因为它必须在每个模型上单独调用。
在每个查询中单独设置MergeOption
((ObjectQuery)query).MergeOption = MergeOption.OverwriteChanges
这可以工作,但必须手动完成每个容易忘记的查询。
各种手动破解反射
有几个SO页面暗示这样的黑客攻击,然而EF7即将出现,并且我不想走这条道路的风险。
问题:如何为DbContext中的所有内容设置MergeOption = OverwriteChanges?或者甚至更好,全球?
答案 0 :(得分:0)
OverwriteChanges上的MSDN:
All current values on the client are overwritten with current values from the data service regardless of whether they have been changed on the client.
如果要重新加载所有内容,请删除上下文并重新加载数据。这是重新加载所有数据的最安全,最快捷的方法。
如果要对应用程序中的所有DbContext实例执行相同操作,则需要对每个实例执行相同操作。
编辑:就你执行的每个查询设置默认的MergeOption,据我所知,thias是不可能的。似乎使用ObjectContext上的反射来获取所有ObjectQueries的旧技巧不适用于DbContext。一种可能的选择是改进您正在使用的一些代码,并将其包装到一个始终应用OverwriteChanges的方法中,例如:
protected ObjectSet<T> GetObjectSet<T>() where T : class
{
var objectContext = ((IObjectContextAdapter)Context).ObjectContext;
ObjectSet<T> set = objectContext.CreateObjectSet<T>();
set.MergeOption = MergeOption.OverwriteChanges;
return set;
}
并调用此方法而不是Context.Set()。
答案 1 :(得分:0)
我已经提出了一个基于T4的解决方案来解决MergeOption
无法访问DbSet
周围的EF疏忽问题
在默认的DataContext
中,你有一些由T4模板生成的实体DBS集:
public virtual DbSet<Person> Persons { get; set; }
public virtual DbSet<Address> Addresses { get; set; }
etc.
修改模板,为每个IQueryable
添加Entity
个getter:
public IQueryable<Person> GetPersons(MergeOption mergeOption = MergeOption.AppendOnly, bool useQueryImplentation = true)
{
return useQueryImplementation ? GetSet<Person>(mergeOption).QueryImplementation() : GetSet<Person>(mergeOption);
}
然后在基地DataContext
public class DataContextBase
{
/// <summary>
/// Gets or sets the forced MergeOption. When this is set all queries
/// generated using GetObjectSet will use this value
/// </summary>
public MergeOption? MergeOption { get; set; }
/// <summary>
/// Gets an ObjectSet of type T optionally providing a MergeOption.
/// <remarks>Warning: if a DataContext.MergeOption is specified it will take precedence over the passed value</remarks>
/// </summary>
/// <typeparam name="TEntity">ObjectSet entity Type</typeparam>
/// <param name="mergeOption">The MergeOption for the query (overriden by DataContext.MergeOption)</param>
protected IQueryable<TEntity> GetObjectSet<TEntity>(MergeOption? mergeOption = null) where TEntity : class
{
var set = Context.CreateObjectSet<TEntity>();
set.MergeOption = MergeOption ?? mergeOption ?? MergeOption.AppendOnly;
return set;
}
通过为IQueryable
创建默认的Extension方法,您可以选择为每个表/类型添加自己的QueryImplementation<T>
实现,以便表的所有用户都可以进行排序或包含等。
(不要求回答这个问题,但它有用)
例如,您可以在调用GetPersons()
public static class CustomQueryImplentations
{
public static IQueryable<Person> QueryImplementation(this IQueryable<Person> source)
{
return source
.Include(r => r.Addresses)
.OrderByDescending(c => c.Name);
}
}
加载一个没有跟踪的简单列表(快!)
var people = Database.GetPersons(MergeOption.NoTracking);
编辑Person
,附加并更新最新的数据库值(慢)
var peson = Database.GetPersons(MergeOption.OverwriteChanges).FirstOrDefault(p => p.PersonID = 1);
在另一台机器上:
var people = Database.GetPersons(MergeOption.OverwriteChanges);
或者,回答原始问题以全局设置MergeOption
Database.MergeOption = MergeOption.OverwriteChanges;
使用现有的get方法获取实体......
Database.MergeOption = null;
注意:要注意的一点是,如果您在进行更改之前加载MergeOption.AsNoTracking
,则需要附加或更好地重新加载MergeOption.OverwriteChanges
以确保您拥有最新的实体。