Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions source/EasyWay.EntityFrameworkCore/Extnsions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using EasyWay.Internals.AggregateRoots;
using EasyWay.Internals.DomainEvents;
using EasyWay.Internals.DomainEvents;
using EasyWay.Internals.Entities;
using EasyWay.Internals.Repositories;
using EasyWay.Internals.Transactions;
using Microsoft.EntityFrameworkCore;
Expand All @@ -18,8 +18,7 @@ public static void AddEntityFrameworkCore<TContext>(

services.AddScoped((Func<IServiceProvider, DbContext>)(sp => sp.GetRequiredService<TContext>()));

services.AddScoped<IAggregateRootsContext, EntityFrameworkAggregateRootsContext>();
services.AddTransient<IDomainEventsContext, DomainEventsAccessor>();
services.AddScoped<IEntitiesContext, EntityFrameworkEntitiesContext>();

services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));

Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using EasyWay.Internals.DomainEvents;
using Microsoft.EntityFrameworkCore;

namespace EasyWay.Internals.Entities
{
internal sealed class EntityFrameworkEntitiesContext : IEntitiesContext
{
private readonly DbContext _dbContext;

public EntityFrameworkEntitiesContext(DbContext dbContext) => _dbContext = dbContext;

public IEnumerable<AggregateRoot> GetAggregateRoots() => _dbContext.ChangeTracker.Entries<AggregateRoot>().Select(x => x.Entity);

public IEnumerable<Entity> GetEntities() => _dbContext.ChangeTracker.Entries<Entity>().Select(x => x.Entity);
}
}
3 changes: 1 addition & 2 deletions source/EasyWay/Entity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ protected void Add<TDomainEvent>(TDomainEvent domainEvent)
var domainEventContext = new DomainEventContext()
{
EventId = GuidGenerator.New,
AggragetRootId = Id, // TODO what when we have object graph
EntityId = Id,
Entity = this,
OccurrenceOnUtc = InternalClock.UtcNow,
DomainEvent = domainEvent,
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
namespace EasyWay.Internals.AggregateRoots
using EasyWay.Internals.DomainEvents;

namespace EasyWay.Internals.AggregateRoots
{
internal sealed class ConcurrencyTokenUpdater
{
private readonly IAggregateRootsContext _aggragateRootsContext;
private readonly IEntitiesContext _context;

public ConcurrencyTokenUpdater(
IAggregateRootsContext aggragateRootsContext)
public ConcurrencyTokenUpdater(IEntitiesContext context)
{
_aggragateRootsContext = aggragateRootsContext;
_context = context;
}

public void Update()
{
var aggragateRoots = _aggragateRootsContext.GetAggregateRoots();
var aggragateRoots = _context.GetAggregateRoots();

foreach (var aggragateRoot in aggragateRoots)
{
Expand Down

This file was deleted.

26 changes: 26 additions & 0 deletions source/EasyWay/Internals/Assemblies/EntitiesFieldsInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Collections.ObjectModel;
using System.Reflection;

namespace EasyWay.Internals.Assemblies
{
internal sealed class EntitiesFieldsInfo
{
internal static ReadOnlyDictionary<Type, FieldInfo[]> Dictionary;

Check warning on line 8 in source/EasyWay/Internals/Assemblies/EntitiesFieldsInfo.cs

View workflow job for this annotation

GitHub Actions / Build & Tests

Non-nullable field 'Dictionary' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

internal EntitiesFieldsInfo(IEnumerable<Type> types)
{
var dictionary = new Dictionary<Type, FieldInfo[]>();

var aggregateRootTypes = types.Where(x => x.IsSubclassOf(typeof(AggregateRoot)));

foreach (var aggregateRootType in aggregateRootTypes)
{
var fields = aggregateRootType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);

dictionary.Add(aggregateRootType, fields);
}

Dictionary = new ReadOnlyDictionary<Type, FieldInfo[]>(dictionary);
}
}
}
19 changes: 19 additions & 0 deletions source/EasyWay/Internals/Assemblies/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;

namespace EasyWay.Internals.Assemblies
{
internal static class Extensions
{
internal static IServiceCollection AddAssemblies(
this IServiceCollection services,
IEnumerable<Assembly> assemblies)
{
var types = assemblies.SelectMany(x => x.GetTypes()).Distinct();

new EntitiesFieldsInfo(types);

return services;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using EasyWay.Internals.Assemblies;
using System.Collections;
using System.Reflection;

namespace EasyWay.Internals.DomainEvents.AggragateRootIds
{
internal sealed class AggrageteRootIdSearcher
{
private readonly IEntitiesContext _context;

private readonly RelationshipsBetweenEntityAndAggragaeRoot _relation;

public AggrageteRootIdSearcher(
IEntitiesContext context,
RelationshipsBetweenEntityAndAggragaeRoot relation)
{
_context = context;
_relation = relation;
}

public Guid SearchId(Entity entity)
{
if (entity.GetType().IsSubclassOf(typeof(AggregateRoot)))
{
return entity.Id;
}

var aggrageteRoots = _context.GetAggregateRoots();

if (aggrageteRoots.Count() == 1)
{
return aggrageteRoots.Single().Id;
}

var aggragateRootType = _relation[entity.GetType()];

var aggragateRoots = _context.GetAggregateRoots().Where(x => x.GetType() == aggragateRootType);

foreach (var aggregateRoot in aggragateRoots)
{
var id = SearchInAggragates(entity, aggregateRoot);

if (id is not null)
{
return id.Value;
}
}

throw new NotFoundAggragateIdForDomainEvent();
}

private static Guid? SearchInAggragates(Entity entity, AggregateRoot aggregateRoot)
{
var fields = EntitiesFieldsInfo.Dictionary[aggregateRoot.GetType()];

foreach (var field in fields)
{
var isEntity = field.FieldType.IsSubclassOf(typeof(Entity));

if (isEntity)
{
var isEqual = field.GetValue(aggregateRoot).Equals(entity);

if (isEqual)
{
return aggregateRoot.Id;
}
}

if (field.FieldType != typeof(string) && typeof(IEnumerable).IsAssignableFrom(field.FieldType))
{
if (field.GetValue(aggregateRoot) is IEnumerable enumerable)
{
foreach (var en in enumerable)
{
if (en is Entity entityItem)
{
if (entityItem == entity)
{
return aggregateRoot.Id;
}

}
}
}
}
}
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using EasyWay.Internals.Assemblies;
using System.Collections;
using System.Reflection;

namespace EasyWay.Internals.DomainEvents.AggragateRootIds
{
internal static class EntityStructureInDomain
{
internal static RelationshipsBetweenEntityAndAggragaeRoot Get(IEnumerable<Assembly> assemblies)
{
var relation = new Dictionary<Type, Type?>();

var types = assemblies.SelectMany(x => x.GetTypes());

var aggregateRoots = types.Where(x => x.IsSubclassOf(typeof(AggregateRoot)));


foreach (var aggregateType in aggregateRoots)
{
var fields = EntitiesFieldsInfo.Dictionary[aggregateType];

foreach (var field in fields)
{
var fieldType = field.FieldType;

var isEntity = fieldType.IsSubclassOf(typeof(Entity));

if (isEntity)
{
relation.Add(fieldType, aggregateType);
}

if (fieldType != typeof(string) && typeof(IEnumerable).IsAssignableFrom(fieldType))
{
relation.Add(fieldType, aggregateType);

//TODO UseCase (very rarely) entity in entity
}
}
}

return new RelationshipsBetweenEntityAndAggragaeRoot(relation);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace EasyWay.Internals.DomainEvents.AggragateRootIds
{
internal sealed class NotFoundAggragateIdForDomainEvent : EasyWayException
{
internal NotFoundAggragateIdForDomainEvent() : base() { }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Collections.ObjectModel;

namespace EasyWay.Internals.DomainEvents.AggragateRootIds
{
internal sealed class RelationshipsBetweenEntityAndAggragaeRoot : ReadOnlyDictionary<Type, Type>
{
public RelationshipsBetweenEntityAndAggragaeRoot(IDictionary<Type, Type> dictionary)
: base(dictionary) { }
}
}
4 changes: 1 addition & 3 deletions source/EasyWay/Internals/DomainEvents/DomainEventContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ internal sealed class DomainEventContext
{
internal required Guid EventId { get; init; }

internal required Guid AggragetRootId { get; init; }

internal required Guid EntityId { get; init; }
internal required Entity Entity { get; init; }

internal required DateTime OccurrenceOnUtc { get; init; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ internal sealed class DomainEventContextDispacher : IDomainEventContextDispacher
{
private readonly IDomainEventBulkPublisher _publisher;

private readonly IDomainEventsContext _context;
private readonly IDomainEventsAccessor _context;

public DomainEventContextDispacher(
IDomainEventBulkPublisher publisher,
IDomainEventsContext context)
IDomainEventsAccessor context)
{
_publisher = publisher;
_context = context;
Expand Down
Loading
Loading