如何在每个查询的动态基础上更改架构?

时间:2014-03-10 19:01:46

标签: c# asp.net-mvc entity-framework

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

这可能吗?

public class UserRepository : GenericRepository<....>
{


    public List<User> GetUsersByLocation(string schema, int locationId)
    {
        ....
    }
}

在每次通话的基础上,我希望能够更改EF查询的架构。

7 个答案:

答案 0 :(得分:6)

如果您希望基于每个查询的多租户共享架构数据库的SQL DML查询,那么

你必须做以下事情

  1. 创建架构时,请创建用户并将其默认架构设置为此架构
  2. 将所有架构和用户名及密码存储在dbo表中作为主
  3. 如果您拥有以上所有数据,那么您可以使用相同的查询通过将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();
    }
}