我希望能够将模式的名称传递给我的所有数据层方法,并以某种方式让Entity Framework在每个查询的基础上更改模式。
这可能吗?
public class UserRepository : GenericRepository<....>
{
public List<User> GetUsersByLocation(string schema, int locationId)
{
....
}
}
在每次通话的基础上,我希望能够更改EF查询的架构。
答案 0 :(得分:6)
如果您希望基于每个查询的多租户共享架构数据库的SQL DML查询,那么
你必须做以下事情
如果您拥有以上所有数据,那么您可以使用相同的查询通过将connectionString更改为所需的db用户名和密码来从服务器获取数据
答案 1 :(得分:2)
从版本6开始,Entity Framework提供了一个易于访问的API来拦截SQL命令。您可以使用此界面动态更改命令文本中的模式名称。
首先,您需要一个实现System.Data.Entity.Infrastructure.Interception.IDbCommandInterceptor
的类。该接口包含许多方法,这些方法清楚地显示(按名称)它们拦截命令执行的时间点。在这种情况下,这些方法中只有一个(或几个)很有趣:
public sealed class ChangeSchemaNameCommandInterceptor : IDbCommandInterceptor
{
private readonly string _schemaName;
public ChangeSchemaNameCommandInterceptor(string schemaName)
{
_schemaName = "[" + schemaName + "].";
}
public void ReaderExecuting(DbCommand command,
DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
if (!string.IsNullOrEmpty(_schemaName))
command.CommandText = command.CommandText
.Replace("[dbo].", _schemaName);
}
public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{ }
public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{ }
public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{ }
public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{ }
public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{ }
}
如您所见,构造函数有一个参数,您可以通过该参数设置模式名称。在执行SQL命令之前,拦截器只是用指定的名称替换无处不在的“dbo”模式名称。 (也许你想在其他“执行”方法中这样做。)
现在你可以在必要时插入拦截器:
public List<User> GetUsersByLocation(string schema, int locationId)
{
var interceptor = new ChangeSchemaNameCommandInterceptor(schema);
try
{
DbInterception.Add(interceptor);
return .... // (your EF LINQ query)
}
finally
{
DbInterception.Remove(interceptor);
}
}
我不认为这是一个非常干净的解决方案,但至少它允许您将其余代码保持相对不变。
答案 2 :(得分:1)
不,不可能。就像这样。 EF认为架构布局是静态的,就像每个ORM一样。对不起,否定答案,但这是不可能的。编译模型时可能会做一些事情(通过在xml中更改它或在属性中动态地等),但在每个查询的基础上都没有。
答案 3 :(得分:0)
如果您可以在构造函数中使用schema参数创建存储库,那么您可以动态切换到新的数据库上下文。
EF数据上下文将具有nameOrConnectionString
参数的构造函数重载。如果您的&#34;架构&#34;参数可以这种方式使用,然后您可以检测方法中的模式上下文,并在发出查询之前重新连接到其他模式。
public class UserRepository : GenericRepository<...>
{
private string _Schema;
public UserRepository(string schema) : base(schema)
{
_Schema = schema;
}
public List<User> GetUsersByLocation(string schema, int locationId)
{
if (schema != _Schema)
{
return (new UserRepository(schema)).GetUsersByLocation(schema, locationid);
}
// query the database ...
}
}
更全面的解决方案将涉及重新设计的存储库,以减少UserRepository
类的实例化数量。
答案 4 :(得分:0)
您可以通过为app.config中的每个模式创建连接字符串并根据模式命名它们来实现此目的。
<connectionStrings>
<add name="schema1" connectionString="Data Source=xxxx;InitialCatalog=schema1;Persist Security Info=True;User ID=xxxx;Password=xxx;MultipleActiveResultSets=True" providerName="System.Data.SqlClient"/>
<add name="schema2" connectionString="Data Source=xxxx;InitialCatalog=schema2;Persist Security Info=True;User ID=xxxx;Password=xxx;MultipleActiveResultSets=True" providerName="System.Data.SqlClient"/>
</connectionStrings>
然后你可以创建一个新的构造函数,它传递模式,如下所示:
public class MyDbContext : DbContext
public ScipCcEntities(string schema)
: base("Name=" + schema)
{
}
如果你有很多模式,你可能想要使用连接字符串构建器并动态构建连接字符串:http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnectionstringbuilder.initialcatalog(v=vs.110).aspx
您在应用程序中的调用将如下所示:
public List<User> GetUsersByLocation(string schema, int locationId)
{
using(var ctx = new MyDbContext(schema))
{
// query the database ...
}
}
答案 5 :(得分:0)
在尝试回答您的问题时,我遇到了这篇博文。 Scott Gu -- Custom Schema Mapping
这篇文章讨论了如何在OnModelCreating中更改数据库模式的名称。 Working with Schema Names
希望指出你正确的方向。
答案 6 :(得分:0)
我认为可以通过覆盖IDbCommandInterceptor来实现,如前一篇文章所述。 但是要使其工作,您需要在ReaderExecuting阶段执行查询并将结果提供给interceptionContext。 那么ReaderExecuted不会按照这里所解释的那样被调用:MSDN - Logging and Intercepting Database Operations,在&#34;抑制执行&#34;部分。
所以我想具体的ReaderExecuting应该改为:
public void ReaderExecuting(DbCommand command,
DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
if (!string.IsNullOrEmpty(_schemaName))
{
command.CommandText = command.CommandText
.Replace("[dbo].", _schemaName);
interceptionContext.Result = command.ExecuteReader();
}
}