【问题标题】:How to set MergeOption = OverwriteChanges globally?如何设置 MergeOption = OverwriteChanges 全局?
【发布时间】:2015-01-09 22:57:13
【问题描述】:

我们的 WinForms 应用程序有各种打开的对话框和屏幕,每个都有自己的 DbContext。通常,我们希望屏幕重新加载其数据或在具有不同 DbContext 的不同表单上修改的部分数据,只需对数据库重新执行查询即可。默认情况下,MergeOption = PreserveChanges,因此数据库更改不会反映在 DbContext 中。

我们考虑过的选项:

为相关对象调用 Refresh(RefreshMode.StoreWins)

ObjectContext.Refresh(RefreshMode.StoreWins, models)

这很笨拙,具有丰富的复杂模型结构和集合,因为它必须在每个模型上单独调用。

在每个查询上单独设置 MergeOption

((ObjectQuery)query).MergeOption = MergeOption.OverwriteChanges

这可行,但必须手动完成每个容易忘记的查询。

通过反射进行的各种手动破解

几个 SO 页面建议像这样的 hack,但是随着 EF7 的出现以及一般风险,我不想走这条路。

问题:如何为 DbContext 中的所有内容设置 MergeOption=OverwriteChanges?或者在全球范围内更好?

【问题讨论】:

    标签: c# entity-framework caching orm dbcontext


    【解决方案1】:

    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,据我所知,这是不可能的。似乎在 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()。

    【讨论】:

      【解决方案2】:

      我想出了一个基于 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&lt;T&gt; 实现,以便表的所有用户都可以排序或包含等。 (不需要回答这个问题,但它很有用)

      因此,例如,您可以在调用 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,以确保您拥有最新的实体。

      【讨论】:

        猜你喜欢
        • 2011-07-09
        • 1970-01-01
        • 1970-01-01
        • 2022-10-23
        • 2018-11-03
        • 2011-02-11
        • 2017-12-06
        • 2017-04-23
        • 1970-01-01
        相关资源
        最近更新 更多