我正在尝试将一个多租户ASP.NET MVC应用程序(包括SQL Azure)迁移到Azure。每个客户都有自己的数据库,其中包含所有成员凭据,是一个独立的数据库。
我们可以在SqlMembershipProvider对象初始化时设置连接字符串,但同一会话中对不同子域的后续请求不会更改连接字符串。我找到了一个示例,该实现重写了SqlMembershipProviders ConnectionString,但这在System.Web dll的4.0版本中不可能。
我们可以实现单个成员资格数据库并针对其进行身份验证......但我们希望在此SAAS模型中保持客户凭据的隔离。
因此问题是如何为每个请求动态更改SQLMembershipProviders连接字符串?
Web.config
TenantMembershipProvider.cs 处理初始化。
我们可以在SqlMembershipProvider对象初始化时设置连接字符串,但同一会话中对不同子域的后续请求不会更改连接字符串。我找到了一个示例,该实现重写了SqlMembershipProviders ConnectionString,但这在System.Web dll的4.0版本中不可能。
我们可以实现单个成员资格数据库并针对其进行身份验证......但我们希望在此SAAS模型中保持客户凭据的隔离。
因此问题是如何为每个请求动态更改SQLMembershipProviders连接字符串?
Web.config
<membership defaultProvider="TenantMembershipProvider">
<providers>
<clear/>
<add name="TenantMembershipProvider" type="ABC.Infrastructure.MultiTenancy.TenantMembershipProvider, ABC"
connectionStringName="ApplicationServices" enablePasswordRetrieval="true" enablePasswordReset="true" requiresQuestionAndAnswer="false"
requiresUniqueEmail="false" passwordFormat="Clear" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6"
minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression="" applicationName="/"/>
</providers>
</membership>
TenantMembershipProvider.cs 处理初始化。
public class TenantMembershipProvider : SqlMembershipProvider
{
private SiteLinqSession _session;
private MasterSession _masterSession;
private static readonly Dictionary<string, Customer> _customers = new Dictionary<string, Customer>();
private static string _host;
public override void Initialize(string name, NameValueCollection config)
{
base.Initialize(name, config);
string connectionString = GetConnectionString();
FieldInfo connectionStringField = GetType().BaseType.GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);
connectionStringField.SetValue(this, connectionString);
}
private string GetConnectionString()
{
var headers = HttpContext.Current.Request.Headers["Host"];
string[] host = headers.Split('.');
_host = host[0];
if (_host == "127") _host = "demo";
var customer = GetSite(_host);
return BuildTenantConnectionString(customer.ConnectionSetting);
}
private Customer GetSite(string host)
{
Customer customer;
//check dictionary if customer exists for the subdomain
_customers.TryGetValue(host, out customer);
if (customer != null)
return customer;
//if not get the customer record and add it to the dictionary
_masterSession = new MasterSession();
var customers = _masterSession.All<Customer>();
customer = customers.SingleOrDefault(x => x.SubDomain == _host);
if (customer != null)
_customers.Add(host, customer);
return customer;
}
private string BuildTenantConnectionString(ConnectionSetting setting)
{
return string.Format("Data Source={0};Initial Catalog={1};User Id={2};Password={3};", setting.DataSource, setting.Catalog, setting.Username, setting.Password);
}
}