我想出了一个基于 T4 的解决方案来解决 MergeOption 周围的 EF 监督问题,在 DbSet 中无法访问
在默认的DataContext 中,您有一些由 T4 模板生成的实体 DBSet:
public virtual DbSet<Person> Persons { get; set; }
public virtual DbSet<Address> Addresses { get; set; }
etc.
编辑模板为每个Entity 添加IQueryable 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 创建一个默认的扩展方法,如下所示,您可以选择为每个表/类型添加自己的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,以确保您拥有最新的实体。