2

私は S#harp アーキテクチャを使用していますが、それを使用して SQL Azure フェデレーションにアクセスする方法を見つけた人はいますか?

SQL Azure ではトランザクション内で "use Federation" ステートメントを使用できないため、次のコマンドはトランザクションの外部で実行する必要があることを認識しています。

use Federation CustomerFederation (CustomerID=2) with reset, filtering=on 
GO
<some sql statement...>
GO

カスタム NHibernate セッション クラスを作成する例を示す別の投稿がここにありますが、S#arp アーキテクチャを使用してこれをどのように達成/拡張できますか?

NHibernate.Shards やマルチテナント S#arp アーキテクチャ拡張など、SQL Azure フェデレーションには他のシャーディング オプションがあることも認識していますが、他のオプションを提供するのではなく、質問に答えてください。

S#arp アーキテクチャと SQL Azure フェデレーションを使用しているのは私だけではありません。Google はあまり提供していません。他の誰かが解決策を見つけた場合は、共有してください。

4

1 に答える 1

2

まだ誰も私の投稿に返信していないので、数日間の調査を経て返信しています。S#harp と 1 つのインターフェイスと 3 つのクラスを統合することができました (すぐに使えるソリューションになることを望んでいましたか?)。

以下に示すコードは、コピーして任意のアプリケーションに貼り付けることができ、そのまま機能するはずです。唯一の例外は、FederationSessionHelper クラスです。これは、情報が変更される可能性があるため、各アプリケーションに固有です。web.config 内に、フェデレーション名などを含むアプリ設定セクションがあります。また、ユーザーが認証されると、ユーザーが送信元のルート URL を解析し、フェデレーション ルートにクエリを実行して、それらがどのテナントであるかを調べます (私は私が作成したカスタムテナントテーブル)。次に、セッション内のテナント ID をキー "FederationKeyValue_Key" の下に配置します。これは、FederationSession クラスで使用され、Use Federation ステートメントを構築します。

/// <summary>
/// Interface used to retrieve app specific info about your federation.
/// </summary>
public interface IFederationSessionHelper
{
    string ConnectionString { get; }
    string FederationName { get; }
    string DistributionName { get; }
    string FederationKeyValue { get; }
}

/// <summary>
/// This is were you would get things specific for your application. I have 3 items in the web.config file and 1 stored in session. You could easily change this to get them all from the repository or wherever meets the needs of your application.
/// </summary>
public class FederationSessionHelper : IFederationSessionHelper
{
    private const string ConnectionStringKey = "ConnectionString_Key";
    private const string FederationNameKey = "FederationName_Key";
    private const string DistributionNameKey = "DistributionName_Key";
    private const string FederationKeyValueKey = "FederationKeyValue_Key";

    public string ConnectionString { get { return ConfigurationManager.ConnectionStrings[ConnectionStringKey].ConnectionString; } }
    public string FederationName { get { return ConfigurationManager.AppSettings[FederationNameKey]; } }
    public string DistributionName { get { return ConfigurationManager.AppSettings[DistributionNameKey]; } }

    //When user authenitcates, retrieve key value and store in session. This will allow to retrieve here.
    public string FederationKeyValue { get { return Session[FederationKeyValueKey]; } }       
}

/// <summary>
/// This is were the magic begins and where the integration with S#arp occurs. It manually creates a Sql Connections and adds it the S#arps storage.  It then runs the Use Federation command and leaves the connection open. So now when you use an NhibernateSession.Current it will work with Sql Azure Federation.
/// </summary>
public class FederationSession : IDisposable
{
    private SqlConnection _sqlConnection;

    public void Init(string factoryKey,
                       string federationName,
                       string distributionName,
                       string federationKeyValue,
                       bool doesFilter,
                       string connectionString)
    {
        var sql = string.Format("USE FEDERATION {0}({1} = '{2}') WITH RESET, FILTERING = {3};", federationName, distributionName, federationKeyValue, (doesFilter) ? "ON" : "OFF");
        _sqlConnection = new SqlConnection(connectionString);

        _sqlConnection.Open();
        var session = NHibernateSession.GetSessionFactoryFor(factoryKey).OpenSession(_sqlConnection);
        NHibernateSession.Storage.SetSessionForKey(factoryKey, session);

        var query = NHibernateSession.Current.CreateSQLQuery(sql);
        query.UniqueResult();
    }

    public void Dispose()
    {
        if (_sqlConnection != null && _sqlConnection.State != ConnectionState.Closed)
            _sqlConnection.Close();
    }
}

/// <summary>
/// This was just icing on the cake.  It inherits from S#arps TransactionAttribute and calls the FederationSession helper to open a connection.  That way all you need to do in decorate your controller with the newly created [FederationTransaction] attribute and thats it.
/// </summary>
public class FederationTransactionAttribute : TransactionAttribute
{
    private readonly string _factoryKey = string.Empty;
    private bool _doesFilter = true;

    /// <summary>
    ///     When used, assumes the <see cref = "factoryKey" /> to be NHibernateSession.DefaultFactoryKey
    /// </summary>
    public FederationTransactionAttribute()
    { }

    /// <summary>
    ///     Overrides the default <see cref = "factoryKey" /> with a specific factory key
    /// </summary>
    public FederationTransactionAttribute(string factoryKey = "", bool doesFilter = true)
        : base(factoryKey)
    {
        _factoryKey = factoryKey;
        _doesFilter = doesFilter;
    }        

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var federationSessionHelper = ServiceLocator.Current.GetInstance<IFederationSessionHelper>();
        var factoryKey = GetEffectiveFactoryKey();
        new FederationSession().Init(factoryKey,
                                        federationSessionHelper.FederationName,
                                        federationSessionHelper.DistributionName,
                                        federationSessionHelper.FederationKeyValue,
                                        _doesFilter,
                                        federationSessionHelper.ConnectionString);

        NHibernateSession.CurrentFor(factoryKey).BeginTransaction();
    }

    private string GetEffectiveFactoryKey()
    {
        return String.IsNullOrEmpty(_factoryKey) ? SessionFactoryKeyHelper.GetKey() : _factoryKey;
    }        
}

これで、次のように S#arp の [Transaction] 属性を新しく作成した [FederationTransaction] に置き換えることができます。

[HttpGet] 
[FederationTransaction]
public ActionResult Index()
{
   var viewModel = NHibernateSession.Current.QueryOver<SomeDemoModel>().List()
   return View(viewModel);            
}

コントローラー内のどのコードも、Sql Azure フェデレーションを使用していることを知る必要はありません。それはすべてうまくいくはずです。

何かご意見は?誰もがより良い解決策を見つけましたか? 共有してください。

于 2012-07-05T20:04:21.710 に答える