Многопользовательский SQLMembershipProvider ASP.NET MVC - PullRequest
4 голосов
/ 15 декабря 2011

Я пытаюсь перейти на Azure (включая SQL Azure) с мультитенантным приложением ASP.NET MVC.Каждый клиент получает свою собственную базу данных, которая включает в себя все свои учетные данные.

Мы можем установить строку подключения для SqlMembershipProvider при инициализации объекта SqlMembershipProvider.Однако последующие запросы к другим поддоменам (в одном сеансе) не изменяют строку подключения.Я нашел пример, в котором реализация переопределяет SqlMembershipProviders ConnectionString, но это невозможно в версии 4.0 библиотеки System.Web.

Мы могли бы создать единую базу данных о членстве и проходить аутентификацию на этом основании ... но мы хотели бы сохранить учетные данные клиентов в этой модели 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);

    }
}

Ответы [ 2 ]

5 голосов
/ 23 апреля 2012

Чтобы спасти людей от ссылки, размещенной Адамом.

В вашем файле 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");
    }

если вы создадите пользовательский членство в сети, то вы получитевместо BaseType

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

Я не уверен, является ли это наиболее подходящим решением, но похоже, что работа выполнена для того, чтобы динамическая connectionString была включена для membersProvider без прокрутки собственного.Чувствую себя немного хакером.

1 голос
/ 15 декабря 2011

Вы должны получить это в начале цикла запроса в 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;
}

другой включает предварительную проверку подлинности.Вы могли бы потенциально сохранить строку в сеансе (установить ее при первоначальном входе в систему) и ссылаться на нее в поставщике или использовать код, основанный на отражении, если все остальное не удается, хотя это не кажется чистым.Если это не сработает, вам нужно будет запустить своего собственного провайдера.

...