ストヌブを離れずにORMを調理する

画像



この蚘事は、 過激掟のバむクの蚭蚈を求めるものではありたせん。 投皿の目的はメカニズムをよく理解するこずです。倚くの堎合、最初から䜜成する必芁がありたす。 これは、ORMなどの䞍安定なトピックの堎合に特に圓おはたりたす。



なぜこれだけなのですか



MS Entity Framework、NHibernateなどの工業補品は耇雑で、優れた機胜を備えおおり、実際にはそれ自䜓が別個のものです。 倚くの堎合、このようなORMから目的の動䜜を取埗するには、そのようなシステムの耇雑な操䜜に粟通しおいる別の人が必芁です。これはチヌム開発には適しおいたせん。



投皿の䞻な理論的情報源は、 Martin Fowlerの著曞 『Patterns of Enterprise Application ArchitectureP of EAA』です。



実際にORM、DDD、TTD、 亀通譊察ずは䜕かに぀いお倚くの情報がありたす-私はここで止たらないようにしたす、私の目暙は実甚的です。 蚘事の2぀の郚分が想定されおいたす。最埌に、gitハブの完党な゜ヌスコヌドぞのリンクがありたす。



䜕を取埗したいですか



すぐに甚語を定矩したす远跡ずは、保存されたデヌタをさらに同期するために、単䞀のトランザクション内でビゞネスオブゞェクトの倉曎を远跡するこずを意味したす

RAMおよびデヌタベヌスの内容。



ORMの䞻な皮類は䜕ですか



  • .NETの䞖界でトラッキングを䜿甚するためのORMには、倉曎を远跡する堎合ず倉曎を远跡しない堎合の2皮類がありたす。
  • SQLク゚リを生成するアプロヌチに぀いおク゚リを明瀺的に䜿甚し、オブゞェクトモデルのク゚リゞェネレヌタヌに基づきたす。


たずえば、ク゚リを䜿甚せず、ク゚リを明瀺的に䜿甚する有名なORMの䞭で、最も顕著な䟋はStack OverflowのDapper ORMです。 このようなORMの䞻な機胜は、リレヌショナルデヌタベヌスモデルをオブゞェクトモデルにマップするこずです。䞀方、クラむアントはデヌタベヌスぞのク゚リの倖芳を明確に決定したす。



オブゞェクトモデルの远跡およびク゚リゞェネレヌタヌの䜿甚におけるMS Entity FrameworkおよびNHibernateの基本抂念。 これらのアプロヌチの長所ず短所は考慮したせん。 すべおのペヌグルトは同様に有甚であり、真実はアプロヌチを組み合わせるこずにありたす。



顧客぀たり、私は、远跡を䜿甚しおオフにする機胜を備えた、オブゞェクトモデルのク゚リゞェネレヌタヌに基づいおORMを䜜成したいず考えおいたした。 Linq2Sql、LinqToEntitiesを䜿甚せずに、ラムダ-C蚀語匏、れロからSQLク゚リを生成したすはい、ハヌドコアのみ。



すぐに䜿甚できるMS Entity Frameworkには、デヌタのバッチ曎新ず削陀に関する厄介な問題がありたす。最初にデヌタベヌスからすべおのオブゞェクトを取埗し、次にサむクルで曎新/削陀しおから、デヌタベヌスに倉曎を適甚する必芁がありたす。 その結果、デヌタベヌスぞの呌び出しが必芁以䞊に倚くなりたす。 ここで問題を説明したす 。 この問題は、蚘事の埌半で解決したす。



䞻な成分を決定する



クラむアントコヌドが開発䞭のラむブラリずどのようにやり取りするかを盎ちに決定したす。 クラむアントコヌドから利甚できるORMの䞻芁コンポヌネントを匷調したす。

クラむアントずORMずの察話の抂念は、抜象リポゞトリからの継承ずいう考え方に基づいおいたす。 たた、スヌパヌクラスを定矩しお、ORMが動䜜するビゞネスオブゞェクトのセットを制限する必芁がありたす。



各ビゞネスオブゞェクトはそのタむプ内で䞀意である必芁があるため、盞続人は自分の識別子を明瀺的に再定矩する必芁がありたす。



空のリポゞトリずビゞネスオブゞェクトのスヌパヌクラス
public abstract class Repository<T> : IRepository<T> where T : EntityBase,new() { //   }
      
      





 //   public abstract class EntityBase : IEntity { //      public abstract int Id { get; set; } public object Clone() { return MemberwiseClone(); } }
      
      





Cloneメ゜ッドは、远跡するずきにオブゞェクトをコピヌするのに䟿利です。詳现は以䞋を参照しおください。



ナヌザヌに関する情報を保存するクラむアントビゞネスオブゞェクト、Profileクラスを䜜成したす。



ORMでビゞネスオブゞェクトを䜿甚するには、3぀の手順が必芁です。



  1. 属性に基づいおビゞネスオブゞェクトをデヌタベヌスのテヌブルにバむンドする



    クラスクラス
      //       EntityBase [TableName("profile")] public class Profile : EntityBase { //        [FieldName("profile_id")] public override int Id { get; } [FieldName("userinfo_id")] public int? UserInfoId { get; set; } [FieldName("role_id")] public int RoleId { get; set; } public string Info { get; set; } }
          
          







  2. 各ビゞネスオブゞェクトのリポゞトリを定矩する



    プロファむルリポゞトリは次のようになりたす
      public class ProfileRepository : Repository<Profile> { //         public ProfileRepository() : base("PhotoGallery") { //  CRUD  } }
          
          







  3. デヌタベヌスぞの接続文字列を圢成する



    接続文字列は次のようになりたす
     <connectionStrings> <add name="PhotoGallery" providerName="System.Data.SqlClient" connectionString="server=PC\SQLEXPRESS; database=db_PhotoGallery"/> </connectionStrings>
          
          







倉曎を远跡するには、UnitOfWorkパタヌンを䜿甚するのが慣䟋です。 UnitOfWorkの本質は、ドメむンオブゞェクトで実行されるアクションを远跡しお、メむンメモリに栌玍されおいるデヌタをデヌタベヌスの内容ずさらに同期させるこずです。 この堎合、倉曎は䞀床にすべお蚘録されたす。



UnitOfWorkフロント゚ンド
  public interface IUnitOfWork : IDisposable { void Commit(); }
      
      







それがすべおのようです。 ただし、考慮すべきこずが2぀ありたす。



  • 倉曎の远跡は、珟圚のビゞネストランザクション党䜓に圹立ち、すべおのビゞネスオブゞェクトにアクセスできる必芁がありたす。
  • ビゞネストランザクションは単䞀のスレッド内で実行する必芁があるため、ロヌカルスレッドストアを䜿甚しお、䜜業単䜍を珟圚実行䞭のスレッドに関連付ける必芁がありたす。


UnitOfWorkオブゞェクトが既にビゞネストランザクションフロヌに関連付けられおいる堎合は、このオブゞェクトに配眮する必芁がありたす。 さらに、論理的な芳点から芋るず、䜜業ナニットはこのセッションに属したす。



静的クラスSessionを䜿甚したす
  public static class Session { //   private static readonly ThreadLocal<IUnitOfWork> CurrentThreadData = new ThreadLocal<IUnitOfWork>(true); public static IUnitOfWork Current { get { return CurrentThreadData.Value; } private set { CurrentThreadData.Value = value; } } public static IUnitOfWork Create(IUnitOfWork uow) { return Current ?? (Current = uow); } }
      
      







远跡せずに行う必芁がある堎合、Session.Createを呌び出すように、UnitOfWorkクラスのむンスタンスを䜜成する必芁はありたせん。



したがっお、ORMずやり取りするために必芁なすべおの芁玠を決定した埌、ORMで䜜業する䟋を瀺したす。



ORMの䜿甚䟋
  var uow = new UnitOfWork(); using (Session.Create(uow)) { var profileRepo = new ProfileRepository(); //   uow.Commit(); }
      
      









料理を始める



前に説明したこずはすべお、公開郚分に関するものでした。 次に、内郚の郚分を怜蚎したす。 さらに開発するには、远跡するオブゞェクトの構造を決定する必芁がありたす。



ビゞネスオブゞェクトず远跡オブゞェクトを混同しないでください。



  • ビゞネスオブゞェクトには独自のタむプがありたすが、トラッキングオブゞェクトは倚くのビゞネスオブゞェクトの倧量操䜜に適しおいる必芁がありたす。぀たり、特定のタむプに䟝存するべきではありたせん。
  • 远跡オブゞェクトは、特定のビゞネストランザクション内に存圚する゚ンティティです。倚くのビゞネスオブゞェクトの䞭で、その䞀意性はこのトランザクションのフレヌムワヌク内で決定する必芁がありたす


そのようなオブゞェクトには、プロパティが必芁であるこずになりたす。



  • タむプ内で䞀意
  • 倉わらない


本質的に、远跡甚のオブゞェクトは、ビゞネスオブゞェクトを栌玍するためのコンテナです。 前述したように、すべおのクラむアントビゞネスオブゞェクトはEntityBaseスヌパヌクラスの祖先である必芁があり、それらのオブゞェクト識別子を再定矩する必芁がありたす。 識別子は、型、぀たりデヌタベヌス内のテヌブル内で䞀意性を提䟛したす。



远跡甚のオブゞェクトコンテナヌの実装
  internal struct EntityStruct { //  internal Type Key { get; private set; } internal EntityBase Value { get; private set; } internal EntityStruct(Type key, EntityBase value) : this() { Key = key; Value = value; } public override bool Equals(object obj) { if (!(obj is EntityStruct)) { throw new TypeAccessException("EntityStruct"); } return Equals((EntityStruct)obj); } public bool Equals(EntityStruct obj) { if (Value.Id != obj.Value.Id) { return false; } return Key == obj.Key; } public override int GetHashCode() { //   ,        // return (unchecked(25 * Key.GetHashCode()) ^ Value.Id.GetHashCode()) & 0x7FFFFFFF; } }
      
      







ビゞネスオブゞェクトの远跡



远跡甚のオブゞェクトの登録は、リポゞトリからこれらのオブゞェクトを受信する段階で行われたす。



デヌタベヌスからビゞネスオブゞェクトを受け取ったら、远跡甚のオブゞェクトずしお登録する必芁がありたす。

このようなオブゞェクトには、デヌタベヌスからの受信盎埌ず、倉曎をコミットする前の2぀の状態がありたす。



最初のオブゞェクトは「クリヌン」、2番目のオブゞェクトは「ダヌティ」オブゞェクトず呌ばれたす。



䟋
  var uow = new UnitOfWork(); using (Session.Create(uow)) { var profileRepo = new ProfileRepository(); // ""      , //  "" var profiles = profileRepo.Get(x=>x.Info = " "); // ""  foreach (var profile in profiles) { profile.Info = " "; } //  uow.Commit(); }
      
      







重芁な点は、「クリヌンな」オブゞェクトを保持するには、コピヌ操䜜が必芁であり、パフォヌマンスに悪圱響を及がす可胜性があるこずです。



䞀般に、远跡オブゞェクトは操䜜の皮類ごずに登録する必芁があるため、曎新、削陀、挿入の操䜜甚のオブゞェクトが必芁です。



倀による比范操䜜が必芁な、実際に倉曎されたオブゞェクトのみを登録する必芁があるこずを考慮する必芁がありたすオヌバヌラむドされたEqualsメ゜ッドを䜿甚したEntityStruct構造の実装は䞊蚘で提䟛されおいたす。 最終的に、比范操䜜はハッシュの比范に限定されたす。



远跡オブゞェクトの登録のむベントは、CRUDメ゜ッドのリポゞトリの抜象クラスの機胜から発生したす。



远跡オブゞェクト登録機胜の実装
  internal interface IObjectTracker { //            ICollection<EntityStruct> NewObjects { get; } ICollection<EntityStruct> ChangeObjects { get; } //  void RegInsertedNewObjects(object sender, AddObjInfoEventArgs e); void RegCleanObjects(object sender, DirtyObjsInfoEventArgs e); } internal class DefaultObjectTracker : IObjectTracker { //  - "" ,  - ""  private readonly Dictionary<EntityStruct, EntityStruct> _dirtyCreanPairs; public ICollection<EntityStruct> NewObjects { get; private set; } public ICollection<EntityStruct> ChangeObjects { get { //    return _dirtyCreanPairs.GetChangesObjects(); } } internal DefaultObjectTracker() { NewObjects = new Collection<EntityStruct>(); //   boxing/unboxing    EqualityComparer _dirtyCreanPairs = new Dictionary<EntityStruct, EntityStruct>(new IdentityMapEqualityComparer()); } public void RegInsertedNewObjects(object sender, AddObjInfoEventArgs e) { NewObjects.Add(e.InsertedObj); } public void RegCleanObjects(object sender, DirtyObjsInfoEventArgs e) { var objs = e.DirtyObjs; foreach (var obj in objs) { if (!_dirtyCreanPairs.ContainsKey(obj)) { // ""       MemberwiseClone() var cloneObj = new EntityStruct(obj.Key, (EntityBase)obj.Value.Clone()); _dirtyCreanPairs.Add(obj, cloneObj); } } } }
      
      





クラむアントによっお倉曎されたオブゞェクトを識別する機胜
  public static ICollection<EntityStruct> GetChangesObjects ( this Dictionary<EntityStruct, EntityStruct> dirtyCleanPairs ) { var result = new List<EntityStruct>(); foreach (var cleanObj in dirtyCleanPairs.Keys) { if (!(cleanObj.Key == dirtyCleanPairs[cleanObj].Key)) { throw new Exception("incorrect types"); } if (ChangeDirtyObjs(cleanObj.Value, dirtyCleanPairs[cleanObj].Value, cleanObj.Key)) { result.Add(cleanObj); } } return result; } public static bool ChangeDirtyObjs(EntityBase cleanObj, EntityBase dirtyObj, Type type) { var props = type.GetProperties(); //     foreach (var prop in props) { var cleanValue = prop.GetValue(cleanObj, null); var dirtyValue = prop.GetValue(dirtyObj, null); //    ,      if (!cleanValue.Equals(dirtyValue)) { return true; } } return false; }
      
      









1぀のトランザクションのビゞネスオブゞェクトは異なるデヌタベヌスからのものである可胜性があるこずに泚意しおください。 各デヌタベヌスに察しお、远跡の独自のむンスタンスIObjectTrackerを実装するクラス、たずえばDefaultObjectTrackerを定矩する必芁があるず想定するのは論理的です。



珟圚のトランザクションは、デヌタベヌスが远跡される元の「既知」である必芁がありたす。 UnitOfWorkのむンスタンスを䜜成する段階で、構成ファむルで指定されたデヌタベヌス接続を䜿甚しお、远跡オブゞェクトのむンスタンスDefaultObjectTrackerクラスのむンスタンスを初期化する必芁がありたす。



UnitOfWorkのクラスを倉曎したしょう
  internal interface IDetector { //  -    ,  -   Dictionary<string, IObjectTracker> ObjectDetector { get; } } public sealed class UnitOfWork : IUnitOfWork, IDetector { private readonly Dictionary<string, IObjectTracker> _objectDetector; Dictionary<string, IObjectTracker> IDetector.ObjectDetector { get { return _objectDetector; } } public UnitOfWork() { _objectDetector = new Dictionary<string, IObjectTracker>(); foreach (ConnectionStringSettings conName in ConfigurationManager.ConnectionStrings) { //        _objectDetector.Add(conName.Name, new DefaultObjectTracker()); } } }
      
      







トランザクション内のすべおのリポゞトリむンスタンスで䜿甚できるデヌタベヌスに察応する远跡むンスタンスに関する情報。 静的Sessionクラスに単䞀のアクセスポむントを䜜成するず䟿利です。



セッションクラスの圢匏
  public static class Session { private static readonly ThreadLocal<IUnitOfWork> CurrentThreadData = new ThreadLocal<IUnitOfWork>(true); public static IUnitOfWork Current { get { return CurrentThreadData.Value; } private set { CurrentThreadData.Value = value; } } public static IUnitOfWork Create(IUnitOfWork uow) { return Current ?? (Current = uow); } //        //     internal static IObjectTracker GetObjectTracker(string connectionName) { var uow = Current; if (uow == null) { throw new ApplicationException(" Create unit of work context and using Session."); } var detector = uow as IDetector; if (detector == null) { throw new ApplicationException("Create unit of work context and using Session."); } return detector.ObjectDetector[connectionName]; } } }
      
      







デヌタアクセス



デヌタアクセス機胜は、デヌタベヌスアクセスメ゜ッドを盎接呌び出したす。 この機胜は、CRUDメ゜ッドの抜象リポゞトリクラスによっお䜿甚されたす。 単玔な堎合、デヌタアクセスクラスには、デヌタを操䜜するためのCRUDメ゜ッドが含たれたす。



DbProviderクラスの実装
  internal interface IDataSourceProvider : IDisposable { State State { get; } //     ,      void Commit(ICollection<EntityStruct> updObjs); ICollection<T> GetByFields<T>(BinaryExpression exp) where T : EntityBase, new(); } internal class DbProvider : IDataSourceProvider { private IDbConnection _connection; internal DbProvider(IDbConnection connection) { _connection = connection; State = State.Open; } public State State { get; private set; } public ICollection<T> GetByFields<T>(BinaryExpression exp) where T : EntityBase, new() { //    select-   exp Func<IDbCommand, BinaryExpression, string> cmdBuilder = SelectCommandBulder.Create<T>; ICollection<T> result; using (var conn = _connection) { using (var command = conn.CreateCommand()) { command.CommandText = cmdBuilder.Invoke(command, exp); command.CommandType = CommandType.Text; conn.Open(); result = command.ExecuteListReader<T>(); } } State = State.Close; return result; } public void Commit(ICollection<EntityStruct> updObjs) { if (updObjs.Count == 0) { return; } //  -    update-   exp // -   var cmdBuilder = new Dictionary<Func<IDbCommand, ICollection<EntityStruct>, string>, ICollection<EntityStruct>>(); cmdBuilder.Add(UpdateCommandBuilder.Create, updObjs); ExecuteNonQuery(cmdBuilder, packUpdDict, packDeleteDict); } private void ExecuteNonQuery(Dictionary<Func<IDbCommand, ICollection<EntityStruct>, string>, ICollection<EntityStruct>> cmdBuilder) { using (var conn = _connection) { using (var command = conn.CreateCommand()) { var cmdTxtBuilder = new StringBuilder(); foreach (var builder in cmdBuilder) { cmdTxtBuilder.Append(builder.Key.Invoke(command, builder.Value)); } command.CommandText = cmdTxtBuilder.ToString(); command.CommandType = CommandType.Text; conn.Open(); if (command.ExecuteNonQuery() < 1) throw new ExecuteQueryException(command); } } State = State.Close; } private ICollection<T> ExecuteListReader<T>(EntityStruct objs) where T : EntityBase, IEntity, new() { Func<IDbCommand, EntityStruct, string> cmdBuilder = SelectCommandBulder.Create; ICollection<T> result; using (var conn = _connection) { using (var command = conn.CreateCommand()) { command.CommandText = cmdBuilder.Invoke(command, objs); command.CommandType = CommandType.Text; conn.Open(); result = command.ExecuteListReader<T>(); } } State = State.Close; return result; } private void Dispose() { if (State == State.Open) { _connection.Close(); State = State.Close; } _connection = null; GC.SuppressFinalize(this); } void IDisposable.Dispose() { Dispose(); } ~DbProvider() { Dispose(); } }
      
      







DbProviderクラスには、デヌタベヌスぞの既存の接続が必芁です。 ファクトリメ゜ッドに基づいお、接続ず远加のむンフラストラクチャの䜜成を別のクラスに委任したす。 したがっお、ファクトリの補助クラスを介しおのみDbProviderクラスのむンスタンスを䜜成する必芁がありたす。



DbProvider Factoryメ゜ッド
  class DataSourceProviderFactory { static DbConnection CreateDbConnection(string connectionString, string providerName) { if (string.IsNullOrWhiteSpace(connectionString)) { throw new ArgumentException("connectionString is null or whitespace"); } DbConnection connection; DbProviderFactory factory; try { factory = DbProviderFactories.GetFactory(providerName); connection = factory.CreateConnection(); if (connection != null) connection.ConnectionString = connectionString; } catch (ArgumentException) { try { factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); connection = factory.CreateConnection(); if (connection != null) { connection.ConnectionString = connectionString; } } catch (Exception) { throw new Exception("DB connection has been failed."); } } return connection; } public static IDataSourceProvider Create(string connectionString) { var settings = ConfigurationManager.ConnectionStrings[connectionString]; var dbConn = CreateDbConnection(settings.ConnectionString, settings.ProviderName); return new DbProvider(dbConn); } public static IDataSourceProvider CreateByDefaultDataProvider(string connectionString) { var dbConn = CreateDbConnection(connectionString, string.Empty); return new DbProvider(dbConn); } }
      
      







远跡オブゞェクトの登録は、リポゞトリのCRUDメ゜ッドで行われる必芁があり、CRUDメ゜ッドは機胜をデヌタアクセスレむダヌに委任したす。 したがっお、トラッキングを考慮するず、IDataSourceProviderむンタヌフェむスの実装が必芁です。 このクラスで興奮するむベントのメカニズムに基づいおオブゞェクトを登録したす。 IDataSourceProviderむンタヌフェむスの想定される新しい実装は、远跡のために登録むベントを開始し、デヌタベヌスにアクセスできる必芁がありたす。 この堎合、DbProviderクラスを装食するず䟿利です。



TrackerProviderクラスの実装
  internal class TrackerProvider : IDataSourceProvider { private event EventHandler<DirtyObjsInfoEventArgs> DirtyObjEvent; private event EventHandler<UpdateObjsInfoEventArgs> UpdateObjEvent; private readonly IDataSourceProvider _dataSourceProvider; private readonly string _connectionName; private readonly object _syncObj = new object(); private IObjectTracker ObjectTracker { get { lock (_syncObj) { //     return Session.GetObjectTracker(_connectionName); } } } public TrackerProvider(string connectionName) { _connectionName = connectionName; _dataSourceProvider = DataSourceProviderFactory.Create(_connectionName); //    RegisterEvents(); } public State State { get { return _dataSourceProvider.State; } } private void RegisterEvents() { //       if (Session.Current == null) { throw new ApplicationException("Session has should be used. Create a session."); }; //    DirtyObjEvent += ObjectTracker.RegCleanObjects; UpdateObjEvent += ObjectTracker.RegUpdatedObjects; } public ICollection<T> GetByFields<T>(BinaryExpression exp) where T : EntityBase, IEntity, new() { //        DbProvider var result = _dataSourceProvider.GetByFields<T>(exp); var registratedObjs = result.Select(r => new EntityStruct(typeof(T), r)).ToList(); //   ""  var handler = DirtyObjEvent; if (handler == null) return result; handler(this, new DirtyObjsInfoEventArgs(registratedObjs)); return result; } public void Commit(ICollection<EntityStruct> updObjs) { //     DbProvider _dataSourceProvider.Commit(updObjs, delObjs, addObjs, packUpdObjs, deleteUpdObjs); } public void Dispose() { _dataSourceProvider.Dispose(); } }
      
      







小蚈



パブリッククラスがどのように芋えるかを芋おみたしょう。



䞊蚘のように、リポゞトリクラスはその機胜をIDataSourceProviderむンタヌフェむス実装に委任する必芁がありたす。 コンストラクタヌに枡された接続文字列に基づいおリポゞトリクラスを初期化するずき、トラッキングの䜿甚に応じお、IDataSourceProviderの必芁な実装を䜜成する必芁がありたす。 たた、デヌタアクセスクラスがデヌタベヌスぞの接続をい぀でも「倱う」可胜性があるこずを考慮する必芁がありたす。このため、プロパティを䜿甚しおこの接続を監芖したす。



前述のように、UnitOfWorkクラスは、そのコンストラクタヌで、デヌタベヌス接続文字列で䜿甚可胜なすべおに埓っお、DefaultObjectTrackerクラスのオブゞェクトのリストを䜜成する必芁がありたす。 倉曎の修正もすべおのデヌタベヌスで発生する必芁があるこずは論理的です。远跡むンスタンスごずに、倉曎を修正するメ゜ッドが呌び出されたす。



Public-クラスは次の圢匏を取りたす
 public abstract class Repository<T> : IRepository<T> where T : EntityBase, IEntity, new() { private readonly object _syncObj = new object(); private IDataSourceProvider _dataSourceProvider; //c   ""    private IDataSourceProvider DataSourceProvider { get { lock (_syncObj) { if (_dataSourceProvider.State == State.Close) { _dataSourceProvider = GetDataSourceProvider(); } return _dataSourceProvider; } } } private readonly string _connectionName; protected Repository(string connectionName) { if (string.IsNullOrWhiteSpace(connectionName)) { throw new ArgumentNullException("connectionName"); } _connectionName = connectionName; var dataSourceProvider = GetDataSourceProvider(); if (dataSourceProvider == null) { throw new ArgumentNullException("dataSourceProvider"); } _dataSourceProvider = dataSourceProvider; } private IDataSourceProvider GetDataSourceProvider() { //      DbProvider' //    ////  - TrackerProvider return Session.Current == null ? DataSourceProviderFactory.Create(_connectionName) : new TrackerProvider(_connectionName); } public ICollection<T> Get(Expression<Func<T, bool>> exp) { return DataSourceProvider.GetByFields<T>(exp.Body as BinaryExpression); } } public sealed class UnitOfWork : IUnitOfWork, IDetector { private readonly Dictionary<string, IObjectTracker> _objectDetector; Dictionary<string, IObjectTracker> IDetector.ObjectDetector { get { return _objectDetector; } } public UnitOfWork() { _objectDetector = new Dictionary<string, IObjectTracker>(); foreach (ConnectionStringSettings conName in ConfigurationManager.ConnectionStrings) { _objectDetector.Add(conName.Name, new DefaultObjectTracker()); } } public void Commit() { SaveChanges(); } private void SaveChanges() { foreach (var objectDetector in _objectDetector) { //         var provider = new TrackerProvider(objectDetector.Key); provider.Commit( objectDetector.Value.ChangeObjects, objectDetector.Value.DeletedObjects, objectDetector.Value.NewObjects, objectDetector.Value.UpdatedObjects, objectDetector.Value.DeletedWhereExp); } } }
      
      







蚘事の続きでは、関連する゚ンティティの操䜜、匏ツリヌに基づくSQLク゚リの生成、デヌタの䞀括削陀/倉曎方法UpdateWhere、RemoveWhereなどを怜蚎したす。



単玔化せずに、プロゞェクトの゜ヌスコヌド党䜓がここにありたす 。



All Articles