4

我正在嘗試使用多租戶ASP.NET MVC應用程序遷移到Azure(包括SQL Azure)。每個客戶都獲得自己的數據庫,這是自包含的,包括他們所有的會員資格證書。多租戶SQLMembershipProvider ASP.NET MVC

我們可以在SqlMembershipProvider對象初始化時將連接字符串設置爲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); 

    } 
} 

回答

1

你需要得到在請求週期的早期在Application_PreRequestHandlerExecute

注意代碼http://forums.asp.net/p/997608/2209437.aspx

有幾種方式,一種是檢查出的樣品提供者的代碼,並使用(我不認爲這是你上面提到什麼?) SqlConnectionHelper(弗朗上面的鏈接)

 
internal static string GetConnectionString(string specifiedConnectionString, bool lookupConnectionString, bool appLevel) 
{ 
    //Your Conn String goes here!! 
    return Factory.ConnectionString; 
} 

其他涉及prerequestauthenticate。您可以將該字符串存儲在會話中(在初次登錄時設置它),並在提供程序中引用它,或者如果所有其他方法都失敗,則使用基於反射的代碼,儘管看起來並不乾淨。如果這些不起作用,那麼你需要推出自己的提供商。

+0

謝謝我將在上面發佈的鏈接中查看asp.net提供程序工具包SQL示例。 – Diesel337 2011-12-15 19:47:59

+0

確定工具包使用的是.NET框架的2.0版本,它看起來像訪問一些azure庫可能存在一些問題[Windows Azure是否與.NET 2.0框架兼容?](http://stackoverflow.com/questions-6442443/is-windows-azure-compatible-the-net-2-0-framework) – Diesel337 2011-12-15 20:04:56

5

從Adam發佈的鏈接中節省了一些時間。

如果您創建一個自定義的MembershipProvider,那麼你會得到BASETYPE代替

var connectionStringField = Membership.Provider.GetType().BaseType.GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic); 

我不知道這是否是您的Global.asax文件的Application_PreRequestHandlerExecute事件

protected void Application_PreRequestHandlerExecute() 
    { 
     SetProviderConnectionString(GetConnectionString()); 
    } 

    private void SetProviderConnectionString(string connectionString) 
    { 
     // Set private property of Membership, Role and Profile providers. Do not try this at home!! 
     var connectionStringField = Membership.Provider.GetType().GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic); 
     if (connectionStringField != null) 
      connectionStringField.SetValue(Membership.Provider, connectionString); 

     var roleField = Roles.Provider.GetType().GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic); 
     if (roleField != null) 
      roleField.SetValue(Roles.Provider, connectionString); 

     var profileField = ProfileManager.Provider.GetType().GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic); 
     if (profileField != null) 
      profileField.SetValue(ProfileManager.Provider, connectionString); 
    } 

    private string GetConnectionString() 
    { 
     return string.Format("Data Source={0};", @".\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|demo.mdf;User Instance=true"); 
    } 

最合適的解決方案,但它似乎完成了工作,以便爲membershipProvider啓用動態連接字符串,而無需自行滾動。雖然感覺有點hackish。