0

我正在将 WPF 应用程序从使用 .Net4/EF 4.4 升级到 .Net4.5/EF 6.1。升级后我将使用 DbContext(因为没有用于 ObjectContext 的 POCO 生成器)。

该应用程序使用 Repository/UnitOfWork 模式访问实体框架,在升级之前我可以将 ObjectSet.MergeOption 设置为 OverwriteChanges(在存储库类中),但 DbSet 类没有此功能。但是,我知道我可以通过使用 IObjectContextAdapter 从 DbContext 获取 ObjectSet。(见下面的代码)。但似乎在创建的 ObjectSet 上设置 MergeOption 不会反映回 DbSet。

所以我的问题是:有没有办法将 ObjectSet 转换回 DbSet(保留 MergeOption 设置)?

这是一些存储库类:

public class SqlRepository<T> : IRepository<T> where T : class, IEntity
{
    protected DbSet<T> dbSet;

    public SqlRepository(DbContext context)
    {
        var objectContext = ((IObjectContextAdapter)context).ObjectContext;
        var set = objectContext.CreateObjectSet<T>();
        set.MergeOption = MergeOption.OverwriteChanges;


        dbSet = context.Set<T>();
//I would like to do something like this: dbSet = (DbSet)set;


    }
}
4

1 回答 1

0

尽管不是您问题的直接答案,但我提出了一个基于 T4 的解决方案,用于解决在 DbSet 中无法访问 MergeOption 的 EF 监督问题。从您的问题看来,这就是您要寻找的东西?

在默认上下文中,您有一些由 T4 生成器生成的内容,例如:

public virtual DbSet<Person> Persons { get; set; }
public virtual DbSet<Address> Addresses { get; set; }

等等

我的方法是编辑 T4 为每个提供直接访问 ObjectSet 作为 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 实现,以便您的表的所有用户都可以排序或包含等(这部分不是回答问题所必需的,但它反正有用)

因此,例如,您可以在调用 GetPersons() 时添加以下内容以始终包含地址

 public static class CustomQueryImplentations
 {
        public static IQueryable<Person> QueryImplementation(this IQueryable<Person> source)
        {
            return source
                .Include(r => r.Addresses)
                .OrderByDescending(c => c.Name);
        }
  }

最后:

//just load a simple list with no tracking (Fast!)
var people = Database.GetPersons(MergeOption.NoTracking);

//user wants to edit Person so now need Attached Tracked Person (Slow)
var peson = Database.GetPersons(MergeOption.OverwriteChanges).FirstOrDefault(p => p.PersonID = 1);

//user makes changes and on another machine sometime later user clicks refresh
var people = Database.GetPersons(MergeOption.OverwriteChanges);

或者你可以(就像我一样)写一些像

Database.MergeOption = MergeOption.OverwriteChanges;

使用现有的 Get 方法刷新实体负载,但现在将全部覆盖附加实体

Database.MergeOption = null;

需要注意的是,如果您在进行更改之前加载 AsNoTracking,则需要重新附加或使用 OverwriteChanges 更好地重新加载,以确保您拥有最新的实体。

于 2016-01-18T16:04:32.700 回答