我正在努力在基于Entity Framework的应用程序中的共享数据库中隔离存储数据。我想使用SQL Server 2016行级安全性,但我更喜欢让我的数据库连接都使用单个用户。所以我想将SQL Server CONTEXT_INFO设置为我所有Entity Framework查询的商店号。然后RLS可以确定该行是否属于商店编号CONTEXT_INFO设置为。
我实现此目的的唯一想法是更新我的DbContext构造函数以创建连接本身并在将连接传递给基础构造函数之前执行SET CONTEXT_INFO语句。我想我可以在DbContext的部分类中添加构造函数重载来执行此操作,或者修改t4模板以生成原始构造函数。
EF提供了更好的方法吗?我先使用DB。
答案 0 :(得分:3)
看起来IDbConnectionInterceptor
的实现者每次打开连接时都能执行一些代码。这允许我执行存储过程来设置CONTEXT_INFO
或SESSION_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) { 。 。 。 }