为动态连接字符串设置Entity Framework

26

我正在开发一个应用程序,它将在多个数据库中使用相同的数据库架构。因此,我创建了一个名为MyTemplate的数据库。当创建新用户时,他们将拥有自己的数据库实例。所以,将创建一个名为MyTemplate_[用户名]的数据库。当用户登录时,我需要将他们的查询指向他们的数据库。出于这个原因,我知道我需要在运行时设置连接字符串。我的问题是,我也想使用Entity Framework。

目前,我使用MyTemplate作为源创建了一个新的.edmx。我认为我可以更新代码并在那里设置连接字符串。不幸的是,我无法弄清楚如何设置它。TemplateEntities的构造函数没有重载允许我传递一个连接字符串。我注意到TemplateEntities派生自DbContext,我不认为这会是问题。

string connectionString = GetUsersConnectionString();
using (TemplateEntities entities = new TemplateEntities())
{
  TemplateEntity entity = new TemplateEntity();

  // Save to the database
  entities.TemplateEntity.Add(entity);
  entities.SaveChanges();
}

我是否错误地创建了 .edmx?或者我完全漏掉了什么?我在 Google 上找到的所有内容都显示应该有一个允许传递连接字符串的重载。然而,我没有可用的重载。

3个回答

45

生成的TemplateEntities类被标记为partial

你所需要做的就是添加另一个文件,里面包含了部分类定义的另一部分,并公开你想要使用的构造函数:

partial class TemplateEntities
{
  public TemplateEntities( string nameOrConnectionString )
    : base( nameOrConnectionString )
  {
  }
}

然后将连接字符串传递到此构造函数中。

您希望将此代码放入不同的文件中,以便在更新edmx模型时不会被覆盖。


2
当我这样做时,我收到一个错误,说:“对象”不包含一个带有1个参数的构造函数。我做错了什么? - user70192
4
新构造函数的部分类定义必须与生成的类在同一命名空间中 - 否则它会声明一个新类(默认情况下派生自object)。 - Nick Butler
1
+1:我很困惑为什么这种类型的构造函数不在默认模板中,但这是一个不错且干净的解决方案,谢谢。 - reSPAWNed
@NicholasButler 这个方法也适用于先建模式的方法吗? - Amit Bisht
我将我的connectionString作为参数传递,但是当我尝试查询数据库时出现错误!这是我放更多细节的链接:http://stackoverflow.com/questions/26116697/entity-framework-error-with-dynamic-connection-string - Piero Alberto
我必须在 nameOrConnectionString 中包含所有的 EF 元数据,否则它会抛出一个异常,说“上下文正在使用 Code First 模式,而该模式是从用于 Database First 或 Model First 开发的 EDMX 文件生成的代码中生成的...”。例如:nameOrConnectionString = "metadata=res://*/Model.MyDb.csdl|res://*/Model.MyDb.ssdl|res://*/Model.MyDb.msl;provider=System.Data.SqlClient;provider connection string=\"" + myDbConnectionString + "App=EntityFramework\";"; - Tom

18

Nicholas Butler的回答是完全正确的。除了他所说的之外,我面临的问题是如何采用现有的实体框架连接字符串,只是将其指向具有相同结构的不同数据库。 我使用以下代码仅更改现有字符串的数据源:

var originalConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["CSName"].ConnectionString;
var ecsBuilder = new EntityConnectionStringBuilder(originalConnectionString);
var sqlCsBuilder = new SqlConnectionStringBuilder(ecsBuilder.ProviderConnectionString)
{
    DataSource = "newDBHost"
};
var providerConnectionString = sqlCsBuilder.ToString();
ecsBuilder.ProviderConnectionString = providerConnectionString;

string contextConnectionString = ecsBuilder.ToString();
using (var db = new SMSContext(contextConnectionString))
{
    ...
}

这些类EntityConnectionStringBuilder和SqlConnectionStringBuilder是dotnet内置的还是由您编写的? - Monojit Sarkar
@MonojitSarkar 这些是.NET类,不是我编写的。这就是它的美妙之处。 :-) - Mark Meuer

0

这是我在构建解决方案时使用的逐步过程:

  1. 在所需项目上,确保使用“管理Nuget包...”选项菜单安装了Entity Framework。
  2. 在所需项目上,右键单击,然后选择添加->新项目,转到数据并选择ADO.NET实体数据模型。
  3. 输入模型名称,比如说“ExampleModel”。点击添加。
  4. 将出现四个选项以选择模型内容,我通常选择第一个,以便从数据库中的现有对象构建模型。点击下一步。
  5. 设置您的数据连接。完成后,输入您的模型实体名称,比如说“ExampleModelEntities”,然后点击下一步。
  6. 从数据库中选择将出现在EF模型中的对象。在模型命名空间输入框中输入与步骤3(“ExampleModel”)相同的模型名称。点击完成。

此时,已创建并添加了一个新的.edmx文件到项目中,其中包含所有准备好工作的对象。唯一不需要的细节是,到目前为止,连接字符串已被指定并保存到我们项目的Web.config文件中。

要删除此内容,只需转到 Web.config 的 <connectionStrings></connectionStrings> 部分块并从那里删除详细信息。我们现在将致力于使连接字符串可以从其他来源动态读取。

正如 Nicholas Butler 指出的那样,下一步将是创建原始部分实体类(ExampleModelEntities)的“版本”,这将允许我们传递动态连接字符串。这是可能的,因为创建的原始实体类继承自包含传递此类连接的构造函数的 DBContext。

要执行上述操作,请向项目添加一个新的空类。确保键入与第5步提供的相同的名称,按照我们的案例研究“ExampleModelEntities”的方式。以下是要实现的代码:

C#

public partial class ExampleModelEntities
{
public ExampleModelEntities(string connString) : base(connString)
{

}
}

VB.Net:

Partial Public Class ExampleModelEntities

Public Sub New(ByVal connString As String)
    MyBase.New(connString)
End Sub
End Class

此时,您的代码已准备好使用来自其他来源的动态连接字符串。其中一个来源可以是传递来自存储在不同数据库上的另一个字段的连接字符串或使用 EntityConnectionStringBuilder 类。

以下示例是使用 VB.Net 实现的,但请使用类似 Telerik 的工具进行翻译。假设我们从某个数据库获取对象列表,只想动态传递来自存储在不同数据库上的另一个字段的连接字符串。为了实现这一点,代码如下所示:

Public Shared Function Get_List(ByVal Param1 As String) As List(Of Stored_Procedure_Code_Result)

Try

Dim Object_List_Result As List(Of Stored_Procedure_Code_Result) = Nothing

Using dbContext As New ExampleModelEntities(Configuration.CONNECTION_STRING)

Object_List_Result = dbContext.Stored_Procedure_Code(Param1).ToList

dbContext.Dispose()
End Using
Return Object_List_Result

Catch ex As Exception
Throw ex
End Try

End Function

Configuration.CONNECTION_STRING 是动态连接字符串的值,使用名为“Configuration”的模块和检索此类值的函数表示。

为了避免格式不准确,应使用以下格式存储该值:

对于使用Entity Framework的Windows身份验证:

UPDATE [DBConnections].[dbo].[ListOfConnectionsTable]
SET ConnValue = 'metadata=res://*/ExampleModel.csdl|res://*/ExampleModel.ssdl|res://*/ExampleModel.msl;provider=System.Data.SqlClient;provider connection string="Data Source=ServerName;Initial Catalog=DBName;Integrated Security=True"'

使用 Entity Framework 进行 SQL 身份验证:

UPDATE [DBConnections].[dbo].[ListOfConnectionsTable]
SET ConnValue = 'metadata=res://*/ExampleModel.csdl|res://*/ExampleModel.ssdl|res://*/ExampleModel.msl;provider=System.Data.SqlClient;provider connection string="Persist Security Info=False;User ID=XXXXXX;Password=XXXXXXX;Initial Catalog=DBName;Data Source=ServerName;App=YourAppName;Network Library=dbmssocn"'

最后,延续Mark提供的答案,在Microsoft上有一个详细的解释,说明如何使用EntityConnectionStringBuilder类进行工作,该类也可以用于构建动态连接字符串,然后根据需要传递此值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接