AsyncDbEntitySetController

Nov 30, 2013 at 9:42 AM
Edited Nov 30, 2013 at 10:27 AM
Hello Long Le,

I have here a AsyncDbEntitySetController for you.
The class DbEntity is equated with your EntityBase class.
Perhaps you've still got a few optimizations for the AsyncDbEntitySetController ready, otherwise I hope I could help you with your excellent project.
using Microsoft.Data.OData;
using Microsoft.Data.OData.Query;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Repository;
using System.Data.Entity.UnitOfWork;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.OData;
using System.Web.Http.OData.Routing;
using System.Web.Http.Routing;

namespace OData.Controllers
{
    public abstract class AsyncDbEntitySetController<TEntity, TKey> : AsyncEntitySetController<TEntity, TKey> where TEntity : DbEntity
    {
        #region Fields

        private readonly IUnitOfWork unitOfWork = null;
        private readonly IRepository<TEntity> repository = null;

        #endregion

        #region Properties

        protected IUnitOfWork UnitOfWork
        {
            get { return this.unitOfWork; }
        }

        protected IRepository<TEntity> Repository
        {
            get { return this.repository; }
        }

        #endregion

        #region Constructor

        protected AsyncDbEntitySetController(IUnitOfWork unitOfWork)
        {
            this.unitOfWork = unitOfWork;
            this.repository = this.unitOfWork.Repository<TEntity>();
        }

        #endregion

        #region AsyncEntitySetController
        
        public override async Task<IEnumerable<TEntity>> Get()
        {
            return await this.repository.Query().GetAsync();
        }

        public override async Task Delete([FromODataUri] TKey key)
        {
            var entity = await this.GetEntityByKeyAsync(key);            
            if (entity == null)
            {
                throw new HttpResponseException(await this.NotFound().ExecuteAsync(CancellationToken.None));
            }

            this.repository.Delete(entity);
            await this.unitOfWork.SaveAsync();
        }

        [AcceptVerbs("POST", "PUT")]
        public override async Task CreateLink([FromODataUri] TKey key, string navigationProperty, [FromBody] Uri link)
        {
            var entity = await this.GetEntityByKeyAsync(key);            
            if (entity == null)
            {
                throw new HttpResponseException(await this.NotFound().ExecuteAsync(CancellationToken.None));
            }

            await this.CreateLink(entity, navigationProperty, link);

            try
            {
                await this.unitOfWork.SaveAsync();
            }
            catch (Exception e)
            {
                throw this.Request.CreateConflict(e);
            }
        }

        protected abstract Task CreateLink(TEntity entity, string navigationProperty, [FromBody] Uri link);

        public override async Task DeleteLink([FromODataUri] TKey key, string navigationProperty, [FromBody] Uri link)
        {
            var entity = await this.GetEntityByKeyAsync(key);
            if (entity == null)
            {
                throw new HttpResponseException(await this.NotFound().ExecuteAsync(CancellationToken.None));
            }

            await this.DeleteLink(entity, navigationProperty, link);

            try
            {
                await this.unitOfWork.SaveAsync();
            }
            catch (Exception e)
            {
                throw this.Request.CreateConflict(e);
            }
        }

        protected abstract Task DeleteLink(TEntity entity, string navigationProperty, [FromBody] Uri link);

        public override async Task DeleteLink([FromODataUri] TKey key, string relatedKey, string navigationProperty)
        {
            var entity = await this.GetEntityByKeyAsync(key);
            if (entity == null)
            {
                throw new HttpResponseException(await this.NotFound().ExecuteAsync(CancellationToken.None));
            }

            await this.DeleteLink(entity, relatedKey, navigationProperty);

            try
            {
                await this.unitOfWork.SaveAsync();
            }
            catch (Exception e)
            {
                throw this.Request.CreateConflict(e);
            }
        }

        protected abstract Task DeleteLink(TEntity entity, string relatedKey, string navigationProperty);

        [AcceptVerbs("GET", "POST", "PUT", "PATCH", "MERGE", "DELETE")]
        public override async Task<HttpResponseMessage> HandleUnmappedRequest(ODataPath odataPath)
        {
            // ToDo: Logik
            return this.Request.CreateResponse(HttpStatusCode.NoContent, odataPath);
        }

        protected override abstract TKey GetKey(TEntity entity);

        protected override async Task<TEntity> GetEntityByKeyAsync(TKey key)
        {
            return await this.repository.FindAsync(key);
        }

        protected override async Task<TEntity> CreateEntityAsync(TEntity entity)
        {
            if (this.ModelState.IsValid == false)
            {
                throw new HttpResponseException(await this.BadRequest(this.ModelState).ExecuteAsync(CancellationToken.None));
            }

            this.repository.Insert(entity);
            await this.unitOfWork.SaveAsync();

            return entity;
        }

        protected override async Task<TEntity> UpdateEntityAsync(TKey key, TEntity update)
        {
            if (this.ModelState.IsValid == false)
            {
                throw new HttpResponseException(await this.BadRequest(this.ModelState).ExecuteAsync(CancellationToken.None));
            }

            if (object.Equals(key, this.GetKey(update)) == false)
            {
                throw new HttpResponseException(await this.BadRequest().ExecuteAsync(CancellationToken.None));
            }

            this.repository.Update(update);

            try
            {
                await this.unitOfWork.SaveAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (this.EntityExists(key).Result == false)
                {
                    throw new HttpResponseException(this.NotFound().ExecuteAsync(CancellationToken.None).Result);
                }
                else
                {
                    throw;
                }
            }

            return update;
        }

        protected override async Task<TEntity> PatchEntityAsync(TKey key, Delta<TEntity> patch)
        {
            if (this.ModelState.IsValid == false)
            {
                throw new HttpResponseException(await this.BadRequest(this.ModelState).ExecuteAsync(CancellationToken.None));
            }
            
            var entity = await this.GetEntityByKeyAsync(key);
            if (entity == null)
            {
                throw this.Request.CreateEntityNotFound();
            }

            patch.Patch(entity);

            try
            {
                await this.unitOfWork.SaveAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (this.EntityExists(key).Result == false)
                {
                    throw new HttpResponseException(this.NotFound().ExecuteAsync(CancellationToken.None).Result);
                }
                else
                {
                    throw;
                }
            }

            return entity;
        }

        #endregion

        #region Helper

        protected abstract Task<bool> EntityExists(TKey key);

        protected TLinkKey GetKeyFromLinkUri<TLinkKey>(Uri link)
        {
            TLinkKey key = default(TLinkKey);

            IHttpRoute route = Request.GetRouteData().Route;
            IHttpRoute newRoute = new HttpRoute(route.RouteTemplate, new HttpRouteValueDictionary(route.Defaults), new HttpRouteValueDictionary(route.Constraints), new HttpRouteValueDictionary(route.DataTokens), route.Handler);

            var linkRequest = new HttpRequestMessage(HttpMethod.Get, link);            
            var routeData = newRoute.GetRouteData(this.Request.GetConfiguration().VirtualPathRoot, linkRequest);

            if (routeData != null)
            {
                ODataPath path = linkRequest.GetODataPath();
                var segment = path.Segments.OfType<KeyValuePathSegment>().FirstOrDefault();
                if (segment != null)
                {
                    key = (TLinkKey)ODataUriUtils.ConvertFromUriLiteral(segment.Value, ODataVersion.V3);
                }
            }

            return key;
        }

        #endregion

        #region IDisposable

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.unitOfWork.Dispose();
            }

            base.Dispose(disposing);
        }

        #endregion
    }
}
Nov 30, 2013 at 10:19 AM
Edited Nov 30, 2013 at 10:24 AM
Here a Sample Implementation
using Entity.Models.PaymentMethod;
using Entity.Models.Voucher;
using System;
using System.Data.Entity.UnitOfWork;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;

namespace OData.Controllers
{
    public class VouchersController : AsyncDbEntitySetController<Voucher, int>
    {
        public VouchersController(IUnitOfWork unitOfWork)
            : base(unitOfWork)
        {
        }

        protected override async Task CreateLink(Voucher entity, string navigationProperty, Uri link)
        {
            switch (navigationProperty)
            {
                case "PaymentMethod":
                    {
                        var paymentMethodKey = this.GetKeyFromLinkUri<int>(link);
                        var paymentMethod = await this.UnitOfWork.Repository<PaymentMethod>().FindAsync(paymentMethodKey);
                        if (paymentMethod == null)
                        {
                            throw new HttpResponseException(await this.NotFound().ExecuteAsync(CancellationToken.None));
                        }

                        entity.PaymentMethod = paymentMethod;
                        await this.UnitOfWork.SaveAsync();
                        break;
                    }
                default:
                    {
                        throw new HttpResponseException(await this.NotFound().ExecuteAsync(CancellationToken.None));
                    }
            }
        }

        protected override async Task DeleteLink(Voucher entity, string navigationProperty, Uri link)
        {
            switch(navigationProperty)
            {
                case "PaymentMethod":
                    {
                        entity.PaymentMethod = null;
                        await this.UnitOfWork.SaveAsync();
                        break;
                    }
                default:
                    {
                        throw new HttpResponseException(await this.NotFound().ExecuteAsync(CancellationToken.None));
                    }
            }
        }

        protected override async Task DeleteLink(Voucher entity, string relatedKey, string navigationProperty)
        {
            switch (navigationProperty)
            {
                case "Entries":
                    {
                        var voucherEntryKey = int.Parse(relatedKey);
                        var voucherEntry = await this.UnitOfWork.Repository<VoucherEntry>().FindAsync(voucherEntryKey);
                        if (voucherEntry == null)
                        {
                            throw new HttpResponseException(await this.NotFound().ExecuteAsync(CancellationToken.None));
                        }

                        entity.Entries.Remove(voucherEntry);
                        this.UnitOfWork.Repository<VoucherEntry>().Delete(entity);
                        await this.UnitOfWork.SaveAsync();
                        break;
                    }
                default:
                    {
                        throw new HttpResponseException(await this.NotFound().ExecuteAsync(CancellationToken.None));
                    }
            }
        }

        protected override int GetKey(Voucher entity)
        {
            return entity.Id;
        }

        protected override async Task<bool> EntityExists(int key)
        {
            if (await this.GetEntityByKeyAsync(key) != null)
            {
                return true;
            }

            return false;
        }
    }
}
Coordinator
Apr 1, 2014 at 6:35 AM
Thank you, we now have a T4 template that will generate an async controller using the framework.

You will need to copy the CodeTemplates folder into your project.

https://genericunitofworkandrepositories.codeplex.com/SourceControl/latest#main/Sample/Northwind.Web/CodeTemplates/ODataControllerWithContext/Controller.cs.t4
Marked as answer by lelong37 on 3/31/2014 at 10:35 PM