Created
June 21, 2018 23:19
-
-
Save bbqchickenrobot/404df8ea7eca3b9c78895ffced08e41a to your computer and use it in GitHub Desktop.
Azure Elastic Scale Client w/ Entity Framework Core (EFCore) 2.1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Configuration; | |
using System.Data.SqlClient; | |
using System.Linq; | |
using Microsoft.Azure.SqlDatabase.ElasticScale.ShardManagement; | |
using Microsoft.EntityFrameworkCore; | |
namespace eftest | |
{ | |
// todo - convert public accessors to appropriate access level | |
public static class MultiTenantConfigurator | |
{ | |
public static MyContext GetContext(int shardKey) => | |
new MyContext( | |
GetDbContextOptions<MyContext>(shardKey, "shardMapManager", "My_ShardMap", "MyDbConnStringName")); | |
public static DbContextOptions<TContext> GetDbContextOptions<TContext>(int shardKey, | |
string shardMapConnstringName, string shardMapName, string dbConnString, int commandTimeout = 600) | |
where TContext : DbContext => | |
GetDbContextOptions<int, TContext>(shardKey, shardMapConnstringName, shardMapName, dbConnString, commandTimeout); | |
public static DbContextOptions<TContext> GetDbContextOptions<Tkey, TContext>(Tkey shardKey, | |
string shardMapConnstringName, string shardMapName, string dbConnString, int commandTimeout = 600) | |
where TContext : DbContext | |
{ | |
var builder = new DbContextOptionsBuilder<TContext>(); | |
var conn = OpenDDRConnection(GetShardMapManager(GetConnectionString(shardMapConnstringName), shardMapName), | |
shardKey, GetConnectionString(dbConnString)); | |
var options = builder.UseSqlServer(conn, b => b.CommandTimeout(commandTimeout)); | |
return options.Options; | |
} | |
public static string GetConnectionString(string name) => ConfigurationManager.ConnectionStrings[name].ConnectionString; | |
public static ShardMap GetShardMapManager(string connString, string shardMapName) | |
{ | |
var smm = ShardMapManagerFactory.GetSqlShardMapManager(connString, ShardMapManagerLoadPolicy.Lazy); | |
var sm = smm.GetListShardMap<int>(shardMapName); | |
return sm; | |
} | |
/// <summary> | |
/// Wrapper function for ShardMap.OpenConnectionForKey() that automatically sets CONTEXT_INFO to the correct | |
/// tenantId before returning a connection. As a best practice, you should only open connections using this | |
/// method to ensure that CONTEXT_INFO is always set before executing a query. | |
/// </summary> | |
public static SqlConnection OpenDDRConnection<T>(ShardMap shardMap, T shardingKey, string connString) | |
{ | |
// No initialization | |
//Database.SetInitializer<MyContext>(null); | |
// Ask shard map to broker a validated connection for the given key | |
SqlConnection conn = null; | |
try | |
{ | |
connString = MassageConnString(connString); | |
conn = shardMap.OpenConnectionForKey(shardingKey, connString, ConnectionOptions.Validate); | |
// Set TenantId in SESSION_CONTEXT to shardingKey to enable Row-Level Security filtering | |
var cmd = conn.CreateCommand(); | |
cmd.CommandText = @"exec sp_set_session_context @key=N'TenantId', @value=@shardingKey"; | |
cmd.Parameters.AddWithValue("@shardingKey", shardingKey); | |
cmd.ExecuteNonQuery(); | |
return conn; | |
} | |
catch (Exception) | |
{ | |
conn?.Dispose(); | |
throw; | |
} | |
string MassageConnString(string connstring) // local function | |
{ | |
var list = connstring.Split(';').ToList(); | |
list.Remove(list.First(x => x.StartsWith("Data Source"))); | |
list.Remove(list.First(x => x.StartsWith("Initial Catalog"))); | |
return string.Join(";", list); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment