与启动prj& amp;在不同的类库中,将Env Conn字符串注入具有EF Core DbContext的.NET Core 2.0。实现IDesignTimeDbContextFactory [英] Injecting Env Conn String into .NET Core 2.0 w/EF Core DbContext in different class lib than Startup prj & implementing IDesignTimeDbContextFactory
问题描述
老实说,我不敢相信这有多难...首先要达到的要求是
I honestly cannot believe how hard this is...first off the requirements that I am going for:
- 实施实体框架核心2.0'
IDesignTimeDbContextFactory
,即 IDbContextFactory 重命名,以减少开发人员对其功能的混淆 - 我不想必须多次加载
appsettings.json
。原因之一是因为我的迁移运行在MyClassLibrary.Data
域中,并且其中没有appsettings.js
文件该类库,我将不得不复制到输出目录
appsettings.js
。另一个原因是它并不十分优雅。
- Implementing Entity Framework Core 2.0'
IDesignTimeDbContextFactory
which is IDbContextFactory renamed to be less confusing to developers as to what it does - I do not want to have to do loading of
appsettings.json
more than once. One reason is because my migrations are running in the domain ofMyClassLibrary.Data
and there is noappsettings.js
file in that class library, I would have to toCopy to Output Directory
appsettings.js
. Another reason is that it just not very elegant.
所以这是我目前可以使用的功能:
So here is what I have that currently works:
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
using AppContext = Tsl.Example.Data.AppContext;
namespace Tsl.Example
{
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<AppContext>
{
public AppContext CreateDbContext(string[] args)
{
string basePath = AppDomain.CurrentDomain.BaseDirectory;
string envName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(basePath)
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{envName}.json", true)
.Build();
var builder = new DbContextOptionsBuilder<AppContext>();
var connectionString = configuration.GetConnectionString("DefaultConnection");
builder.UseMySql(connectionString);
return new AppContext(builder.Options);
}
}
}
这是我的程序。 cs:
And here is my Program.cs:
using System.IO;
using System.Reflection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Tsl.Example
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
//public static IWebHost BuildWebHost(string[] args) =>
// WebHost.CreateDefaultBuilder(args)
// .UseStartup<Startup>()
// .Build();
/// <summary>
/// This the magical WebHost.CreateDefaultBuilder method "unboxed", mostly, ConfigureServices uses an internal class so there is one piece of CreateDefaultBuilder that cannot be used here
/// https://andrewlock.net/exploring-program-and-startup-in-asp-net-core-2-preview1-2/
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
public static IWebHost BuildWebHost(string[] args)
{
return new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
IHostingEnvironment env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
if (env.IsDevelopment())
{
var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
if (appAssembly != null)
{
config.AddUserSecrets(appAssembly, optional: true);
}
}
config.AddEnvironmentVariables();
if (args != null)
{
config.AddCommandLine(args);
}
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
})
//.UseIISIntegration()
.UseDefaultServiceProvider((context, options) =>
{
options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
})
.UseStartup<Startup>()
.Build();
}
}
}
这是我的创业公司。 cs:
And here is my Startup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using ServiceStack;
using Tsl.Example.Interfaces;
using Tsl.Example.Provider;
using AppContext = Tsl.Example.Data.AppContext;
namespace Tsl.Example
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IAppContext, AppContext>();
services.AddTransient<IExampleDataProvider, ExampleDataProvider>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseServiceStack(new AppHost());
}
}
}
我想做什么使用 IOptions模式,因此我创建了此类:
What I would like to do is use the IOptions pattern, so I created this class:
namespace Tsl.Example
{
/// <summary>
/// Strongly typed settings to share in app using the .NET Core IOptions pattern
/// https://andrewlock.net/how-to-use-the-ioptions-pattern-for-configuration-in-asp-net-core-rc2/
/// </summary>
public class AppSettings
{
public string DefaultConnection { get; set; }
}
}
将此行添加到 Startup.ConfigureServices
:
services.Configure<AppSettings>(options => Configuration.GetSection("AppSettings").Bind(options));
然后尝试更改我对 IDesignTimeDbContextFactory< AppContext>
到:
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<AppContext>
{
private readonly AppSettings _appSettings;
public DesignTimeDbContextFactory(IOptions<AppSettings> appSettings)
{
this._appSettings = appSettings.Value;
}
public AppContext CreateDbContext(string[] args)
{
var builder = new DbContextOptionsBuilder<AppContext>();
builder.UseMySql(_appSettings.DefaultConnection);
return new AppContext(builder.Options);
}
}
不幸的是,由于<$ c $不会注入 public DesignTimeDbContextFactory(IOptions< AppSettings> appSettings)
构造函数的c> Ioptions< AppSettings> 参数。我认为这是因为 IDesignTimeDbContextFactory< AppContext>
的实现是在设计时调用的,而在设计时.NET Core应用程序中的依赖注入是否还没有就绪?
Unfortunately this did not work because the Ioptions<AppSettings>
argument of public DesignTimeDbContextFactory(IOptions<AppSettings> appSettings)
constructor is not injected. I assume this is because implementations of IDesignTimeDbContextFactory<AppContext>
are called at Design time and dependency injection is just not "ready" in .NET Core apps at design time?
我觉得很奇怪,使用实现 IDesignTimeDbContextFactory $的Entity Framework Core 2.0模式很难注入特定于环境的连接字符串c $ c>,也不必多次复制和加载诸如
appsettings.json
之类的设置文件。
I think it is kind of strange that it is so hard to inject an environment specific connection string using the Entity Framework Core 2.0 pattern of implementing IDesignTimeDbContextFactory
, and also not having to copy and load settings files like appsettings.json
more than once.
推荐答案
如果您正在寻找从 appsettings.json
文件初始化的自定义设置类中获取数据库连接字符串的解决方案-就是这样你可以这样做。不幸的是,您不能通过 DI
将 IOptions
注入到 IDesignTimeDbContextFactory
If you are looking for solution to get database connection string from your custom settings class initialized from appsettings.json
file - that is how you can do this. Unfortunatelly you can't inject IOptions
via DI
to your IDesignTimeDbContextFactory
implementation constructor.
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<AppContext>
{
public AppContext CreateDbContext(string[] args)
{
// IDesignTimeDbContextFactory is used usually when you execute EF Core commands like Add-Migration, Update-Database, and so on
// So it is usually your local development machine environment
var envName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
// Prepare configuration builder
var configuration = new ConfigurationBuilder()
.SetBasePath(Path.Combine(Directory.GetCurrentDirectory()))
.AddJsonFile("appsettings.json", optional: false)
.AddJsonFile($"appsettings.{envName}.json", optional: false)
.Build();
// Bind your custom settings class instance to values from appsettings.json
var settingsSection = configuration.GetSection("Settings");
var appSettings = new AppSettings();
settingsSection.Bind(appSettings);
// Create DB context with connection from your AppSettings
var optionsBuilder = new DbContextOptionsBuilder<AppContext>()
.UseMySql(appSettings.DefaultConnection);
return new AppContext(optionsBuilder.Options);
}
}
当然在您的 AppSettings中
类和 appsettings.json
,您甚至可以使用更复杂的逻辑来构建连接字符串。例如,像这样:
Of course in your AppSettings
class and appsettings.json
you could have even more sophisticated logic of building the connection string. For instance, like this:
public class AppSettings
{
public bool UseInMemory { get; set; }
public string Server { get; set; }
public string Port { get; set; }
public string Database { get; set; }
public string User { get; set; }
public string Password { get; set; }
public string BuildConnectionString()
{
if(UseInMemory) return null;
// You can set environment variable name which stores your real value, or use as value if not configured as environment variable
var server = Environment.GetEnvironmentVariable(Host) ?? Host;
var port = Environment.GetEnvironmentVariable(Port) ?? Port;
var database = Environment.GetEnvironmentVariable(Database) ?? Database;
var user = Environment.GetEnvironmentVariable(User) ?? User;
var password = Environment.GetEnvironmentVariable(Password) ?? Password;
var connectionString = $"Server={server};Port={port};Database={database};Uid={user};Pwd={password}";
return connectionString;
}
}
仅将值存储在中appsettings.json
:
{
"Settings": {
"UseInMemory": false,
"Server": "myserver",
"Port": "1234",
"Database": "mydatabase",
"User": "dbuser",
"Password": "dbpassw0rd"
}
}
将密码和用户存储在环境变量中:
With password and user stored in environment variables:
{
"Settings": {
"UseInMemory": false,
"Server": "myserver",
"Port": "1234",
"Database": "mydatabase",
"User": "MY-DB-UID-ENV-VAR",
"Password": "MY-DB-PWD-ENV-VAR"
}
}
在这种情况下,您应该这样使用:
In this case you should use it this way:
// Create DB context with connection from your AppSettings
var optionsBuilder = new DbContextOptionsBuilder<AppContext>();
if(appSettings.UseInMemory) {
optionsBuilder = appSettings.UseInMemory
? optionsBuilder.UseInMemoryDatabase("MyInMemoryDB")
: optionsBuilder.UseMySql(appSettings.BuildConnectionString());
return new AppContext(optionsBuilder.Options);
这篇关于与启动prj& amp;在不同的类库中,将Env Conn字符串注入具有EF Core DbContext的.NET Core 2.0。实现IDesignTimeDbContextFactory的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!