Skip to content

Commit

Permalink
Merge pull request #20 from marcwittke/hotfix/2.0.5
Browse files Browse the repository at this point in the history
Hotfix/2.0.5
  • Loading branch information
marcwittke committed Mar 5, 2018
2 parents 271e682 + 030e71e commit 968ec7c
Show file tree
Hide file tree
Showing 25 changed files with 190 additions and 60 deletions.
Binary file added lib/GitVersion/GitVersion.exe
Binary file not shown.
Binary file not shown.
Binary file added lib/GitVersion/lib/osx/libgit2-381caf5.dylib
Binary file not shown.
6 changes: 2 additions & 4 deletions src/Backend.Fx.Bootstrapping/Modules/DomainModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,17 @@ protected override void Register(Container container, ScopedLifestyle scopedLife

// the current IIdentity is resolved using the scoped CurrentIdentityHolder that is maintained when opening a scope
container.Register<ICurrentTHolder<IIdentity>, CurrentIdentityHolder>();
container.Register(() => container.GetInstance<ICurrentTHolder<IIdentity>>().Current);

// same for the current TenantId
container.Register<ICurrentTHolder<TenantId>, CurrentTenantIdHolder>();
container.Register(() => container.GetInstance<ICurrentTHolder<TenantId>>().Current);


container.RegisterDomainAndApplicationServices(assemblies);

RegisterAuthorization(container);

// domain event subsystem
container.RegisterCollection(typeof(IDomainEventHandler<>), assemblies);
container.RegisterSingleton<IEventAggregator>(eventAggregator);
container.RegisterSingleton(eventAggregator);

// initial data generation subsystem
container.RegisterCollection<InitialDataGenerator>(assemblies);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ protected override void Register(Container container, ScopedLifestyle lifestyle)
container.Register(typeof(IAggregateMapping<>), new[] { typeof(TDbContext).GetTypeInfo().Assembly });

// IQueryable for framework use only, since it bypasses authorization
container.Register(typeof(IQueryable<>), typeof(AggregateQueryable<>));
container.Register(typeof(IQueryable<>), typeof(EntityQueryable<>));

// EF unit of work
var uowRegistration = lifestyle.CreateRegistration<EfUnitOfWork>(container);
Expand Down
5 changes: 3 additions & 2 deletions src/Backend.Fx.EfCorePersistence/EfUnitOfWork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Logging;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using Patterns.DependencyInjection;
using Patterns.UnitOfWork;

public class EfUnitOfWork : UnitOfWork, ICanInterruptTransaction
Expand All @@ -14,8 +15,8 @@ public class EfUnitOfWork : UnitOfWork, ICanInterruptTransaction
private IDisposable transactionLifetimeLogger;
private IDbContextTransaction currentTransaction;

public EfUnitOfWork(IClock clock, IIdentity identity, DbContext dbContext)
: base(clock, identity)
public EfUnitOfWork(IClock clock, ICurrentTHolder<IIdentity> identityHolder, DbContext dbContext)
: base(clock, identityHolder)
{
DbContext = dbContext;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,18 @@
using System.Linq;
using System.Linq.Expressions;
using BuildingBlocks;
using Environment.MultiTenancy;
using Microsoft.EntityFrameworkCore;

public class AggregateQueryable<TAggregateRoot> : IQueryable<TAggregateRoot> where TAggregateRoot : AggregateRoot
public class EntityQueryable<TEntity> : IQueryable<TEntity> where TEntity : Entity
{
private readonly DbContext dbContext;
private readonly TenantId tenantId;

public AggregateQueryable(DbContext dbContext, TenantId tenantId)

public EntityQueryable(DbContext dbContext)
{
this.dbContext = dbContext;
this.tenantId = tenantId;
}

public IEnumerator<TAggregateRoot> GetEnumerator()
public IEnumerator<TEntity> GetEnumerator()
{
return InnerQueryable.GetEnumerator();
}
Expand All @@ -45,16 +42,11 @@ public IQueryProvider Provider
get { return InnerQueryable.Provider; }
}

private IQueryable<TAggregateRoot> InnerQueryable
private IQueryable<TEntity> InnerQueryable
{
get
{
if (tenantId.HasValue)
{
return dbContext.Set<TAggregateRoot>().Where(agg => agg.TenantId == tenantId.Value);
}

return dbContext.Set<TAggregateRoot>().Where(agg => false);
return dbContext.Set<TEntity>();
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/Backend.Fx.EfCorePersistence/ReadonlyEfUnitOfWork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Logging;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using Patterns.DependencyInjection;
using Patterns.UnitOfWork;

public class ReadonlyEfUnitOfWork : ReadonlyUnitOfWork
Expand All @@ -14,7 +15,7 @@ public class ReadonlyEfUnitOfWork : ReadonlyUnitOfWork
private IDisposable transactionLifetimeLogger;
private IDbContextTransaction currentTransaction;

public ReadonlyEfUnitOfWork(DbContext dbContext, IIdentity identity) : base(identity)
public ReadonlyEfUnitOfWork(DbContext dbContext, ICurrentTHolder<IIdentity> identityHolder) : base(identityHolder)
{
this.dbContext = dbContext;
this.dbContext.ChangeTracker.AutoDetectChangesEnabled = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ protected override void Register(Container container, ScopedLifestyle scopedLife
container.Register(typeof(IRepository<>), typeof(InMemoryRepository<>));
container.Register(typeof(IQueryable<>), typeof(InMemoryQueryable<>));

var uowRegistration = Lifestyle.Scoped.CreateRegistration(() => new InMemoryUnitOfWork(new FrozenClock(), new SystemIdentity()), container);
var uowRegistration = Lifestyle.Scoped.CreateRegistration(() => new InMemoryUnitOfWork(new FrozenClock(), CurrentIdentityHolder.CreateSystem()), container);
container.AddRegistration(typeof(IUnitOfWork), uowRegistration);
container.AddRegistration(typeof(ICanFlush), uowRegistration);
container.Register(A.Fake<IReadonlyUnitOfWork>);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
using System;
using System.Security.Principal;
using Environment.DateAndTime;
using Patterns.DependencyInjection;
using Patterns.UnitOfWork;

public class InMemoryUnitOfWork : UnitOfWork
{
public int CommitCalls { get; private set; }
public int RollbackCalls { get; private set; }

public InMemoryUnitOfWork(IClock clock, IIdentity identity) : base(clock, identity)
public InMemoryUnitOfWork(IClock clock, ICurrentTHolder<IIdentity> identityHolder) : base(clock, identityHolder)
{ }

protected override void UpdateTrackingProperties(string userId, DateTime utcNow)
Expand Down
123 changes: 123 additions & 0 deletions src/Backend.Fx.Testing/ServiceLocation/ServiceLocator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
namespace Backend.Fx.Testing.ServiceLocation
{
using System;
using System.Linq;
using System.Reflection;
using System.Security.Principal;
using BuildingBlocks;
using EfCorePersistence;
using Environment.DateAndTime;
using Environment.MultiTenancy;
using Patterns.Authorization;
using Patterns.DependencyInjection;

public class ServiceLocator
{
private readonly Assembly[] assemblies;

public IClock Clock { get; }

public ServiceLocator(IClock clock, params Assembly[] assemblies)
{
Clock = clock;
this.assemblies = assemblies;
}


/// <summary>
/// This naive auto wiring implementation assumes, that no other types beside IIdentity, IClock and IRepository are required by authorization classes
/// </summary>
/// <returns></returns>
public IAggregateAuthorization<TAggregateRoot> GetAggregateAuthorization<TAggregateRoot>(
EfUnitOfWork unitOfWork,
ICurrentTHolder<TenantId> tenantIdHolder,
ICurrentTHolder<IIdentity> identityHolder) where TAggregateRoot : AggregateRoot
{
return (IAggregateAuthorization<TAggregateRoot>)GetAggregateAuthorization(
unitOfWork, tenantIdHolder, identityHolder, typeof(TAggregateRoot));
}

public IRepository<TAggregateRoot> GetRepository<TAggregateRoot>(
EfUnitOfWork unitOfWork,
ICurrentTHolder<TenantId> tenantIdHolder) where TAggregateRoot : AggregateRoot
{
return (IRepository<TAggregateRoot>)GetRepository(unitOfWork, tenantIdHolder, typeof(TAggregateRoot));
}

private object GetAggregateAuthorization(
EfUnitOfWork unitOfWork,
ICurrentTHolder<TenantId> tenantIdHolder,
ICurrentTHolder<IIdentity> identityHolder,
Type aggregateRootType)
{
Type aggregateDefinitionType = assemblies
.SelectMany(ass => ass.GetTypes())
.Where(t => t.GetTypeInfo().IsClass && !t.GetTypeInfo().IsAbstract)
.SingleOrDefault(t => typeof(IAggregateAuthorization<>).MakeGenericType(aggregateRootType).GetTypeInfo().IsAssignableFrom(t));
if (aggregateDefinitionType == null)
{
throw new InvalidOperationException(string.Format("No Aggregate authorization for {0} found", aggregateRootType.Name));
}

var constructorParameterTypes = aggregateDefinitionType.GetConstructors().Single().GetParameters();
object[] constructorParameters = new object[constructorParameterTypes.Length];
for (int i = 0; i < constructorParameterTypes.Length; i++)
{
if (constructorParameterTypes[i].ParameterType == typeof(IIdentity))
{
constructorParameters[i] = identityHolder.Current;
continue;
}

if (constructorParameterTypes[i].ParameterType == typeof(ICurrentTHolder<IIdentity>))
{
constructorParameters[i] = identityHolder;
continue;
}

if (constructorParameterTypes[i].ParameterType == typeof(IClock))
{
constructorParameters[i] = Clock;
continue;
}

var genericTypeDefinition = constructorParameterTypes[i].ParameterType.GetGenericTypeDefinition();
if (typeof(IRepository<>).IsAssignableFrom(genericTypeDefinition))
{
constructorParameters[i] = GetRepository(unitOfWork, tenantIdHolder, constructorParameterTypes[i].ParameterType.GenericTypeArguments[0]);
}
}

return Activator.CreateInstance(aggregateDefinitionType, constructorParameters);
}

public IAggregateMapping<TAggregateRoot> GetAggregateMapping<TAggregateRoot>() where TAggregateRoot : AggregateRoot
{
return (IAggregateMapping<TAggregateRoot>)GetAggregateMapping(typeof(TAggregateRoot));
}

private IAggregateMapping GetAggregateMapping(Type aggregateRootType)
{
Type aggregateDefinitionType = assemblies
.SelectMany(ass => ass.GetTypes())
.Where(t => IntrospectionExtensions.GetTypeInfo(t).IsClass && !IntrospectionExtensions.GetTypeInfo(t).IsAbstract)
.SingleOrDefault(t => typeof(IAggregateMapping<>).MakeGenericType(aggregateRootType).GetTypeInfo().IsAssignableFrom(t));
if (aggregateDefinitionType == null)
{
throw new InvalidOperationException(string.Format("No Aggregate Definition for {0} found", aggregateRootType.Name));
}

return (IAggregateMapping)Activator.CreateInstance(aggregateDefinitionType);
}

private object GetRepository(
EfUnitOfWork unitOfWork,
ICurrentTHolder<TenantId> tenantIdHolder,
Type aggregateRootType)
{
var aggregateAuthorization = GetAggregateAuthorization(unitOfWork, tenantIdHolder, unitOfWork.IdentityHolder, aggregateRootType);
var efRepositoryType = typeof(EfRepository<>).MakeGenericType(aggregateRootType);
return Activator.CreateInstance(efRepositoryType, unitOfWork.DbContext, GetAggregateMapping(aggregateRootType), tenantIdHolder, aggregateAuthorization);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,12 @@ protected override string Describe(IIdentity instance)
string auth = instance.IsAuthenticated ? $"authenticated via {instance.AuthenticationType}" : "not authenticated";
return $"Identity: {instance.Name}, {auth}";
}

public static ICurrentTHolder<IIdentity> CreateSystem()
{
var currentIdentityHolder = new CurrentIdentityHolder();
currentIdentityHolder.ReplaceCurrent(new SystemIdentity());
return currentIdentityHolder;
}
}
}
6 changes: 5 additions & 1 deletion src/Backend.Fx/Exceptions/Error.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
{
public class Error
{
public const string GenericKey = "_error";
public Error(string message)
{
Code = "";
Message = message;
}

public Error(object code, string message)
{
Expand Down
6 changes: 3 additions & 3 deletions src/Backend.Fx/Exceptions/Errors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

public class Errors : IReadOnlyDictionary<string, Error[]>
{
public const string DefaultErrorKey = "_error";
public const string GenericErrorKey = "_error";
private readonly IDictionary<string, List<Error>> dictionaryImplementation = new Dictionary<string, List<Error>>();

public bool ContainsKey(string key)
Expand Down Expand Up @@ -65,12 +65,12 @@ public void Add(string key, Error error)

public void Add(Error error)
{
Add(DefaultErrorKey, error);
Add(GenericErrorKey, error);
}

public void Add(IEnumerable<Error> errors)
{
Add(DefaultErrorKey, errors);
Add(GenericErrorKey, errors);
}

public IEnumerator<KeyValuePair<string, Error[]>> GetEnumerator()
Expand Down
10 changes: 5 additions & 5 deletions src/Backend.Fx/Exceptions/ExceptionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ internal ExceptionBuilder()

public void Add(Error error)
{
clientException.Errors.Add(Error.GenericKey, error);
clientException.Errors.Add(Errors.GenericErrorKey, error);
}

public void Add(string key, Error error)
Expand All @@ -25,7 +25,7 @@ public void AddNotFoundWhenNull<T>(object id, T t)
{
if (t == null)
{
clientException.Errors.Add(Error.GenericKey, new Error("NotFound", $"{typeof(T).Name} [{id}] not found"));
clientException.Errors.Add(Errors.GenericErrorKey, new Error("NotFound", $"{typeof(T).Name} [{id}] not found"));
}
}

Expand All @@ -49,7 +49,7 @@ public void AddIf(bool condition, Error error)
{
if (condition)
{
clientException.Errors.Add(Error.GenericKey, error);
clientException.Errors.Add(Errors.GenericErrorKey, error);
}
}

Expand All @@ -71,7 +71,7 @@ public T CatchPossibleException<T>(Func<T> function)
catch (Exception ex)
{
Logger.Info(ex, $"Exception of type {ex.GetType().Name} will be appended to an {nameof(UnprocessableException)}.");
clientException.Errors.Add(Error.GenericKey, new Error(ex.GetType().Name, ex.Message));
clientException.Errors.Add(Errors.GenericErrorKey, new Error(ex.GetType().Name, ex.Message));
}
return t;
}
Expand Down Expand Up @@ -100,7 +100,7 @@ public void CatchPossibleException(Action action)
catch (Exception ex)
{
Logger.Info(ex, $"Exception of type {ex.GetType().Name} will be appended to an {nameof(UnprocessableException)}.");
clientException.Errors.Add(Error.GenericKey, new Error(ex.GetType().Name, ex.Message));
clientException.Errors.Add(Errors.GenericErrorKey, new Error(ex.GetType().Name, ex.Message));
}
}

Expand Down
7 changes: 4 additions & 3 deletions src/Backend.Fx/Patterns/UnitOfWork/IReadonlyUnitOfWork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{
using System;
using System.Security.Principal;
using DependencyInjection;
using Logging;

/// <summary>
Expand All @@ -19,12 +20,12 @@ public abstract class ReadonlyUnitOfWork : IReadonlyUnitOfWork
private bool? isCompleted;
private IDisposable lifetimeLogger;

protected ReadonlyUnitOfWork(IIdentity identity)
protected ReadonlyUnitOfWork(ICurrentTHolder<IIdentity> identityHolder)
{
Identity = identity;
IdentityHolder = identityHolder;
}

public IIdentity Identity { get; }
public ICurrentTHolder<IIdentity> IdentityHolder { get; }

public virtual void Begin()
{
Expand Down

0 comments on commit 968ec7c

Please sign in to comment.