Commit dd9cd1d
Changed files (15)
trunk
product
MyMoney
Infrastructure
trunk/product/MyMoney/Infrastructure/transactions2/ChangeTracker.cs
@@ -0,0 +1,45 @@
+using System.Collections.Generic;
+using System.Linq;
+using MoMoney.Utility.Extensions;
+
+namespace MoMoney.Infrastructure.transactions2
+{
+ public class ChangeTracker<T> : IChangeTracker<T>
+ {
+ readonly ITrackerEntryMapper<T> mapper;
+ readonly IStatementRegistry registry;
+ readonly IList<ITrackerEntry<T>> items;
+
+ public ChangeTracker(ITrackerEntryMapper<T> mapper, IStatementRegistry registry)
+ {
+ this.mapper = mapper;
+ this.registry = registry;
+ items = new List<ITrackerEntry<T>>();
+ }
+
+ public void register(T entity)
+ {
+ items.Add(mapper.map_from(entity));
+ }
+
+ public void commit_to(IDatabase database)
+ {
+ items.each(x => commit(x, database));
+ }
+
+ public bool is_dirty()
+ {
+ return items.Count(x => x.contains_changes()) > 0;
+ }
+
+ public void Dispose()
+ {
+ items.Clear();
+ }
+
+ void commit(ITrackerEntry<T> entry, IDatabase database)
+ {
+ if (entry.contains_changes()) database.apply(registry.prepare_command_for(entry.current));
+ }
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/transactions2/ChangeTrackerFactory.cs
@@ -0,0 +1,21 @@
+using MoMoney.Infrastructure.Container;
+
+namespace MoMoney.Infrastructure.transactions2
+{
+ public class ChangeTrackerFactory : IChangeTrackerFactory
+ {
+ readonly IStatementRegistry statement_registry;
+ readonly IDependencyRegistry registry;
+
+ public ChangeTrackerFactory(IStatementRegistry statement_registry, IDependencyRegistry registry)
+ {
+ this.statement_registry = statement_registry;
+ this.registry = registry;
+ }
+
+ public IChangeTracker<T> create_for<T>()
+ {
+ return new ChangeTracker<T>(registry.get_a<ITrackerEntryMapper<T>>(), statement_registry);
+ }
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/transactions2/ChangeTrackerFactorySpecs.cs
@@ -0,0 +1,20 @@
+using developwithpassion.bdd.contexts;
+using MoMoney.Domain.Core;
+using MoMoney.Testing.spechelpers.contexts;
+using MoMoney.Testing.spechelpers.core;
+
+namespace MoMoney.Infrastructure.transactions2
+{
+ public class ChangeTrackerFactorySpecs
+ {
+ }
+
+ public class when_creating_a_change_tracker_for_an_item : concerns_for<IChangeTrackerFactory, ChangeTrackerFactory>
+ {
+ it should_return_a_new_tracker = () => { result.should_not_be_null(); };
+
+ because b = () => { result = sut.create_for<IEntity>(); };
+
+ static IChangeTracker<IEntity> result;
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/transactions2/ChangeTrackerSpecs.cs
@@ -0,0 +1,100 @@
+using developwithpassion.bdd.contexts;
+using MoMoney.Domain.Core;
+using MoMoney.Testing.MetaData;
+using MoMoney.Testing.spechelpers.contexts;
+using MoMoney.Testing.spechelpers.core;
+
+namespace MoMoney.Infrastructure.transactions2
+{
+ public class ChangeTrackerSpecs
+ {
+ }
+
+ [Concern(typeof (ChangeTracker<IEntity>))]
+ public abstract class behaves_like_change_tracker : concerns_for<IChangeTracker<IEntity>, ChangeTracker<IEntity>>
+ {
+ context c = () =>
+ {
+ mapper = the_dependency<ITrackerEntryMapper<IEntity>>();
+ registry = the_dependency<IStatementRegistry>();
+ };
+
+ protected static ITrackerEntryMapper<IEntity> mapper;
+ protected static IStatementRegistry registry;
+ }
+
+ public class when_commit_that_changes_made_to_an_item : behaves_like_change_tracker
+ {
+ it should_save_the_changes_to_the_database = () => database.was_told_to(x => x.apply(statement));
+
+ context c = () =>
+ {
+ item = an<IEntity>();
+ statement = an<IStatement>();
+ database = an<IDatabase>();
+ var entry = an<ITrackerEntry<IEntity>>();
+
+ when_the(mapper).is_told_to(x => x.map_from(item)).it_will_return(entry);
+ when_the(entry).is_told_to(x => x.contains_changes()).it_will_return(true);
+ when_the(entry).is_told_to(x => x.current).it_will_return(item);
+ when_the(registry).is_told_to(x => x.prepare_command_for(item)).it_will_return(statement);
+ };
+
+ because b = () =>
+ {
+ sut.register(item);
+ sut.commit_to(database);
+ };
+
+ static IEntity item;
+ static IDatabase database;
+ static IStatement statement;
+ }
+
+ public class when_checking_if_there_are_changes_and_there_are : behaves_like_change_tracker
+ {
+ it should_tell_the_truth = () => result.should_be_true();
+
+ context c = () =>
+ {
+ item = an<IEntity>();
+ var registration = an<ITrackerEntry<IEntity>>();
+
+ when_the(mapper).is_told_to(x => x.map_from(item)).it_will_return(registration);
+ when_the(registration).is_told_to(x => x.contains_changes()).it_will_return(true);
+ when_the(registration).is_told_to(x => x.current).it_will_return(item);
+ };
+
+ because b = () =>
+ {
+ sut.register(item);
+ result = sut.is_dirty();
+ };
+
+ static bool result;
+ static IEntity item;
+ }
+
+ public class when_checking_if_there_are_changes_and_there_are_not : behaves_like_change_tracker
+ {
+ it should_tell_the_truth = () => result.should_be_false();
+
+ context c = () =>
+ {
+ item = an<IEntity>();
+ var entry = an<ITrackerEntry<IEntity>>();
+
+ when_the(mapper).is_told_to(x => x.map_from(item)).it_will_return(entry);
+ when_the(entry).is_told_to(x => x.contains_changes()).it_will_return(false);
+ };
+
+ because b = () =>
+ {
+ sut.register(item);
+ result = sut.is_dirty();
+ };
+
+ static bool result;
+ static IEntity item;
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/transactions2/Database.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using MoMoney.Domain.Core;
+
+namespace MoMoney.Infrastructure.transactions2
+{
+ public class Database : IDatabase
+ {
+ public IEnumerable<T> fetch_all<T>() where T : IEntity
+ {
+ throw new NotImplementedException();
+ }
+
+ public void apply(IStatement statement)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/transactions2/IChangeTracker.cs
@@ -1,3 +1,5 @@
+using System;
+
namespace MoMoney.Infrastructure.transactions2
{
public interface IChangeTracker
@@ -6,7 +8,7 @@ namespace MoMoney.Infrastructure.transactions2
void commit_to(IDatabase database);
}
- public interface IChangeTracker<T> : IChangeTracker
+ public interface IChangeTracker<T> : IChangeTracker, IDisposable
{
void register(T value);
}
trunk/product/MyMoney/Infrastructure/transactions2/IStatementRegistry.cs
@@ -1,9 +1,16 @@
+using System;
+
namespace MoMoney.Infrastructure.transactions2
{
public interface IStatementRegistry
{
+ [Obsolete]
IStatement prepare_insert_statement_for<T>(T entity);
+
+ [Obsolete]
IStatement prepare_update_statement_for<T>(T entity);
+
IStatement prepare_delete_statement_for<T>(T entity);
+ IStatement prepare_command_for<T>(T entity);
}
}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/transactions2/ITrackerEntry.cs
@@ -0,0 +1,8 @@
+namespace MoMoney.Infrastructure.transactions2
+{
+ public interface ITrackerEntry<T>
+ {
+ T current { get; }
+ bool contains_changes();
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/transactions2/ITrackerEntryMapper.cs
@@ -0,0 +1,8 @@
+using MoMoney.Utility.Core;
+
+namespace MoMoney.Infrastructure.transactions2
+{
+ public interface ITrackerEntryMapper<T> : IMapper<T, ITrackerEntry<T>>
+ {
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/transactions2/SessionFactory.cs
@@ -0,0 +1,27 @@
+using MoMoney.Utility.Core;
+
+namespace MoMoney.Infrastructure.transactions2
+{
+ public interface ISessionFactory : IFactory<ISession>
+ {
+ }
+
+ public class SessionFactory : ISessionFactory
+ {
+ readonly IDatabase database;
+ readonly IStatementRegistry registry;
+ readonly IChangeTrackerFactory factory;
+
+ public SessionFactory(IDatabase database, IStatementRegistry registry, IChangeTrackerFactory factory)
+ {
+ this.database = database;
+ this.registry = registry;
+ this.factory = factory;
+ }
+
+ public ISession create()
+ {
+ return new Session(new Transaction(registry, database, factory), database);
+ }
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/transactions2/SessionFactorySpecs.cs
@@ -0,0 +1,19 @@
+using developwithpassion.bdd.contexts;
+using MoMoney.Testing.spechelpers.contexts;
+using MoMoney.Testing.spechelpers.core;
+
+namespace MoMoney.Infrastructure.transactions2
+{
+ public class SessionFactorySpecs
+ {
+ }
+
+ public class when_creating_a_new_session : concerns_for<ISessionFactory, SessionFactory>
+ {
+ it should_return_a_new_session = () => result.should_not_be_null();
+
+ because b = () => { result = sut.create(); };
+
+ static ISession result;
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/transactions2/StatementRegistry.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace MoMoney.Infrastructure.transactions2
+{
+ public class StatementRegistry : IStatementRegistry
+ {
+ public IStatement prepare_insert_statement_for<T>(T entity)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IStatement prepare_update_statement_for<T>(T entity)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IStatement prepare_delete_statement_for<T>(T entity)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IStatement prepare_command_for<T>(T entity)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/transactions2/Transaction.cs
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using MoMoney.Domain.Core;
-using MoMoney.Infrastructure.Extensions;
using MoMoney.Utility.Extensions;
namespace MoMoney.Infrastructure.transactions2
@@ -25,7 +23,7 @@ namespace MoMoney.Infrastructure.transactions2
readonly List<IEntity> transients;
readonly List<IEntity> dirty;
readonly List<IEntity> to_be_deleted;
- IDictionary<Type, IChangeTracker> change_trackers;
+ readonly IDictionary<Type, IChangeTracker> change_trackers;
public Transaction(IStatementRegistry registry, IDatabase database, IChangeTrackerFactory factory)
{
@@ -76,12 +74,7 @@ namespace MoMoney.Infrastructure.transactions2
IChangeTracker<T> get_change_tracker_for<T>() where T : IEntity
{
- if (!change_trackers.ContainsKey(typeof(T)))
- {
- var tracker = factory.create_for<T>();
- this.log().debug("tracker: {0}", tracker);
- change_trackers.Add(typeof(T), tracker);
- }
+ if (!change_trackers.ContainsKey(typeof (T))) change_trackers.Add(typeof (T), factory.create_for<T>());
return change_trackers[typeof (T)].downcast_to<IChangeTracker<T>>();
}
}
trunk/product/MyMoney/Infrastructure/transactions2/TransactionSpecs.cs
@@ -77,14 +77,12 @@ namespace MoMoney.Infrastructure.transactions2
entity = an<IEntity>();
update_statement = an<IStatement>();
- Log.For(entity).debug("context");
when_the(registry).is_told_to(x => x.prepare_update_statement_for(entity)).it_will_return( update_statement).Repeat.Any();
};
because b =
() =>
{
- Log.For(entity).debug("because");
sut.add_dirty(entity);
sut.commit_changes();
};
trunk/product/MyMoney/MyMoney.csproj
@@ -211,6 +211,11 @@
<Compile Include="Domain\Core\range_specs.cs" />
<Compile Include="Domain\repositories\IBillRepository.cs" />
<Compile Include="Domain\repositories\ICompanyRepository.cs" />
+ <Compile Include="Infrastructure\transactions2\ChangeTracker.cs" />
+ <Compile Include="Infrastructure\transactions2\ChangeTrackerFactory.cs" />
+ <Compile Include="Infrastructure\transactions2\ChangeTrackerFactorySpecs.cs" />
+ <Compile Include="Infrastructure\transactions2\ChangeTrackerSpecs.cs" />
+ <Compile Include="Infrastructure\transactions2\Database.cs" />
<Compile Include="Infrastructure\transactions2\IChangeTracker.cs" />
<Compile Include="Infrastructure\transactions2\IChangeTrackerFactory.cs" />
<Compile Include="Infrastructure\transactions2\IdentityMapProxy.cs" />
@@ -302,8 +307,13 @@
<Compile Include="Infrastructure\transactions2\IDatabase.cs" />
<Compile Include="Infrastructure\transactions2\IStatement.cs" />
<Compile Include="Infrastructure\transactions2\IStatementRegistry.cs" />
+ <Compile Include="Infrastructure\transactions2\ITrackerEntry.cs" />
+ <Compile Include="Infrastructure\transactions2\ITrackerEntryMapper.cs" />
<Compile Include="Infrastructure\transactions2\Session.cs" />
+ <Compile Include="Infrastructure\transactions2\SessionFactory.cs" />
+ <Compile Include="Infrastructure\transactions2\SessionFactorySpecs.cs" />
<Compile Include="Infrastructure\transactions2\SessionSpecs.cs" />
+ <Compile Include="Infrastructure\transactions2\StatementRegistry.cs" />
<Compile Include="Infrastructure\transactions2\Transaction.cs" />
<Compile Include="Infrastructure\transactions2\TransactionSpecs.cs" />
<Compile Include="Infrastructure\transactions\IUnitOfWorkRegistrationFactory.cs" />