C# 如何基于每个查询动态更改模式?
我希望能够将模式的名称传递给我的所有数据层方法,并以某种方式让EntityFramework在每个查询的基础上更改模式 这可能吗C# 如何基于每个查询动态更改模式?,c#,asp.net-mvc,entity-framework,C#,Asp.net Mvc,Entity Framework,我希望能够将模式的名称传递给我的所有数据层方法,并以某种方式让EntityFramework在每个查询的基础上更改模式 这可能吗 public class UserRepository : GenericRepository<....> { public List<User> GetUsersByLocation(string schema, int locationId) { .... } } 公共类用户存储库:Generi
public class UserRepository : GenericRepository<....>
{
public List<User> GetUsersByLocation(string schema, int locationId)
{
....
}
}
公共类用户存储库:GenericRepository
{
公共列表GetUsersByLocation(字符串模式,int locationId)
{
....
}
}
在每次调用的基础上,我希望能够更改EF查询的模式。不,不可能。就这么简单。EF认为模式布局是静态的,就像几乎所有的ORM一样。很抱歉我的回答是否定的,但这是不可能的。您可以在编译模型时执行某些操作(通过在xml中或在属性中动态更改模型等),但不能基于每个查询执行任何操作。如果您可以在构造函数中使用架构参数创建存储库,则可以动态切换到新的数据库上下文 EF数据上下文将有一个带有
nameOrConnectionString
参数的构造函数重载。如果您的“schema”参数可以这样使用,那么您可以在方法中检测您的模式上下文,并在发出查询之前重新连接到其他模式
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 ...
}
}
公共类用户存储库:GenericRepository
{
私有字符串模式;
公共用户存储库(字符串架构):基本(架构)
{
_模式=模式;
}
公共列表GetUsersByLocation(字符串模式,int locationId)
{
如果(模式!=\u模式)
{
return(newuserrepository(schema)).GetUsersByLocation(schema,locationid);
}
//查询数据库。。。
}
}
更全面的解决方案包括重新设计存储库,以减少
UserRepository
类的实例化数量。您可以通过为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 List<User> GetUsersByLocation(string schema, int locationId)
{
using(var ctx = new MyDbContext(schema))
{
// query the database ...
}
}
public List GetUsersByLocation(字符串模式,int-locationId)
{
使用(var ctx=newmydbcontext(schema))
{
//查询数据库。。。
}
}
如果您希望将多租户共享架构数据库的SQL DML查询作为每个查询的基础,那么
你必须做以下事情
如果您拥有上述所有数据,那么您可以使用相同的查询来从服务器获取数据,方法是根据db用户名和密码的要求更改connectionString 这篇文章讨论了如何在OnModelCreating中更改数据库模式的名称。
希望这为您指明了正确的方向。从版本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)
{ }
}
公共密封类变更SchemaNameCommandInterceptor:IDbCommandInterceptor
{
私有只读字符串_schemaName;
公共更改SchemaNameCommandInterceptor(字符串schemaName)
{
_schemaName=“[”+schemaName+“]”;
}
public void ReaderExecuting(DbCommand,
DbCommandInterceptionContext(截取上下文)
{
如果(!string.IsNullOrEmpty(_schemaName))
command.CommandText=command.CommandText
.替换(“[dbo]”,_schemaName);
}
public void非查询执行(DbCommand命令,DbCommandInterceptionContext interceptionContext)
{ }
public void NonQueryExecuted(DbCommand命令,DbCommandInterceptionContext interceptionContext)
{ }
public void ReaderExecuted(DbCommand命令,DbCommandInterceptionContext interceptionContext)
{ }
public void scalare执行(DbCommand命令、DbCommandInterceptionContext interceptionContext)
{ }
已执行公共空标度(DbCommand命令、DbCommandInterceptionContext 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);
}
}
public List GetUsersByLocation(字符串模式,int-locationId)
{
var拦截器=新的ChangeSchemaNameCommandInterceptor(模式);
尝试
{
添加(拦截器);
return..../(您的EF LINQ查询)
}
最后
{
删除(拦截器);
}
}
我不认为这是一个非常干净的解决方案,但至少它允许你让剩下的代码相对不变。
< P>我认为可以通过重写IDBCurrdB拦截器,如前一篇文章中所建议的那样。 但是,要使其正常工作,您需要在ReaderExecuting阶段执行查询,并将结果提供给interceptionContext。 那么ReaderExecuted就不会像这里所解释的那样被调用,在“抑制执行”部分 因此,我认为混凝土读卡器的执行应改为:public void ReaderExecuting(DbCommand command,
DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
if (!string.IsNullOrEmpty(_schemaName))
{
command.CommandText = command.CommandText
.Replace("[dbo].", _schemaName);
interceptionContext.Result = command.ExecuteReader();
}
}
public void ReaderExecuting(DbCommand,
DbCommandInterceptionContext(截取上下文)
{
如果(!string.IsNullOrEmpty(_
public void ReaderExecuting(DbCommand command,
DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
if (!string.IsNullOrEmpty(_schemaName))
{
command.CommandText = command.CommandText
.Replace("[dbo].", _schemaName);
interceptionContext.Result = command.ExecuteReader();
}
}