由于还没有人回复我的帖子,所以经过几天的研究,我正在回复它。我能够与具有 1 个接口和 3 个类的 S#harp 集成(我希望它们是一个开箱即用的解决方案?)。
下面提供的代码可以复制并粘贴到任何应用程序中,它应该可以正常工作。唯一的例外是 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;
}
}
现在我可以用新创建的 [FederationTransaction] 替换 S#arp 的 [Transaction] 属性,如下所示:
[HttpGet]
[FederationTransaction]
public ActionResult Index()
{
var viewModel = NHibernateSession.Current.QueryOver<SomeDemoModel>().List()
return View(viewModel);
}
控制器中的任何代码都不需要知道它使用 Sql Azure 联合。这一切都应该正常工作。
有什么想法吗?有人找到更好的解决方案吗?请分享。