Skip to content

Commit

Permalink
Fix assembly registry bug (#16)
Browse files Browse the repository at this point in the history
* Improve error handling in SimpleEventRegistry construction

* Utilize new error handling in derived registries

* Split registry interface in two

* Whitespace

* Give translating registry some love

* Add tests for assembly event registry
  • Loading branch information
Tomas Lycken authored Oct 3, 2017
1 parent d0c04bd commit b563da1
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 29 deletions.
28 changes: 28 additions & 0 deletions src/RdbmsEventStore.Tests/AssemblyEventRegistryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using RdbmsEventStore.EventRegistry;
using Xunit;

namespace RdbmsEventStore.Tests
{
public class AssemblyEventRegistryTests
{
// Create two types with lambdas in them, to test handling of closure types
private class FooContainer { private Func<object, bool> Foo = _ => true; }
private class BarContainer { private Func<object, bool> Bar = _ => false; }

[Fact]
public void AssemblyRegistryCanHandleLambdaClosureTypes()
{
var _ = new AssemblyEventRegistry(typeof(AssemblyEventRegistryTests));
}

[Fact]
public void AssemblyRegistryThrowsNiceExceptionsWithSameNamedTypes()
{
// use a really stupid namer, to create naming conflicts
string StupidNamer(Type type) => type.Name.Substring(0, 1);

Assert.Throws<EventTypeRegistrationException>(() => new AssemblyEventRegistry(typeof(AssemblyEventRegistryTests), (Func<Type, string>)StupidNamer));
}
}
}
4 changes: 2 additions & 2 deletions src/RdbmsEventStore/EventRegistry/AssemblyEventRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ public AssemblyEventRegistry(Type markerType, Func<Type, string> namer, Func<Typ
.GetTypeInfo()
.Assembly
.GetTypes()
.Where(inclusionPredicate)
.ToDictionary(namer, type => type))
.Where(type => !type.Name.StartsWith("<>"))
.Where(inclusionPredicate), namer)
{
}
}
Expand Down
6 changes: 2 additions & 4 deletions src/RdbmsEventStore/EventRegistry/ComposedEventRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ namespace RdbmsEventStore.EventRegistry
{
public class ComposedEventRegistry : SimpleEventRegistry
{
public ComposedEventRegistry(params IEventRegistry[] registries)
: base(registries
.SelectMany(r => r.Registrations)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value))
public ComposedEventRegistry(params IComposableEventRegistry[] registries)
: base(registries.SelectMany(r => r.Registrations))
{
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace RdbmsEventStore.EventRegistry
{
public class EventTypeRegistrationException : InvalidOperationException
{
public EventTypeRegistrationException(string message, Exception innerException) : base(message, innerException)
{
}
}
}
25 changes: 19 additions & 6 deletions src/RdbmsEventStore/EventRegistry/SimpleEventRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,27 @@

namespace RdbmsEventStore.EventRegistry
{
public class SimpleEventRegistry : IEventRegistry
public class SimpleEventRegistry : IComposableEventRegistry
{
private readonly IReadOnlyDictionary<Type, string> _typeToString;
public SimpleEventRegistry(IReadOnlyDictionary<string, Type> eventTypes)
public SimpleEventRegistry(IEnumerable<Type> eventTypes, Func<Type, string> namer)
: this(eventTypes.Select(type => new KeyValuePair<string, Type>(namer(type), type)))
{
Registrations = eventTypes;
_typeToString = eventTypes
.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
}

public SimpleEventRegistry(IEnumerable<KeyValuePair<string, Type>> registrations)
{
try
{
Registrations = registrations
.ToDictionary(registration => registration.Key, registration => registration.Value);
_typeToString = Registrations
.ToDictionary(registration => registration.Value, kvp => kvp.Key);
}
catch (ArgumentException ex)
{
throw new EventTypeRegistrationException("Invalid event type registration; two or more types are either named the same, or registered twice", ex);
}
}

public Type TypeFor(string eventType)
Expand All @@ -20,7 +33,7 @@ public Type TypeFor(string eventType)
: throw new EventTypeNotFoundException(eventType, Registrations);

public string NameFor(Type eventType)
=>_typeToString.TryGetValue(eventType, out var name)
=> _typeToString.TryGetValue(eventType, out var name)
? name
: throw new EventNameNotFoundException(eventType, _typeToString);

Expand Down
44 changes: 27 additions & 17 deletions src/RdbmsEventStore/EventRegistry/TranslatingEventRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,55 @@

namespace RdbmsEventStore.EventRegistry
{
public class TranslatingEventRegistry : IEventRegistry
public class TranslatingEventRegistry : IComposableEventRegistry
{
private readonly IReadOnlyDictionary<string, string> _translations;
private readonly IReadOnlyDictionary<string, string> _inverseTranslations;
private readonly IEventRegistry _registry;
private readonly IComposableEventRegistry _inner;
private readonly bool _translateNewEvents;

public TranslatingEventRegistry(IReadOnlyDictionary<string, string> translations, IEventRegistry registry, bool translateNewEvents = false)
public TranslatingEventRegistry(IReadOnlyDictionary<string, string> translations, IComposableEventRegistry inner, bool translateNewEvents = false)
{
_translations = translations;
if (translateNewEvents)
{
_inverseTranslations = translations.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
try
{
_inverseTranslations = translations.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
}
catch (ArgumentException ex)
{
throw new EventTypeRegistrationException(
"Invalid translation registration; two or more old names translate to the same new name.\n" +
"If you want to translate both Foo and Bar to Baz, add Foo=>Bar and Bar=>Baz instead of Foo=>Baz and Bar=>Baz.", ex);
}
}

_registry = registry;
_inner = inner;
_translateNewEvents = translateNewEvents;
}

public Type TypeFor(string eventType)
=> _translations.TryGetValue(eventType, out var translated)
? TypeFor(translated)
: _registry.TypeFor(eventType);
=> _inner.TypeFor(TranslationFor(eventType));

public string NameFor(Type eventType)
=> InverseTranslate(this._registry.NameFor(eventType));
=> InverseTranslationFor(_inner.NameFor(eventType));

private string TranslationFor(string eventType)
=> _translations.TryGetValue(eventType, out var translated)
? TranslationFor(translated)
: eventType;

private string InverseTranslate(string registeredName)
=> _translateNewEvents && _inverseTranslations.TryGetValue(registeredName, out var translated)
? InverseTranslate(translated)
: registeredName;
private string InverseTranslationFor(string eventType)
=> _inverseTranslations.TryGetValue(eventType, out var translated)
? InverseTranslationFor(translated)
: eventType;

public IReadOnlyDictionary<string, Type> Registrations
=> _registry
=> _inner
.Registrations
.ToDictionary(
kvp => _translations.TryGetValue(kvp.Key, out var translated)
? translated
: kvp.Key,
kvp => TranslationFor(kvp.Key),
kvp => kvp.Value);
}
}
3 changes: 3 additions & 0 deletions src/RdbmsEventStore/IEventRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ public interface IEventRegistry
Type TypeFor(string eventType);

string NameFor(Type eventType);
}

public interface IComposableEventRegistry : IEventRegistry
{
IReadOnlyDictionary<string, Type> Registrations { get; }
}
}

0 comments on commit b563da1

Please sign in to comment.