2
\$\begingroup\$

Unity Configuration

public static void RegisterTypes(IUnityContainer container)
    {
        container.RegisterType<ITrackoApiDbContext, TrackoApiDbContext>(new PerRequestLifetimeManager());
        container.RegisterType<IUnitOfWorkAsync, UnitOfWork>(new PerRequestLifetimeManager());

        //Call External Unity Configuration Library as Rest of the Unity Configuration is kept outside of WebApi Project
        new TrackoApi.Unity.Configure(container);
    }

Statup.cs for Owin

public void Configuration(IAppBuilder app)
{
   var config=new HttpConfiguration();
   var resolver = new UnityHierarchicalDependencyResolver(UnityConfig.GetConfiguredContainer());
   config.DependencyResolver = resolver;
   app.UseStageMarker(PipelineStage.MapHandler);
   WebApiConfig.Register(config);
 }

BatchServer.cs

public class BatchServer : HttpServer
{
    private readonly HttpConfiguration _config;

    public BatchServer(HttpConfiguration configuration)
        : base(configuration)
    {
        _config = configuration;
    }

    protected override void Initialize()
    {
        var firstInPipeline = _config.MessageHandlers.FirstOrDefault();
        if (firstInPipeline != null && firstInPipeline.InnerHandler != null)
        {
            InnerHandler = firstInPipeline;
        }
        else
        {
            base.Initialize();
        }
    }
}

ODataBatchHandlerSingleTransaction.cs

public class ODataBatchHandlerSingleTransaction : DefaultODataBatchHandler
{
    private readonly IUnityContainer _unity;
    private IUnitOfWorkAsync _uow;
    public ODataBatchHandlerSingleTransaction(HttpServer httpServer) : base(httpServer)
    {
        Server = httpServer;
        var uhdr = (UnityHierarchicalDependencyResolver)Server.Configuration.DependencyResolver;
        _unity= ((IUnityContainer)uhdr.GetService(typeof(IUnityContainer)));
    }

    public HttpServer Server { get; private set; }

    public override async Task<IList<ODataBatchResponseItem>> ExecuteRequestMessagesAsync(
        IEnumerable<ODataBatchRequestItem> requests, CancellationToken cancellationToken)
    {
        if (requests == null)
        {
            throw new ArgumentNullException(nameof(requests));
        }
        //return base.ExecuteRequestMessagesAsync(requests, cancellationToken);

        IList<ODataBatchResponseItem> responses = new List<ODataBatchResponseItem>();
        try
        {
            foreach (ODataBatchRequestItem request in requests)
            {
                var operation = request as OperationRequestItem;
                if (operation != null)
                {
                    responses.Add(await operation.SendRequestAsync(Invoker, cancellationToken));
                }
                else
                {
                    await ExecuteChangeSet((ChangeSetRequestItem)request, responses, cancellationToken);
                }
            }
        }
        catch
        {
            foreach (ODataBatchResponseItem response in responses)
            {
                response?.Dispose();
            }
            throw;
        }
        return responses;
    }

    private async Task ExecuteChangeSet(ChangeSetRequestItem changeSet, IList<ODataBatchResponseItem> responses,
        CancellationToken cancellationToken)
    {
        _uow = _unity.Resolve<IUnitOfWorkAsync>();
        using (_uow)
        {
            _uow.BeginTransaction(IsolationLevel.Serializable);
            foreach (HttpRequestMessage request in changeSet.Requests)
            {
                request.SetContext(_uow);
            }
            var changeSetResponse = (ChangeSetResponseItem)await changeSet.SendRequestAsync(Invoker, cancellationToken);
            responses.Add(changeSetResponse);
            if (changeSetResponse.Responses.All(x => x.IsSuccessStatusCode))
            {
                _uow.Commit();
            }
            else
            {
                _uow.Rollback();
            }
        }
    }
}

HttpRequestMessageExtensions.cs An extrension class to set and get Owin Context to and from HttpRequest

public static class HttpRequestMessageExtensions
{
    private const string DbContext = "Batch_DbContext";

    public static void SetContext(this HttpRequestMessage request, IUnitOfWorkAsync context)
    {
       try
       {
         request.Properties[DbContext] = context;
       }
       catch (Exception)
       {
         throw;
       }        
     }

     public static IUnitOfWorkAsync GetContext(this HttpRequestMessage request)
     {
       try
       {
        object trackoApiContext;
        if (request.Properties.TryGetValue(DbContext, out trackoApiContext))
        {
            return (IUnitOfWorkAsync)trackoApiContext;
        }
        var uhdr = (UnityHierarchicalDependencyResolver)request.GetConfiguration().DependencyResolver;
        var _uow = ((IUnityContainer)uhdr.GetService(typeof(IUnityContainer))).Resolve<IUnitOfWorkAsync>();
        SetContext(request, _uow);
        //request.RegisterForDispose(unity);
        return _uow;
       }
       catch (Exception)
       {            
        throw;
       }
     }
  }

WebApiConfig.cs Attach Batch Handler to GlobalConfiguration

ODataBatchHandler odataBatchHandler = new ODataBatchHandlerSingleTransaction(new BatchServer(config));
config.MapODataServiceRoute("ODataRoute", "odata", Configure.GetEdnModel(), defaultHandler: odataBatchHandler);

Consuming it Controller

await Request.GetContext().SaveChangesAsync();

UnitOfWork.cs

public class UnitOfWork : IUnitOfWorkAsync
{
    #region Private Fields

    private ITrackoApiDbContext _dataContext;
    private bool _disposed;
    private ObjectContext _objectContext;
    private DbTransaction _transaction;
    private Dictionary<string, dynamic> _repositories;

    #endregion Private Fields

    #region Constuctor/Dispose

    public UnitOfWork(ITrackoApiDbContext dataContext)
    {
        _dataContext = dataContext;
        //_objectContext=_dataContext as ObjectContext;
        _objectContext = ((IObjectContextAdapter)_dataContext).ObjectContext;
        var db = _dataContext as DbContext;

        _repositories = new Dictionary<string, dynamic>();
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    public virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
        {
            // free other managed objects that implement
            // IDisposable only

            try
            {
                if (_objectContext != null && _objectContext.Connection.State == ConnectionState.Open)
                {
                    _objectContext.Connection.Close();
                }
            }
            catch (ObjectDisposedException)
            {
                // do nothing, the objectContext has already been disposed
            }

            if (_dataContext != null)
            {
                _dataContext.Dispose();
                _dataContext = null;
            }
        }

        // release any unmanaged objects
        // set the object references to null

        _disposed = true;
    }

    #endregion Constuctor/Dispose

    public int SaveChanges()
    {
        return _dataContext.SaveChanges();
    }

    public IRepository<TEntity> Repository<TEntity>() where TEntity : class
    {

        return RepositoryAsync<TEntity>();
    }

    public Task<int> SaveChangesAsync()
    {
        return _dataContext.SaveChangesAsync();
    }

    public Task<int> SaveChangesAsync(CancellationToken cancellationToken)
    {
        return _dataContext.SaveChangesAsync(cancellationToken);
    }

    public IRepositoryAsync<TEntity> RepositoryAsync<TEntity>() where TEntity : class
    {
        //if (ServiceLocator.IsLocationProviderSet)
        //{
        //    return ServiceLocator.Current.GetInstance<IRepositoryAsync<TEntity>>();
        //}

        if (_repositories == null)
        {
            _repositories = new Dictionary<string, dynamic>();
        }

        var type = typeof(TEntity).Name;

        if (_repositories.ContainsKey(type))
        {
            return (IRepositoryAsync<TEntity>)_repositories[type];
        }

        var repositoryType = typeof(Repository<>);

        _repositories.Add(type, Activator.CreateInstance(repositoryType.MakeGenericType(typeof(TEntity)), _dataContext, this));

        return _repositories[type];
    }

    #region Unit of Work Transactions

    public void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified)
    {
        //_objectContext = ((IObjectContextAdapter) _dataContext).ObjectContext;
        if (_objectContext.Connection.State != ConnectionState.Open)
        {
            _objectContext.Connection.Open();
        }

        _transaction = _objectContext.Connection.BeginTransaction(isolationLevel);
    }

    public bool Commit()
    {
        _transaction.Commit();
        return true;
    }

    public void Rollback()
    {
        _transaction.Rollback();
        _dataContext.SyncObjectsStatePostCommit();
    }

    #endregion
}
\$\endgroup\$
  • \$\begingroup\$ I have used similar solution but for Autofac. Section _uow.BeginTransaction seems to work good. \$\endgroup\$ – Afshar Mohebbi May 28 '16 at 8:24
  • \$\begingroup\$ Yes, Also it's working well with Unity. As of now I didn't found any bug \$\endgroup\$ – Mukesh May 28 '16 at 8:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.