首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >将环境连接字符串注入不同类库中的.NET Core2.0 w/EF核心DbContext中,而不是Startup prj &实现IDesignTimeDbContextFactory

将环境连接字符串注入不同类库中的.NET Core2.0 w/EF核心DbContext中,而不是Startup prj &实现IDesignTimeDbContextFactory
EN

Stack Overflow用户
提问于 2017-09-07 07:01:22
回答 2查看 8.5K关注 0票数 22

我真的不敢相信这个is...first有多难满足我的需求:

  • Implementing Entity Framework Core2.0‘IDesignTimeDbContextFactory已更名为IDbContextFactory,以减少开发人员对它的用途的困惑
  • 我不想多次加载appsettings.json。一个原因是因为我的迁移是在MyClassLibrary.Data域中运行的,而类库中没有appsettings.js文件,所以我必须使用Copy to Output Directory appsettings.js。另一个原因是它不是很优雅。

所以这就是我目前使用的方法:

代码语言:javascript
复制
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);
        }
    }
}

下面是我的Program.cs:

代码语言:javascript
复制
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();
        }
    }
}

下面是我的Startup.cs:

代码语言:javascript
复制
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 pattern,所以我创建了这个类:

代码语言:javascript
复制
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

代码语言:javascript
复制
  services.Configure<AppSettings>(options => Configuration.GetSection("AppSettings").Bind(options));

然后尝试将我的IDesignTimeDbContextFactory<AppContext>实现更改为:

代码语言:javascript
复制
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);
    }
}

不幸的是,这不起作用,因为public DesignTimeDbContextFactory(IOptions<AppSettings> appSettings)构造函数的Ioptions<AppSettings>参数没有被注入。我猜想这是因为IDesignTimeDbContextFactory<AppContext>的实现在设计时被调用,而依赖注入只是在设计时没有在.NET核心应用中“准备好”?

我认为这有点奇怪,使用实现IDesignTimeDbContextFactory的实体框架核心2.0模式注入特定于环境的连接字符串是如此困难,而且不必多次复制和加载像appsettings.json这样的设置文件。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-12-26 02:58:56

如果您正在寻找从appsettings.json文件初始化的自定义设置类中获取数据库连接字符串的解决方案-这就是您可以做到的。不幸的是,您不能通过DIIOptions注入到IDesignTimeDbContextFactory实现构造函数中。

代码语言:javascript
复制
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中,您可以使用更复杂的逻辑来构建连接字符串。例如,如下所示:

代码语言:javascript
复制
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

代码语言:javascript
复制
{
  "Settings": {
    "UseInMemory": false,
    "Server": "myserver",
    "Port": "1234",
    "Database": "mydatabase",
    "User": "dbuser",
    "Password": "dbpassw0rd"
  }
}

密码和用户存储在环境变量中:

代码语言:javascript
复制
{
  "Settings": {
    "UseInMemory": false,
    "Server": "myserver",
    "Port": "1234",
    "Database": "mydatabase",
    "User": "MY-DB-UID-ENV-VAR",
    "Password": "MY-DB-PWD-ENV-VAR"
  }
}

在这种情况下,您应该这样使用它:

代码语言:javascript
复制
// 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);
票数 12
EN

Stack Overflow用户

发布于 2018-06-02 08:54:35

我对你的问题有点困惑。你是在为DbContext使用依赖注入,还是在尝试特别初始化和构造上下文?

我正在做你在我的一个解决方案中描述的事情。以下是我的解决方案结构:

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

Startup.cs

代码语言:javascript
复制
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<MyDbContext>(
        options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
        sqlOptions => sqlOptions.EnableRetryOnFailure()));

    // SQL configuration for non-injected dbcontext
    DbContextOptionsBuilder<MyDbContext> builder = new DbContextOptionsBuilder<MyDbContext>();
    builder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
    services.AddSingleton(builder.Options);

    // ...
}

MyDbContext.cs

代码语言:javascript
复制
public class MyDbContext : IdentityDbContext<ApplicationUser>
{
    public MyDbContext(DbContextOptions options) : base(options) { }
}

如果您没有使用依赖项注入来传递DbContext,则可以通过注入DbContextOptions<MyDbContext>来访问SQL属性。

在本例中,appsettings文件只读取一次,所有内容都正常工作。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46085500

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档