如何在Entity Framework连接上设置CONTEXT_INFO

时间:2017-06-19 18:50:19

标签: sql-server entity-framework

我正在努力在基于Entity Framework的应用程序中的共享数据库中隔离存储数据。我想使用SQL Server 2016行级安全性,但我更喜欢让我的数据库连接都使用单个用户。所以我想将SQL Server CONTEXT_INFO设置为我所有Entity Framework查询的商店号。然后RLS可以确定该行是否属于商店编号CONTEXT_INFO设置为。

我实现此目的的唯一想法是更新我的DbContext构造函数以创建连接本身并在将连接传递给基础构造函数之前执行SET CONTEXT_INFO语句。我想我可以在DbContext的部分类中添加构造函数重载来执行此操作,或者修改t4模板以生成原始构造函数。

EF提供了更好的方法吗?我先使用DB。

2 个答案:

答案 0 :(得分:3)

看起来IDbConnectionInterceptor的实现者每次打开连接时都能执行一些代码。这允许我执行存储过程来设置CONTEXT_INFOSESSION_CONTEXT变量。我的拦截器看起来像这样:

public class MAContextInterceptor : IDbConnectionInterceptor
{
    private static ITenantIDProvider _tenantIDProvider;
    public MAContextInterceptor(ITenantIDProvider tenantIDProvider)
    {
        _tenantIDProvider = tenantIDProvider;
    }

    public void Opened(DbConnection connection, DbConnectionInterceptionContext interceptionContext)
    {
        // Set SESSION_CONTEXT to current tenant ID whenever EF opens a connection
        try
        {
            Guid tenantID = _tenantIDProvider == null ? Guid.Empty : _tenantIDProvider.GetTenantID();

            if (tenantID != Guid.Empty)
            {
                DbCommand cmd = connection.CreateCommand();
                cmd.CommandText = "ma_setcontextinfo";
                cmd.CommandType = System.Data.CommandType.StoredProcedure;
                DbParameter param = cmd.CreateParameter();
                param.ParameterName = "@tenantID";
                param.Value = tenantID;
                cmd.Parameters.Add(param);
                cmd.ExecuteNonQuery();
            }
        }
        catch (System.NullReferenceException)
        {
            //log error
        }
    }
}

由于这是在数据层类库中,我添加了一个ITenantIDProvider,它允许上面的层提供一个提供租户ID的实现。当应用程序启动时,最终会在我的Global.asax中设置。它还调用System.Data.Entity.Infrastructure.Interception.DbInterception.Add(new MAContextInterceptor(tenantIDProvider));来注册拦截器。

它似乎适用于所有DbContexts,我可以看到being problematic,但出于我的目的,这是有效的。 This page几乎完全拼写了这一切。在提出这个问题之前,我不知道为什么我的搜索没有出现!

答案 1 :(得分:2)

您不仅需要运行sp_set_session_context,还需要强制连接在DbContext生命周期的持续时间内保持打开状态。如果DbContext没有使用开放连接构建,则需要运行:

db.Database.Connection.Open();

以防止EF使用连接池。

不,EF没有更好的方法来做到这一点。

除了您提到的选项之外,您还可以拥有一个中心DbContext工厂方法。当您需要更改连接字符串或连接到联合(分片)后端时,通常会执行此操作。例如:

public static MyDbContext Connect(string username,bool readOnly) {    。 。 。 }