Browse Source

Created basic dynamic connection string resolve and dbcontext configuration infrastructure.

pull/81/head
Halil İbrahim Kalkan 10 years ago
parent
commit
5f9a0e58b2
  1. 27
      src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/AbpDeskConsoleDemoModule.cs
  2. 9
      src/AbpDesk/AbpDesk.ConsoleDemo/appsettings.json
  3. 22
      src/AbpDesk/AbpDesk.ConsoleDemo/project.json
  4. 3
      src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskEntityFrameworkCoreModule.cs
  5. 29
      src/AbpDesk/AbpDesk.UI/AbpDeskWebAppModule.cs
  6. 9
      src/AbpDesk/AbpDesk.UI/appsettings.json
  7. 1
      src/AbpDesk/AbpDesk.UI/project.json
  8. 34
      src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpDbContextConfigurationContext.cs
  9. 34
      src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpDbContextConfigurerAction.cs
  10. 47
      src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs
  11. 15
      src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/IAbpDbContextConfigurer.cs
  12. 2
      src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  13. 34
      src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs
  14. 1
      src/Volo.Abp/Volo/Abp/AbpKernelModule.cs
  15. 38
      src/Volo.Abp/Volo/Abp/Data/ConnectionStringResolver.cs
  16. 16
      src/Volo.Abp/Volo/Abp/Data/ConnectionStringsOption.cs
  17. 12
      src/Volo.Abp/Volo/Abp/Data/DbConnectionOptions.cs
  18. 17
      src/Volo.Abp/Volo/Abp/Data/IConnectionInfoResolver.cs
  19. 7
      src/Volo.Abp/Volo/Abp/Data/IConnectionStringResolver.cs
  20. 3
      src/Volo.Abp/project.json
  21. 15
      test/AbpDesk/AbpDesk.Application.Tests/AbpDesk/AbpDeskApplicationTestModule.cs

27
src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/AbpDeskConsoleDemoModule.cs

@ -1,6 +1,10 @@
using AbpDesk.EntityFrameworkCore; using System.IO;
using AbpDesk.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace AbpDesk.ConsoleDemo namespace AbpDesk.ConsoleDemo
@ -10,12 +14,29 @@ namespace AbpDesk.ConsoleDemo
{ {
public override void ConfigureServices(IServiceCollection services) public override void ConfigureServices(IServiceCollection services)
{ {
services.AddDbContext<AbpDeskDbContext>(options => var configuration = BuildConfiguration();
services.Configure<DbConnectionOptions>(configuration);
services.Configure<AbpDbContextOptions>(options =>
{ {
options.UseSqlServer("Server=localhost;Database=AbpDesk;Trusted_Connection=True;"); options.Configure(context =>
{
context.DbContextOptions.UseSqlServer(context.ConnectionString);
});
}); });
services.AddAssemblyOf<AbpDeskConsoleDemoModule>(); services.AddAssemblyOf<AbpDeskConsoleDemoModule>();
} }
private static IConfigurationRoot BuildConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
var configuration = builder.Build();
return configuration;
}
} }
} }

9
src/AbpDesk/AbpDesk.ConsoleDemo/appsettings.json

@ -0,0 +1,9 @@
{
"ConnectionStrings": {
"Default": "Server=localhost;Database=AbpDesk;Trusted_Connection=True;",
"Modules": {
"Module1": "Module1-ConnString", //TODO: Remove this dummy definitions
"Module2": "Module2-ConnString"
}
}
}

22
src/AbpDesk/AbpDesk.ConsoleDemo/project.json

@ -1,7 +1,10 @@
{ {
"version": "1.0.0-*", "version": "1.0.0-*",
"buildOptions": {
"emitEntryPoint": true "frameworks": {
"netcoreapp1.1": {
"imports": "dnxcore50"
}
}, },
"dependencies": { "dependencies": {
@ -15,16 +18,19 @@
"Microsoft.EntityFrameworkCore.Tools": { "Microsoft.EntityFrameworkCore.Tools": {
"type": "build", "type": "build",
"version": "1.1.0-preview4-final" "version": "1.1.0-preview4-final"
} },
"Microsoft.Extensions.Configuration.Json": "1.1.0",
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0"
}, },
"tools": { "tools": {
"Microsoft.EntityFrameworkCore.Tools": "1.1.0-preview4-final" "Microsoft.EntityFrameworkCore.Tools": "1.1.0-preview4-final"
}, },
"frameworks": { "buildOptions": {
"netcoreapp1.1": { "emitEntryPoint": true,
"imports": "dnxcore50" "copyToOutput": [
} "appsettings.json"
]
} }
} }

3
src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskEntityFrameworkCoreModule.cs

@ -10,7 +10,8 @@ namespace AbpDesk.EntityFrameworkCore
public override void ConfigureServices(IServiceCollection services) public override void ConfigureServices(IServiceCollection services)
{ {
services.AddAssemblyOf<AbpDeskEntityFrameworkCoreModule>(); services.AddAssemblyOf<AbpDeskEntityFrameworkCoreModule>();
services.AddDefaultEfCoreRepositories<AbpDeskDbContext>(); services.AddAbpDbContext<AbpDeskDbContext>();
services.AddDefaultEfCoreRepositories<AbpDeskDbContext>(); //TODO: Move this into AddAbpDbContext as option
} }
} }
} }

29
src/AbpDesk/AbpDesk.UI/AbpDeskWebAppModule.cs

@ -1,11 +1,16 @@
using AbpDesk.EntityFrameworkCore; using System.IO;
using AbpDesk.EntityFrameworkCore;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Volo.Abp.AspNetCore.Modularity; using Volo.Abp.AspNetCore.Modularity;
using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace AbpDesk namespace AbpDesk
@ -17,6 +22,18 @@ namespace AbpDesk
{ {
public override void ConfigureServices(IServiceCollection services) public override void ConfigureServices(IServiceCollection services)
{ {
var configuration = BuildConfiguration();
services.Configure<DbConnectionOptions>(configuration);
services.Configure<AbpDbContextOptions>(options =>
{
options.Configure(context =>
{
context.DbContextOptions.UseSqlServer(context.ConnectionString);
});
});
services.AddDbContext<AbpDeskDbContext>(options => services.AddDbContext<AbpDeskDbContext>(options =>
{ {
options.UseSqlServer("Server=localhost;Database=AbpDesk;Trusted_Connection=True;"); options.UseSqlServer("Server=localhost;Database=AbpDesk;Trusted_Connection=True;");
@ -45,5 +62,15 @@ namespace AbpDesk
template: "{controller=Home}/{action=Index}/{id?}"); template: "{controller=Home}/{action=Index}/{id?}");
}); });
} }
private static IConfigurationRoot BuildConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory()) //TODO: Get from IHostingEnvironment!!!
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
var configuration = builder.Build();
return configuration;
}
} }
} }

9
src/AbpDesk/AbpDesk.UI/appsettings.json

@ -0,0 +1,9 @@
{
"ConnectionStrings": {
"Default": "Server=localhost;Database=AbpDesk;Trusted_Connection=True;",
"Modules": {
"Module1": "Module1-ConnString", //TODO: Remove this dummy definitions
"Module2": "Module2-ConnString"
}
}
}

1
src/AbpDesk/AbpDesk.UI/project.json

@ -11,6 +11,7 @@
"Microsoft.AspNetCore.StaticFiles": "1.1.0", "Microsoft.AspNetCore.StaticFiles": "1.1.0",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0", "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0",
"Microsoft.Extensions.Configuration.Json": "1.1.0", "Microsoft.Extensions.Configuration.Json": "1.1.0",
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
"AbpDesk.Application.Contracts": "1.0.0-*", "AbpDesk.Application.Contracts": "1.0.0-*",
"Microsoft.EntityFrameworkCore.Tools": { "Microsoft.EntityFrameworkCore.Tools": {
"type": "build", "type": "build",

34
src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpDbContextConfigurationContext.cs

@ -0,0 +1,34 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace Microsoft.Extensions.DependencyInjection
{
public class AbpDbContextConfigurationContext
{
public string ConnectionString { get; }
public string ModuleName { get; }
public virtual DbContextOptionsBuilder DbContextOptions { get; protected set; }
public AbpDbContextConfigurationContext(string connectionString, [CanBeNull] string moduleName)
{
ConnectionString = connectionString;
ModuleName = moduleName;
DbContextOptions = new DbContextOptionsBuilder();
}
}
public class AbpDbContextConfigurationContext<TDbContext> : AbpDbContextConfigurationContext
where TDbContext : AbpDbContext<TDbContext>
{
public new DbContextOptionsBuilder<TDbContext> DbContextOptions => (DbContextOptionsBuilder<TDbContext>)base.DbContextOptions;
public AbpDbContextConfigurationContext(string connectionString, [CanBeNull] string moduleName)
: base(connectionString, moduleName)
{
base.DbContextOptions = new DbContextOptionsBuilder<TDbContext>();
}
}
}

34
src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpDbContextConfigurerAction.cs

@ -0,0 +1,34 @@
using System;
using JetBrains.Annotations;
using Volo;
using Volo.Abp.EntityFrameworkCore;
namespace Microsoft.Extensions.DependencyInjection
{
public class AbpDbContextConfigurerAction : IAbpDbContextConfigurer
{
[NotNull]
public Action<AbpDbContextConfigurationContext> Action { get; }
public AbpDbContextConfigurerAction([NotNull] Action<AbpDbContextConfigurationContext> action)
{
Check.NotNull(action, nameof(action));
Action = action;
}
public void Configure(AbpDbContextConfigurationContext context)
{
Action.Invoke(context);
}
}
public class AbpDbContextConfigurerAction<TDbContext> : AbpDbContextConfigurerAction
where TDbContext : AbpDbContext<TDbContext>
{
public AbpDbContextConfigurerAction([NotNull] Action<AbpDbContextConfigurationContext> action)
: base(action)
{
}
}
}

47
src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs

@ -1,13 +1,58 @@
using Microsoft.Extensions.DependencyInjection.Extensions; using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Repositories.EntityFrameworkCore; using Volo.Abp.Repositories.EntityFrameworkCore;
using Volo.ExtensionMethods.Collections.Generic;
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
{ {
public static class AbpEfCoreServiceCollectionExtensions public static class AbpEfCoreServiceCollectionExtensions
{ {
public static IServiceCollection AddAbpDbContext<TDbContext>(
this IServiceCollection services)
where TDbContext : AbpDbContext<TDbContext>
{
services //TODO: This are copied from EntityFrameworkServiceCollectionExtensions, we should think on that later
.AddMemoryCache()
.AddLogging();
services.TryAddTransient<TDbContext>();
services.TryAddSingleton(serviceProvider =>
{
const string moduleName = "";
var connInfoResolver = serviceProvider.GetRequiredService<IConnectionStringResolver>();
var context = new AbpDbContextConfigurationContext<TDbContext>(connInfoResolver.Resolve(moduleName), moduleName);
var dbContextOptions = serviceProvider.GetRequiredService<IOptions<AbpDbContextOptions>>().Value;
var configureAction = dbContextOptions.ConfigureActions.GetOrDefault(typeof(TDbContext));
if (configureAction != null)
{
((Action<AbpDbContextConfigurationContext<TDbContext>>) configureAction).Invoke(context);
}
else if(dbContextOptions.DefaultConfigureAction != null)
{
dbContextOptions.DefaultConfigureAction.Invoke(context);
}
else
{
throw new AbpException("Should set a configure action for dbcontext"); //TODO: Better message
}
return context.DbContextOptions.Options;
});
return services;
}
public static IServiceCollection AddDefaultEfCoreRepositories<TDbContext>(this IServiceCollection services) public static IServiceCollection AddDefaultEfCoreRepositories<TDbContext>(this IServiceCollection services)
where TDbContext : AbpDbContext<TDbContext> where TDbContext : AbpDbContext<TDbContext>
{ {

15
src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/IAbpDbContextConfigurer.cs

@ -0,0 +1,15 @@
using Volo.Abp.EntityFrameworkCore;
namespace Microsoft.Extensions.DependencyInjection
{
public interface IAbpDbContextConfigurer
{
void Configure(AbpDbContextConfigurationContext context);
}
public interface IAbpDbContextConfigurer<TDbContext>
where TDbContext : AbpDbContext<TDbContext>
{
void Configure(AbpDbContextConfigurationContext<TDbContext> context);
}
}

2
src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs

@ -11,4 +11,4 @@ namespace Volo.Abp.EntityFrameworkCore
} }
} }
} }

34
src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace Volo.Abp.EntityFrameworkCore
{
public class AbpDbContextOptions
{
internal Action<AbpDbContextConfigurationContext> DefaultConfigureAction { get; set; }
internal Dictionary<Type, object> ConfigureActions { get; set; }
public AbpDbContextOptions()
{
ConfigureActions = new Dictionary<Type, object>();
}
public void Configure([NotNull] Action<AbpDbContextConfigurationContext> action)
{
Check.NotNull(action, nameof(action));
DefaultConfigureAction = action;
}
public void Configure<TDbContext>([NotNull] Action<AbpDbContextConfigurationContext<TDbContext>> action)
where TDbContext : AbpDbContext<TDbContext>
{
Check.NotNull(action, nameof(action));
ConfigureActions[typeof(TDbContext)] = action;
}
}
}

1
src/Volo.Abp/Volo/Abp/AbpKernelModule.cs

@ -7,6 +7,7 @@ namespace Volo.Abp
{ {
public override void ConfigureServices(IServiceCollection services) public override void ConfigureServices(IServiceCollection services)
{ {
services.AddOptions();
services.AddAssemblyOf<AbpKernelModule>(); services.AddAssemblyOf<AbpKernelModule>();
} }
} }

38
src/Volo.Abp/Volo/Abp/Data/ConnectionStringResolver.cs

@ -0,0 +1,38 @@
using Microsoft.Extensions.Options;
using Volo.DependencyInjection;
using Volo.ExtensionMethods;
using Volo.ExtensionMethods.Collections.Generic;
namespace Volo.Abp.Data
{
public class ConnectionStringResolver : IConnectionStringResolver, ITransientDependency
{
private readonly DbConnectionOptions _options; //TODO: Use IOptionsSnapshot
public ConnectionStringResolver(IOptions<DbConnectionOptions> options)
{
_options = options.Value;
}
public string Resolve(string modulename)
{
//Get default value
var connString = _options.ConnectionStrings.Default;
//Override by module specific value if provided
if (!modulename.IsNullOrEmpty())
{
var moduleConnString = _options.ConnectionStrings.Modules.GetOrDefault(modulename);
if (!moduleConnString.IsNullOrEmpty())
{
connString = moduleConnString;
}
}
//TODO: Override by tenant conn string
//TODO: Override by tenant module specific conn string
return connString;
}
}
}

16
src/Volo.Abp/Volo/Abp/Data/ConnectionStringsOption.cs

@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace Volo.Abp.Data
{
public class ConnectionStringsOption
{
public string Default { get; set; }
public Dictionary<string, string> Modules { get; set; }
public ConnectionStringsOption()
{
Modules = new Dictionary<string, string>();
}
}
}

12
src/Volo.Abp/Volo/Abp/Data/DbConnectionOptions.cs

@ -0,0 +1,12 @@
namespace Volo.Abp.Data
{
public class DbConnectionOptions
{
public ConnectionStringsOption ConnectionStrings { get; set; }
public DbConnectionOptions()
{
ConnectionStrings = new ConnectionStringsOption();
}
}
}

17
src/Volo.Abp/Volo/Abp/Data/IConnectionInfoResolver.cs

@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Volo.Abp.Data
{
public interface IConnectionInfoResolver
{
ConnectionInfo Resolve();
}
public class ConnectionInfo
{
public string ConnectionString { get; set; }
}
}

7
src/Volo.Abp/Volo/Abp/Data/IConnectionStringResolver.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.Data
{
public interface IConnectionStringResolver
{
string Resolve(string modulename);
}
}

3
src/Volo.Abp/project.json

@ -10,7 +10,8 @@
"Nito.AsyncEx.Context": "1.1.0", "Nito.AsyncEx.Context": "1.1.0",
"System.Runtime.Loader": "4.3.0", "System.Runtime.Loader": "4.3.0",
"System.Linq.Queryable": "4.3.0", "System.Linq.Queryable": "4.3.0",
"Volo.Abp.ApplicationContracts": "1.0.0-*" "Volo.Abp.ApplicationContracts": "1.0.0-*",
"Microsoft.Extensions.Options": "1.1.0"
}, },
"frameworks": { "frameworks": {

15
test/AbpDesk/AbpDesk.Application.Tests/AbpDesk/AbpDeskApplicationTestModule.cs

@ -2,6 +2,8 @@
using AbpDesk.EntityFrameworkCore; using AbpDesk.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace AbpDesk namespace AbpDesk
@ -12,9 +14,18 @@ namespace AbpDesk
public override void ConfigureServices(IServiceCollection services) public override void ConfigureServices(IServiceCollection services)
{ {
services.AddEntityFrameworkInMemoryDatabase(); services.AddEntityFrameworkInMemoryDatabase();
services.AddDbContext<AbpDeskDbContext>(options =>
services.Configure<DbConnectionOptions>(options =>
{
options.ConnectionStrings.Default = Guid.NewGuid().ToString();
});
services.Configure<AbpDbContextOptions>(options =>
{ {
options.UseInMemoryDatabase(Guid.NewGuid().ToString()); options.Configure(context =>
{
context.DbContextOptions.UseInMemoryDatabase(context.ConnectionString);
});
}); });
} }
} }

Loading…
Cancel
Save