Skip to content

Commit

Permalink
Merge pull request #19 from marcwittke/hotfix/2.0.4
Browse files Browse the repository at this point in the history
Removed TenantId state from Repository, allowing changing the current…
  • Loading branch information
marcwittke committed Mar 3, 2018
2 parents af01fbf + 6299c01 commit 271e682
Show file tree
Hide file tree
Showing 26 changed files with 223 additions and 165 deletions.
2 changes: 1 addition & 1 deletion demo/DemoBlog.Application/Persistence/BlogDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public BlogDbContext(DbContextOptions<BlogDbContext> options) : base(options)
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
this.ApplyAggregateRootMappings(builder);
this.ApplyAggregateMappings(builder);
builder.RegisterRowVersionProperty();
builder.RegisterEntityIdAsNeverGenerated();
}
Expand Down
47 changes: 3 additions & 44 deletions demo/DemoBlog.Mvc/Infrastructure/Bootstrapping/BlogApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
{
using System;
using System.Data.SqlClient;
using System.Globalization;
using System.Linq;
using System.Net.Sockets;
using Backend.Fx.Bootstrapping;
Expand All @@ -27,8 +26,7 @@ public class BlogApplication : BackendFxApplication
{
private static readonly ILogger Logger = LogManager.Create<BlogApplication>();
private readonly string connectionString;
private bool doEnsureDevelopmentTenantExistenceOnBoot;


private BlogApplication(string connectionString,
ICompositionRoot compositionRoot,
IDatabaseManager databaseManager,
Expand All @@ -48,7 +46,7 @@ public static BlogApplication Build(string connectionString)
SimpleInjectorCompositionRoot compositionRoot = new SimpleInjectorCompositionRoot();
compositionRoot.RegisterModules(
new BlogPersistenceModule(blogDbContextOptions),
new BlogApplicationModule());
new BlogDomainModule());

var blogApplication = new BlogApplication(
connectionString,
Expand All @@ -73,9 +71,8 @@ public void ConfigureServices(IServiceCollection services)
services.AddSingleton(this);
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
public void Configure(IApplicationBuilder app)
{
doEnsureDevelopmentTenantExistenceOnBoot = env.IsDevelopment();
CompositionRoot.RegisterModules(new AspNetMvcViewModule(app));

// this instance should be gracefully disposed on shutdown
Expand All @@ -85,16 +82,6 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
app.UseMiddleware<BlogMiddleware>();
}

public override void Boot()
{
base.Boot();

if (doEnsureDevelopmentTenantExistenceOnBoot)
{
EnsureDevelopmentTenantExistence();
}
}

public void CheckDatabaseExistence()
{
var connectionStringBuilder = new SqlConnectionStringBuilder(connectionString);
Expand Down Expand Up @@ -137,33 +124,5 @@ public void WaitForDatabase(int retries, int secondsToWait)

retryPolicy.Execute(CheckDatabaseExistence);
}

/// <summary>
/// This is only supported in development environments. Running inside of an IIS host will result in timeouts during the first
/// request, leaving the system in an unpredicted state. To achieve the same effect in a hosted demo environment, use the same
/// functionality via service endpoints.
/// </summary>
public void EnsureDevelopmentTenantExistence()
{
const string devTenantCode = "dev";

if (DatabaseManager.DatabaseExists)
{
if (TenantManager.GetTenants().Any(t => t.IsDemoTenant && t.Name == devTenantCode))
{
return;
}
}

Logger.Info("Creating dev tenant");

// This will create a demonstration tenant. Note that by using the TenantManager directly instead of the TenantsController
// there won't be any TenantCreated event published...
TenantId tenantId = TenantManager.CreateDemonstrationTenant(devTenantCode, "dev tenant", true, new CultureInfo("en-US"));

// ... therefore it's up to us to do the initialization. Which is fine, because we are not spinning of a background action
// but blocking in our thread.
TenantManager.EnsureTenantIsInitialized(tenantId);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
using Domain;
using SimpleInjector;

public class BlogApplicationModule : ApplicationModule
public class BlogDomainModule : DomainModule
{
public BlogApplicationModule() : base(typeof(Blog).GetTypeInfo().Assembly)
public BlogDomainModule() : base(typeof(Blog).GetTypeInfo().Assembly)
{ }

protected override void Register(Container container, ScopedLifestyle scopedLifestyle)
Expand Down
9 changes: 6 additions & 3 deletions demo/DemoBlog.Mvc/Infrastructure/Bootstrapping/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,13 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, Microsof
identityApplication.Boot();

// booting blogApplication
blogApplication.Configure(app, env);
blogApplication.Boot();

blogApplication.Configure(app);

app.UseMvc(routes => routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}"));

app.ApplicationServices.GetRequiredService<IApplicationLifetime>()
.ApplicationStarted
.Register(async () => { await blogApplication.Boot(env.IsDevelopment()); });
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />
<PackageReference Include="SimpleInjector" Version="4.0.12" />
</ItemGroup>

Expand Down
40 changes: 39 additions & 1 deletion src/Backend.Fx.Bootstrapping/BackendFxApplication.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
namespace Backend.Fx.Bootstrapping
{
using System;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Environment.MultiTenancy;
using Environment.Persistence;
using Logging;
Expand Down Expand Up @@ -54,11 +57,46 @@ public abstract class BackendFxApplication : IDisposable

public IJobExecutor JobExecutor { get; }

public virtual void Boot()
public virtual async Task Boot(bool doEnsureDevelopmentTenantExistenceOnBoot)
{
Logger.Info("Booting application");
CompositionRoot.Verify();
DatabaseManager.EnsureDatabaseExistence();

if (doEnsureDevelopmentTenantExistenceOnBoot)
{
EnsureDevelopmentTenantExistence();
}

await Task.CompletedTask;
}

/// <summary>
/// This is only supported in development environments. Running inside of an IIS host will result in timeouts during the first
/// request, leaving the system in an unpredicted state. To achieve the same effect in a hosted demo environment, use the same
/// functionality via service endpoints.
/// </summary>
public virtual void EnsureDevelopmentTenantExistence()
{
const string devTenantCode = "dev";
if (DatabaseManager.DatabaseExists)
{
var tenants = TenantManager.GetTenants();
if (tenants.Any(t => t.IsDemoTenant && t.Name == devTenantCode))
{
return;
}
}

Logger.Info("Creating dev tenant");

// This will create a demonstration tenant. Note that by using the TenantManager directly instead of the TenantsController
// there won't be any TenantCreated event published...
TenantId tenantId = TenantManager.CreateDemonstrationTenant(devTenantCode, "dev tenant", true, new CultureInfo("en-US"));

// ... therefore it's up to us to do the initialization. Which is fine, because we are not spinning of a background action
// but blocking in our thread.
TenantManager.EnsureTenantIsInitialized(tenantId);
}

protected virtual void Dispose(bool disposing)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
/// the collections of <see cref="IDomainEventHandler{TDomainEvent}"/>s, <see cref="IJob"/>s and <see cref="InitialDataGenerator"/>s
/// found in the given list of domain assemblies.
/// </summary>
public abstract class ApplicationModule : SimpleInjectorModule
public abstract class DomainModule : SimpleInjectorModule
{
private readonly Assembly[] assemblies;
private IEventAggregator eventAggregator;

protected ApplicationModule(params Assembly[] domainAssemblies)
protected DomainModule(params Assembly[] domainAssemblies)
{
assemblies = domainAssemblies.Concat(new[] {
typeof(Entity).GetTypeInfo().Assembly,
Expand All @@ -48,7 +48,7 @@ protected override void Register(Container container, ScopedLifestyle scopedLife
container.Register<ICurrentTHolder<TenantId>, CurrentTenantIdHolder>();
container.Register(() => container.GetInstance<ICurrentTHolder<TenantId>>().Current);

RegisterDomainAndApplicationServices(container);
container.RegisterDomainAndApplicationServices(assemblies);

RegisterAuthorization(container);

Expand All @@ -66,31 +66,6 @@ protected override void Register(Container container, ScopedLifestyle scopedLife
}
}

/// <summary>
/// Auto registering all implementors of <see cref="IApplicationService" /> and <see cref="IDomainService" /> with
/// their implementations as scoped instances
/// </summary>
private void RegisterDomainAndApplicationServices(Container container)
{
var serviceRegistrations = container
.GetTypesToRegister(typeof(IDomainService), assemblies)
.Concat(container.GetTypesToRegister(typeof(IApplicationService), assemblies))
.SelectMany(type =>
type.GetTypeInfo()
.ImplementedInterfaces
.Where(i => typeof(IDomainService) != i && typeof(IApplicationService) != i && (i.Namespace.StartsWith("Backend") || assemblies.Contains(i.GetTypeInfo().Assembly)))
.Select(service => new
{
Service = service,
Implementation = type
})
);
foreach (var reg in serviceRegistrations)
{
container.Register(reg.Service, reg.Implementation);
}
}

/// <summary>
/// Auto registering all aggregate authorization classes
/// </summary>
Expand Down
55 changes: 55 additions & 0 deletions src/Backend.Fx.Bootstrapping/SimpleInjectorContainerEx.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
namespace Backend.Fx.Bootstrapping
{
using System;
using System.Linq;
using System.Reflection;
using BuildingBlocks;
using Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using SimpleInjector;

public static class SimpleInjectorContainerEx
{
private static readonly ILogger Logger = LogManager.Create(typeof(SimpleInjectorContainerEx));

/// <summary>
/// Auto registering all implementors of <see cref="IApplicationService" /> and <see cref="IDomainService" /> with
/// their implementations as scoped instances
/// </summary>
public static void RegisterDomainAndApplicationServices(this Container container, Assembly[] assemblies)
{

var serviceRegistrations = container
.GetTypesToRegister(typeof(IDomainService), assemblies)
.Concat(container.GetTypesToRegister(typeof(IApplicationService), assemblies))
.SelectMany(type =>
type.GetTypeInfo()
.ImplementedInterfaces
.Where(i => typeof(IDomainService) != i && typeof(IApplicationService) != i && (i.Namespace.StartsWith("Backend") || assemblies.Contains(i.GetTypeInfo().Assembly)))
.Select(service => new
{
Service = service,
Implementation = type
})
);
foreach (var reg in serviceRegistrations)
{
Logger.Debug($"Registering scoped service {reg.Service.Name} with implementation {reg.Implementation.Name}");
container.Register(reg.Service, reg.Implementation);
}
}

public static void Configure<TOptions>(this Container container, Action<TOptions> configure) where TOptions : class
{
container.RegisterSingleton<IConfigureOptions<TOptions>>(new ConfigureOptions<TOptions>(configure));
}

public static void Configure<TOptions>(this Container container, IConfiguration configuration) where TOptions : class
{
container.RegisterSingleton<IOptionsChangeTokenSource<TOptions>>(new ConfigurationChangeTokenSource<TOptions>(Options.DefaultName, configuration));
container.RegisterSingleton<IConfigureOptions<TOptions>>(new NamedConfigureFromConfigurationOptions<TOptions>(Options.DefaultName, configuration));
}

}
}
4 changes: 2 additions & 2 deletions src/Backend.Fx.EfCorePersistence/DbContextExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ public static void RegisterEntityIdAsNeverGenerated(this ModelBuilder modelBuild
.ForAll(mt => modelBuilder.Entity(mt.ClrType).Property(nameof(Entity.Id)).ValueGeneratedNever());
}

public static void ApplyAggregateRootMappings(this DbContext dbContext, ModelBuilder modelBuilder)
public static void ApplyAggregateMappings(this DbContext dbContext, ModelBuilder modelBuilder)
{
//CAVE: IAggregateRootMapping implementations must reside in the same assembly as the Applications DbContext-type
//CAVE: IAggregateMapping implementations must reside in the same assembly as the Applications DbContext-type
var aggregateDefinitionTypeInfos = dbContext
.GetType()
.GetTypeInfo()
Expand Down
7 changes: 4 additions & 3 deletions src/Backend.Fx.EfCorePersistence/EfRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Patterns.Authorization;

using Patterns.DependencyInjection;

public class EfRepository<TAggregateRoot> : Repository<TAggregateRoot> where TAggregateRoot : AggregateRoot
{
private static readonly ILogger Logger = LogManager.Create<EfRepository<TAggregateRoot>>();
Expand All @@ -18,8 +19,8 @@ public class EfRepository<TAggregateRoot> : Repository<TAggregateRoot> where TAg
private readonly IAggregateAuthorization<TAggregateRoot> aggregateAuthorization;

public EfRepository(DbContext dbContext, IAggregateMapping<TAggregateRoot> aggregateMapping,
TenantId tenantId, IAggregateAuthorization<TAggregateRoot> aggregateAuthorization)
: base(tenantId, aggregateAuthorization)
ICurrentTHolder<TenantId> currentTenantIdHolder, IAggregateAuthorization<TAggregateRoot> aggregateAuthorization)
: base(currentTenantIdHolder, aggregateAuthorization)
{
this.dbContext = dbContext;
this.aggregateMapping = aggregateMapping;
Expand Down

This file was deleted.

11 changes: 11 additions & 0 deletions src/Backend.Fx.Testing/InMemoryPersistence/InMemoryDomainModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Backend.Fx.Testing.InMemoryPersistence
{
using System.Reflection;
using Bootstrapping.Modules;

public class InMemoryDomainModule : DomainModule
{
public InMemoryDomainModule(params Assembly[] domainAssemblies) : base(domainAssemblies)
{ }
}
}

0 comments on commit 271e682

Please sign in to comment.