C# 如何基于每个查询动态更改模式?

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

我希望能够将模式的名称传递给我的所有数据层方法,并以某种方式让EntityFramework在每个查询的基础上更改模式

这可能吗

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查询作为每个查询的基础,那么

你必须做以下事情

  • 创建架构时,请创建一个用户并将其默认架构设置为此架构
  • 将dbo表中的所有架构、用户名和密码存储为master

  • 如果您拥有上述所有数据,那么您可以使用相同的查询来从服务器获取数据,方法是根据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();
        }
    }