如何使用 PostSharp AOP 获取当前 NHibernate 会话以使用 Transaction 属性?

How to get current NHibernate session for using Transaction attribute using PostSharp AOP?

我想通过 PostSharp AOP 获取当前的 NHibernate 会话以使用事务属性。

而不是下面,

public void Create<TEntity>(TEntity entity) where TEntity : class, IIdentity
{
    var session = SessionFactory.CurrentSession;

    using (ITransaction transaction = session.BeginTransaction())
    {
        try
    { 
        session.Save(entity);
            transaction.Commit(); 
        } 
        catch {
            transaction.Rollback();
            throw;
        }
    }
}

我想这样用,

[NHibernateTransaction]
public void Create<TEntity>(TEntity entity) where TEntity : class, IIdentity   
{       
    session.Save(entity);
}

由于 PostSharp 方面是基于属性的,因此您不能轻易地将依赖项(例如 NHibernate 会话)注入其中。您需要依赖环境上下文(静态全局可访问 properties/methods)。因此,存储当前会话的位置将根据您正在构建的应用程序类型(ASP、WPF 等)而有所不同。

我通常做的是使用像 Castle Windsor 这样的依赖注入容器来为我提供 AOP 功能。因为它使用动态拦截而不是 IL Weaving,并且因为方面也是从容器解析的,所以您可以轻松地将依赖项注入它们。然后你唯一需要做的就是在容器中以适当的生活方式注册你的会话对象(例如网络应用程序中的 PerWebRequest)。

我使用 Castle Windsor 创建了一个 AOP 演示 here

在 PostSharp 文档的 "Consuming Dependencies" 部分,您可以找到有关将依赖项注入方面的不同方法的描述和示例。

一种常见的方法是让依赖项注入容器初始化现有方面实例的依赖项。您可以从方面的 RuntimeInitialize 方法执行此操作。例如,对于 StructureMap 容器:

[Serializable]
public class NHibernateTransactionAttribute : OnMethodBoundaryAspect
{
    public ISessionFactory SessionFactory { get; set; }

    public override void RuntimeInitialize(MethodBase method)
    {
        // Initialize the SessionFactory according to the container configuration.
        ObjectFactory.BuildUp(this);
    }

    // ...
}

另一种解决方案是使用 DI 容器作为方面内的全局服务定位器 - 只需要求容器为您检索所需的实例。作为一种优化,您可能还想将 session 存储在 OnEntry 方法内的 MethodExecutionArgs.MethodExecutionTag 属性 中,并在 OnSuccess 和 [=17 中检索它=] 方法。

public override void OnEntry(MethodExecutionArgs args)
{
    ISessionFactory sessionFactory = ObjectFactory.GetInstance<ISessionFactory>();
    var session = SessionFactory.CurrentSession;
    // Store the session in the method context.
    args.MethodExecutionTag = session;
    // ...
}

public override void OnSuccess(MethodExecutionArgs args)
{
    // Retrieve the session from the method context.
    var session = (ISession) args.MethodExecutionTag;
    // ...
}

使用 PostSharp 专业版,您还可以将目标 class 的属性和方法导入方面。所以,如果你的 class 有一个 SessionFactory 属性,你可以在你的方面导入和使用它。请注意,在这种情况下,您还需要实施 IInstanceScopedAspect

[Serializable]
public class NHibernateTransactionAttribute : OnMethodBoundaryAspect, IInstanceScopedAspect
{
    [ImportMember("SessionFactory", IsRequired = true)] 
    public Property<ISessionFactory> SessionFactoryProperty;

    public override void OnEntry(MethodExecutionArgs args)
    {
        var session = this.SessionFactoryProperty.Get().CurrentSession;
        // ...
    }

    object IInstanceScopedAspect.CreateInstance(AdviceArgs adviceArgs)
    {
        return this.MemberwiseClone();
    }

    void IInstanceScopedAspect.RuntimeInitializeInstance()
    {
    }

    // ...
}

感谢您的快速回复。 AlexD 你的第二个建议对我有帮助,但有一些补充如下,

在 NHibernateTransactionAttribute class 我这样做了,

[Serializable]
public sealed class NHibernateTransactionAttribute : OnMethodBoundaryAspect, IInstanceScopedAspect
{
    [ImportMember("Session", IsRequired = true)] 
    public Property<ISession> SessionProperty;

    public override void OnEntry(MethodExecutionArgs args)
    {
        var session = this.SessionProperty.Get();
        session.Transaction.Begin();
    }

    public override void OnSuccess(MethodExecutionArgs args)
    {
        var session = this.SessionProperty.Get();
        session.Transaction.Commit();
    }

    public override void OnException(MethodExecutionArgs args)
    {
        var session = this.SessionProperty.Get();
        session.Transaction.Rollback();
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        var session = this.SessionProperty.Get();
        session.Close();
    }

    public object CreateInstance(AdviceArgs adviceArgs)
    {
        return this.MemberwiseClone();
    }

    public void RuntimeInitializeInstance()
    {
    }
}

在我为 NHibernate 提供 "Session" 属性 的地方,我这样做了,

[IntroduceMember(Visibility = Visibility.Family, OverrideAction = MemberOverrideAction.Ignore)]
[CopyCustomAttributes(typeof (ImportAttribute))]
[Import(typeof(ISession))]
public ISession Session
{
  get
  {
    if(session == null || !session.IsOpen)
    {
       session = sessionFactory.OpenSession();
    }
   return session;
  }
}

我现在可以在事务方面获取当前的 NHibernate 会话,我可以像这样使用创建,

[NHibernateTransaction]
public void Create<TEntity>(TEntity entity) where TEntity : class, IIdentity   
{       
    session.Save(entity);
}