Skip to content

Instantly share code, notes, and snippets.

@bbqchickenrobot
Created June 21, 2018 23:19
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bbqchickenrobot/404df8ea7eca3b9c78895ffced08e41a to your computer and use it in GitHub Desktop.
Save bbqchickenrobot/404df8ea7eca3b9c78895ffced08e41a to your computer and use it in GitHub Desktop.
Azure Elastic Scale Client w/ Entity Framework Core (EFCore) 2.1
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