加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

asp.net-mvc-3 – 多租户SQLMembershipProvider ASP.NET MVC

发布时间:2020-12-16 06:46:40 所属栏目:asp.Net 来源:网络整理
导读:我正在尝试使用多租户ASP.NET MVC应用程序迁移到Azure(包括SQL Azure).每个客户都拥有自己的数据库,这些数据库是独立的,包括所有会员资格. 我们可以在初始化SqlMembershipProvider对象时将连接字符串设置为SqlMembershipProvider.但是,对不同子域(在同一会话
我正在尝试使用多租户ASP.NET MVC应用程序迁移到Azure(包括SQL Azure).每个客户都拥有自己的数据库,这些数据库是独立的,包括所有会员资格.

我们可以在初始化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);

    }
}

解决方法

从Adam发布的链接中节省一些时间.

在Application_PreRequestHandlerExecute事件的Global.asax文件中

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,那么您将获得BaseType

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

我不确定这是否是最合适的解决方案,但似乎已完成工作以为membershipProvider启用动态connectionString而无需自行滚动.虽然觉得有点hackish.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读