CompileTimeAOP.TransactionAwareness 3.9.1

A VisualStudio tool to allow programmers to develop Transaction Aware classes for NHibernate. TransactionAware AOP is implemented at compile time with CompileTimeWeaver.Fody.

Install-Package CompileTimeAOP.TransactionAwareness -Version 3.9.1
dotnet add package CompileTimeAOP.TransactionAwareness --version 3.9.1
paket add CompileTimeAOP.TransactionAwareness --version 3.9.1
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

NHibernate Transaction Awareness Framework

NHibernate Transaction Awareness Framework is implemented by CompileTimeWeaver.Fody (https://brooksidebeauty.blogspot.com/2019/02/compiletimeweaverfody-v317.html) to gain the best performance. It removes the burden of NHibernate developers of managing sessions, transactions, and domain model validations.

1. Transaction-aware Object

    [TransactionAware(DbName="CustomerDb")]
    public class MyClass: IDisposable
    {
        public async Task TravelAllPersonsAsync()
        {
            var session = TransactionContext.GetCurrentSession("CustomerDb");

            var persons = await session.Query<Person>().ToListAsync().ConfigureAwait(false);
            foreach (var person in persons)
            {
                Trace.WriteLine(person.Id + " - " + person.FirstName);
            }
        }

        public void Dispose()
        {
            //do nothing
        }
    }

It is a C# class decorated by TransactionAwareAttribute. In the TravelAllPersonsAsync() method above, developers can write code to read or write database with NHibernate session without think about when the transaction is commited or when the session is opened/closed, the framework manages the session lift-cycle and transaction lift-cycle automatically.

Use Transaction-aware class:

    using (var testObj = new MyClass())
    {
        await testObj.TravelAllPersonsAsync();
    }

2. Transaction-aware methods

A method is Transaction-aware method if:
(1) it is a non-static method, and
(2) it is decorated by TransactionAwareAttribute, or the class is decorated by TransactionAwareAttribute.

It can either be sync method or async method since version 3.9, and it is not thread-safe. It can call static TransactionContext.GetCurrentSession() method to get current context session.

    ISession session = TransactionContext..GetCurrentSession("CustomerDb");
    var user1 = session.Get<User>(1);
    user1.Name = "Simon Lu";
    session.Update(users);

3. Requirements of a Transaction-aware Object

A transaction-aware object is an instance of an ordinary .net class meeting a few simple requirements:

  • Decorated with TransactionAwareAttribute.
  • Implement IDisposable interface, the void Dispose() method must exist event though it is empty.
  • Call TransactionContext's methods in transaction-aware methods only.
  • Don't explicitly close session or commit/rollback transaction

4. Transaction

Because framework manages the life-time of transactions, developers don't need to start a transaction and commit/roolback it in the code. The framework makes the transaction control more easy with these two programming interfaces:

Using AutoComplete

The AutoCompleteAttribute indicates the Debit() method below to abort the transaction if any exception is thrown, and commits the transaction otherwise.

[TransactionAware(DbName="CustomerDb")]
public class Account
{
    [AutoComplete]
    public virtual void Debit(int amount)
    {
        // Do some database work. Any exception thrown here aborts the transaction;
        // otherwise, transaction commits.
    }
}
Voting in an Automatic Transaction

The following code fragment shows the SetAbort and SetComplete methods in use.

//Try to do something crucial to the transaction in progress.
if( !DoSomeWork() )
{
  //Something goes wrong.
  TransactionContext.SetAbort();
}
else
{
  //All goes well.
  TransactionContext.SetComplete();
}

SetComplete indicates that your method votes to commit its work; SetAbort indicates that your object encountered a problem and votes to abort the ongoing transaction, a single abort vote from any method participating in the transaction causes the entire transaction to fail.

5. Join existing session or open new session

  [TransactionAware(DbName="your_database_name", TransactionAwareOption = TransactionAwareOption.Required)]
  public class MyClass: IDisposable
  {
        ...
  }

When applying the TransactionAwareAttribute, you can set TransactionAwareOption property to control if the object joins existing session or open a new session.

  • TransactionAwareOption.Required (default)
    Indicates that the object requires a session. It runs in the scope of an existing session, if one exists. If no session exists, the object starts one.
  • TransactionAwareOption.RequiresNew
    Indicates that the object requires a new session.

6. Object Injection

A Transaction-aware object can inject objects as its properties or fields by declare the properties/fields with InjectedOnCreatedAttribute, and transparently dispose them when it is disposed. This open the door for developers to inject Transaction-aware objects into the other Transaction-aware object so that they can share the same database session. Below is an example class, and instances of EntityRepository is created and injected as private field _entityRepository, and it is disposed automatically even though the Dispose method body is empty.

    [TransactionAware(DbName="your_database_name")]
    public class TransactionAwareUnitOfWork : IDisposable
    {
        [InjectedOnCreated]
        private EntityRepository _entityRepository;
        ...
        public void Dispose()
        {
            //Transaction Awareness framework will automatically dispose all injected objects
        }
    }

7. Multiple Databases in Distributed-transaction

    [TransactionAware("TransactionDb"]
    public class OnlineShoppingStore: IDisposable
    {
        [TransactionAware("CustomerDb"]
        [AutoComplete]
        public void PlaceOrder(OrderDto order)
        {
            var customerDbSession = TransactionContext.GetCurrentSession("CustomerDb");
            var customer = customerDbSession.Query<CustomerEntity>(order.CustomerId);
            ...
            var transDbSession = TransactionContext.GetCurrentSession("TransactionDb");
            transDbSession.Save(new OrderEntity(){...});
        }
        ...
    }

In the PlaceOrder method above, framework puts two database update in one distributed transaction so that the integrity across multi-database is guaranteed.

8. Fluent Validation

The out-of-the-box FluentValidation feature guarantees the integrity of domain model whenever there is database update. It allows to put all model validation rules in different assembly to avoid domain model polution.

Usage:

  • Create a C# project to contain all the FluentValidation validators.
  • Add all validators to validate entity classes, for example, the PersonValidator class below to validate Person entity class:
    using FluentValidation;

    public class PersonValidator : AbstractValidator<Person>
    {
        public PersonValidator()
        {
            RuleFor(x => x.Id).NotEmpty();
            RuleFor(x => x.FirstName).NotNull().Length(1, 10);
        }
    }

Wiki

See wiki for detail usage: https://www.blogger.com/blogger.g?blogID=8870847280196751159#allposts

NHibernate Transaction Awareness Framework

NHibernate Transaction Awareness Framework is implemented by CompileTimeWeaver.Fody (https://brooksidebeauty.blogspot.com/2019/02/compiletimeweaverfody-v317.html) to gain the best performance. It removes the burden of NHibernate developers of managing sessions, transactions, and domain model validations.

1. Transaction-aware Object

    [TransactionAware(DbName="CustomerDb")]
    public class MyClass: IDisposable
    {
        public async Task TravelAllPersonsAsync()
        {
            var session = TransactionContext.GetCurrentSession("CustomerDb");

            var persons = await session.Query<Person>().ToListAsync().ConfigureAwait(false);
            foreach (var person in persons)
            {
                Trace.WriteLine(person.Id + " - " + person.FirstName);
            }
        }

        public void Dispose()
        {
            //do nothing
        }
    }

It is a C# class decorated by TransactionAwareAttribute. In the TravelAllPersonsAsync() method above, developers can write code to read or write database with NHibernate session without think about when the transaction is commited or when the session is opened/closed, the framework manages the session lift-cycle and transaction lift-cycle automatically.

Use Transaction-aware class:

    using (var testObj = new MyClass())
    {
        await testObj.TravelAllPersonsAsync();
    }

2. Transaction-aware methods

A method is Transaction-aware method if:
(1) it is a non-static method, and
(2) it is decorated by TransactionAwareAttribute, or the class is decorated by TransactionAwareAttribute.

It can either be sync method or async method since version 3.9, and it is not thread-safe. It can call static TransactionContext.GetCurrentSession() method to get current context session.

    ISession session = TransactionContext..GetCurrentSession("CustomerDb");
    var user1 = session.Get<User>(1);
    user1.Name = "Simon Lu";
    session.Update(users);

3. Requirements of a Transaction-aware Object

A transaction-aware object is an instance of an ordinary .net class meeting a few simple requirements:

  • Decorated with TransactionAwareAttribute.
  • Implement IDisposable interface, the void Dispose() method must exist event though it is empty.
  • Call TransactionContext's methods in transaction-aware methods only.
  • Don't explicitly close session or commit/rollback transaction

4. Transaction

Because framework manages the life-time of transactions, developers don't need to start a transaction and commit/roolback it in the code. The framework makes the transaction control more easy with these two programming interfaces:

Using AutoComplete

The AutoCompleteAttribute indicates the Debit() method below to abort the transaction if any exception is thrown, and commits the transaction otherwise.

[TransactionAware(DbName="CustomerDb")]
public class Account
{
    [AutoComplete]
    public virtual void Debit(int amount)
    {
        // Do some database work. Any exception thrown here aborts the transaction;
        // otherwise, transaction commits.
    }
}
Voting in an Automatic Transaction

The following code fragment shows the SetAbort and SetComplete methods in use.

//Try to do something crucial to the transaction in progress.
if( !DoSomeWork() )
{
  //Something goes wrong.
  TransactionContext.SetAbort();
}
else
{
  //All goes well.
  TransactionContext.SetComplete();
}

SetComplete indicates that your method votes to commit its work; SetAbort indicates that your object encountered a problem and votes to abort the ongoing transaction, a single abort vote from any method participating in the transaction causes the entire transaction to fail.

5. Join existing session or open new session

  [TransactionAware(DbName="your_database_name", TransactionAwareOption = TransactionAwareOption.Required)]
  public class MyClass: IDisposable
  {
        ...
  }

When applying the TransactionAwareAttribute, you can set TransactionAwareOption property to control if the object joins existing session or open a new session.

  • TransactionAwareOption.Required (default)
    Indicates that the object requires a session. It runs in the scope of an existing session, if one exists. If no session exists, the object starts one.
  • TransactionAwareOption.RequiresNew
    Indicates that the object requires a new session.

6. Object Injection

A Transaction-aware object can inject objects as its properties or fields by declare the properties/fields with InjectedOnCreatedAttribute, and transparently dispose them when it is disposed. This open the door for developers to inject Transaction-aware objects into the other Transaction-aware object so that they can share the same database session. Below is an example class, and instances of EntityRepository is created and injected as private field _entityRepository, and it is disposed automatically even though the Dispose method body is empty.

    [TransactionAware(DbName="your_database_name")]
    public class TransactionAwareUnitOfWork : IDisposable
    {
        [InjectedOnCreated]
        private EntityRepository _entityRepository;
        ...
        public void Dispose()
        {
            //Transaction Awareness framework will automatically dispose all injected objects
        }
    }

7. Multiple Databases in Distributed-transaction

    [TransactionAware("TransactionDb"]
    public class OnlineShoppingStore: IDisposable
    {
        [TransactionAware("CustomerDb"]
        [AutoComplete]
        public void PlaceOrder(OrderDto order)
        {
            var customerDbSession = TransactionContext.GetCurrentSession("CustomerDb");
            var customer = customerDbSession.Query<CustomerEntity>(order.CustomerId);
            ...
            var transDbSession = TransactionContext.GetCurrentSession("TransactionDb");
            transDbSession.Save(new OrderEntity(){...});
        }
        ...
    }

In the PlaceOrder method above, framework puts two database update in one distributed transaction so that the integrity across multi-database is guaranteed.

8. Fluent Validation

The out-of-the-box FluentValidation feature guarantees the integrity of domain model whenever there is database update. It allows to put all model validation rules in different assembly to avoid domain model polution.

Usage:

  • Create a C# project to contain all the FluentValidation validators.
  • Add all validators to validate entity classes, for example, the PersonValidator class below to validate Person entity class:
    using FluentValidation;

    public class PersonValidator : AbstractValidator<Person>
    {
        public PersonValidator()
        {
            RuleFor(x => x.Id).NotEmpty();
            RuleFor(x => x.FirstName).NotNull().Length(1, 10);
        }
    }

Wiki

See wiki for detail usage: https://www.blogger.com/blogger.g?blogID=8870847280196751159#allposts

Release Notes

- support .netstandard 2.0 and .net framework 4.6.1
- allow async methods

Version History

Version Downloads Last updated
3.9.1 58 2/6/2019
3.8.1 243 10/5/2017
3.8.0 218 7/2/2017
3.7.0 194 6/29/2017
3.6.1 197 6/2/2017
3.5.3 240 5/2/2017
3.5.1 231 4/30/2017
3.5.0 235 4/27/2017
3.4.1 253 4/10/2017
3.3.4 238 4/1/2017
3.1.4 232 3/11/2017
3.1.3 232 3/10/2017
3.1.2 239 3/9/2017
3.1.1 237 3/8/2017
3.1.0 230 3/8/2017
3.0.1 266 3/3/2017
3.0.0 243 3/2/2017