【问题标题】:Dynamic Connection String in Entity Framework实体框架中的动态连接字符串
【发布时间】:2015-01-07 22:55:41
【问题描述】:

为已部署的桌面 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"));
}

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

【问题讨论】:

  • 很高兴您告诉我们您尝试了什么,但您应该向我们展示一些示例。毕竟,我们是程序员,我们想看代码:)
  • 不再有工厂更改的代码(因为它抛出异常从未提交更改)。

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


【解决方案1】:

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

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

我建议不要使用DbContextHelper.GetContext()(显然是静态)(您认为开发人员可能会误用或忘记),而是使用您自己的 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 道具。

现在重命名类:

DatabaseContext -> InternalDatabaseContextDontUseMe
ANewContext -> DatabaseContext

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

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

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


如果您真的有兴趣了解我在挖掘 EF 源、LazyContext、工厂、解析器等时发现的内容,请查看this article of fine。我把我今天能记得的所有东西都放在那里,虽然有点混乱,但如果你喜欢挖掘和反编译,它可能会对你有所帮助。尤其是最后提到的EntityConnection-DbProviderFactory-Resolvers。

【讨论】:

  • 你是正确的,上下文是部分的。但是,为了使用与 app.config 文件中的连接字符串不同的连接字符串来实例化上下文,必须使用连接调用基本构造函数。反编译时,基本构造函数调用一个内部方法:InitializeLazyInternalContext。如果我们从上下文继承,那么我们将无法访问此基本构造函数。这就是为什么我们考虑使用 DbContextHelper 来以我们需要的方式实例化上下文的原因。告诉我有关 ConnectionFactory/Provider 的更多信息,因为我们只完成了一半。
  • @Greg: InitializeLazyInternalContext - 是的,我记得那个。然而,这只是冰山一角。我找到了我玩过它的项目,结果证明它是 CodeFirst(但由于 InitializeLazyInternalContext 在你的情况下,它可能 Code/Db-First 并不重要)并且我在 .. 测试的上下文中研究了该主题.我试图在不触及 appconfig 的情况下神奇地将所有 DB-Ops 重新路由到测试数据库。目前,我没有发现任何痕迹,而且我似乎已经开始使用 InMemoryAsyncQueryable 而不是真正的测试数据库进行模拟。
  • @Greg:但我不敢相信我刚刚删除了所有的发现......它一定在某个地方......
  • @Greg:好的,我发现了一些东西,但是文本已经增长了很多,我把它移到了侧页。我在答案末尾添加了一个链接,它将指向您的文章。此外,我已经编辑了答案并澄清了我对利用partial and inheritance 来获得你想要的东西的意思。真的,我认为这种简单的方法就足够了。
  • 所以,继承确实有效。并与助手一起想出了一个很好的方法(使用布尔)来使单元测试快乐。另外,非常感谢你:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多