Last active
March 19, 2019 09:57
-
-
Save jrgcubano/1f2536d322813b5d542d2310b33d9ea8 to your computer and use it in GitHub Desktop.
ValueObject and Reflection to get aggregate apply methods on static constructor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Via https://github.com/eventflow/EventFlow/blob/e0f60416d3598117aa8c2c2dc706d42604eedbd1/Source/EventFlow/Aggregates/AggregateRoot.cs | |
public static class TypeExtensions | |
{ | |
internal static IReadOnlyDictionary<Type, Action<T, IAggregateEvent>> GetAggregateEventApplyMethods<TAggregate, TIdentity, T>(this Type type) | |
where TAggregate : IAggregateRoot<TIdentity> | |
where TIdentity : IIdentity | |
{ | |
var aggregateEventType = typeof(IAggregateEvent<TAggregate, TIdentity>); | |
return type | |
.GetTypeInfo() | |
.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) | |
.Where(mi => | |
{ | |
if (mi.Name != "Apply") return false; | |
var parameters = mi.GetParameters(); | |
return | |
parameters.Length == 1 && | |
aggregateEventType.GetTypeInfo().IsAssignableFrom(parameters[0].ParameterType); | |
}) | |
.ToDictionary( | |
mi => mi.GetParameters()[0].ParameterType, | |
mi => ReflectionHelper.CompileMethodInvocation<Action<T, IAggregateEvent>>(type, "Apply", mi.GetParameters()[0].ParameterType)); | |
} | |
} | |
} | |
// ReflectionHelper on https://github.com/eventflow/EventFlow/blob/develop/Source/EventFlow/Core/ReflectionHelper.cs | |
public abstract class AggregateRoot<TAggregate, TIdentity> : IAggregateRoot<TIdentity> | |
where TAggregate : AggregateRoot<TAggregate, TIdentity> | |
where TIdentity : IIdentity | |
{ | |
private static readonly IReadOnlyDictionary<Type, Action<TAggregate, IAggregateEvent>> ApplyMethods; | |
static AggregateRoot() | |
{ | |
ApplyMethods = typeof(TAggregate).GetAggregateEventApplyMethods<TAggregate, TIdentity, TAggregate>(); | |
} | |
protected virtual void ApplyEvent(IAggregateEvent<TAggregate, TIdentity> aggregateEvent) | |
{ | |
Action<TAggregate, IAggregateEvent> applyMethod; | |
if (!ApplyMethods.TryGetValue(eventType, out applyMethod)) | |
throw new NotImplementedException(...) | |
applyMethod(this as TAggregate, aggregateEvent); | |
Version++; | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public abstract class ValueObject | |
{ | |
static readonly ConcurrentDictionary<Type, IReadOnlyCollection<PropertyInfo>> TypeProperties = | |
new ConcurrentDictionary<Type, IReadOnlyCollection<PropertyInfo>>(); | |
public override bool Equals(object obj) | |
{ | |
if (ReferenceEquals(this, obj)) return true; | |
if (ReferenceEquals(null, obj)) return false; | |
if (GetType() != obj.GetType()) return false; | |
var other = obj as ValueObject; | |
return other != null && GetEqualityComponents().SequenceEqual(other.GetEqualityComponents()); | |
} | |
public override int GetHashCode() | |
{ | |
unchecked | |
{ | |
return GetEqualityComponents() | |
.Aggregate(17, (current, obj) => current * 23 + (obj?.GetHashCode() ?? 0)); | |
} | |
} | |
public static bool operator ==(ValueObject left, ValueObject right) => Equals(left, right); | |
public static bool operator !=(ValueObject left, ValueObject right) => !Equals(left, right); | |
public override string ToString() | |
{ | |
return $"{{{string.Join(", ", GetProperties().Select(f => $"{f.Name}: {f.GetValue(this)}"))}}}"; | |
} | |
protected virtual IEnumerable<object> GetEqualityComponents() | |
{ | |
return GetProperties().Select(x => x.GetValue(this)); | |
} | |
protected virtual IEnumerable<PropertyInfo> GetProperties() | |
{ | |
return TypeProperties.GetOrAdd( | |
GetType(), | |
t => t | |
.GetTypeInfo() | |
.GetProperties(BindingFlags.Instance | BindingFlags.Public) | |
.OrderBy(p => p.Name) | |
.ToList()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment