将Env Conn字符串注入.NET Core 2.0 w / EF Core DbContext中的不同类lib比启动prj并实现IDesignTimeDbContextFactory

老实说,我不敢相信这有多难……首先是我要求的要求:

  • 实现entity frameworkCore IDesignTimeDbContextFactory , IDbContextFactory被重命名, 以免对开发人员造成什么困扰
  • 我不想不止一次加载appsettings.json 。 一个原因是因为我的迁移在MyClassLibrary.Data域中运行,并且该类库中没有appsettings.js文件,我将不得不Copy to Output Directory appsettings.js 。 另一个原因是它不是很优雅。

所以这就是我目前的工作原理:

 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 { 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(); var connectionString = configuration.GetConnectionString("DefaultConnection"); builder.UseMySql(connectionString); return new AppContext(builder.Options); } } } 

这是我的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() // .Build(); ///  /// 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/ ///  ///  ///  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() .Build(); } } } 

这是我的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(); services.AddTransient(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseServiceStack(new AppHost()); } } } 

我想要做的是使用IOptions模式 ,所以我创建了这个类:

 namespace Tsl.Example { ///  /// 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/ ///  public class AppSettings { public string DefaultConnection { get; set; } } } 

将此行添加到Startup.ConfigureServices

  services.Configure(options => Configuration.GetSection("AppSettings").Bind(options)); 

然后尝试将IDesignTimeDbContextFactory实现更改为:

 public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory { private readonly AppSettings _appSettings; public DesignTimeDbContextFactory(IOptions appSettings) { this._appSettings = appSettings.Value; } public AppContext CreateDbContext(string[] args) { var builder = new DbContextOptionsBuilder(); builder.UseMySql(_appSettings.DefaultConnection); return new AppContext(builder.Options); } } 

不幸的是,这不起作用,因为未注入public DesignTimeDbContextFactory(IOptions appSettings)构造函数的Ioptions参数。 我假设这是因为IDesignTimeDbContextFactory实现在设计时调用,并且dependency injection在.NET Core应用程序设计时并没有“准备好”?

我认为使用实现IDesignTimeDbContextFactory的Entity Framework Core 2.0模式注入特定于环境的连接字符串是如此困难,并且不必IDesignTimeDbContextFactory复制和加载appsettings.json类的设置文件。

我对你的问题有点困惑。 您是否正在为DbContext使用dependency injection,或者您是在尝试初始化和构建特定的上下文?

我正在做我在我的一个解决方案中描述的内容。 这是我的解决方案结构:

  • Corp.ApplicationName.Data
  • Corp.ApplicationName.Web

Startup.cs

 public Startup(IHostingEnvironment env) { IConfigurationBuilder builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", false, true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json") .AddEnvironmentVariables(); // ... } public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddDbContext( options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), sqlOptions => sqlOptions.EnableRetryOnFailure())); // SQL configuration for non-injected dbcontext DbContextOptionsBuilder builder = new DbContextOptionsBuilder(); builder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")); services.AddSingleton(builder.Options); // ... } 

MyDbContext.cs

 public class MyDbContext : IdentityDbContext { public MyDbContext(DbContextOptions options) : base(options) { } } 

如果您没有使用dependency injection来传递DbContext,则可以通过注入DbContextOptions来访问SQL属性。

在这个例子中,appsettings文件只是每次读取一次,一切正常。