0

.Net4/EF 4.4 から .Net4.5/EF 6.1 への WPF アプリケーションのアップグレードに取り組んでいます。アップグレード後、DbContext を使用します (ObjectContext 用の POCO ジェネレーターがなかったため)。

アプリケーションは Repository/UnitOfWork-pattern を使用して Entity Framework にアクセスします。アップグレード前に 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

あなたの質問に対する直接的な回答ではありませんが、DbSet でアクセスできない MergeOption に関する EF の監視に対する T4 ベースの解決策を考え出しました。あなたの質問から、これはあなたが探しているものだと思われますか?

デフォルトのコンテキストには、次のような T4 ジェネレーターによって生成されたものがあります。

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

私のアプローチは、T4 を編集して、IQueryable として ObjectSet に直接アクセスできる各エンティティに 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 に答える