Entity Framework 中的动态连接字符串
Dynamic Connection String in Entity Framework
目标:对已部署的桌面 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 class 都是部分的。使用 partial
您可以在另一个文件中添加代码(只记得那里的 partial
关键字)并且仍然要重新生成所有内容。 Generator 只会覆盖它生成的文件,所有其他额外添加到该部分 class 的文件都不会消失。在那里维护生成的和手写的部分没有问题。
此外,生成的 class 不是 sealed
。你可以继承它。因此,您可以尝试继承并开始使用派生的 class,而不是直接使用 DatabaseContext
。这个派生的class不会继承构造函数,但会继承所有其他public重要的东西。然后您将能够提供您自己的构造函数,甚至是默认构造函数,即调用参数化基 class 构造函数。其实我没试过,不过看起来很简单,应该可以吧。
我建议不使用 DbContextHelper.GetContext()
(显然是 static)(您认为开发人员可能误用或忘记了),而是使用您自己的 DbContext class.
在您拥有 EDMX 并生成 DatabaseContext
上下文的项目中,添加一个文件:
public partial class DatabaseContext
{
protected DatabaseContext(string nameOrConnstring) : base(nameOrConnstring) { }
}
它将添加一个新的重载,它将公开采用 connstring 的基本 DbContext 构造函数。
然后向其中添加另一个文件:
public class ANewContext : DatabaseContext
{
public ANewContext() : base(DbContextHelper.FindMyConnectionString()){ }
}
仅此而已。由于您的助手无论如何都是静态的,所以我们可以这样称呼它。只需将其更改为 return connstring 道具,它无论如何都需要确定。
现在重命名 classes:
DatabaseContext -> InternalDatabaseContextDontUseMe
ANewContext -> DatabaseContext
或类似的东西,我敢打赌,没有人会对应该在任何地方使用其中的一个感到困惑。用法:
public void SomeMethod()
{
using(var db = new DatabaseContext()) // that's ANewContext after renaming
{
...
}
}
在InternalDatabaseContextDontUseMe
中使用partial
,您将可以重新生成模型,并且不会删除额外添加的ctor。有了一个额外的继承级别,自动生成的默认构造函数将被隐藏,使用派生的 class 的开发人员将无法意外调用它,他们将收到新的默认构造函数来完成所需的工作。
如果您真的有兴趣了解我在挖掘 EF 源、LazyContext、工厂、解析器等时的发现,请查看 this article of fine。我把我今天能想起的所有东西都放在那里,虽然它有点混乱,但如果你喜欢挖掘和反编译,它可能会对你有所帮助。尤其是最后提到的EntityConnection-DbProviderFactory-Resolvers
目标:对已部署的桌面 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 class 都是部分的。使用 partial
您可以在另一个文件中添加代码(只记得那里的 partial
关键字)并且仍然要重新生成所有内容。 Generator 只会覆盖它生成的文件,所有其他额外添加到该部分 class 的文件都不会消失。在那里维护生成的和手写的部分没有问题。
此外,生成的 class 不是 sealed
。你可以继承它。因此,您可以尝试继承并开始使用派生的 class,而不是直接使用 DatabaseContext
。这个派生的class不会继承构造函数,但会继承所有其他public重要的东西。然后您将能够提供您自己的构造函数,甚至是默认构造函数,即调用参数化基 class 构造函数。其实我没试过,不过看起来很简单,应该可以吧。
我建议不使用 DbContextHelper.GetContext()
(显然是 static)(您认为开发人员可能误用或忘记了),而是使用您自己的 DbContext class.
在您拥有 EDMX 并生成 DatabaseContext
上下文的项目中,添加一个文件:
public partial class DatabaseContext
{
protected DatabaseContext(string nameOrConnstring) : base(nameOrConnstring) { }
}
它将添加一个新的重载,它将公开采用 connstring 的基本 DbContext 构造函数。
然后向其中添加另一个文件:
public class ANewContext : DatabaseContext
{
public ANewContext() : base(DbContextHelper.FindMyConnectionString()){ }
}
仅此而已。由于您的助手无论如何都是静态的,所以我们可以这样称呼它。只需将其更改为 return connstring 道具,它无论如何都需要确定。
现在重命名 classes:
DatabaseContext -> InternalDatabaseContextDontUseMe
ANewContext -> DatabaseContext
或类似的东西,我敢打赌,没有人会对应该在任何地方使用其中的一个感到困惑。用法:
public void SomeMethod()
{
using(var db = new DatabaseContext()) // that's ANewContext after renaming
{
...
}
}
在InternalDatabaseContextDontUseMe
中使用partial
,您将可以重新生成模型,并且不会删除额外添加的ctor。有了一个额外的继承级别,自动生成的默认构造函数将被隐藏,使用派生的 class 的开发人员将无法意外调用它,他们将收到新的默认构造函数来完成所需的工作。
如果您真的有兴趣了解我在挖掘 EF 源、LazyContext、工厂、解析器等时的发现,请查看 this article of fine。我把我今天能想起的所有东西都放在那里,虽然它有点混乱,但如果你喜欢挖掘和反编译,它可能会对你有所帮助。尤其是最后提到的EntityConnection-DbProviderFactory-Resolvers