【问题标题】:Using Entity Framework Code First with a dynamic connection string将 Entity Framework Code First 与动态连接字符串一起使用
【发布时间】:2015-06-22 09:13:54
【问题描述】:

我正在使用 Entity Framework(最新版本 - 我们所说的 6.0)和最新的 .NET 版本(我们所说的 4.5.1 版本)编写应用程序。

我面临的问题是我想使用代码优先的方法,因为我真的很喜欢它,但应用程序由各种数据库组成。现在,添加迁移和更新数据库已经不是问题了。我知道执行此操作的命令,即使 Entity Framework 需要跟踪多个上下文对象。

但是,这是我正在处理的具体情况:

  • 我使用包含各种用户的通用“设置”数据库,每个用户都有自己的特定设置。每个用户都通用的一种设置是数据库连接字符串。

这确实意味着我有一个设置表,其中可以配置 5 个用户,但每个用户确实需要另一个数据库(但每个数据库的数据库结构都是相同的)。即使模型相同,我们使用不同的数据库也有非常具体的原因,但我不会详细说明。

现在,问题在于我需要如何在我的 Visual Studio 项目中定义我的第二个上下文。我知道在我的DbContext 的构造函数上我可以传递一个连接字符串,所以在运行时完全没有问题,但是当我开发时,我也在使用 NuGet 包管理器来管理我的数据库的迁移。

有什么方法可以一次升级所有数据库(或一次升级一个),但是通过获取存储在数据库中的连接字符串(因为它取决于用户)?

亲切的问候

【问题讨论】:

  • 我本来想将数据库迁移到一个单独的进程,但不是在应用程序的正常执行期间。可能此过程将需要对数据库的独占访问权限。
  • 我现在正在做一些事情,但它并不完全有效。也许您对如何使用连接字符串而不是连接字符串名称来调用 Database.SetInitializer 有任何想法。
  • 我认为你可以用DbConnection 参数构造DbContext,你可以像你一样创建,f.e.您可以将连接字符串传递给SqlConnection 构造函数。
  • 我找到了解决方案。请参阅下面的答案。

标签: c# sql-server database entity-framework


【解决方案1】:

好的,我自己已经设法找到了解决方案,对此我很满意。

首先,我将列出应用程序的结构。

我确实有一个名为AppServerSettingsContextDbContext,它在源代码中定义如下:

/// <summary>
///     Initializes a new instance of the <see cref="AppServerSettingsDataContext"/>.
/// </summary>
public AppServerSettingsDataContext()
    : base("AppServerSettingsDataContext")
{ }

此上下文有 2 个不同的实体(一个用于成员,一个用于该特定成员的所有设置)。

为了使迁移能够执行,我需要在应用程序配置文件中有一个ConnectionString,就像我们使用的方式一样。

然后,我有另一个名为 AppServerDataContext 的上下文。 这有 2 个构造函数,如下所示:

/// <summary>
///     Initializes a new instance of the <see cref="AppServerDataContext"/>.
/// </summary>
public AppServerDataContext() : 
    this(ConfigurationManager.ConnectionStrings["AppServer"].ConnectionString) 
{ }

/// <summary>
///     Initializes a new instance of the <see cref="AppServerDataContext"/>.
/// </summary>
/// <param name="connectionString">The full connection string which is used to connect to the database.</param>
public AppServerDataContext(string connectionString)
    : base(connectionString) { }

您将在代码中看到,我可以指定连接字符串,也可以为应用程序配置文件加载连接字符串。

稍后您会明白为什么这很重要。

我有DbContext,它指向配置文件中的连接字符串。这不是必须的,但我习惯于那样工作。因此,该连接字符串将仅在调用 add-migration 命令时使用。这是因为该命令需要数据库来检查数据库的当前状态并添加正确的迁移。

现在,我在一个包含 2 个上下文文件的项目中工作,因此 NuGet 包管理器控制台需要一种方法来识别它。

因此,可以使用以下命令:

  1. 为特定上下文启用迁移:

    • `PM> 启用迁移 -ContextTypeName: -MigrationsDirectory:

这是我需要执行两次的命令,每个上下文一次。

然后,在我的AppServerSettings 上下文的Seed 方法中,我将编写以下代码:

/// <summary>
///     Runs after upgrading to the latest migration to allow seed data to be updated.
/// </summary>
/// <param name="context">Context to be used for updating seed data.</param>
protected override void Seed(AppServerSettingsDataContext context)
{
    // Creates the Member and assign all the settings which are required for the application to function.
    context.Members.AddOrUpdate(x => x.Name, new Member("Povlo")
    {
        Settings = new List<MemberSettings>
        {
            new MemberSettings("DatabaseConnectionString", "Removed for Security Reasons"),
        }
    });

    // Make sure that for every member, the database is created by using the "MigrateDatabaseToLatestVersion" migration.
    foreach (var setting in context.Members.Select(member => member.Settings.FirstOrDefault(x => x.Key == "DatabaseConnectionString")))
    {
        using (var appServerContext = new AppServerDataContext(setting.Value))
        {
            var de = new MigrateDatabaseToLatestVersion<AppServerDataContext, AppServer.Configuration>();
            de.InitializeDatabase(appServerContext);

            appServerContext.Database.Initialize(true);
        }
    }
}

我在这里所做的基本上是首先使用给定的连接字符串创建一个成员(可以有多个)。

然后,在相同的方法中,我确实有一个 foreach 循环,它将根据数据库中的连接字符串创建一个上下文。那么对于这个上下文,数据库正在升级到最新版本。

这样做的好处是我使用的是代码优先的方法,并且所有数据库始终是最新版本。

这样做的一个缺点是所有模型都需要完全相同。

【讨论】:

    猜你喜欢
    • 2011-10-24
    • 2023-04-09
    • 2011-05-06
    • 1970-01-01
    • 2011-03-18
    • 1970-01-01
    • 1970-01-01
    • 2011-06-27
    • 2012-12-14
    相关资源
    最近更新 更多