Commit 9ce736d

mokhan <mokhan@ce5e1baf-6525-42e4-a1b2-857ea38da20a>
2009-04-02 13:28:12
started implementing the transaction.
git-svn-id: https://svn.xp-dev.com/svn/mokhan-mo.money@134 ce5e1baf-6525-42e4-a1b2-857ea38da20a
1 parent 80961f7
trunk/product/MyMoney/Infrastructure/transactions2/IDatabase.cs
@@ -6,5 +6,6 @@ namespace MoMoney.Infrastructure.transactions2
     public interface IDatabase
     {
         IEnumerable<T> fetch_all<T>() where T : IEntity;
+        void apply(IStatement statement);
     }
 }
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/caching/IdentityMapSpecs.cs → trunk/product/MyMoney/Infrastructure/transactions2/IdentityMapSpecs.cs
@@ -3,7 +3,7 @@ using MoMoney.Testing.MetaData;
 using MoMoney.Testing.spechelpers.contexts;
 using MoMoney.Testing.spechelpers.core;
 
-namespace MoMoney.Infrastructure.caching
+namespace MoMoney.Infrastructure.transactions2
 {
     [Concern(typeof (IdentityMap<,>))]
     public class behaves_like_identity_map : concerns_for<IIdentityMap<int, string>, IdentityMap<int, string>>
trunk/product/MyMoney/Infrastructure/caching/IIdentityMap.cs → trunk/product/MyMoney/Infrastructure/transactions2/IIdentityMap.cs
@@ -1,7 +1,7 @@
 using System;
 using System.Collections.Generic;
 
-namespace MoMoney.Infrastructure.caching
+namespace MoMoney.Infrastructure.transactions2
 {
     public interface IIdentityMap<TKey, TValue>
     {
@@ -10,6 +10,7 @@ namespace MoMoney.Infrastructure.caching
         void update_the_item_for(TKey key, TValue new_value);
         bool contains_an_item_for(TKey key);
         TValue item_that_belongs_to(TKey key);
+        void remove(TKey key);
     }
 
     public class IdentityMap<TKey, TValue> : IIdentityMap<TKey, TValue>
@@ -50,5 +51,10 @@ namespace MoMoney.Infrastructure.caching
         {
             return contains_an_item_for(key) ? items_in_map[key] : default(TValue);
         }
+
+        public void remove(TKey key)
+        {
+            throw new NotImplementedException();
+        }
     }
 }
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/transactions2/IIdentityMapFactory.cs
@@ -1,10 +0,0 @@
-using System;
-using MoMoney.Infrastructure.caching;
-
-namespace MoMoney.Infrastructure.transactions2
-{
-    public interface IIdentityMapFactory
-    {
-        IIdentityMap<Guid, T> create_for<T>();
-    }
-}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/transactions2/IStatement.cs
@@ -0,0 +1,7 @@
+namespace MoMoney.Infrastructure.transactions2
+{
+    public interface IStatement
+    {
+        
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/transactions2/IStatementRegistry.cs
@@ -0,0 +1,8 @@
+namespace MoMoney.Infrastructure.transactions2
+{
+    public interface IStatementRegistry
+    {
+        IStatement prepare_insert_statement_for<T>(T entity);
+        IStatement prepare_update_statement_for<T>(T entity);
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/transactions2/ITransaction.cs
@@ -1,12 +0,0 @@
-using MoMoney.Domain.Core;
-
-namespace MoMoney.Infrastructure.transactions2
-{
-    public interface ITransaction
-    {
-        void add_transient<T>(T entity) where T : IEntity;
-        void add_dirty<T>(T modified) where T : IEntity;
-        void commit_changes();
-        void rollback_changes();
-    }
-}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/transactions2/Session.cs
@@ -2,7 +2,6 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using MoMoney.Domain.Core;
-using MoMoney.Infrastructure.caching;
 using MoMoney.Utility.Extensions;
 
 namespace MoMoney.Infrastructure.transactions2
@@ -13,19 +12,18 @@ namespace MoMoney.Infrastructure.transactions2
         IEnumerable<T> all<T>() where T : IEntity;
         void save<T>(T entity) where T : IEntity;
         void update<T>(T entity) where T : IEntity;
+        void delete<T>(T entity) where T : IEntity;
         void flush();
     }
 
     public class Session : ISession
     {
-        readonly IIdentityMapFactory factory;
         ITransaction transaction;
         readonly IDatabase database;
         readonly IDictionary<Type, object> identity_maps;
 
-        public Session(IIdentityMapFactory factory, ITransaction transaction, IDatabase database)
+        public Session(ITransaction transaction, IDatabase database)
         {
-            this.factory = factory;
             this.database = database;
             this.transaction = transaction;
             identity_maps = new Dictionary<Type, object>();
@@ -33,29 +31,28 @@ namespace MoMoney.Infrastructure.transactions2
 
         public IEntity find<T>(Guid id) where T : IEntity
         {
-            var identity_map = get_identity_map_for<T>();
-            if (identity_map.contains_an_item_for(id))
+            if (get_identity_map_for<T>().contains_an_item_for(id))
             {
-                return identity_map.item_that_belongs_to(id);
+                return get_identity_map_for<T>().item_that_belongs_to(id);
             }
+
             var entity = database.fetch_all<T>().Single(x => x.Id.Equals(id));
-            identity_map.add(id, entity);
+            get_identity_map_for<T>().add(id, entity);
             return entity;
         }
 
         public IEnumerable<T> all<T>() where T : IEntity
         {
-            var identity_map = get_identity_map_for<T>();
             var uncached_items = database
                 .fetch_all<T>()
-                .where(x => !identity_map.contains_an_item_for(x.Id));
-            uncached_items.each(x => identity_map.add(x.Id, x));
-            return uncached_items.join_with(identity_map.all());
+                .where(x => !get_identity_map_for<T>().contains_an_item_for(x.Id));
+            uncached_items.each(x => get_identity_map_for<T>().add(x.Id, x));
+            return get_identity_map_for<T>().all();
         }
 
         public void save<T>(T entity) where T : IEntity
         {
-            create_map_for<T>().add(entity.Id, entity);
+            get_identity_map_for<T>().add(entity.Id, entity);
             transaction.add_transient(entity);
         }
 
@@ -65,6 +62,12 @@ namespace MoMoney.Infrastructure.transactions2
             transaction.add_dirty(entity);
         }
 
+        public void delete<T>(T entity) where T : IEntity
+        {
+            transaction.mark_for_deletion(entity);
+            get_identity_map_for<T>().remove(entity.Id);
+        }
+
         public void flush()
         {
             transaction.commit_changes();
@@ -78,15 +81,14 @@ namespace MoMoney.Infrastructure.transactions2
 
         IIdentityMap<Guid, T> get_identity_map_for<T>()
         {
-            var type = typeof (T);
-            return identity_maps.ContainsKey(type)
-                       ? identity_maps[type].downcast_to<IIdentityMap<Guid, T>>()
+            return identity_maps.ContainsKey(typeof (T))
+                       ? identity_maps[typeof (T)].downcast_to<IIdentityMap<Guid, T>>()
                        : create_map_for<T>();
         }
 
         IIdentityMap<Guid, T> create_map_for<T>()
         {
-            var identity_map = factory.create_for<T>();
+            var identity_map = transaction.create_for<T>();
             identity_maps.Add(typeof (T), identity_map);
             return identity_map;
         }
trunk/product/MyMoney/Infrastructure/transactions2/SessionSpecs.cs
@@ -2,7 +2,6 @@ using System;
 using System.Collections.Generic;
 using developwithpassion.bdd.contexts;
 using MoMoney.Domain.Core;
-using MoMoney.Infrastructure.caching;
 using MoMoney.Testing.spechelpers.contexts;
 using MoMoney.Testing.spechelpers.core;
 
@@ -16,12 +15,10 @@ namespace MoMoney.Infrastructure.transactions2
     {
         context c = () =>
                         {
-                            factory = the_dependency<IIdentityMapFactory>();
                             transaction = the_dependency<ITransaction>();
                             database = the_dependency<IDatabase>();
                         };
 
-        protected static IIdentityMapFactory factory;
         protected static ITransaction transaction;
         protected static IDatabase database;
     }
@@ -39,7 +36,7 @@ namespace MoMoney.Infrastructure.transactions2
                             map = an<IIdentityMap<Guid, ITestEntity>>();
 
                             when_the(entity).is_told_to(x => x.Id).it_will_return(guid);
-                            when_the(factory).is_told_to(x => x.create_for<ITestEntity>()).it_will_return(map);
+                            when_the(transaction).is_told_to(x => x.create_for<ITestEntity>()).it_will_return(map);
                         };
 
         because b = () => sut.save(entity);
@@ -89,7 +86,7 @@ namespace MoMoney.Infrastructure.transactions2
                             map = an<IIdentityMap<Guid, ITestEntity>>();
                             when_the(original).is_told_to(x => x.Id).it_will_return(guid);
                             when_the(modified).is_told_to(x => x.Id).it_will_return(guid);
-                            when_the(factory).is_told_to(x => x.create_for<ITestEntity>()).it_will_return(map);
+                            when_the(transaction).is_told_to(x => x.create_for<ITestEntity>()).it_will_return(map);
                             when_the(database).is_told_to(x => x.fetch_all<ITestEntity>()).it_will_return(original);
                             when_the(map).is_told_to(x => x.all()).it_will_return_nothing();
                         };
@@ -127,7 +124,8 @@ namespace MoMoney.Infrastructure.transactions2
                             when_the(cached_item).is_told_to(x => x.Id).it_will_return(id);
                             when_the(database_item).is_told_to(x => x.Id).it_will_return(id);
                             when_the(uncached_item).is_told_to(x => x.Id).it_will_return(id_of_the_uncached_item);
-                            when_the(factory).is_told_to(x => x.create_for<ITestEntity>()).it_will_return(identity_map);
+                            when_the(transaction).is_told_to(x => x.create_for<ITestEntity>()).it_will_return(
+                                identity_map);
                             when_the(identity_map).is_told_to(x => x.contains_an_item_for(id)).it_will_return(true);
                             when_the(identity_map).is_told_to(x => x.all()).it_will_return(cached_item);
                             when_the(database).is_told_to(x => x.fetch_all<ITestEntity>())
@@ -167,7 +165,7 @@ namespace MoMoney.Infrastructure.transactions2
                             when_the(database)
                                 .is_told_to(x => x.fetch_all<ITestEntity>())
                                 .it_will_return(wrong_item, correct_item);
-                            when_the(factory).is_told_to(x => x.create_for<ITestEntity>())
+                            when_the(transaction).is_told_to(x => x.create_for<ITestEntity>())
                                 .it_will_return(map);
                         };
 
@@ -180,6 +178,35 @@ namespace MoMoney.Infrastructure.transactions2
         static IIdentityMap<Guid, ITestEntity> map;
     }
 
+    public class when_deleting_an_item_from_the_database : behaves_like_session
+    {
+        it should_remove_that_item_from_the_cache = () => map.was_told_to(x => x.remove(id));
+
+        it should_mark_the_item_for_deletion_when_the_transaction_is_committed =
+            () => transaction.was_told_to(x => x.mark_for_deletion(entity));
+
+        context c = () =>
+                        {
+                            id = Guid.NewGuid();
+                            entity = an<ITestEntity>();
+                            map = an<IIdentityMap<Guid, ITestEntity>>();
+
+                            when_the(entity).is_told_to(x => x.Id).it_will_return(id);
+                            when_the(transaction).is_told_to(x => x.create_for<ITestEntity>()).it_will_return(map);
+                            when_the(database).is_told_to(x => x.fetch_all<ITestEntity>()).it_will_return(entity);
+                        };
+
+        because b = () =>
+                        {
+                            sut.find<ITestEntity>(id);
+                            sut.delete(entity);
+                        };
+
+        static Guid id;
+        static IIdentityMap<Guid, ITestEntity> map;
+        static ITestEntity entity;
+    }
+
     public interface ITestEntity : IEntity
     {
     }
trunk/product/MyMoney/Infrastructure/transactions2/Transaction.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using MoMoney.Domain.Core;
+using MoMoney.Utility.Extensions;
+
+namespace MoMoney.Infrastructure.transactions2
+{
+    public interface ITransaction
+    {
+        IIdentityMap<Guid, T> create_for<T>();
+        void add_transient<T>(T entity) where T : IEntity;
+        void add_dirty<T>(T modified) where T : IEntity;
+        void mark_for_deletion<T>(T entity) where T : IEntity;
+        void commit_changes();
+        void rollback_changes();
+    }
+
+    public class Transaction : ITransaction
+    {
+        readonly IStatementRegistry registry;
+        readonly IDatabase database;
+        readonly ICollection<IEntity> transients;
+        readonly ICollection<IEntity> dirty;
+
+        public Transaction(IStatementRegistry registry, IDatabase database)
+        {
+            this.registry = registry;
+            this.database = database;
+            transients = new HashSet<IEntity>();
+            dirty = new HashSet<IEntity>();
+        }
+
+        public IIdentityMap<Guid, T> create_for<T>()
+        {
+            return new IdentityMap<Guid, T>();
+        }
+
+        public void add_transient<T>(T entity) where T : IEntity
+        {
+            transients.Add(entity);
+        }
+
+        public void add_dirty<T>(T modified) where T : IEntity
+        {
+            dirty.Add(modified);
+        }
+
+        public void mark_for_deletion<T>(T entity) where T : IEntity
+        {
+            throw new NotImplementedException();
+        }
+
+        public void commit_changes()
+        {
+            dirty.each(x => database.apply(registry.prepare_update_statement_for(x)));
+            transients.each(x => database.apply(registry.prepare_insert_statement_for(x)));
+        }
+
+        public void rollback_changes()
+        {
+            throw new NotImplementedException();
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/transactions2/TransactionSpecs.cs
@@ -0,0 +1,86 @@
+using System;
+using developwithpassion.bdd.contexts;
+using MoMoney.Testing.spechelpers.contexts;
+using MoMoney.Testing.spechelpers.core;
+
+namespace MoMoney.Infrastructure.transactions2
+{
+    public class TransactionSpecs
+    {
+    }
+
+    public class behaves_like_transaction : concerns_for<ITransaction, Transaction>
+    {
+        context c = () =>
+                        {
+                            registry = the_dependency<IStatementRegistry>();
+                            database = the_dependency<IDatabase>();
+                        };
+
+        protected static IStatementRegistry registry;
+        protected static IDatabase database;
+    }
+
+    public class when_creating_an_identity_map_for_a_specific_entity : behaves_like_transaction
+    {
+        it should_return_a_new_identity_map = () => result.should_not_be_null();
+
+        because b = () => { result = sut.create_for<ITestEntity>(); };
+
+        static IIdentityMap<Guid, ITestEntity> result;
+    }
+
+    public class when_commiting_a_set_of_transient_instances_to_The_database : behaves_like_transaction
+    {
+        it should_prepare_an_insert_command_for_each_transient_instance =
+            () => registry.was_told_to(x => x.prepare_insert_statement_for(entity));
+
+        it should_apply_the_insert_statement_against_the_database_for_each_entity =
+            () => database.was_told_to(x => x.apply(insert_statement));
+
+        context c = () =>
+                        {
+                            entity = an<ITestEntity>();
+                            insert_statement = an<IStatement>();
+                            when_the(registry)
+                                .is_told_to(x => x.prepare_insert_statement_for(entity))
+                                .it_will_return(insert_statement);
+                        };
+
+        because b = () =>
+                        {
+                            sut.add_transient(entity);
+                            sut.commit_changes();
+                        };
+
+        static ITestEntity entity;
+        static IStatement insert_statement;
+    }
+
+    public class when_commiting_a_set_of_dirty_instances_to_The_database : behaves_like_transaction
+    {
+        it should_prepare_an_insert_command_for_each_transient_instance =
+            () => registry.was_told_to(x => x.prepare_update_statement_for(entity));
+
+        it should_apply_the_insert_statement_against_the_database_for_each_entity =
+            () => database.was_told_to(x => x.apply(update_statement));
+
+        context c = () =>
+                        {
+                            entity = an<ITestEntity>();
+                            update_statement = an<IStatement>();
+                            when_the(registry)
+                                .is_told_to(x => x.prepare_update_statement_for(entity))
+                                .it_will_return(update_statement);
+                        };
+
+        because b = () =>
+                        {
+                            sut.add_dirty(entity);
+                            sut.commit_changes();
+                        };
+
+        static ITestEntity entity;
+        static IStatement update_statement;
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/MyMoney.csproj
@@ -211,8 +211,8 @@
     <Compile Include="Domain\Core\range_specs.cs" />
     <Compile Include="Domain\repositories\IBillRepository.cs" />
     <Compile Include="Domain\repositories\ICompanyRepository.cs" />
-    <Compile Include="Infrastructure\caching\IdentityMapSpecs.cs" />
-    <Compile Include="Infrastructure\caching\IIdentityMap.cs" />
+    <Compile Include="Infrastructure\transactions2\IdentityMapSpecs.cs" />
+    <Compile Include="Infrastructure\transactions2\IIdentityMap.cs" />
     <Compile Include="Infrastructure\cloning\BinarySerializer.cs" />
     <Compile Include="Infrastructure\cloning\BinarySerializerSpecs.cs" />
     <Compile Include="Infrastructure\cloning\ISerializer.cs" />
@@ -297,10 +297,12 @@
       <SubType>Component</SubType>
     </Compile>
     <Compile Include="Infrastructure\transactions2\IDatabase.cs" />
-    <Compile Include="Infrastructure\transactions2\IIdentityMapFactory.cs" />
-    <Compile Include="Infrastructure\transactions2\ITransaction.cs" />
+    <Compile Include="Infrastructure\transactions2\IStatement.cs" />
+    <Compile Include="Infrastructure\transactions2\IStatementRegistry.cs" />
     <Compile Include="Infrastructure\transactions2\Session.cs" />
     <Compile Include="Infrastructure\transactions2\SessionSpecs.cs" />
+    <Compile Include="Infrastructure\transactions2\Transaction.cs" />
+    <Compile Include="Infrastructure\transactions2\TransactionSpecs.cs" />
     <Compile Include="Infrastructure\transactions\IUnitOfWorkRegistrationFactory.cs" />
     <Compile Include="Infrastructure\transactions\NullUnitOfWork.cs" />
     <Compile Include="Infrastructure\transactions\UnitOfWork.cs" />
@@ -813,6 +815,7 @@
   </ItemGroup>
   <ItemGroup>
     <Folder Include="DataAccess\sqlcompact\" />
+    <Folder Include="Infrastructure\caching\" />
     <Folder Include="Presentation\module\" />
     <Folder Include="Presentation\Views\Menu\Mappers\" />
     <Folder Include="Tasks\domain\" />