4

I want to get current NHibernate Session for using the Transaction attribute using PostSharp AOP.

Instead of below,

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;
        }
    }
}

I want to use like this,

[NHibernateTransaction]
public void Create<TEntity>(TEntity entity) where TEntity : class, IIdentity   
{       
    session.Save(entity);
}
Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
Ninad Naik
  • 43
  • 1
  • 6

3 Answers3

1

In the "Consuming Dependencies" section of the PostSharp documentation you can find descriptions and examples for different approaches of injecting dependencies into aspects.

One common approach is to let the dependency injection container initialize the dependencies of the existing aspect's instance. You can do this from the RuntimeInitialize method of the aspect. For example, with StructureMap container:

[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);
    }

    // ...
}

Another solution is to use the DI container as the global service locator inside your aspect - just ask the container to retrieve a required instance for you. As an optimization, you may also want to store the session in the MethodExecutionArgs.MethodExecutionTag property inside the OnEntry method and retrieve it inside the OnSuccess and OnException methods.

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;
    // ...
}

With the Professional Edition of PostSharp you can also import the properties and methods of the target class into the aspect. So, if your class has a SessionFactory property, you can import and use it inside your aspect. Note, that you also need to implement IInstanceScopedAspect in this case.

[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
  • 5,011
  • 2
  • 23
  • 34
0

Since PostSharp aspects are attribute based you can't easily inject dependencies (like for example NHibernate session) into them. You need to rely on ambient context (static globally accessible properties/methods). So the place for storing your current session will vary depending on type of application you are building (ASP, WPF etc.).

What I usually do is I use dependency injection container like Castle Windsor to provide me also AOP capabilities. Because it uses dynamic interception instead of IL Weaving and because aspect are also resolved from container you can easily inject dependencies into them. Then only thing you need to do then is to register your session object in the container with appropriate lifestyle (for example PerWebRequest in web applications).

I created a demo of AOP using Castle Windsor here.

Krzysztof Branicki
  • 7,417
  • 3
  • 38
  • 41
0

Thanks for your quick replies. AlexD your 2nd suggestion helped me but with few additions as below,

In NHibernateTransactionAttribute class I did this,

[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()
    {
    }
}

And where I had the "Session" property for NHibernate I did this,

[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;
  }
}

I am now able to get the current NHibernate session in Transaction aspect and I can use the Create like this,

[NHibernateTransaction]
public void Create<TEntity>(TEntity entity) where TEntity : class, IIdentity   
{       
    session.Save(entity);
}
Ninad Naik
  • 43
  • 1
  • 6