2016-05-13 2 views
4

私はEFの新人ですが、私は21年間プログラミングしてきました。私は物事をドライとジェネリックにするのが好きですが、私がやったことについて何かが間違っているように見えますが、私はそれに指をかけられません。EFを基本クラスにカプセル化する必要がありますか?

私が見たEFのすべての例では、開発者がPOCOクラスごとに4つの別々のCRUDメソッドを作成しています。

モデル:すべてのビジネス層のための

public class Model1 : DbContext 
    { 
    public Model1() 
     : base("name=Model1") 
    { 
    } 

    public virtual DbSet<Member> Members { get; set; } 
    } 

基本クラス:

using System.Data.Entity; 
using System.Reflection; 

namespace biz 
{ 
    public abstract class EFObject<T> where T : EFObject<T> 
    { 
    public int Id { get; set; } 

    internal static readonly string DbSetProperyName = typeof(T).Name + "s"; 

    public static EFCollection<T> Collection 
    { 
     get 
     { 
     using (var db = new Model1()) 
     { 
      PropertyInfo p = db.GetType().GetProperty(DbSetProperyName); 
      DbSet<T> collection = (DbSet<T>)p.GetValue(db); 
      return new EFCollection<T>(collection); 
     } 
     } 
    } 

    public void Insert() 
    { 
     using (var db = new Model1()) 
     { 
     PropertyInfo p = db.GetType().GetProperty(DbSetProperyName); 
     DbSet<T> collection = (DbSet<T>)p.GetValue(db); 
     collection.Add((T)this); 
     db.SaveChanges(); 
     } 
    } 

    public void Save() 
    { 
     if (Id == 0) 
     Insert(); 
     else 
     Update(); 
    } 

    public void Update() 
    { 
     using (var db = new Model1()) 
     { 
     PropertyInfo p = db.GetType().GetProperty(DbSetProperyName); 
     DbSet<T> collection = (DbSet<T>)p.GetValue(db); 
     T dbItem = collection.Find(Id); 
     foreach (PropertyInfo pi in typeof(T).GetProperties()) 
     { 
      pi.SetValue(dbItem, pi.GetValue(this)); 
     } 
     db.SaveChanges(); 
     } 
    } 
    } 
} 

ジェネリックコレクションクラス:だから私はそれを行う必要があり、これは私が思い付いたものですではないに着手しました

using System.Collections.Generic; 

namespace biz 
{ 
    public class EFCollection<T> : List<T> where T : EFObject<T> 
    { 
    public EFCollection() 
    { 
    } 

    public EFCollection(IEnumerable<T> collection) 
    { 
     AddRange(collection); 
    } 

    public void Save() 
    { 
     foreach (T item in this) 
     item.Save(); 
    } 
    } 
} 

例中間層クラス:

namespace biz 
{ 
    public class Member : EFObject<Member> 
    { 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 

    public Client[] Clients; 
    public Good[] Goods; 
    public decimal Percentage; 
    } 
} 

と使用方法:

var member = new biz.Member() { FirstName = "Brad", LastName = "Pitt", Percentage = 1 };// 
    member.Save(); 
    member = biz.Member.Collection.Find(o=>o.Id == member.Id); 
    member.FirstName = "Cherry"; 
    member.Save(); 

使用コードは動作しますが、私はこのアプローチはに私を実行しようとしている問題の種類思ったんだけど?

私がやったことについて私に間違ったやりかたをさせるものは、おそらくEFを十分に知っている私のせいかもしれません。更新シナリオでは、1)1つのセッションを使用してコレクションからオブジェクトを取得する、2)切断する、3)オブジェクトのプロパティを更新する、3)新しいセッションを開始する、3)一致するオブジェクトをdb(それはもはや同じオブジェクトではありません!)、4)リフレクションによって更新し、次に5)変更を保存します。したがって、1つではなく、2つのオブジェクトが関わっています。私は一度それを取得した元のオブジェクトを維持するために接続の "手放す"と私はこれを修正する方法がわからないと思う。

+0

なしワンショットは、オブジェクトグラフのコミット、トランザクション、N + 1つのクエリ、いくつか例を挙げると。このアプローチは、エンティティが永続性を知らない共通のEFワークフローに垂直です。これはアクティブなレコードを思い起こさせるもので、リポジトリ+作業単位とはまったく異なるデータアクセスパターンです。 –

+0

また、継承を使用してエンティティを直接Entity Frameworkに結び付けています。 – Amy

+0

@Amy、はい、それは本当です。それは小さなプロジェクトです。あるいは、私は戦略/橋のパターンでその問題を解決することができます。 CRUD関数へのアクセスは引き続き基本クラス経由で行われます。あなたが気づいたことが他にありましたか? – toddmo

答えて

0

コアベースクラスをEF(または永続性のもの)と結びつけることには限界があります。ビジネス層は永続性に依存しません。だから、EFはビジネスやデータプロジェクトからの参照であってはいけません!

ここで私は何をしたのですか。私は基底クラスDatabaseObjectからCRUDメソッドの同じ利点を得て、DIを使ってパーシスタンスレイヤをスワップアウトします。私のEFの "アドイン" dllは、ビジネス層とデータ層を認識します。ビルド後のコマンドによってビンに展開されます。

EF implements IPersistenceProvider interface

PersistenceProvider.cs

using Atlas.Data.Kernel; 
using System.Collections.Generic; 
using System.Data.Entity; 
using System.Linq; 
using System.Reflection; 
using System; 
using Atlas.Core.Kernel.Extensions; 
using System.ComponentModel.DataAnnotations.Schema; 

namespace Atlas.Data.EntityFramework.Kernel 
{ 
    public class PersistenceProvider<T> : IPersistenceProvider<T> where T : DatabaseObject<T> 
    { 
    public static readonly PersistenceProvider<T> Current = new PersistenceProvider<T>(); 
    public static readonly string DbSetProperyName = typeof(T).Pluralize(); 
    public static readonly PropertyInfo DbSetProperyInfo = typeof(DatabaseContext).GetProperty(DbSetProperyName); 

    // C 
    public void Insert(T item) 
    { 
     DatabaseOperation((databaseContext, collection) => 
     { 
     collection.Add(item); 
     }, 
     item.Inserting, 
     item.Inserted 
    ); 
    } 

    // R 
    public IEnumerable<T> Select(Func<T, bool> predicate = null) 
    { 
     using (var databaseContext = new DatabaseContext()) 
     { 
     DbSet<T> collection = (DbSet<T>)DbSetProperyInfo.GetValue(databaseContext); 
     return predicate != null ? collection.Where(predicate).ToList() : collection.ToList(); 
     } 
    } 

    // U 
    public void Update(T item) 
    { 
     DatabaseOperation((databaseContext, collection) => 
     { 
     collection.Attach(item); 
     MarkModified(databaseContext, item); 
     }, 
     item.Updating, 
     item.Updated 
    ); 
    } 

    // D 
    public void Delete(T item) 
    { 
     DatabaseOperation((databaseContext, collection) => 
     { 
     collection.Attach(item); 
     collection.Remove(item); 
     }, 
     item.Deleting, 
     item.Deleted 
    ); 
    } 

    private void MarkModified(DatabaseContext databaseContext, DatabaseObject<T> efObject) 
    { 
     databaseContext.Entry(efObject).State = efObject.Id != null ? EntityState.Modified : EntityState.Added; 
     foreach (var pi in efObject.GetType().GetProperties().Where(pi => !pi.GetCustomAttributes(typeof(NotMappedAttribute), false).Any() && pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericArguments()[0].IsClass)) 
     { 
     var col = (IEnumerable<T>)pi.GetValue(efObject); 
     if (col != null) 
      foreach (DatabaseObject<T> item in col) 
      MarkModified(databaseContext, item); 
     } 
    } 

    private DatabaseContext databaseContext = null; 
    private void DatabaseOperation(Action<DatabaseContext, DbSet<T>> action, Action executing, Action executed) 
    { 
     bool outerOperation = databaseContext == null; 
     try 
     { 
     if (outerOperation) 
      databaseContext = new DatabaseContext(); 
     executing(); 
     DbSet<T> collection = (DbSet<T>)DbSetProperyInfo.GetValue(databaseContext); 
     action(databaseContext, collection); 
     executed(); 
     databaseContext.SaveChanges(); 
     } 
     finally 
     { 
     if (outerOperation) 
     { 
      databaseContext.Dispose(); 
      databaseContext = null; 
     } 
     } 
    } 

    } 
} 

DatabaseObject.cs

using Microsoft.Practices.Unity; 
using Microsoft.Practices.Unity.Configuration; 
using System; 
using System.Collections.Generic; 
using System.ComponentModel.DataAnnotations; 
using System.ComponentModel.DataAnnotations.Schema; 
using System.Configuration; 
using System.Linq; 
using System.Web; 

namespace Atlas.Data.Kernel 
{ 
    public class DatabaseObject<T> where T : DatabaseObject<T> 
    { 

    #region Constructors 
    public DatabaseObject() 
    { 
     Id = Guid.NewGuid(); 
    } 
    #endregion 

    #region Fields 

    [Key] 
    [Column(Order = 0)] 
    public Guid Id { get; set; } 

    #endregion 

    // C 
    public virtual void Insert() 
    { 
     PersistenceProvider.Insert((T)this); 
    } 

    // R 
    public static T SingleOrDefault(Guid Id) 
    { 
     return SingleOrDefault(o => o.Id == Id); 
    } 

    public static T SingleOrDefault(Func<T, bool> predicate) 
    { 
     return PersistenceProvider.Select(predicate).SingleOrDefault(); 
    } 

    public static IEnumerable<T> Select(Func<T, bool> predicate = null) 
    { 
     return PersistenceProvider.Select(predicate); 
    } 

    // U 
    public virtual void Update() 
    { 
     PersistenceProvider.Update((T)this); 
    } 

    // D 
    public virtual void Delete() 
    { 
     PersistenceProvider.Delete((T)this); 
    } 


    #region Callbacks 
    public virtual void Deleting() { } 
    public virtual void Deleted() { } 
    public virtual void Inserting() { } 
    public virtual void Inserted() { } 
    public virtual void Updating() { } 
    public virtual void Updated() { } 
    #endregion 

    #region Static Properties 
    private static IPersistenceProvider<T> persistenceProvider; 
    [Dependency] 
    public static IPersistenceProvider<T> PersistenceProvider 
    { 
     get 
     { 
     if(persistenceProvider == null) 
     { 
      var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = HttpContext.Current.Server.MapPath("~/bin/Atlas.Data.Kernel.dll.config") }; 
      Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); 
      var unitySection = (UnityConfigurationSection)configuration.GetSection("unity"); 

      var container = new UnityContainer().LoadConfiguration(unitySection); 
      persistenceProvider = container.Resolve<IPersistenceProvider<T>>(); 
     } 
     return persistenceProvider; 
     } 
     set => persistenceProvider = value; 
    } 
    #endregion 
    } 
} 
1

これは一般的な反パターンです(さまざまな理由による)。

EFは既にUoWとリポジトリパターンの両方を実装しているため、本質的には抽象化を超えた抽象化を作成しています。

これが悪い理由として、以下の記事を参照してください。

+0

Daffy、OPは一般的なリポジトリを提案していません。 –

+0

@GertArnold私は誤解しているかもしれませんが、OPのコードサンプルでは、​​彼がその方向に向かっているかのように見えます。 –

+1

@toddmo引用したSOの回答からコメントを引用します。「機能が豊富ではないインターフェースの中によく知られている、テスト済みのリポジトリ(Entity Framework)をラッピングしているだけでなく、消費者が持っている機能を人為的に制限しています利益を得る。 –

関連する問題