Showing posts with label transactions. Show all posts
Showing posts with label transactions. Show all posts

Mar 28, 2012

ASP.NET MVC extension points in action

Recently I made a tech talk about my alt.net web stack of love. It has lots of things there. Validation, NHibernate, Routing etc. So here are slides:

Source code for it is on bitbucket. Its purpose is just see it all in action. If you want, you should move code your new/old projects. Don’t try to make some type of project template out of it.

To try things out

Once again link to sources.

I was asked to give some kind of practice task to try all stack together. So here it is:

Implement blog details page to show posts inside it. So when I open localhost/blogs/3 I should see something like:

untitled_page

When I navigate to post details page I should be able to see post content and leave comments. Just like on this blog Smile.

Mar 5, 2012

Using ASP.NET MVC 4 WebAPI with NHibernate and Autofac

Wanted to try how they play together. So it will yet another tutorial with sample application built from scratch. First of all I don’t want to manage ISession and ISessionFactory lifetime manually, so I’ll use Autofac to do the job. So after creating new Web API project execute following commands in nuget console:

Uninstall-Package EntityFramework
Install-Package NHibernate
Install-Package Autofac.Mvc3

First remove EntityFramework, then install NHibernate and Autofac.Mvc3 package. The last package has some really useful extensions like implementation of dependency resolver for MVC and instance per web request life style. Now setup autofac:

var builder = new ContainerBuilder();
// Register ISessionFactory as Singleton 
builder.Register(x => NHibernateConfigurator.BuildSessionFactory())
    .SingleInstance();
// Register ISession as instance per web request
builder.Register(x => x.Resolve<ISessionFactory>().OpenSession())
    .InstancePerHttpRequest();

// Register all controllers
builder.RegisterAssemblyTypes(typeof(ProductsController).Assembly)
    .InNamespaceOf<ProductsController>()
    .AsSelf();

// override default dependency resolver to use Autofac
DependencyResolver.SetResolver(new AutofacDependencyResolver(builder.Build()));

// this override is needed because WebAPI is not using DependencyResolver to build controllers 
GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
    DependencyResolver.Current.GetService, 
    DependencyResolver.Current.GetServices);

This code is executed one time on Application start. I won’t put code for domain (its simple one entity) and NHibernateConfigurator class you can find them on github.

Now we are ready to add our first controller that is going to expose web api:

public class ProductsController : ApiController
{
    private readonly ISession nhSession;

    public ProductsController(ISession nhSession)
    {
        if (nhSession == null) throw new ArgumentNullException("nhSession");
        this.nhSession = nhSession;
    }

    public IQueryable<Product> Get()
    {
        return nhSession.Query<Product>();
    }
}

Notice that I don’t need to do anything to get the ISession instance. Autofac will find that in order to get it it needs ISessionFactory and will configure factory first to give ISession for controller. Now we can visit url http://localhost:54270/api/products and see our list of products in XML format. Notice that because returned type is IQueryable request to http://localhost:54270/api/products?$top=1&$skip=0 will return only first product from the list.

PUT, POST and DELETE methods are pretty straight forward and won’t be different from the same in entity framework. So I won’t cover it. The last thing I want to try is transaction management. In mvc projects I used to do it with action filter. Here is slight catch involved. There are two ActionFilterAttribute classes. One in System.Web.Http.Filters and other is in System.Web.Mvc. In order to get working in webapi we need to implement the one in System.Web.Http.Filters namespace. So the implementation is the following:

using System.Data;
using System.Web.Mvc;
using NHibernate;
using ActionFilterAttribute = System.Web.Http.Filters.ActionFilterAttribute;

namespace webapi.Infrastructure
{
    public class TransactionAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            base.OnActionExecuting(actionContext);
            DependencyResolver.Current.GetService<ISession>().BeginTransaction(IsolationLevel.ReadCommitted);
        }

        public override void OnActionExecuted(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext)
        {
            base.OnActionExecuted(actionExecutedContext);
            ITransaction currentTransaction = DependencyResolver.Current.GetService<ISession>().Transaction;

            try
            {
                if (currentTransaction.IsActive)
                    if (actionExecutedContext.Exception != null)
                        currentTransaction.Rollback();
                    else
                        currentTransaction.Commit();
            }
            finally
            {
                currentTransaction.Dispose();
            }
        }
    }
}

I’ve put all the code there just be sure that you can figure out all required namespaces. The last thing I want to notice here is that if you implement System.Web.Mvc version of action filter, you won’t see any error messages or exceptions. Your filter just won’t work.

All code you can find here.

Apr 9, 2011

Lightweight NHibernate and ASP.NET MVC integration with Autofac

Many times when new project stars and we want to use NHibernate relatively a lot of work need to be done. Among them are:

  • Mapping of entities (I prefer automapping)
  • ISessionFactory singleton
  • ISession lifetime management (Per web request)
  • Transaction management

Sharp architecture project has all of them done and ready to use. But as for me this project has become too big and hard to understand. I wanted to have full control on what is happening in my app and didn’t want to have such a lot of abstractions. For example Repository and Entity objects that have inheritance chain about to 5 or 6 objects.

So I decided to show how very simple integration can be made with a minimum amount of code. To get all mentioned libraries I will use NuGet. We will need:

  • NHibernate
  • Fluent NHibernate
  • Autofac
  • Autofac.Mvc3

Lets start with mapping / session factory configuration:

public Configuration Configure()
{
    var configuration = new Configuration();
    configuration.SessionFactory()
                 .Proxy.Through<ProxyFactoryFactory>()
                 .Integrate.Using<MsSql2005Dialect>()
                 .Connected.ByAppConfing("dbConnection");


    FluentConfiguration fluentConfiguration = Fluently.Configure(configuration);
    fluentConfiguration.Mappings(map => map.AutoMappings.Add(
                                            new ModelGenerator().Generate()));

    return fluentConfiguration.BuildConfiguration();
}

public ISessionFactory GetSessionFactory()
{
    var configuration = Configure(); 
    return configuration.BuildSessionFactory();
}

Few things to note here. Combination of Loquacious and Fluent configuration is used because first one is supporting all NHibernate features, second one handles mappings integration. Also Model generator class is used:

private class ModelGenerator
{
    public AutoPersistenceModel Generate()
    {
        AutoPersistenceModel automap = new AutoPersistenceModel();

        automap.Conventions.AddFromAssemblyOf<ModelGenerator>();
        automap.UseOverridesFromAssemblyOf<ModelGenerator>();
        automap.AddEntityAssembly(Assembly.GetAssembly(typeof (Entity)))
            .Where(objectType => objectType.IsSubclassOf(typeof(Entity)));

        return automap;
    }
}

Here we setup location for the conventions, overriding's and entities. All classes that are inherited from Entity will be mapped. For conventions I’m using sharp architecture’s with small tweaks to have nice constraints names when generating schema from mappings:

public class ReferenceConvention : IReferenceConvention
{
    public void Apply(FluentNHibernate.Conventions.Instances.IManyToOneInstance instance)
    {
        string fkName = string.Format("{0}_{1}_FK", 
                                      instance.Name, instance.EntityType.Name);
        instance.ForeignKey(fkName);

        instance.Column(instance.Property.Name + "Fk");
    }
}

public class HasManyToManyConvention : IHasManyToManyConvention
{
    public void Apply(IManyToManyCollectionInstance instance)
    {
        string fkName = string.Format("{0}_{1}_FK", 
                                      instance.Member.Name, instance.EntityType.Name);
        instance.Key.ForeignKey(fkName);

        instance.Cascade.SaveUpdate();
    }
}

Now to the web part. In global asax on Application_Start event we need to setup Autofac and change the default controllers factory. To do this:

var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetAssembly(typeof (AuthorizationController)));
builder.Register(x => new NHibernateConfigurator().GetSessionFactory())
    .SingleInstance();
builder.Register(x => x.Resolve<ISessionFactory>().OpenSession())
    .InstancePerHttpRequest();

builder.RegisterModule(new AutofacWebTypesModule());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

Code here is pretty clear. We setup ISessionFactory to be singleton, ISession instance is resolved by container and has PerHttpRequest lifestyle. Notice call of builder.RegisterModule that is going to add all the required http modules to support per web request lifestyle and change default controller factory to the one that uses Autofac. So now we are able to write code like this:

public class AuthorizationController : Controller
{
      private ISession session;

      public AuthorizationController(ISession session)
      {
          this.session = session;
      }

      public ActionResult Index()
      {
          var users = this.session.QueryOver<User>().List();
          return View(users);
      }
}

So we have controller that depends on ISession, which depends on ISessionFactory, which depends on our Nhibernate configurator class. Isn’t it kind from Autofac to handle all this? Smile

One last but important thing we need to do. Each call to the data base should be wrapped to correct transaction. You can read here why. The easiest way to handle this is to create action filter:

public class TransactionAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        DependencyResolver.Current.GetService<ISession>().BeginTransaction(IsolationLevel.ReadCommitted);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        ITransaction currentTransaction = DependencyResolver.Current.GetService<ISession>().Transaction;

        if (currentTransaction.IsActive)
        {
            if (filterContext.Exception != null && filterContext.ExceptionHandled)
            {
                currentTransaction.Rollback();
            }
        }
    }

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        ITransaction currentTransaction = DependencyResolver.Current.GetService<ISession>().Transaction;

        base.OnResultExecuted(filterContext);
        try
        {
            if (currentTransaction.IsActive)
            {
                if (filterContext.Exception != null && !filterContext.ExceptionHandled)
                {
                    currentTransaction.Rollback();
                }
                else
                {
                    currentTransaction.Commit();
                }
            }
        }
        finally
        {
            currentTransaction.Dispose();
        }
    }
}

Original implementation is taken from Sharp architecture and small changes made due to Autofac. So now out Index method should be marked with transaction attribute:

[Transaction]
public ActionResult Index()
{
    return View();
}

That's probably all we need to have NHibernate. As always source code is attached:

Apr 20, 2010

WCF NHibernate управление сессией и транзакциями

В WCF службах, как и в любых других приложениях где есть БД и ORM становится вопрос об управлении жизненным циклом объектов и транзакциями. Для собственных служб мне хотелось реализовать схему как в веб приложениях, т.е. :

  • ISession живет один запрос.
  • Транзация открывается в начале запроса, и закрывается в конце.
  • В случае наличия ошибки транзакция откатывается, в обратном подтверждается.

Реализовать это можно при помощи IDispatchMessageInspector. Данный интерфейс позволяет обработать события начала и окончания запроса. Итак реализация:

public class TransactionManager : IDispatchMessageInspector
{
    public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
    {
        SessionStorage.OpenSession();
        SessionStorage.CurrentSession.BeginTransaction(IsolationLevel.ReadCommitted);
        return null;
    }
    
    public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
    {
        var session = SessionStorage.CurrentSession;

        if (session.Transaction.IsActive)
        {
            if (reply.IsFault)
            {
                session.Transaction.Rollback();
            }
            else
            {
                session.Transaction.Commit();
            }
        }
    }
}

Чтобы зарегистрировать TransactionManager нам потребуется еще два вспомогательных класса. 1й добавит TransactionManager к службе:

public class TransactionBehaviour : IEndpointBehavior
{
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        throw new Exception("Behavior not supported on the consumer side!");
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        TransactionManager inspector = new TransactionManager();
        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    }
}

Второй будет использоваться для регистрации секции в конфигурационном файле:

public class TransactionBehaviorExtensionElement : BehaviorExtensionElement
{
    protected override object CreateBehavior()
    {
        return new TransactionBehaviour();
    }
    
    public override Type BehaviorType
    {
        get
        {
            return typeof(TransactionBehaviour);
        }
    }
}

И последний штрих – настройка самого сервиса где все это собирается воедино:

<system.serviceModel>
  <services>
    <service name="WcfTransactions.TestService">
      <endpoint behaviorConfiguration="transactionsEnabledBehaviour" address="" binding="basicHttpBinding" contract="WcfTransactions.ITestService"/>
      <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
    </service>
  </services>

  <behaviors>
    <serviceBehaviors>
      <behavior>
        <serviceMetadata httpGetEnabled="true"/>
        <serviceDebug includeExceptionDetailInFaults="true"/>
      </behavior>
    </serviceBehaviors>

    <endpointBehaviors>
      <behavior name="transactionsEnabledBehaviour">
        <transactionBehaviour />
      </behavior>
    </endpointBehaviors>
  </behaviors>
  <extensions>
    <behaviorExtensions>
      <add
        name="transactionBehaviour"
        type="WcfTransactions.Transactions.TransactionBehaviorExtensionElement, WcfTransactions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </behaviorExtensions>
  </extensions>
</system.serviceModel>

Тут есть маленькая особенность, когда мы регистрируем behaviorExtension обязательно указывать полный путь к типу, с указанием версии, культуры и токена сборки.

Как по мне это слишком сложно и слишком много всего приходится делать. В Asp.net mvc есть ActionFilters которые позволяют сделать все тоже самое, но с помощью всего одного атрибута на контроллере. Подобных вещей для WCF я не нашел, буду рад если кто-то подскажет.

Разобраться в этом помогут только исходники :).

Oct 21, 2009

NHibernate transactions. Почему рекомендуют все запросы выполнять внутри транзакций

Если выполнить для доменной модели (описана тут) следующий код без транзакции:

Category category = new Category
     {
          DisplayName = "Our first category"

     };
Product product = new Product
     {
         Description = "First product description",
         Name = "First product",
         Price = 1
     };
Order order = new Order(product)
     {
         Customer = "customer"

     };
Global.CurrentSession.SaveOrUpdate(category);
Global.CurrentSession.SaveOrUpdate(product);

То SqlProfiler покажет следующее:

image

Как вы видите операции логин/логаут выполняются после каждого запроса. Если же использовать код в global.asax, как описано ранее, то получим следующий результат:

image

И это только на 3х запросах, в реальных же приложениях это может существенно повлиять на производительность.