我该如何在.NET Core中实现DbContext连接字符串?

53

我的情况与此链接非常相似,或者至少我的代码类似,并且我正在尝试找到一种将这种方法应用于.NET Core语法的方法。

将连接字符串传递给Code First DbContext

我具体的代码如下:

public partial class CompanyFormsContext : DbContext
{
    public CompanyFormsContext()
        : base("name=CompanyFormsContext")
    {
    }

    public CompanyFormsContext(string connName)
        : base("name=" + connName)
    {
    }
    ...
}

我收到一个错误提示:

Error CS1503 第一个参数:无法将“string”转换为“Microsoft.EntityFrameworkCore.DbContextOptions” CompanyForms..NETCoreApp,版本=v1.0

当我查看`base("name=CompanyFormsContext")`或`base("name=" = connName)`中的括号时。在.NET Core中实现此功能的正确方法是什么?

编辑:

我想分享一下我的appsettings.json文件中的数据库连接信息:(但是,我在startup.cs中没有设置)

  "Data": {
    "CompanyFormsContext": {
      "ConnectionString": "Server=(localdb)\\projectsv13;Database=companyforms;Trusted_Connection=True;"
    },
    "CompanyFormsContextQA": {
      "ConnectionString": "Server=(localdb)\\projectsv13;Database=companyforms;Trusted_Connection=True;"
    }
  }

我找到了以下链接Adding DbContextOptions in Startup.cs not registering data store,我想知道一个简单的protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)是否足以解决我的连接问题?

来自链接的内容:

services.AddEntityFramework(Configuration)
    .AddSqlServer()
    .AddDbContext<MyDbContext>(
        options =>
        options.UseSqlServer(Configuration.Get("Data:CompanyFormsContext:ConnectionString"))
    );

我需要在我的Startup.cs文件中使用这种服务吗?


那个链接对于学习不同类型的数据库连接非常有用,现在我更清楚问题所在了。谢谢。 - Kemal Tezer Dilsiz
抱歉错过了你的编辑。你肯定在正确的轨道上,看看我的答案是否有帮助。 - Peter
8个回答

79

另一个选项是调用接受 DbContextOptions 的基础构造函数:

public BooksContext(string connectionString) : base(GetOptions(connectionString))
{
}

private static DbContextOptions GetOptions(string connectionString)
{
    return SqlServerDbContextOptionsExtensions.UseSqlServer(new DbContextOptionsBuilder(), connectionString).Options;
}

1
不得不把“this”改为“base”,但解决方案非常好! - aherrick
我该如何在Oracle数据库中使用它?没有UseOracle或类似的东西。 - Terai
3
微软没有为Oracle提供适配器,您需要使用第三方适配器。根据不同的适配器,扩展方法可能会有所不同。请参阅 https://learn.microsoft.com/en-us/ef/core/providers/。 - Ricardo Peres
7
如果您需要获得一个强类型的 DbContextOptions<TContext>,您需要使用 new DbContextOptionsBuilder<BooksContext> - Tobias J
1
它工作了。这个答案对我很有帮助。谢谢。 - Vamos

23

通常,在启动时您需要从配置文件中读取它,然后使用连接字符串为进程配置Entity Framework DbContext服务。

1)向您的appsettings.json添加一行:

"DbConnectionString": "Server=s;Database=db;Trusted_Connection=True;",

2)阅读你的 Startup.cs 类中的一行代码(在调用 Startup 方法构建 Configuration 之后,通常在 ConfigureServices 方法中),如下所示:

var connection = Configuration["DbConnectionString"];

3)如果使用Entity Framework,请添加一个数据库上下文服务(MyDbContext是由EF生成的上下文类)。您还要告诉内置的依赖注入如何实例化您的数据库上下文:

services.AddDbContext<MyDbContext>(options => options.UseSqlServer(connection));
services.AddScoped<IMyDbContext, MyDbContext>();

IMyDbContext通常只是你从MyDbContext中提取出来的一个接口。

4) 现在,您可以定义控制器以使用MyDbContext,并且依赖注入将在调用控制器时负责构建它并传递它:

public MyController(IMyDbContext context)
{
    _context = context  // store for later use
}

11

IMO 最佳实践:

将以下内容添加到您的 configuration.json 文件中:

     "ConnectionStrings": {
    "BooksContext": "Server=MyServer;Database=MyDb;Trusted_Connection=True;"
  }

并初始化 部分:

services.AddDbContext<BooksContext>(options => options.UseSqlServer(configuration.GetConnectionString(nameof(BooksContext))));

8
一种简单的方法是,只需使用OptionBuilder来获取上下文(Context):
    public static MyDbContext GetMyDbContext(string databaseName)
    {
        var optionsBuilder = new DbContextOptionsBuilder<MyDbContext>();

        optionsBuilder.UseSqlServer($@"Data Source=.\SqlExpress;Initial Catalog={databaseName};Integrated Security=True");

        return new MyDbContext(optionsBuilder.Options);

    }

3

静态连接的 Startup.cs

services.AddScoped<MyContext>(_ => new MyContext(Configuration.GetConnectionString("myDB")));

针对动态连接的Table1Repository.cs

using (var _context = new MyContext(@"server=....){
context.Table1....
}

MyContext.cs

public MyContext(string connectionString) : base(GetOptions(connectionString))
{
}

private static DbContextOptions GetOptions(string connectionString)
{
    return SqlServerDbContextOptionsExtensions.UseSqlServer(new DbContextOptionsBuilder(), connectionString).Options;
}

3
所以我搜索了整个网络,寻找解决我的问题的方法,即基于在连接之前没有的数据动态连接到数据库。基本上是动态上下文。我没有传递URL上的数据,也没有一个可能要附加到的数据库的短列表。因此,这里是我提出的解决方案。此代码将允许您使用appsettings.json文件定义连接字符串,并用运行时代码替换占位符。这可以在控制器或其他类中根据需要完成。
我同时使用静态上下文和动态上下文,但您也可以只使用动态上下文。
希望有人会偶然发现这篇文章并说声谢天谢地……虽然也有可能有人会说,这家伙疯了。无论如何,祝愉快。
using System;
using System.Globalization;
using System.Linq;
using Microsoft.Extensions.Configuration;

namespace CallCenter.Repositories
{
    public class TestRepository : ITestRepository 
    {
        private readonly InsuranceContext _context;
        public TestRepository(InsuranceContext context)
        {
            _context = context;
        }

        public void Add(string passedInStringWhichTellsWhatDatabaseToUse)
        {
            var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");

            var configuration = builder.Build();
            var connectionString = configuration.GetConnectionString("DefaultConnection");
            

                var agencyContext = new AgencyContext(connectionString.Replace("Database=replacethisstring", "Database=" + passedInStringWhichTellsWhatDatabaseToUse));

                var company = agencyContext.Companys.FirstOrDefault(x => x.ColumnNameInDb == "xyz");
                if (company != null)
                {
                    companyId = company.CompanyId.ToString();
                }

... your other code here which could include the using the passed in _context from the injected code (or you could not have any context passed in and just use dynamic context
            }

        }
    }
}

//The AgencyContext class would look like this:

using Microsoft.EntityFrameworkCore;

namespace CallCenter.Entities
{
    public class AgencyContext : DbContext
    {
        public AgencyContext(string connectionString) : base(GetOptions(connectionString))
        {
            
        }

        private static DbContextOptions GetOptions(string connectionString)
        {
            return SqlServerDbContextOptionsExtensions.UseSqlServer(new DbContextOptionsBuilder(), connectionString).Options;
        }

        public DbSet<Companys> Companys { get; set; }
    }
}

//The startup.c IServiceProvider module has this:

        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddOptions();
           
            services.AddDbContext<InsuranceContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), b => b.UseRowNumberForPaging()));
            services.AddScoped<ITestRepository , TestRepository >();
....
}

最后,appsettings.jason文件中将包含以下内容:
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=yourservername;Database=replacethisstring;User ID=youruserid;Password=yourpassword;TrustServerCertificate=True;Trusted_Connection=False;Connection Timeout=30;Integrated Security=False;Persist Security Info=False;Encrypt=True;MultipleActiveResultSets=True;",
}
}

2

如果您正在寻找简单、代码量较少的解决方案,可以这样做:只需为基类添加另一个构造函数,该构造函数以静态工厂方法调用作为参数:

 public YourDbContext(string connectionString)
        : base(new DbContextOptionsBuilder().UseSqlServer(connectionString).Options)
    {   
    }

1

当连接字符串在appsettings.json文件中时,接受的答案是好的,但在某些情况下,我们需要动态构建连接字符串。例如,在多租户系统中。每个租户都有自己的数据库。

在这种类型的系统中,连接字符串的存储位置可能会随时间而变化,例如,在最初的几天中,它可能是DB、配置文件,然后在以后移动到更安全的存储。

在这种情况下,您可以按照以下步骤操作。

1-引入一个接口,例如IConnectionStringBuilder。然后实现ConnectionStringBuilder。

public interface IConnectionStringBuilder
{
    string TenantConnectionString(string tenantIdentifier);
}

public class ConnectionStringBuilder : IConnectionStringBuilder
{
    private readonly DbConnectionStringsConfig _stringsConfig;
    public ConnectionStringBuilder(DbConnectionStringsConfig stringsConfig)
    {
        _stringsConfig = stringsConfig;
    }

    public string TenantConnectionString(string tenantIdentifier)
    {
        return @$"Server={_stringsConfig.Server};Database={_stringsConfig.Database};Trusted_Connection=True;MultipleActiveResultSets=true;Encrypt=False;";
    }
}

2 - DbConnectionStringsConfig 只是一个类,用于将 appsettings.json 配置映射到代码中。

  "DbConnectionStringsConfig": {
    "Server": "localhost",
    "Database": "MyDB",
    "UserId": "DEV_USER",
    "Password": "DEV_PASWORD",
  }

3 - 在 Startup 或 Program.cs 中设置新类

    services.AddSingleton<DbConnectionStringsConfig>(configuration.GetSection(nameof(DbConnectionStringsConfig)).Get<DbConnectionStringsConfig>());

然后

    services.AddTransient<IConnectionStringBuilder, ConnectionStringBuilder>();

4 - 然后配置 DbContext:

services.AddDbContext<TenantDbContext>((s, o) =>
{
    var connectionStringBuilder = s.GetService<IConnectionStringBuilder>();

    // read the current tenant from HttpHeader in ITenantContext
    var tenant = s.GetService<ITenantContext>()?.Tenant;
    
    // build the connectionString for the current Tenant
    var connectionString = connectionStringBuilder.TenantConnectionString(tenant?.Identifier) 
                ?? DbConstants.DEV_DEFAULT_TENANT_CONNECTION_STRING;

    o.UseSqlServer(connectionString, builder => builder.MigrationsAssembly(typeof(TenantDbContext).Assembly.FullName));
});

这个解决方案的美妙之处在于,将来无论ConnectionStrings存储的位置如何改变,您只需要实现IConnectionStringBuilder的一个版本并交换类型即可,就像这样。
public class SuperSecretConnectionStringBuilder : IConnectionStringBuilder
{
...
}

   services.AddTransient<IConnectionStringBuilder, SuperSecretConnectionStringBuilder>();

你甚至可以为不同的环境使用不同类型的ConnectionStringBuilders,因此对于DEV、STAGING和QA,你可以使用appsettings.config文件,而对于PROD则使用其他东西。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接