diff --git a/Source/Bifrost.Specs/Bifrost.Specs.csproj b/Source/Bifrost.Specs/Bifrost.Specs.csproj
index daa7374e2..0d4f9d16f 100644
--- a/Source/Bifrost.Specs/Bifrost.Specs.csproj
+++ b/Source/Bifrost.Specs/Bifrost.Specs.csproj
@@ -106,6 +106,7 @@
+
@@ -326,6 +327,12 @@
+
+
+
+
+
+
@@ -374,6 +381,10 @@
+
+
+
+
diff --git a/Source/Bifrost.Specs/Commands/for_CommandCoordinator/given/a_command_coordinator.cs b/Source/Bifrost.Specs/Commands/for_CommandCoordinator/given/a_command_coordinator.cs
index b140ec093..8f7012812 100644
--- a/Source/Bifrost.Specs/Commands/for_CommandCoordinator/given/a_command_coordinator.cs
+++ b/Source/Bifrost.Specs/Commands/for_CommandCoordinator/given/a_command_coordinator.cs
@@ -1,6 +1,6 @@
using Bifrost.Commands;
+using Bifrost.Exceptions;
using Bifrost.Security;
-using Bifrost.Validation;
using Machine.Specifications;
using Moq;
@@ -13,15 +13,17 @@ public abstract class a_command_coordinator : Globalization.given.a_localizer_mo
protected static Mock command_context_manager_mock;
protected static Mock command_security_manager_mock;
protected static Mock command_validators_mock;
- protected static Mock command_mock;
protected static Mock command_context_mock;
+ protected static Mock exception_publisher_mock;
+ protected static ICommand command;
Establish context = () =>
{
- command_mock = new Mock();
+ command = Mock.Of();
command_handler_manager_mock = new Mock();
command_context_manager_mock = new Mock();
command_validators_mock = new Mock();
+ exception_publisher_mock = new Mock();
command_context_mock = new Mock();
command_context_manager_mock.Setup(c => c.EstablishForCommand(Moq.It.IsAny())).
@@ -36,7 +38,8 @@ public abstract class a_command_coordinator : Globalization.given.a_localizer_mo
command_context_manager_mock.Object,
command_security_manager_mock.Object,
command_validators_mock.Object,
- localizer_mock.Object
+ localizer_mock.Object,
+ exception_publisher_mock.Object
);
};
}
diff --git a/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_a_command_that_fails_security.cs b/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_a_command_that_fails_security.cs
index 51bd89192..59701207c 100644
--- a/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_a_command_that_fails_security.cs
+++ b/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_a_command_that_fails_security.cs
@@ -1,7 +1,6 @@
using Bifrost.Commands;
using Bifrost.Security;
using Machine.Specifications;
-using Bifrost.Testing.Fakes.Commands;
using Moq;
using It = Machine.Specifications.It;
@@ -10,7 +9,6 @@ namespace Bifrost.Specs.Commands.for_CommandCoordinator
[Subject(typeof(CommandCoordinator))]
public class when_handling_a_command_that_fails_security : given.a_command_coordinator
{
- static ICommand command;
static CommandResult result;
static Mock authorization_result;
@@ -19,8 +17,7 @@ public class when_handling_a_command_that_fails_security : given.a_command_coord
authorization_result = new Mock();
authorization_result.Setup(r => r.IsAuthorized).Returns(false);
authorization_result.Setup(r => r.BuildFailedAuthorizationMessages()).Returns(new[] { "Something went wrong" });
- command = new SimpleCommand();
- command_security_manager_mock.Setup(c => c.Authorize(Moq.It.IsAny())).Returns(authorization_result.Object);
+ command_security_manager_mock.Setup(c => c.Authorize(command)).Returns(authorization_result.Object);
};
Because of = () => result = coordinator.Handle(command);
diff --git a/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_an_invalid_command.cs b/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_an_invalid_command.cs
index 0295a0422..740f1b895 100644
--- a/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_an_invalid_command.cs
+++ b/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_an_invalid_command.cs
@@ -1,38 +1,40 @@
using Bifrost.Commands;
-using Machine.Specifications;
using Bifrost.Validation;
+using Machine.Specifications;
+using Moq;
+using It = Machine.Specifications.It;
namespace Bifrost.Specs.Commands.for_CommandCoordinator
{
[Subject(typeof(CommandCoordinator))]
public class when_handling_an_invalid_command : given.a_command_coordinator
{
- static CommandResult Result;
+ static CommandResult result;
static CommandValidationResult validation_errors;
Establish context = () =>
- {
- validation_errors = new CommandValidationResult
- {
- ValidationResults = new ValidationResult[]
- {
- new ValidationResult("First validation failure"),
- new ValidationResult("Second validation failure")
- }
- };
+ {
+ validation_errors = new CommandValidationResult
+ {
+ ValidationResults = new[]
+ {
+ new ValidationResult("First validation failure"),
+ new ValidationResult("Second validation failure")
+ }
+ };
- command_validators_mock.Setup(cvs => cvs.Validate(command_mock.Object)).Returns(
- validation_errors);
- };
+ command_validators_mock
+ .Setup(cvs => cvs.Validate(command))
+ .Returns(validation_errors);
+ };
- Because of = () => Result = coordinator.Handle(command_mock.Object);
-
+ Because of = () => result = coordinator.Handle(command);
It should_have_validated_the_command = () => command_validators_mock.VerifyAll();
- It should_have_a_result = () => Result.ShouldNotBeNull();
- It should_have_success_set_to_false = () => Result.Success.ShouldBeFalse();
- It should_have_a_record_of_each_validation_failure = () => Result.ValidationResults.ShouldContainOnly(validation_errors.ValidationResults);
- It should_not_handle_the_command = () => command_handler_manager_mock.Verify(chm => chm.Handle(command_mock.Object), Moq.Times.Never());
- It should_rollback_the_command_context = () => command_context_mock.Verify(c => c.Rollback(), Moq.Times.Once());
+ It should_have_a_result = () => result.ShouldNotBeNull();
+ It should_have_success_set_to_false = () => result.Success.ShouldBeFalse();
+ It should_have_a_record_of_each_validation_failure = () => result.ValidationResults.ShouldContainOnly(validation_errors.ValidationResults);
+ It should_not_handle_the_command = () => command_handler_manager_mock.Verify(chm => chm.Handle(command), Times.Never());
+ It should_rollback_the_command_context = () => command_context_mock.Verify(c => c.Rollback(), Times.Once());
}
}
\ No newline at end of file
diff --git a/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_command_and_an_exception_occurs_during_authorization.cs b/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_command_and_an_exception_occurs_during_authorization.cs
index 26ac148b3..27f0a48db 100644
--- a/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_command_and_an_exception_occurs_during_authorization.cs
+++ b/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_command_and_an_exception_occurs_during_authorization.cs
@@ -1,5 +1,6 @@
using System;
using Bifrost.Commands;
+using Bifrost.Exceptions;
using Machine.Specifications;
namespace Bifrost.Specs.Commands.for_CommandCoordinator
@@ -11,14 +12,15 @@ public class when_handling_command_and_an_exception_occurs_during_authorization
static Exception exception;
Establish context = () =>
- {
- exception = new Exception();
- command_security_manager_mock.Setup(cvs => cvs.Authorize(command_mock.Object)).Throws(exception);
- };
+ {
+ exception = new Exception();
+ command_security_manager_mock.Setup(cvs => cvs.Authorize(command)).Throws(exception);
+ };
- Because of = () => result = coordinator.Handle(command_mock.Object);
+ Because of = () => result = coordinator.Handle(command);
It should_have_exception_in_result = () => result.Exception.ShouldEqual(exception);
It should_have_success_set_to_false = () => result.Success.ShouldBeFalse();
+ It should_have_published_the_exception = () => exception_publisher_mock.Verify(m => m.Publish(exception));
}
}
\ No newline at end of file
diff --git a/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_command_and_an_exception_occurs_during_handling.cs b/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_command_and_an_exception_occurs_during_handling.cs
index 940a75abf..9aa5cd78a 100644
--- a/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_command_and_an_exception_occurs_during_handling.cs
+++ b/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_command_and_an_exception_occurs_during_handling.cs
@@ -1,8 +1,8 @@
using System;
using Bifrost.Commands;
-using Machine.Specifications;
-using It = Machine.Specifications.It;
+using Bifrost.Exceptions;
using Bifrost.Validation;
+using Machine.Specifications;
namespace Bifrost.Specs.Commands.for_CommandCoordinator
{
@@ -13,18 +13,19 @@ public class when_handling_command_and_an_exception_occurs_during_handling : giv
static Exception exception;
Establish context = () =>
- {
- exception = new Exception();
- var validation_results = new CommandValidationResult { ValidationResults = new ValidationResult[0] };
- command_validators_mock.Setup(cvs => cvs.Validate(command_mock.Object)).Returns(validation_results);
- command_handler_manager_mock.Setup(c => c.Handle(Moq.It.IsAny())).Throws(exception);
- };
+ {
+ exception = new Exception();
+ var validationResults = new CommandValidationResult {ValidationResults = new ValidationResult[0]};
+ command_validators_mock.Setup(cvs => cvs.Validate(command)).Returns(validationResults);
+ command_handler_manager_mock.Setup(c => c.Handle(Moq.It.IsAny())).Throws(exception);
+ };
- Because of = () => result = coordinator.Handle(command_mock.Object);
+ Because of = () => result = coordinator.Handle(command);
It should_have_validated_the_command = () => command_validators_mock.VerifyAll();
It should_have_authenticated_the_command = () => command_security_manager_mock.VerifyAll();
It should_have_exception_in_result = () => result.Exception.ShouldEqual(exception);
It should_have_success_set_to_false = () => result.Success.ShouldBeFalse();
+ It should_have_published_the_exception = () => exception_publisher_mock.Verify(m => m.Publish(exception));
}
-}
+}
\ No newline at end of file
diff --git a/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_command_and_an_exception_occurs_during_validation.cs b/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_command_and_an_exception_occurs_during_validation.cs
index b373404ec..a92f365b3 100644
--- a/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_command_and_an_exception_occurs_during_validation.cs
+++ b/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_command_and_an_exception_occurs_during_validation.cs
@@ -1,5 +1,6 @@
using System;
using Bifrost.Commands;
+using Bifrost.Exceptions;
using Machine.Specifications;
namespace Bifrost.Specs.Commands.for_CommandCoordinator
@@ -11,15 +12,16 @@ public class when_handling_command_and_an_exception_occurs_during_validation : g
static Exception exception;
Establish context = () =>
- {
- exception = new Exception();
- command_validators_mock.Setup(cvs => cvs.Validate(command_mock.Object)).Throws(exception);
- };
+ {
+ exception = new Exception();
+ command_validators_mock.Setup(cvs => cvs.Validate(command)).Throws(exception);
+ };
- Because of = () => result = coordinator.Handle(command_mock.Object);
+ Because of = () => result = coordinator.Handle(command);
It should_have_authorized_the_command = () => command_security_manager_mock.VerifyAll();
It should_have_exception_in_result = () => result.Exception.ShouldEqual(exception);
It should_have_success_set_to_false = () => result.Success.ShouldBeFalse();
+ It should_have_published_the_exception = () => exception_publisher_mock.Verify(m => m.Publish(exception));
}
}
\ No newline at end of file
diff --git a/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_command_with_success.cs b/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_command_with_success.cs
index e9e045ed1..d35de9f5c 100644
--- a/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_command_with_success.cs
+++ b/Source/Bifrost.Specs/Commands/for_CommandCoordinator/when_handling_command_with_success.cs
@@ -1,28 +1,20 @@
using Bifrost.Commands;
using Machine.Specifications;
-using It = Machine.Specifications.It;
-using Bifrost.Validation;
namespace Bifrost.Specs.Commands.for_CommandCoordinator
{
[Subject(typeof(CommandCoordinator))]
public class when_handling_command_with_success : given.a_command_coordinator
{
- static CommandResult Result;
-
+ static CommandResult result;
+
Establish context = () =>
- {
- var validation_results = new CommandValidationResult();
- command_validators_mock.Setup(cvs => cvs.Validate(command_mock.Object)).Returns(validation_results);
- };
+ command_validators_mock.Setup(cvs => cvs.Validate(command)).Returns(new CommandValidationResult());
- Because of = () =>
- {
- Result = coordinator.Handle(command_mock.Object);
- };
+ Because of = () => result = coordinator.Handle(command);
It should_have_validated_the_command = () => command_validators_mock.VerifyAll();
- It should_have_a_result = () => Result.ShouldNotBeNull();
- It should_have_success_set_to_true = () => Result.Success.ShouldBeTrue();
+ It should_have_a_result = () => result.ShouldNotBeNull();
+ It should_have_success_set_to_true = () => result.Success.ShouldBeTrue();
}
}
\ No newline at end of file
diff --git a/Source/Bifrost.Specs/Commands/for_CommandHandlerInvoker/when_receiving_asynchronous_initialization.cs b/Source/Bifrost.Specs/Commands/for_CommandHandlerInvoker/when_receiving_asynchronous_initialization.cs
new file mode 100644
index 000000000..27b017133
--- /dev/null
+++ b/Source/Bifrost.Specs/Commands/for_CommandHandlerInvoker/when_receiving_asynchronous_initialization.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Threading;
+using Bifrost.Commands;
+using Machine.Specifications;
+using Moq;
+using It = Machine.Specifications.It;
+
+namespace Bifrost.Specs.Commands.for_CommandHandlerInvoker
+{
+ [Subject(Subjects.handling_commands)]
+ public class when_receiving_asynchronous_initialization : given.a_command_handler_invoker_with_no_command_handlers
+ {
+ protected static bool result;
+
+ Because of = () =>
+ {
+ var command = new Command();
+ var thread = new Thread(() => invoker.TryHandle(command));
+
+ type_discoverer_mock
+ .Setup(t => t.FindMultiple())
+ .Callback(
+ () =>
+ {
+ thread.Start();
+ Thread.Sleep(50);
+ })
+ .Returns(new Type[0]);
+ result = invoker.TryHandle(command);
+ thread.Join();
+ };
+
+ It should_initialize_only_once = () =>
+ type_discoverer_mock.Verify(m => m.FindMultiple(), Times.Once);
+ }
+}
diff --git a/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/given/an_ordered_instances_of.cs b/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/given/an_ordered_instances_of.cs
new file mode 100644
index 000000000..9a6cad021
--- /dev/null
+++ b/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/given/an_ordered_instances_of.cs
@@ -0,0 +1,39 @@
+using System.Linq;
+using Bifrost.Execution;
+using Machine.Specifications;
+using Moq;
+
+namespace Bifrost.Specs.Execution.for_OrderedInstancesOf.given
+{
+ public class an_ordered_instances_of
+ {
+ public interface IDummy { }
+
+ protected static Mock type_discoverer_mock;
+ protected static Mock container_mock;
+ protected static OrderedInstancesOf ordered_instances_of;
+
+ protected static IDummy[] result;
+
+ Establish context = () =>
+ {
+ type_discoverer_mock = new Mock();
+ container_mock = new Mock();
+ };
+
+ protected static void Register(params IDummy[] instances)
+ {
+ type_discoverer_mock
+ .Setup(m => m.FindMultiple())
+ .Returns(instances.Select(i => i.GetType()));
+ foreach (var instance in instances)
+ {
+ container_mock
+ .Setup(m => m.Get(instance.GetType()))
+ .Returns(instance);
+ }
+
+ ordered_instances_of = new OrderedInstancesOf(type_discoverer_mock.Object, container_mock.Object);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/when_instances_are_ordered.cs b/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/when_instances_are_ordered.cs
new file mode 100644
index 000000000..7169b2ed9
--- /dev/null
+++ b/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/when_instances_are_ordered.cs
@@ -0,0 +1,28 @@
+using System.Linq;
+using Bifrost.Execution;
+using Machine.Specifications;
+
+namespace Bifrost.Specs.Execution.for_OrderedInstancesOf
+{
+ [Subject(typeof(OrderedInstancesOf<>))]
+ public sealed class when_instances_have_dependencies : given.an_ordered_instances_of
+ {
+ class Dummy1 : IDummy { }
+
+ [After(typeof(Dummy1))]
+ class Dummy2 : IDummy { }
+
+ [After(typeof(Dummy2))]
+ class Dummy3 : IDummy { }
+
+ static readonly IDummy dummy1 = new Dummy1();
+ static readonly IDummy dummy2 = new Dummy2();
+ static readonly IDummy dummy3 = new Dummy3();
+
+ Establish context = () => Register(dummy3, dummy2, dummy1);
+
+ Because of = () => result = ordered_instances_of.ToArray();
+
+ It should_return_correct_order = () => result.SequenceEqual(new[] {dummy1, dummy2, dummy3 }).ShouldBeTrue();
+ }
+}
\ No newline at end of file
diff --git a/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/when_instances_are_ordered_incorrectly.cs b/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/when_instances_are_ordered_incorrectly.cs
new file mode 100644
index 000000000..7cbe8ee10
--- /dev/null
+++ b/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/when_instances_are_ordered_incorrectly.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Linq;
+using Bifrost.Execution;
+using Machine.Specifications;
+
+namespace Bifrost.Specs.Execution.for_OrderedInstancesOf
+{
+ [Subject(typeof(OrderedInstancesOf<>))]
+ public sealed class when_instances_are_ordered_incorrectly : given.an_ordered_instances_of
+ {
+ static Exception exception;
+
+ [Order(1000)]
+ class Dummy1 : IDummy { }
+
+ [Order(3)]
+ [After(typeof(Dummy1))]
+ class Dummy2 : IDummy { }
+
+ static readonly IDummy dummy1 = new Dummy1();
+ static readonly IDummy dummy2 = new Dummy2();
+
+ Establish context = () => Register(dummy2, dummy1);
+
+ Because of = () => result = ordered_instances_of.ToArray();
+
+ It should_return_correct_order = () => result.SequenceEqual(new[] { dummy1, dummy2 }).ShouldBeTrue();
+ }
+}
\ No newline at end of file
diff --git a/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/when_instances_have_circular_dependencies.cs b/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/when_instances_have_circular_dependencies.cs
new file mode 100644
index 000000000..bb7bf2e85
--- /dev/null
+++ b/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/when_instances_have_circular_dependencies.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Linq;
+using Bifrost.Execution;
+using Machine.Specifications;
+using Moq;
+using It = Machine.Specifications.It;
+
+namespace Bifrost.Specs.Execution.for_OrderedInstancesOf
+{
+ [Subject(typeof(OrderedInstancesOf<>))]
+ public sealed class when_instances_have_circular_dependencies : given.an_ordered_instances_of
+ {
+ static Exception exception;
+
+ [After(typeof(Dummy2))]
+ class Dummy1 : IDummy { }
+
+ [After(typeof(Dummy1))]
+ class Dummy2 : IDummy { }
+
+ static readonly IDummy dummy1 = new Dummy1();
+ static readonly IDummy dummy2 = new Dummy2();
+
+ Establish context = () => Register(dummy1, dummy2);
+
+ Because of = () => exception = Catch.Only(() => ordered_instances_of.ToArray());
+
+ It should_throw_exception = () => exception.Message.ShouldContain("circular");
+
+ It should_not_have_created_the_instances = () =>
+ container_mock.Verify(m => m.Get(Moq.It.IsAny()), Times.Never);
+ }
+}
\ No newline at end of file
diff --git a/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/when_instances_have_dependencies.cs b/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/when_instances_have_dependencies.cs
new file mode 100644
index 000000000..24e888c5b
--- /dev/null
+++ b/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/when_instances_have_dependencies.cs
@@ -0,0 +1,29 @@
+using System.Linq;
+using Bifrost.Execution;
+using Machine.Specifications;
+
+namespace Bifrost.Specs.Execution.for_OrderedInstancesOf
+{
+ [Subject(typeof(OrderedInstancesOf<>))]
+ public sealed class when_instances_are_ordered : given.an_ordered_instances_of
+ {
+ [Order(1)]
+ class Dummy1 : IDummy { }
+
+ [Order(2)]
+ class Dummy2 : IDummy { }
+
+ [Order(3)]
+ class Dummy3 : IDummy { }
+
+ static readonly IDummy dummy1 = new Dummy1();
+ static readonly IDummy dummy2 = new Dummy2();
+ static readonly IDummy dummy3 = new Dummy3();
+
+ Establish context = () => Register(dummy3, dummy2, dummy1);
+
+ Because of = () => result = ordered_instances_of.ToArray();
+
+ It should_return_correct_order = () => result.SequenceEqual(new[] {dummy1, dummy2, dummy3 }).ShouldBeTrue();
+ }
+}
\ No newline at end of file
diff --git a/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/when_there_are_no_instances.cs b/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/when_there_are_no_instances.cs
new file mode 100644
index 000000000..58728fe31
--- /dev/null
+++ b/Source/Bifrost.Specs/Execution/for_OrderedInstancesOf/when_there_are_no_instances.cs
@@ -0,0 +1,16 @@
+using System.Linq;
+using Bifrost.Execution;
+using Machine.Specifications;
+
+namespace Bifrost.Specs.Execution.for_OrderedInstancesOf
+{
+ [Subject(typeof(OrderedInstancesOf<>))]
+ public sealed class when_there_are_no_instances : given.an_ordered_instances_of
+ {
+ Establish context = () => Register();
+
+ Because of = () => result = ordered_instances_of.ToArray();
+
+ It should_not_return_anything = () => result.ShouldBeEmpty();
+ }
+}
\ No newline at end of file
diff --git a/Source/Bifrost.Specs/Extensions/for_TypeExtensions/Custom1Attribute.cs b/Source/Bifrost.Specs/Extensions/for_TypeExtensions/Custom1Attribute.cs
new file mode 100644
index 000000000..1cd55590e
--- /dev/null
+++ b/Source/Bifrost.Specs/Extensions/for_TypeExtensions/Custom1Attribute.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace Bifrost.Specs.Extensions.for_TypeExtensions
+{
+ public class Custom1Attribute : Attribute
+ {
+ }
+}
\ No newline at end of file
diff --git a/Source/Bifrost.Specs/Extensions/for_TypeExtensions/Custom2Attribute.cs b/Source/Bifrost.Specs/Extensions/for_TypeExtensions/Custom2Attribute.cs
new file mode 100644
index 000000000..f9314eaf3
--- /dev/null
+++ b/Source/Bifrost.Specs/Extensions/for_TypeExtensions/Custom2Attribute.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Bifrost.Specs.Extensions.for_TypeExtensions
+{
+ public class Custom2Attribute : Attribute
+ {
+ public string Data { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Source/Bifrost.Specs/Extensions/for_TypeExtensions/when_checking_if_types_have_attribute.cs b/Source/Bifrost.Specs/Extensions/for_TypeExtensions/when_checking_if_types_have_attribute.cs
new file mode 100644
index 000000000..8689b8e51
--- /dev/null
+++ b/Source/Bifrost.Specs/Extensions/for_TypeExtensions/when_checking_if_types_have_attribute.cs
@@ -0,0 +1,30 @@
+using System;
+using Bifrost.Extensions;
+using Machine.Specifications;
+
+namespace Bifrost.Specs.Extensions.for_TypeExtensions
+{
+ public class when_checking_if_types_have_attributes
+ {
+ [Custom1]
+ interface ITest { }
+
+ [Custom2]
+ class Type1 : ITest { }
+
+ class Type2 : Type1 { }
+
+ static Type type_1 = new Type1().GetType();
+ static Type type_2 = new Type2().GetType();
+
+ It should_find_attribute_directly_on_class = () => type_1.HasAttribute().ShouldBeTrue();
+
+ It should_not_search_interfaces_by_default = () => type_2.HasAttribute().ShouldBeFalse();
+
+ It should_not_search_supertype_by_default = () => type_2.HasAttribute().ShouldBeFalse();
+
+ It should_search_interfaces_if_asked = () => type_2.HasAttribute(true).ShouldBeTrue();
+
+ It should_search_supertypes_if_asked = () => type_2.HasAttribute(true).ShouldBeTrue();
+ }
+}
\ No newline at end of file
diff --git a/Source/Bifrost.Specs/Extensions/for_TypeExtensions/when_getting_attributes.cs b/Source/Bifrost.Specs/Extensions/for_TypeExtensions/when_getting_attributes.cs
new file mode 100644
index 000000000..c9c06ff89
--- /dev/null
+++ b/Source/Bifrost.Specs/Extensions/for_TypeExtensions/when_getting_attributes.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Linq;
+using Bifrost.Extensions;
+using Machine.Specifications;
+
+namespace Bifrost.Specs.Extensions.for_TypeExtensions
+{
+ public class when_getting_attributes
+ {
+ [Custom1]
+ interface ITest { }
+
+ [Custom1, Custom2(Data = "Hello")]
+ class Type1 : ITest { }
+
+ class Type2 : Type1 { }
+
+ static Type type_1 = new Type1().GetType();
+ static Type type_2 = new Type2().GetType();
+
+ It should_find_one_attribute_directly_on_class = () => type_1.GetAttributes().Count().ShouldEqual(1);
+
+ It should_get_attribute_data = () => type_1.GetAttributes().First().Data.ShouldEqual("Hello");
+
+ It should_not_search_interfaces_by_default = () => type_2.GetAttributes().ShouldBeEmpty();
+
+ It should_not_search_supertype_by_default = () => type_2.GetAttributes().ShouldBeEmpty();
+
+ It should_search_interfaces_if_asked = () => type_2.GetAttributes(true).Count().ShouldEqual(2);
+
+ It should_search_supertypes_if_asked = () => type_2.GetAttributes(true).Count().ShouldEqual(1);
+ }
+}
\ No newline at end of file
diff --git a/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/a_query_coordinator.cs b/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/a_query_coordinator.cs
index 0a8cbe4cd..1aa5eda3e 100644
--- a/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/a_query_coordinator.cs
+++ b/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/a_query_coordinator.cs
@@ -22,7 +22,8 @@ public class a_query_coordinator : all_dependencies
container_mock.Object,
fetching_security_manager_mock.Object,
query_validator_mock.Object,
- read_model_filters_mock.Object);
+ read_model_filters_mock.Object,
+ exception_publisher_mock.Object);
};
}
}
diff --git a/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/a_query_coordinator_with_known_provider.cs b/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/a_query_coordinator_with_known_provider.cs
index e367d1bb1..692f6cac2 100644
--- a/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/a_query_coordinator_with_known_provider.cs
+++ b/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/a_query_coordinator_with_known_provider.cs
@@ -26,7 +26,8 @@ public class a_query_coordinator_with_known_provider : a_query_coordinator
container_mock.Object,
fetching_security_manager_mock.Object,
query_validator_mock.Object,
- read_model_filters_mock.Object);
+ read_model_filters_mock.Object,
+ exception_publisher_mock.Object);
};
}
}
diff --git a/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/a_query_coordinator_with_non_generic_known_provider.cs b/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/a_query_coordinator_with_non_generic_known_provider.cs
index c14e091cf..d32c3986c 100644
--- a/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/a_query_coordinator_with_non_generic_known_provider.cs
+++ b/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/a_query_coordinator_with_non_generic_known_provider.cs
@@ -26,7 +26,8 @@ public class a_query_coordinator_with_non_generic_known_provider : a_query_coord
container_mock.Object,
fetching_security_manager_mock.Object,
query_validator_mock.Object,
- read_model_filters_mock.Object);
+ read_model_filters_mock.Object,
+ exception_publisher_mock.Object);
};
}
}
diff --git a/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/a_query_coordinator_with_provider_for_known_query_and_one_for_derived_type.cs b/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/a_query_coordinator_with_provider_for_known_query_and_one_for_derived_type.cs
index 39a9a9f2e..420fd0873 100644
--- a/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/a_query_coordinator_with_provider_for_known_query_and_one_for_derived_type.cs
+++ b/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/a_query_coordinator_with_provider_for_known_query_and_one_for_derived_type.cs
@@ -30,7 +30,8 @@ public class a_query_coordinator_with_provider_for_known_query_and_one_for_deriv
container_mock.Object,
fetching_security_manager_mock.Object,
query_validator_mock.Object,
- read_model_filters_mock.Object);
+ read_model_filters_mock.Object,
+ exception_publisher_mock.Object);
};
}
}
diff --git a/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/all_dependencies.cs b/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/all_dependencies.cs
index dc575cfac..7807cc874 100644
--- a/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/all_dependencies.cs
+++ b/Source/Bifrost.Specs/Read/for_QueryCoordinator/given/all_dependencies.cs
@@ -1,4 +1,5 @@
-using Bifrost.Execution;
+using Bifrost.Exceptions;
+using Bifrost.Execution;
using Bifrost.Read;
using Bifrost.Read.Validation;
using Machine.Specifications;
@@ -13,6 +14,7 @@ public class all_dependencies
protected static Mock fetching_security_manager_mock;
protected static Mock read_model_filters_mock;
protected static Mock query_validator_mock;
+ protected static Mock exception_publisher_mock;
Establish context = () =>
{
@@ -21,6 +23,7 @@ public class all_dependencies
fetching_security_manager_mock = new Mock();
read_model_filters_mock = new Mock();
query_validator_mock = new Mock();
+ exception_publisher_mock = new Mock();
};
}
}
diff --git a/Source/Bifrost.Specs/Read/for_QueryCoordinator/when_executing_and_provider_throws_an_exception.cs b/Source/Bifrost.Specs/Read/for_QueryCoordinator/when_executing_and_provider_throws_an_exception.cs
index 99e90cd86..de31930d8 100644
--- a/Source/Bifrost.Specs/Read/for_QueryCoordinator/when_executing_and_provider_throws_an_exception.cs
+++ b/Source/Bifrost.Specs/Read/for_QueryCoordinator/when_executing_and_provider_throws_an_exception.cs
@@ -29,5 +29,7 @@ public class when_executing_and_provider_throws_an_exception : given.a_query_coo
Because of = () => result = coordinator.Execute(query, paging);
It should_set_the_exception_on_the_result = () => result.Exception.ShouldEqual(exception_thrown);
+
+ It should_publish_the_exception = () => exception_publisher_mock.Verify(m => m.Publish(exception_thrown));
}
}
diff --git a/Source/Bifrost.Specs/Sagas/for_SagaNarrator/given/a_saga_narrator.cs b/Source/Bifrost.Specs/Sagas/for_SagaNarrator/given/a_saga_narrator.cs
index cfc893aa7..8185507a5 100644
--- a/Source/Bifrost.Specs/Sagas/for_SagaNarrator/given/a_saga_narrator.cs
+++ b/Source/Bifrost.Specs/Sagas/for_SagaNarrator/given/a_saga_narrator.cs
@@ -1,7 +1,7 @@
using Bifrost.Events;
+using Bifrost.Exceptions;
using Bifrost.Execution;
using Bifrost.Sagas;
-using Bifrost.Validation;
using Machine.Specifications;
using Moq;
@@ -14,21 +14,23 @@ public class a_saga_narrator
protected static Mock container_mock;
protected static Mock chapter_validation_service_mock;
protected static Mock event_store_mock;
+ protected static Mock exception_publisher_mock;
Establish context = () =>
- {
- librarian_mock = new Mock();
- container_mock = new Mock();
- chapter_validation_service_mock = new Mock();
- event_store_mock = new Mock();
+ {
+ librarian_mock = new Mock();
+ container_mock = new Mock();
+ chapter_validation_service_mock = new Mock();
+ event_store_mock = new Mock();
+ exception_publisher_mock = new Mock();
- narrator = new SagaNarrator(
- librarian_mock.Object,
- container_mock.Object,
- chapter_validation_service_mock.Object,
- event_store_mock.Object
- );
-
- };
+ narrator = new SagaNarrator(
+ librarian_mock.Object,
+ container_mock.Object,
+ chapter_validation_service_mock.Object,
+ event_store_mock.Object,
+ exception_publisher_mock.Object
+ );
+ };
}
}
diff --git a/Source/Bifrost.Specs/Sagas/for_SagaNarrator/when_concluding_a_saga_that_causes_an_exception.cs b/Source/Bifrost.Specs/Sagas/for_SagaNarrator/when_concluding_a_saga_that_causes_an_exception.cs
index 750ca406f..749905559 100644
--- a/Source/Bifrost.Specs/Sagas/for_SagaNarrator/when_concluding_a_saga_that_causes_an_exception.cs
+++ b/Source/Bifrost.Specs/Sagas/for_SagaNarrator/when_concluding_a_saga_that_causes_an_exception.cs
@@ -1,6 +1,6 @@
using System;
-using Bifrost.Testing.Fakes.Sagas;
using Bifrost.Sagas;
+using Bifrost.Testing.Fakes.Sagas;
using Machine.Specifications;
namespace Bifrost.Specs.Sagas.for_SagaNarrator
@@ -11,17 +11,20 @@ public class when_concluding_a_saga_that_has_begun_that_causes_an_exception : gi
static SagaWithOneChapterProperty saga;
static IChapter chapter;
static SagaConclusion conclusion;
-
+ static Exception exception;
+
Establish context = () =>
- {
- chapter = new SimpleChapter();
- saga = new SagaWithOneChapterProperty(chapter);
- saga.Begin();
- librarian_mock.Setup(l => l.Close(saga)).Throws(new ArgumentException());
- };
+ {
+ chapter = new SimpleChapter();
+ saga = new SagaWithOneChapterProperty(chapter);
+ saga.Begin();
+ exception = new ArgumentException();
+ librarian_mock.Setup(l => l.Close(saga)).Throws(exception);
+ };
Because of = () => conclusion = narrator.Conclude(saga);
It should_have_a_non_successful_conclusion = () => conclusion.Success.ShouldBeFalse();
+ It should_publish_the_exception = () => exception_publisher_mock.Verify(m => m.Publish(exception));
}
-}
+}
\ No newline at end of file
diff --git a/Source/Bifrost.Specs/Sagas/for_SagaNarrator/when_concluding_a_saga_that_is_already_concluded.cs b/Source/Bifrost.Specs/Sagas/for_SagaNarrator/when_concluding_a_saga_that_is_already_concluded.cs
index e1936ff59..2981b3648 100644
--- a/Source/Bifrost.Specs/Sagas/for_SagaNarrator/when_concluding_a_saga_that_is_already_concluded.cs
+++ b/Source/Bifrost.Specs/Sagas/for_SagaNarrator/when_concluding_a_saga_that_is_already_concluded.cs
@@ -1,7 +1,6 @@
-using System;
-using Bifrost.Testing.Fakes.Sagas;
using Bifrost.Sagas;
using Bifrost.Sagas.Exceptions;
+using Bifrost.Testing.Fakes.Sagas;
using Machine.Specifications;
namespace Bifrost.Specs.Sagas.for_SagaNarrator
@@ -14,17 +13,19 @@ public class when_concluding_a_saga_that_is_already_concluded : given.a_saga_nar
static SagaConclusion conclusion;
Establish context = () =>
- {
- chapter = new SimpleChapter();
- saga = new SagaWithOneChapterProperty(chapter);
- saga.Begin();
- saga.Conclude();
- };
+ {
+ chapter = new SimpleChapter();
+ saga = new SagaWithOneChapterProperty(chapter);
+ saga.Begin();
+ saga.Conclude();
+ };
Because of = () => conclusion = narrator.Conclude(saga);
It should_have_a_non_successful_conclusion = () => conclusion.Success.ShouldBeFalse();
It should_have_an_invalid_saga_state_transition_exception = () => conclusion.Exception.ShouldBeOfExactType();
It should_not_have_called_the_on_conclude_method_again = () => saga.OnConcludeCalled.ShouldEqual(1);
+ It should_publish_the_exception = () =>
+ exception_publisher_mock.Verify(m => m.Publish(Moq.It.IsAny()));
}
}
\ No newline at end of file
diff --git a/Source/Bifrost.Specs/Sagas/for_SagaNarrator/when_concluding_a_saga_that_is_new.cs b/Source/Bifrost.Specs/Sagas/for_SagaNarrator/when_concluding_a_saga_that_is_new.cs
index e1981983d..e68482231 100644
--- a/Source/Bifrost.Specs/Sagas/for_SagaNarrator/when_concluding_a_saga_that_is_new.cs
+++ b/Source/Bifrost.Specs/Sagas/for_SagaNarrator/when_concluding_a_saga_that_is_new.cs
@@ -1,7 +1,6 @@
-using System;
-using Bifrost.Testing.Fakes.Sagas;
using Bifrost.Sagas;
using Bifrost.Sagas.Exceptions;
+using Bifrost.Testing.Fakes.Sagas;
using Machine.Specifications;
namespace Bifrost.Specs.Sagas.for_SagaNarrator
@@ -13,15 +12,17 @@ public class when_concluding_a_saga_that_is_new : given.a_saga_narrator
static IChapter chapter;
static SagaConclusion conclusion;
Establish context = () =>
- {
- chapter = new SimpleChapter();
- saga = new SagaWithOneChapterProperty(chapter);
- };
+ {
+ chapter = new SimpleChapter();
+ saga = new SagaWithOneChapterProperty(chapter);
+ };
Because of = () => conclusion = narrator.Conclude(saga);
It should_have_a_non_successful_conclusion = () => conclusion.Success.ShouldBeFalse();
It should_have_an_invalid_saga_state_transition_exception = () => conclusion.Exception.ShouldBeOfExactType();
It should_not_have_called_the_on_conclude_method = () => saga.OnConcludeCalled.ShouldEqual(0);
+ It should_publish_the_exception = () =>
+ exception_publisher_mock.Verify(m => m.Publish(Moq.It.IsAny()));
}
}
\ No newline at end of file
diff --git a/Source/Bifrost.Testing/CommandScenario.cs b/Source/Bifrost.Testing/CommandScenario.cs
index ff5888d3d..bbaaae06f 100644
--- a/Source/Bifrost.Testing/CommandScenario.cs
+++ b/Source/Bifrost.Testing/CommandScenario.cs
@@ -7,6 +7,7 @@
using Bifrost.Commands;
using Bifrost.Domain;
using Bifrost.Events;
+using Bifrost.Exceptions;
using Bifrost.Execution;
using Bifrost.Globalization;
using Bifrost.Principal;
@@ -79,7 +80,8 @@ public CommandScenario()
command_context_manager,
command_security_manager_mock.Object,
command_validators_mock.Object,
- localizer.Object);
+ localizer.Object,
+ Mock.Of());
null_validator_mock = new Mock>();
null_validator = null_validator_mock.Object;
diff --git a/Source/Bifrost.Testing/Fakes/Configuration/TestInstancesOf.cs b/Source/Bifrost.Testing/Fakes/Configuration/TestInstancesOf.cs
index 7e5749773..0077073bc 100644
--- a/Source/Bifrost.Testing/Fakes/Configuration/TestInstancesOf.cs
+++ b/Source/Bifrost.Testing/Fakes/Configuration/TestInstancesOf.cs
@@ -4,9 +4,9 @@
namespace Bifrost.Testing.Fakes.Configuration
{
- public class TestInstancesOf : IInstancesOf where T : class
+ public class TestInstancesOf : IInstancesOf, IOrderedInstancesOf where T : class
{
- IEnumerable _instances;
+ readonly IEnumerable _instances;
public TestInstancesOf(params T[] instances)
{
diff --git a/Source/Bifrost.Web/Services/RestServiceRouteHttpHandler.cs b/Source/Bifrost.Web/Services/RestServiceRouteHttpHandler.cs
index 4e4c2c569..81d47187d 100644
--- a/Source/Bifrost.Web/Services/RestServiceRouteHttpHandler.cs
+++ b/Source/Bifrost.Web/Services/RestServiceRouteHttpHandler.cs
@@ -3,15 +3,12 @@
* Licensed under the MIT License. See LICENSE in the project root for license information.
*--------------------------------------------------------------------------------------------*/
using System;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.Security;
using System.Web;
using System.Web.SessionState;
using Bifrost.Configuration;
+using Bifrost.Exceptions;
using Bifrost.Execution;
using Bifrost.Security;
-using Bifrost.Serialization;
using Bifrost.Services;
namespace Bifrost.Web.Services
@@ -19,12 +16,13 @@ namespace Bifrost.Web.Services
// Todo : add async support - performance gain!
public class RestServiceRouteHttpHandler : IHttpHandler, IRequiresSessionState // IHttpAsyncHandler
{
- Type _type;
- string _url;
- IRequestParamsFactory _factory;
- IRestServiceMethodInvoker _invoker;
- IContainer _container;
- private readonly ISecurityManager _securityManager;
+ readonly Type _type;
+ readonly string _url;
+ readonly IRequestParamsFactory _factory;
+ readonly IRestServiceMethodInvoker _invoker;
+ readonly IContainer _container;
+ readonly ISecurityManager _securityManager;
+ readonly IExceptionPublisher _exceptionPublisher;
public RestServiceRouteHttpHandler(Type type, string url)
: this(
@@ -33,10 +31,18 @@ public RestServiceRouteHttpHandler(Type type, string url)
Configure.Instance.Container.Get(),
Configure.Instance.Container.Get(),
Configure.Instance.Container,
- Configure.Instance.Container.Get())
+ Configure.Instance.Container.Get(),
+ Configure.Instance.Container.Get())
{}
- public RestServiceRouteHttpHandler(Type type, string url, IRequestParamsFactory factory, IRestServiceMethodInvoker invoker, IContainer container, ISecurityManager securityManager)
+ public RestServiceRouteHttpHandler(
+ Type type,
+ string url,
+ IRequestParamsFactory factory,
+ IRestServiceMethodInvoker invoker,
+ IContainer container,
+ ISecurityManager securityManager,
+ IExceptionPublisher exceptionPublisher)
{
_type = type;
_url = url;
@@ -44,9 +50,10 @@ public RestServiceRouteHttpHandler(Type type, string url, IRequestParamsFactory
_invoker = invoker;
_container = container;
_securityManager = securityManager;
+ _exceptionPublisher = exceptionPublisher;
}
- public bool IsReusable { get { return true; } }
+ public bool IsReusable => true;
public void ProcessRequest(HttpContext context)
{
@@ -65,9 +72,10 @@ public void ProcessRequest(HttpContext context)
var result = _invoker.Invoke(_url, serviceInstance, context.Request.Url, form);
context.Response.Write(result);
}
- catch( Exception e)
+ catch (Exception e)
{
- if (e.InnerException != null && e.InnerException is HttpStatus.HttpStatusException)
+ _exceptionPublisher.Publish(e);
+ if (e.InnerException is HttpStatus.HttpStatusException)
{
var ex = e.InnerException as HttpStatus.HttpStatusException;
context.Response.StatusCode = ex.Code;
@@ -80,18 +88,5 @@ public void ProcessRequest(HttpContext context)
}
}
}
-
- /*
- public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
- {
- ProcessRequest(context);
-
- throw new System.NotImplementedException();
- }
-
- public void EndProcessRequest(IAsyncResult result)
- {
- throw new System.NotImplementedException();
- }*/
}
}
diff --git a/Source/Bifrost/Bifrost.csproj b/Source/Bifrost/Bifrost.csproj
index 535cb4051..9fd7018d0 100644
--- a/Source/Bifrost/Bifrost.csproj
+++ b/Source/Bifrost/Bifrost.csproj
@@ -133,7 +133,13 @@
+
+
+
+
+
+
@@ -173,6 +179,8 @@
+
+
diff --git a/Source/Bifrost/Commands/CommandCoordinator.cs b/Source/Bifrost/Commands/CommandCoordinator.cs
index a5e9e9a0b..019453d45 100644
--- a/Source/Bifrost/Commands/CommandCoordinator.cs
+++ b/Source/Bifrost/Commands/CommandCoordinator.cs
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
using System;
using System.Reflection;
+using Bifrost.Exceptions;
using Bifrost.Globalization;
using Bifrost.Lifecycle;
using Bifrost.Sagas;
@@ -20,7 +21,7 @@ public class CommandCoordinator : ICommandCoordinator
readonly ICommandValidators _commandValidationService;
readonly ICommandSecurityManager _commandSecurityManager;
readonly ILocalizer _localizer;
-
+ readonly IExceptionPublisher _exceptionPublisher;
///
/// Initializes a new instance of the CommandCoordinator
@@ -30,29 +31,32 @@ public class CommandCoordinator : ICommandCoordinator
/// A for dealing with security and commands
/// A for validating a before handling
/// A to use for controlling localization of current thread when handling commands
+ /// An to send exceptions to
public CommandCoordinator(
ICommandHandlerManager commandHandlerManager,
ICommandContextManager commandContextManager,
ICommandSecurityManager commandSecurityManager,
ICommandValidators commandValidators,
- ILocalizer localizer)
+ ILocalizer localizer,
+ IExceptionPublisher exceptionPublisher)
{
_commandHandlerManager = commandHandlerManager;
_commandContextManager = commandContextManager;
_commandSecurityManager = commandSecurityManager;
_commandValidationService = commandValidators;
_localizer = localizer;
+ _exceptionPublisher = exceptionPublisher;
}
#pragma warning disable 1591 // Xml Comments
public CommandResult Handle(ISaga saga, ICommand command)
{
- return Handle(_commandContextManager.EstablishForSaga(saga,command), command);
+ return Handle(_commandContextManager.EstablishForSaga(saga, command), command);
}
public CommandResult Handle(ICommand command)
{
- return Handle( _commandContextManager.EstablishForCommand(command),command);
+ return Handle(_commandContextManager.EstablishForCommand(command), command);
}
CommandResult Handle(ITransaction transaction, ICommand command)
@@ -85,12 +89,14 @@ CommandResult Handle(ITransaction transaction, ICommand command)
}
catch (TargetInvocationException ex)
{
+ _exceptionPublisher.Publish(ex);
commandResult.Exception = ex.InnerException;
transaction.Rollback();
}
- catch (Exception exception)
+ catch (Exception ex)
{
- commandResult.Exception = exception;
+ _exceptionPublisher.Publish(ex);
+ commandResult.Exception = ex;
transaction.Rollback();
}
}
@@ -102,14 +108,16 @@ CommandResult Handle(ITransaction transaction, ICommand command)
}
catch (TargetInvocationException ex)
{
+ _exceptionPublisher.Publish(ex);
commandResult.Exception = ex.InnerException;
}
catch (Exception ex)
{
+ _exceptionPublisher.Publish(ex);
commandResult.Exception = ex;
}
- return commandResult;
+ return commandResult;
}
#pragma warning restore 1591 // Xml Comments
}
diff --git a/Source/Bifrost/Commands/CommandHandlerInvoker.cs b/Source/Bifrost/Commands/CommandHandlerInvoker.cs
index 17c5edcfe..0c342dfa3 100644
--- a/Source/Bifrost/Commands/CommandHandlerInvoker.cs
+++ b/Source/Bifrost/Commands/CommandHandlerInvoker.cs
@@ -24,6 +24,7 @@ public class CommandHandlerInvoker : ICommandHandlerInvoker
readonly ITypeDiscoverer _discoverer;
readonly IContainer _container;
readonly Dictionary _commandHandlers = new Dictionary();
+ readonly object _initializationLock = new object();
bool _initialized;
///
@@ -38,40 +39,56 @@ public CommandHandlerInvoker(ITypeDiscoverer discoverer, IContainer container)
_initialized = false;
}
- private void Initialize()
+ void EnsureInitialized()
+ {
+ if (_initialized)
+ {
+ return;
+ }
+
+ lock (_initializationLock)
+ {
+ if (!_initialized)
+ {
+ Initialize();
+ _initialized = true;
+ }
+ }
+ }
+
+ void Initialize()
{
var handlers = _discoverer.FindMultiple();
handlers.ForEach(Register);
- _initialized = true;
}
///
- /// Register a command handler explicitly
+ /// Register a command handler explicitly
///
///
///
- /// The registration process will look into the handler and find methods that
+ /// The registration process will look into the handler and find methods that
/// are called Handle() and takes a command as parameter
///
public void Register(Type handlerType)
{
- var allMethods = handlerType.GetRuntimeMethods().Where(m => m.IsPublic || !m.IsStatic);
- var query = from m in allMethods
- where m.Name.Equals(HandleMethodName) &&
- m.GetParameters().Length == 1 &&
- typeof(ICommand)
- .GetTypeInfo().IsAssignableFrom(m.GetParameters()[0].ParameterType.GetTypeInfo())
- select m;
+ var handleMethods = handlerType
+ .GetRuntimeMethods()
+ .Where(m => m.IsPublic || !m.IsStatic)
+ .Where(m => m.Name.Equals(HandleMethodName))
+ .Where(m => m.GetParameters().Length == 1)
+ .Where(m => typeof(ICommand).GetTypeInfo().IsAssignableFrom(m.GetParameters()[0].ParameterType));
- foreach (var method in query)
+ foreach (var method in handleMethods)
+ {
_commandHandlers[method.GetParameters()[0].ParameterType] = method;
+ }
}
#pragma warning disable 1591 // Xml Comments
public bool TryHandle(ICommand command)
{
- if( !_initialized)
- Initialize();
+ EnsureInitialized();
var commandType = command.GetType();
if (_commandHandlers.ContainsKey(commandType))
diff --git a/Source/Bifrost/Exceptions/ExceptionPublisher.cs b/Source/Bifrost/Exceptions/ExceptionPublisher.cs
new file mode 100644
index 000000000..3c7634abf
--- /dev/null
+++ b/Source/Bifrost/Exceptions/ExceptionPublisher.cs
@@ -0,0 +1,38 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2008-2017 Dolittle. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+using System;
+using Bifrost.Execution;
+
+namespace Bifrost.Exceptions
+{
+ ///
+ /// Publishes exceptions to all s.
+ ///
+ public class ExceptionPublisher : IExceptionPublisher
+ {
+ readonly IInstancesOf _subscribers;
+
+ ///
+ /// Initializes a new instance of .
+ ///
+ /// All known subscribers.
+ public ExceptionPublisher(IInstancesOf subscribers)
+ {
+ _subscribers = subscribers;
+ }
+
+ ///
+ /// Publishes the exception to all .
+ ///
+ ///
+ public void Publish(Exception exception)
+ {
+ foreach (var subscriber in _subscribers)
+ {
+ subscriber.Handle(exception);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Bifrost/Exceptions/IExceptionPublisher.cs b/Source/Bifrost/Exceptions/IExceptionPublisher.cs
new file mode 100644
index 000000000..2497eb70d
--- /dev/null
+++ b/Source/Bifrost/Exceptions/IExceptionPublisher.cs
@@ -0,0 +1,20 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2008-2017 Dolittle. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+using System;
+
+namespace Bifrost.Exceptions
+{
+ ///
+ /// Publishes exceptions to all s.
+ ///
+ public interface IExceptionPublisher
+ {
+ ///
+ /// Publishes the exception to all .
+ ///
+ ///
+ void Publish(Exception exception);
+ }
+}
\ No newline at end of file
diff --git a/Source/Bifrost/Exceptions/IExceptionSubscriber.cs b/Source/Bifrost/Exceptions/IExceptionSubscriber.cs
new file mode 100644
index 000000000..582ef3a47
--- /dev/null
+++ b/Source/Bifrost/Exceptions/IExceptionSubscriber.cs
@@ -0,0 +1,24 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2008-2017 Dolittle. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+using System;
+using Bifrost.Conventions;
+
+namespace Bifrost.Exceptions
+{
+ ///
+ /// Implement this interface to subscribe to exceptions.
+ ///
+ ///
+ /// Types inheriting from this interface will be automatically registered.
+ /// An application can implement any number of these conventions.
+ ///
+ public interface IExceptionSubscriber : IConvention
+ {
+ ///
+ /// Handle the exception.
+ ///
+ void Handle(Exception exception);
+ }
+}
\ No newline at end of file
diff --git a/Source/Bifrost/Execution/AfterAttribute.cs b/Source/Bifrost/Execution/AfterAttribute.cs
new file mode 100644
index 000000000..c3b408167
--- /dev/null
+++ b/Source/Bifrost/Execution/AfterAttribute.cs
@@ -0,0 +1,31 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2008-2017 Dolittle. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+using System;
+
+namespace Bifrost.Execution
+{
+ ///
+ /// Indicates that a type used for injecting into an must be ordered after
+ /// another type injected into the same .
+ ///
+ [AttributeUsage(AttributeTargets.Class)]
+ public class AfterAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ /// The types the decorated type is dependant upon, and must come after.
+ public AfterAttribute(params Type[] dependantTypes)
+ {
+ DependantTypes = dependantTypes;
+ }
+
+ ///
+ /// List of types that must be injected before this decorated type is injected.
+ ///
+ /// This will take precedence over any attributes.
+ public Type[] DependantTypes { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Source/Bifrost/Execution/CyclicDependencyException.cs b/Source/Bifrost/Execution/CyclicDependencyException.cs
new file mode 100644
index 000000000..398981a6b
--- /dev/null
+++ b/Source/Bifrost/Execution/CyclicDependencyException.cs
@@ -0,0 +1,30 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2008-2017 Dolittle. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+using System;
+
+namespace Bifrost.Execution
+{
+ ///
+ /// The exception that is thrown if classes injected into have cyclic
+ /// dependencies.
+ ///
+ public class CyclicDependencyException : Exception
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ public CyclicDependencyException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of .
+ ///
+ /// The exception message.
+ public CyclicDependencyException(string message) : base(message)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Bifrost/Execution/IInstancesOf.cs b/Source/Bifrost/Execution/IInstancesOf.cs
index 63012caba..14ffa976d 100644
--- a/Source/Bifrost/Execution/IInstancesOf.cs
+++ b/Source/Bifrost/Execution/IInstancesOf.cs
@@ -11,7 +11,7 @@ namespace Bifrost.Execution
/// when enumerated over
///
/// Base type to discover for - must be an abstract class or an interface
- public interface IInstancesOf : IEnumerable
+ public interface IInstancesOf : IEnumerable
where T : class
{
}
diff --git a/Source/Bifrost/Execution/IOrderedInstancesOf.cs b/Source/Bifrost/Execution/IOrderedInstancesOf.cs
new file mode 100644
index 000000000..548f0ea01
--- /dev/null
+++ b/Source/Bifrost/Execution/IOrderedInstancesOf.cs
@@ -0,0 +1,18 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2008-2017 Dolittle. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+using System.Collections.Generic;
+
+namespace Bifrost.Execution
+{
+ ///
+ /// Defines something that can discover types and give instance of these types
+ /// in a predefined order when enumerated over.
+ ///
+ /// Base type to discover for - must be an abstract class or an interface.
+ /// Enumeration will throw a if cycles are detected.
+ public interface IOrderedInstancesOf : IEnumerable where T : class
+ {
+ }
+}
diff --git a/Source/Bifrost/Execution/InstancesOf.cs b/Source/Bifrost/Execution/InstancesOf.cs
index cfbacf8e8..9c6ab6dd8 100644
--- a/Source/Bifrost/Execution/InstancesOf.cs
+++ b/Source/Bifrost/Execution/InstancesOf.cs
@@ -20,7 +20,7 @@ public class InstancesOf : IInstancesOf
IContainer _container;
///
- /// Initalizes an instance of
+ /// Initalizes an instance of
///
/// used for discovering types
/// used for managing instances of the types when needed
diff --git a/Source/Bifrost/Execution/OrderAttribute.cs b/Source/Bifrost/Execution/OrderAttribute.cs
new file mode 100644
index 000000000..3a5a596c7
--- /dev/null
+++ b/Source/Bifrost/Execution/OrderAttribute.cs
@@ -0,0 +1,31 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2008-2017 Dolittle. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+using System;
+
+namespace Bifrost.Execution
+{
+ ///
+ /// Indicates that a type used for injecting into an has a specific
+ /// ordering relative to other types decorated with this attribute.
+ ///
+ [AttributeUsage(AttributeTargets.Class)]
+ public class OrderAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ /// The order of the decorated type.
+ public OrderAttribute(int order)
+ {
+ Order = order;
+ }
+
+ ///
+ /// The order of the decorated type.
+ ///
+ /// Types without this attribute have order 0.
+ public int Order { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/Source/Bifrost/Execution/OrderedInstancesOf.cs b/Source/Bifrost/Execution/OrderedInstancesOf.cs
new file mode 100644
index 000000000..1903d2b64
--- /dev/null
+++ b/Source/Bifrost/Execution/OrderedInstancesOf.cs
@@ -0,0 +1,75 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) 2008-2017 Dolittle. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using Bifrost.Extensions;
+
+namespace Bifrost.Execution
+{
+ ///
+ /// Represents an implementation of .
+ ///
+ /// Base type to discover for - must be an abstract class or an interface.
+ public class OrderedInstancesOf : IOrderedInstancesOf where T : class
+ {
+ readonly IEnumerable _types;
+ readonly IContainer _container;
+
+ ///
+ /// Initializes an instance of .
+ ///
+ /// used for discovering types.
+ /// used for managing instances of the types when needed.
+ public OrderedInstancesOf(ITypeDiscoverer typeDiscoverer, IContainer container)
+ {
+ _types = typeDiscoverer.FindMultiple();
+ _container = container;
+ }
+
+#pragma warning disable 1591 // Xml Comments
+ public IEnumerator GetEnumerator()
+ {
+ IList queue = _types.OrderBy(Order).ToList();
+ ISet initialized = new HashSet();
+
+ while (queue.Count > 0)
+ {
+ var progress = false;
+ var ready = queue
+ .Where(s => s
+ .GetAttributes()
+ .SelectMany(a => a.DependantTypes)
+ .All(initialized.Contains))
+ .ToList();
+ foreach (var type in ready)
+ {
+ yield return _container.Get(type) as T;
+ initialized.Add(type);
+ queue.Remove(type);
+ progress = true;
+ }
+
+ if (!progress)
+ {
+ throw new CyclicDependencyException(
+ $"Circular dependency detected when ordering instances of {typeof(T)} between these types: {string.Join(", ", queue)}");
+ }
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+#pragma warning restore 1591 // Xml Comments
+
+ static int Order(Type type)
+ {
+ return type.GetAttributes().Select(a => a.Order).FirstOrDefault();
+ }
+ }
+}
diff --git a/Source/Bifrost/Extensions/TypeExtensions.cs b/Source/Bifrost/Extensions/TypeExtensions.cs
index 163f74f58..4f4f69b4d 100644
--- a/Source/Bifrost/Extensions/TypeExtensions.cs
+++ b/Source/Bifrost/Extensions/TypeExtensions.cs
@@ -14,18 +14,31 @@ namespace Bifrost.Extensions
///
public static class TypeExtensions
{
- static HashSet AdditionalPrimitiveTypes = new HashSet
- {
- typeof(decimal),typeof(string),typeof(Guid),typeof(DateTime),typeof(DateTimeOffset),typeof(TimeSpan)
- };
+ static readonly HashSet AdditionalPrimitiveTypes = new HashSet
+ {
+ typeof (decimal),
+ typeof (string),
+ typeof (Guid),
+ typeof (DateTime),
+ typeof (DateTimeOffset),
+ typeof (TimeSpan)
+ };
- static HashSet NumericTypes = new HashSet
+ static readonly HashSet NumericTypes = new HashSet
{
- typeof(byte), typeof(sbyte),
- typeof(short), typeof(int), typeof(long),
- typeof(ushort), typeof(uint), typeof(ulong),
- typeof(double), typeof(decimal), typeof(Single)
+ typeof (byte),
+ typeof (sbyte),
+ typeof (short),
+ typeof (int),
+ typeof (long),
+ typeof (ushort),
+ typeof (uint),
+ typeof (ulong),
+ typeof (double),
+ typeof (decimal),
+ typeof (Single)
};
+
#pragma warning disable 1591 // Xml Comments
static ITypeInfo GetTypeInfo(Type type)
{
@@ -37,14 +50,56 @@ static ITypeInfo GetTypeInfo(Type type)
#pragma warning restore 1591 // Xml Comments
///
- /// Check if a type has an attribute associated with it
+ /// Check if a type has an attribute associated with it. The inheritance chain is not used to find the attribute.
///
- /// Type to check
- /// True if there is an attribute, false if not
+ /// The type which is searched for the attributes.
+ /// Type to check.
+ /// True if there is an attribute, false if not.
public static bool HasAttribute(this Type type) where T : Attribute
{
- var attributes = type.GetTypeInfo().GetCustomAttributes(typeof(T), false).ToArray();
- return attributes.Length == 1;
+ return type.HasAttribute(false);
+ }
+
+ ///
+ /// Check if a type has an attribute associated with it.
+ ///
+ /// The type which is searched for the attributes.
+ /// Specifies whether to search this member's inheritance chain to find the attributes.
+ /// Interfaces will be searched, too.
+ /// Type to check.
+ /// True if there is an attribute, false if not.
+ public static bool HasAttribute(this Type type, bool inherit) where T : Attribute
+ {
+ return type.GetAttributes(inherit).Any();
+ }
+
+ /// Searches and returns attributes. The inheritance chain is not used to find the attributes.
+ /// The type of attribute to search for.
+ /// The type which is searched for the attributes.
+ /// Returns all attributes of the given type.
+ public static IEnumerable GetAttributes(this Type type) where T : Attribute
+ {
+ return type.GetAttributes(false);
+ }
+
+ /// Searches and returns attributes.
+ /// The type of attribute to search for.
+ /// The type which is searched for the attributes.
+ /// Specifies whether to search this member's inheritance chain to find the attributes.
+ /// Interfaces will be searched, too.
+ /// Returns all attributes.
+ public static IEnumerable GetAttributes(this Type type, bool inherit) where T : Attribute
+ {
+ var attributes = type.GetTypeInfo().GetCustomAttributes(inherit);
+ if (inherit)
+ {
+ attributes = attributes.Concat(
+ type
+ .GetInterfaces()
+ .SelectMany(i => i.GetCustomAttributes(false)));
+ }
+
+ return attributes;
}
///
@@ -85,7 +140,7 @@ public static bool IsDate(this Type type)
/// True if type is a boolean, false if not
public static bool IsBoolean(this Type type)
{
- return type == typeof (Boolean) || Nullable.GetUnderlyingType(type) == typeof (Boolean);
+ return type == typeof (bool) || Nullable.GetUnderlyingType(type) == typeof (bool);
}
///
@@ -226,7 +281,7 @@ public static IEnumerable AllBaseAndImplementingTypes(this Type type)
return type.BaseTypes()
.Concat(type.GetTypeInfo().GetInterfaces())
.SelectMany(ThisAndMaybeOpenType)
- .Where(t=>t != type && t != typeof(Object));
+ .Where(t => t != type && t != typeof (object));
}
static IEnumerable BaseTypes(this Type type)
diff --git a/Source/Bifrost/Read/QueryCoordinator.cs b/Source/Bifrost/Read/QueryCoordinator.cs
index c1a033372..7b62aabe9 100644
--- a/Source/Bifrost/Read/QueryCoordinator.cs
+++ b/Source/Bifrost/Read/QueryCoordinator.cs
@@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
+using Bifrost.Exceptions;
using Bifrost.Execution;
using Bifrost.Read.Validation;
@@ -20,11 +21,12 @@ public class QueryCoordinator : IQueryCoordinator
const string ExecuteMethodName = "Execute";
Dictionary _queryProviderTypesPerTargetType;
- ITypeDiscoverer _typeDiscoverer;
- IContainer _container;
- IReadModelFilters _filters;
- IQueryValidator _validator;
- IFetchingSecurityManager _fetchingSecurityManager;
+ readonly ITypeDiscoverer _typeDiscoverer;
+ readonly IContainer _container;
+ readonly IFetchingSecurityManager _fetchingSecurityManager;
+ readonly IQueryValidator _validator;
+ readonly IReadModelFilters _filters;
+ readonly IExceptionPublisher _exceptionPublisher;
///
/// Initializes a new instance of
@@ -34,18 +36,21 @@ public class QueryCoordinator : IQueryCoordinator
/// to use for securing queries
/// to use for validating queries
/// Filters used to filter any of the read models coming back after a query
+ /// An to send exceptions to
public QueryCoordinator(
ITypeDiscoverer typeDiscoverer,
IContainer container,
IFetchingSecurityManager fetchingSecurityManager,
IQueryValidator validator,
- IReadModelFilters filters)
+ IReadModelFilters filters,
+ IExceptionPublisher exceptionPublisher)
{
_typeDiscoverer = typeDiscoverer;
_container = container;
+ _fetchingSecurityManager = fetchingSecurityManager;
_validator = validator;
_filters = filters;
- _fetchingSecurityManager = fetchingSecurityManager;
+ _exceptionPublisher = exceptionPublisher;
DiscoverQueryTypesPerTargetType();
}
@@ -72,7 +77,6 @@ public QueryResult Execute(IQuery query, PagingInfo paging)
result.Items = new object[0];
return result;
}
-
var property = GetQueryPropertyFromQuery(query);
var actualQuery = property.GetValue(query, null);
@@ -84,10 +88,12 @@ public QueryResult Execute(IQuery query, PagingInfo paging)
}
catch (TargetInvocationException ex)
{
+ _exceptionPublisher.Publish(ex.InnerException);
result.Exception = ex.InnerException;
}
catch (Exception ex)
{
+ _exceptionPublisher.Publish(ex);
result.Exception = ex;
}
diff --git a/Source/Bifrost/Sagas/SagaNarrator.cs b/Source/Bifrost/Sagas/SagaNarrator.cs
index d47f2bf4a..474813499 100644
--- a/Source/Bifrost/Sagas/SagaNarrator.cs
+++ b/Source/Bifrost/Sagas/SagaNarrator.cs
@@ -5,6 +5,7 @@
using System;
using System.Linq;
using Bifrost.Events;
+using Bifrost.Exceptions;
using Bifrost.Execution;
namespace Bifrost.Sagas
@@ -18,6 +19,7 @@ public class SagaNarrator : ISagaNarrator
readonly IContainer _container;
readonly IChapterValidationService _chapterValidationService;
readonly IEventStore _eventStore;
+ readonly IExceptionPublisher _exceptionPublisher;
///
/// Initializes a new instance of
@@ -26,16 +28,19 @@ public class SagaNarrator : ISagaNarrator
/// for creating instances
/// for validating chapters
///
+ /// An to send exceptions to
public SagaNarrator(
ISagaLibrarian librarian,
IContainer container,
IChapterValidationService chapterValidationService,
- IEventStore eventStore)
+ IEventStore eventStore,
+ IExceptionPublisher exceptionPublisher)
{
_librarian = librarian;
_container = container;
_chapterValidationService = chapterValidationService;
_eventStore = eventStore;
+ _exceptionPublisher = exceptionPublisher;
}
#pragma warning disable 1591 // Xml Comments
@@ -62,8 +67,10 @@ public SagaConclusion Conclude(ISaga saga)
saga.Conclude();
saga.SaveUncommittedEventsToEventStore(_eventStore);
_librarian.Close(saga);
- } catch( Exception ex)
+ }
+ catch (Exception ex)
{
+ _exceptionPublisher.Publish(ex);
conclusion.Exception = ex;
}