EXECUTE AS when using a DbContext with Linq

↘锁芯ラ 提交于 2019-12-08 09:49:34

Have you tried myDbContext.Submit() or SubmitAll ? I cant remember excatly, but it should be something like that...

Most of the answer I needed was found here:

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;
  }
}

Then you can access the Schema's information like this:

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

This bypasses actually using the database user however. I'm still working on how to use the database user's context instead of just specifying the schema name.


Update:

I finally solved the last hurdle here. The key was the following code:

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 );
    }
  }

The EXECUTE AS command is issued on the connection before it is used to perform later linq to entity commands. As long as the connection remains open, the user's context remains in place. In my database the schema name and username for a tenant are the same.

Changing the user's execution context multiple times will cause errors, so a quick query is used to determine the current user context. A small entity class is needed to retrieve the information using the connection:

private class UserContext
{
  public string Name { get; set; }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!