2012-07-22 102 views
1

我正在設計一個多租戶數據庫,其中每個租戶都有一個相應的數據庫用戶。爲用戶分配與租戶關聯的架構的訪問權限以及dbo架構中對象的特定權限。EXECUTE AS與Linq一起使用DbContext時

一旦我確定的租戶,我想通過執行一個SQL語句,像下面切換到相應的用戶上下文:

EXECUTE AS User = 'Tenant1' WITH NO REVERT 

當我執行使用的DbContext的數據庫屬性的ExecuteSqlCommand這個命令,一切似乎正常工作。當我後來做使用LINQ to模型的變化並調用該方法

myDbContext.SaveChanges(); 

我得到一系列異常:啓動提供商 連接上一個交易的發生

錯誤。詳情請參閱內部例外。

用的內異常:

了嚴重錯誤發生在當前命令。應該丟棄結果,如果有的話, 。

是否有可能以這種方式更改用戶的執行上下文,如果是這樣,那麼最好的方法是什麼?

回答

0

大部分我所需要的答案在這裏找到:

http://romiller.com/2011/05/23/ef-4-1-multi-tenant-with-code-first/

public partial class MyDBContext 
{ 
    public MyDBContext() : base() { } 
    private MyDBContext(DbConnection connection, DbCompiledModel model) : base(connection, model, contextOwnsConnection: false) { } 

    private static ConcurrentDictionary<Tuple<string, string>, DbCompiledModel> modelCache 
     = new ConcurrentDictionary<Tuple<string, string>, DbCompiledModel>(); 

    public static MyDBContext Create(string tenantSchema, DbConnection connection) 
    { 
    var compiledModel = modelCache.GetOrAdd 
    (
     Tuple.Create(connection.ConnectionString, tenantSchema), 
     t => 
     { 
     var builder = new DbModelBuilder(); 
     builder.Conventions.Remove<IncludeMetadataConvention>(); 
     builder.Entity<Location>().ToTable("Locations", tenantSchema); 
     builder.Entity<User>().ToTable("Users", tenantSchema); 

     var model = builder.Build(connection); 
     return model.Compile(); 
     } 
    ); 

    var context = new FmsDBContext(connection, compiledModel); 

    if(!string.IsNullOrEmpty(tenantSchema) && !tenantSchema.Equals("dbo", StringComparison.OrdinalIgnoreCase)) 
    { 
    var objectContext = ((IObjectContextAdapter)context).ObjectContext; 
    objectContext.Connection.Open(); 
    var currentUser = objectContext.ExecuteStoreQuery<UserContext>("SELECT CURRENT_USER AS Name", null).FirstOrDefault(); 
    if(currentUser.Name.Equals(tenantSchema, StringComparison.OrdinalIgnoreCase)) 
    { 
     var executeAs = string.Format("REVERT; EXECUTE AS User = '{0}';", tenantSchema); 
     objectContext.ExecuteStoreCommand(executeAs); 
    } 
    } 

    return context; 
    } 
} 

然後你就可以像這樣訪問架構的信息:

using (var db = MyDBContext.Create(schemaName, dbConn)) 
{ 
    // ... 
} 

但是,這實際上繞過了使用數據庫用戶。我仍在研究如何使用數據庫用戶的上下文,而不是隻指定模式名稱。


更新:

我終於在這裏解決了最後一關。的關鍵是以下代碼:

if(!string.IsNullOrEmpty(tenantSchema) && !tenantSchema.Equals("dbo", StringComparison.OrdinalIgnoreCase)) 
    { 
    var objectContext = ((IObjectContextAdapter)context).ObjectContext; 
    objectContext.Connection.Open(); 
    var currentUser = objectContext.ExecuteStoreQuery<UserContext>("SELECT CURRENT_USER AS Name", null).FirstOrDefault(); 
    if(currentUser.Name.Equals(tenantSchema, StringComparison.OrdinalIgnoreCase)) 
    { 
     var executeAs = string.Format("REVERT; EXECUTE AS User = '{0}';", tenantSchema); 
     objectContext.ExecuteStoreCommand(executeAs); 
    } 
    } 

AS命令在連接上發出它被用於執行稍後LINQ到實體命令之前執行。只要連接保持打開狀態,用戶的環境就會保持原樣。在我的數據庫中,租戶的模式名稱和用戶名是相同的。

多次更改用戶的執行上下文將導致錯誤,因此使用快速查詢來確定當前用戶上下文。需要一個小實體類來檢索使用連接的信息:

private class UserContext 
{ 
    public string Name { get; set; } 
} 
0

您是否試過myDbContext.Submit()或SubmitAll?我不能excatly記得,但它應該是類似的東西...

+0

DbContext類中沒有以Submit開頭的方法。 – Sparafusile 2012-07-22 17:35:46

+0

'SubmitChanges'來自'LinqToSql'。它幾乎與「LinqToEntities」中的SaveChanges不完全相同 – Silvermind 2012-07-22 18:43:15

相關問題