使用 Entity Framework 代码优先中的动态连接字符串迁移(来自文本文件的连接字符串)

Migration with dynamic connection string in Entity Framework code-first (the connection string from text file)

我首先使用 EF6 代码,首先,我将连接字符串放在一个名为 'Settings.txt'

的文本文件中

'Settings.txt'文件中的数据为

DataProvider: sqlserver
DataConnectionString: Data Source=.\SQLEXPRESS;Initial Catalog=MyDb;Integrated Security=True;Persist Security Info=False;Enlist=False;

这里是我用的 dbContext class:

public class DbDataContext : BaseDbContext
{
    static DbDataContext()
    {
        Database.SetInitializer(new ContextInitializer());
    }

    public DbDataContext():base() { }

    public DbDataContext(string nameOrConnectionString)
        : base(nameOrConnectionString) { }

    ...    
}

[DbConfigurationType(typeof(MyDbConfiguration))]
public abstract partial class BaseDbContext : DbContext, IDbContext
{
    public BaseDbContext() : this(GetConnectionString())
    { }

    public BaseDbContext(string nameOrConnectionString) : base(nameOrConnectionString)
    { }

    public static string GetConnectionString()
    {
        if (DataSettings.DataSettings.Current.IsValid())
        {
            return DataSettings.DataSettings.Current.DataConnectionString;
        }

        throw Error.Application("A connection string could not be resolved for the parameterless constructor of the derived DbContext. Either the database is not installed, or the file 'Settings.txt' does not exist or contains invalid content.");
    }
}

public class MyDbConfiguration : DbConfiguration
{
    public MyDbConfiguration()
    {
        IEfDataProvider provider = null;

        try
        {
            provider = (new EfDataProviderFactory(DataSettings.DataSettings.Current).LoadDataProvider()) as IEfDataProvider;
        }
        catch {
        }

        if (provider != null)
        {
            base.SetDefaultConnectionFactory(provider.GetConnectionFactory());
        }
    }
}

public partial class EfDataProviderFactory : DataProviderFactory
{
    public EfDataProviderFactory()
        : this(DataSettings.DataSettings.Current){ }

    public EfDataProviderFactory(DataSettings.DataSettings settings)
        : base(settings) { }

    public override IDataProvider LoadDataProvider()
    {
        var providerName = Settings.DataProvider;

        if (providerName.IsEmpty())
        {
            throw new Exception("Data Settings doesn't contain a providerName");
        }

        switch (providerName.ToLowerInvariant())
        {
            case "sqlserver":
                return new SqlServerDataProvider();

            case "sqlserverce":
                return new SqlServerCeDataProvider();

            default:
                throw new Exception(string.Format("Unsupported dataprovider name: {0}", providerName));
        }
    }
}

public class SqlServerDataProvider : IEfDataProvider
{
    public virtual IDbConnectionFactory GetConnectionFactory()
    {
        return new SqlConnectionFactory();
    }

    public bool StoredProceduresSupported
    {
        get { return false; }
    }

    public DbParameter GetParameter()
    {
        return new SqlParameter();
    }

    public string ProviderInvariantName
    {
        get { return "System.Data.SqlClient"; }
    }
}

我在 'BaseDbContext' class 中使用了一个名为 'GetConnectionString()'

的静态函数

此函数仅用于 return 来自文本文件的连接字符串。此行为在运行时运行良好,但在添加迁移时不起作用。

这就是问题所在:如何通过这种方式添加迁移,知道当我像这样将连接字符串直接放在函数中时

    public static string GetConnectionString()
    {
         return (@"Data Source=.\SQLEXPRESS;Initial Catalog=MyDb;Integrated Security=True;Persist Security Info=False;Enlist=False;");
    }

Add-Migration 命令正在运行

如何在不强制代码中的连接字符串的情况下解决这个问题?

我猜你在包管理器控制台中使用了 Add-Migration 命令。

如果您是 运行 宁 Add-Migration 手动您可以只添加连接字符串作为 -ConnectionString 参数:

Add-Migration -ConnectionString "Data Source=.\SQLEXPRESS;Initial Catalog=MyDb;Integrated Security=True;Persist Security Info=False;Enlist=False;"

您可能还必须添加参数 -ConnectionProviderName,除非您在 app.config 中定义了提供者。

我建议您停止使用此 Settings.txt 文件并将您的连接字符串移动到 app.config 文件的 connectionStrings 部分。这是处理连接字符串的推荐方法,比使用像 Settings.txt 文件这样的自定义文件要容易得多。

<connectionStrings>
  <add name="MyLocalDatabase" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=MyDb;Integrated Security=True;Persist Security Info=False;Enlist=False;" />
</connectionStrings>

如果这样做,您可以在包管理器控制台中使用参数 -ConnectionStringName,使用您在 app.config:

中定义的名称
Add-Migration -ConnectionStringName "MyLocalDatabase"

此外,使用 app.config 文件中的连接字符串,您可以将构造函数添加到您的上下文中,该构造函数接收连接字符串名称作为参数,并且可以在使用包管理器控制台时使用:

public DbDataContext():base("MyLocalDatabase") { }

这将允许您在程序包管理器控制台中 运行 您的命令,而无需指定任何连接字符串参数。只需确保在控制台中选择的启动项目的 app.config 文件中包含正确的连接字符串。

并且您可以删除 GetConnectionString 代码。您只是在重新实现使用 app.settings connectionString 部分时开箱即用的代码。这就是 DbContext 基本构造函数参数 NameOrConnectionString 的用法。您可以提供完整的连接字符串或 app.settings 文件中定义的连接字符串的名称。

我解决了这个问题, 在设计模式下获取文件路径(文本文件)时出现问题,甚至 单元测试

string filePath = Path.Combine(MapPath("~/App_Data/"), "Settings.txt");

public static string MapPath(string path)
    {
            path = path.Replace("~/", "").TrimStart('/').Replace('/', '\');

            var testPath = Path.Combine(baseDirectory, path);

            var dir = FindSolutionRoot(baseDirectory);

            if (dir != null)
                {
                    baseDirectory = Path.Combine(dir.FullName, "MyProjectName.WebAPI");
                    testPath = Path.Combine(baseDirectory, path);


            return testPath;
        }
    }

private static DirectoryInfo FindSolutionRoot(string currentDir)
    {
        var dir = Directory.GetParent(currentDir);
        while (true)
        {
            if (dir == null || IsSolutionRoot(dir))
                break;

            dir = dir.Parent;
        }

        return dir;
    }

    private static bool IsSolutionRoot(DirectoryInfo dir)
    {
        return File.Exists(Path.Combine(dir.FullName, "MySolutionName.sln"));
    }

这样,我们就可以得到runtime-mode下的文件路径