Commit 9ce736d
Changed files (12)
trunk
product
MyMoney
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\" />