まだ誰も私の投稿に返信していないので、数日間の調査を経て返信しています。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 フェデレーションを使用していることを知る必要はありません。それはすべてうまくいくはずです。
何かご意見は?誰もがより良い解決策を見つけましたか? 共有してください。