c# - Entity Framework 中的动态连接字符串

标签 c# wpf entity-framework dynamic connection-string

目标:为已部署的桌面 wpf 应用程序使用数据库优先范式(而非代码优先),并为最终用户提供独特的数据库:

1) 让 EntityFramework 使用运行时确定的连接字符串。
2) 不部署不同的 app.config 文件。

尝试的事情:

1) 重载构造函数——虽然成功了,但这种解决方案并不受欢迎,因为它为开发人员犯错敞开了大门,使单元测试更加困难。
2) 尝试修改连接/上下文工厂 - 抛出异常。
3) 更改默认构造函数 - 可能会成功,此解决方案不受欢迎,因为默认构造函数是自动生成的。
4) 尝试修改 ConfigurationSettings - 抛出异常,它是只读的。
5) 在客户端部署 app.config - 虽然看似合理,但这种解决方案并不受欢迎,因为它需要重写我们的部署引擎。

帮忙吗?

编辑: 与我们尝试的第一项相关的一些代码(重载构造函数):

public partial class DatabaseContext
{
    public DatabaseContext(EntityConnection con)
        : base(con, true)
    {
    }
}

public static class DbContextHelper
{
    public static string ConnectionString { get; set; }

    public static CounterpartDatabaseContext GetDbContext()
    {
        EntityConnectionStringBuilder builder = new EntityConnectionStringBuilder
        {
            Provider = "System.Data.SqlClient",
            ProviderConnectionString = ConnectionString,
            Metadata = @"res://*/DatabaseContext.csdl|res://*/DatabaseContext.ssdl|res://*/DatabaseContext.msl"
        };
        EntityConnection con = new EntityConnection(builder.ToString());
        return new DatabaseContext(con);
    }
}

用法:

public void SomeMethod()
{
    using(DatabaseContext db = DbContextHelper.GetDbContext())
    {
       // db things
    }
}

使用配置管理器添加连接字符串的编辑代码:

public MainWindow()
{
    InitializeComponent();
        ConfigurationManager.ConnectionStrings.Add(new ConnectionStringSettings("DatabaseContext", @"metadata=res://*/DatabaseContext.csdl|res://*/DatabaseContext.ssdl|res://*/DatabaseContext.msl;provider=System.Data.SqlClient;provider connection string="data source=sqldev;initial catalog=Dev;persist security info=True;user id=user;password=password;MultipleActiveResultSets=True;App=EntityFramework"", "System.Data.EntityClient"));
}

配置管理器代码只是抛出一个异常,所以之后的任何代码都没有意义。

最佳答案

生成的 DatabaseContext 类都是部分的。使用 partial 您可以在另一个文件中添加代码(只记得那里的 partial 关键字)并且仍然会重新生成所有内容。 Generator 只会覆盖它生成的文件,所有其他添加到该部分类的文件都不会消失。在那里维护生成的和手写的部分没有问题。

另外,生成的类不是密封的。你可以继承它。因此,您可以尝试从它继承并开始使用派生类,而不是直接使用 DatabaseContext。这个派生类不会继承构造函数,但会继承所有其他公共(public)重要的东西。然后,您将能够提供自己的构造函数,甚至是默认构造函数,即调用参数化基类构造函数。实际上,我没有那样尝试过,但它看起来很简单并且可能有效。

我的建议是不使用 DbContextHelper.GetContext()(这显然是 static)(您认为开发人员可能会误用或忘记),而是使用您的自己的 DbContext 类。

在您拥有 EDMX 并生成 DatabaseContext 上下文的项目中,添加一个文件:

public partial class DatabaseContext
{
    protected DatabaseContext(string nameOrConnstring) : base(nameOrConnstring) { }
}

它将添加一个新的重载,它将公开采用 connstring 的基本 DbContext 构造函数。

然后向其中添加另一个文件:

public class ANewContext : DatabaseContext
{
    public ANewContext() : base(DbContextHelper.FindMyConnectionString()){ }
}

就是这样。由于您的助手无论如何都是静态的,所以我们可以这样调用它。只需更改它以返回它无论如何都需要确定的 connstring Prop 。

现在重命名类:

DatabaseContext -> InternalDatabaseContextDontUseMe
ANewContext -> DatabaseContext

或类似的东西,我敢打赌,没有人会对应该在任何地方使用它们中的哪一个感到困惑。用法:

public void SomeMethod()
{
    using(var db = new DatabaseContext()) // that's ANewContext after renaming
    {
       ...
    }
}

InternalDatabaseContextDontUseMe 中使用 partial,您将能够重新生成模型,并且不会删除额外添加的 ctor。有了一个额外的继承级别,自动生成的默认构造函数将被隐藏,使用派生类的开发人员将无法意外调用它,他们将收到新的默认构造函数,它将执行所需的操作。


如果您真的有兴趣了解我在挖掘 EF 源代码、LazyContext、Factories、Resolvers 等时的发现,请查看 this article of fine .我把我今天能想起的所有东西都放在那里,虽然它有点困惑,但如果你喜欢挖掘和反编译,它可能会对你有所帮助。特别是最后提到的EntityConnection-DbProviderFactory-Resolvers。

关于c# - Entity Framework 中的动态连接字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27830155/

相关文章:

c# - 如何取消初始化 FrameworkElement?

vb.net - 'Default' 和 'Legacy ObjectContext' 代码生成之间有根本区别吗

mysql - EF6+MySQL - 在单个 SaveChanges 中插入 2 个相关对象时出错

mysql - 在 Entity Framework 6 中左联接

C# 继承和默认构造函数

c# - 是否可以在 Asp.NET razor 代码中嵌套 foreach 循环?

c# - 无法加载文件或程序集'MySql.Data,版本 = 6.2.2.0

c# - 通过 Serial 从 Arduino 读取字符串

wpf - 为什么 Visual Studio 不希望我向 WPF 项目添加新窗口?

c# - 我的代码中出现"Specified element is already the logical child of another element. Disconnect it first"错误,有什么解决办法吗?