簡體   English   中英

實體框架 - 使用IDbConnectionInterceptor設置session_context

[英]Entity Framework - Setting session_context using IDbConnectionInterceptor

我正在學習本教程 ,以便通過Entity Framework 6 CodeFirst在SQL Server中使用行級安全性。 教程代碼示例演示如何使用IDbConnectionInterceptor並在session_context設置當前用戶ID。 要檢索用戶ID,它使用靜態訪問器方法HttpContext.Current.User.Identity.GetUserId() ,它與Asp.Net標識和System.Web命名空間相結合。

在我的多租戶Web應用程序中,我希望使用Unity將tenantId注入到DbConnectionInterceptor中(不創建與HttpContext硬耦合)並在session_context設置tenantId。 我發現DbConnectionInterceptor需要全局注冊(例如,在應用程序啟動時),因此每個請求都不能讓Unity創建DbConnectionInterceptor實例。

我的解決方案中還有2個DbContexts代表2個不同的數據庫(租戶數據庫和系統數據庫),我只想將session_context應用於租戶數據庫。

似乎剩下的唯一選擇是讓tenantId通過Unity注入DbContext isntance並訪問DbConnectionInterceptorOpened()方法內的DbContext實例。 為此,我想到在Opened()方法中使用interceptionContext參數。 interceptionContext具有DbContexts (復數)屬性。 沒有關於此的文檔,所以我假設這樣的東西會起作用:

public void Opened(DbConnection connection, DbConnectionInterceptionContext interceptionContext)
{
    var firstDbContext = interceptionContext.DbContexts.FirstOrDefault(d => d is TenantDataContext);
    if (firstDbContext != null)
    {
        var dataContext = firstDbContext as TenantDataContext;
        var tenantId = dataContext.TenantId;

        DbCommand cmd = connection.CreateCommand();
        cmd.CommandText = $"EXEC sp_set_session_context @key=N'TenantId', @value={tenantId};";
        cmd.ExecuteNonQuery();
    }
}

我的代碼檢查DbContexts集合是否包含TenantDataContext作為第一個元素並執行sp_set_session_context 但我擔心的是,DbContexts是否有機會在同一時間出現? 如果是這種情況,與我的其他數據庫的連接也會設置我不需要的session_context 我想知道為什么Microsoft將此作為集合屬性而不是單個DbContext屬性提供。 此屬性使您想知道多個DbContexts是否可以使用相同的連接。

有沒有人達到我想要的目標? 對此interceptionContext的任何解釋對我也有幫助。

如果您正在使用EF,則可以使用DbContext的Connection_StateChaned事件。

 static void Main(string[] args)
    {               
        using (var db = new AdventureWorks2016CTP3Entities())
        {
            db.Database.Connection.StateChange += Connection_StateChange;
            db.Database.Log = (log) => System.Diagnostics.Debug.WriteLine(log);

            var purchase = db.SalesOrderHeader.Select(i => i.SalesPersonID);

            foreach (var m in purchase)
            {
                Console.WriteLine(m);
            }
        }

    }

    private static void Connection_StateChange(object sender, System.Data.StateChangeEventArgs e)
    {
        if(e.CurrentState == System.Data.ConnectionState.Open)
        {
            var cmd = (sender as System.Data.SqlClient.SqlConnection).CreateCommand();
            cmd.CommandType = System.Data.CommandType.Text;
            cmd.CommandText = "exec sp_set_session_context 'UserId', N'290'";

            cmd.ExecuteNonQuery();
        }
    }

我意識到這是一個較老的問題,但我想我會為那些尋找一個問題的人發布我們的解決方案。 我們使用攔截器將SQLServer session_context語句注入到通過EF運行的命令/連接中。

在我們的例子中,我們必須為DbCommand和DbConnection創建攔截器來處理EF Linq查詢和通過命令運行的原始SQL查詢。 這些Interceptor類分別實現IDbCommandInterceptor和IDbConnectionInterceptor。

對於DbCommandInterceptor,我們使用SqlCommand.CommandText將EXEC sp_set_session_context原始SQL添加到通過攔截器的每個命令。

public class SessionContextDbCommandInterceptor : IDbCommandInterceptor

對於DbConnectionInterceptor,我們實現Opened方法並對運行sp_set_session_context SQL的連接執行SqlCommand。

public class SessionContextDbConnectionInterceptor : IDbConnectionInterceptor
{
    public void Opened(DbConnection connection, DbConnectionInterceptionContext interceptionContext)
    {...}

然后我們創建了一個DbConfiguration類,在構造函數中添加了攔截器:

public class SessionContextConfiguration : DbConfiguration
{
    public SessionContextConfiguration()
    {
        AddInterceptor(new SessionContextDbConnectionInterceptor());
        AddInterceptor(new SessionContextDbCommandInterceptor());
    }
}

然后通過DbConfigurationType屬性以及我們的web.config將此DbConfiguration類添加到我們的DbContext類:

[DbConfigurationType(typeof(SessionContextConfiguration))]
public class MyContext : DbContext

<entityFramework codeConfigurationType="MyAssembly.SessionContextConfiguration, MyAssembly">

我們像往常一樣使用Autofac注入DbContexts,並且由於Configuration類,攔截器會自動添加到DbContext實例中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM