Skip to content

Services Pattern Example

rdhainaut edited this page Nov 15, 2017 · 4 revisions

Create a Service and IService

Example: Northwind.Service.CustomerService.cs

*All methods from Service<TEntity> are overridable, so that business logic can be added. Business logic should only be in Service Layer and not in the Repository Layer to address Separation of Concerns.

public interface ICustomerService : IService<Customer>
{
    // Add any custom business logic (methods) here
    // All methods in Service<TEntity> are ovverridable for any custom implementations
}

public class CustomerService : Service<Customer>, ICustomerService
{
    public CustomerService(IRepositoryAsync<Customer> repository) : base(repository)
    {
    }

    // Add any custom business logic (methods) here
    // All methods in Service<TEntity> are ovverridable for any custom implementations
    // Can ovveride any of the Repository methods to add business logic in them
    // e.g.
    //public override void Delete(Customer entity)
    //{
    //    // Add business logic before or after deleting entity.
    //    base.Delete(entity);
    //}
}

Register Service with IoC & DI Container

Example: Northwind.AppStart.UnitConfig.cs

container
    .RegisterType<IDataContextAsync, NorthwindContext>(new PerRequestLifetimeManager())
    .RegisterType<IUnitOfWorkAsync, UnitOfWork>(new PerRequestLifetimeManager())
    .RegisterType<IRepositoryAsync<Customer>, Repository<Customer>>()
    .RegisterType<IRepositoryAsync<Product>, Repository<Product>>()
    .RegisterType<IProductService, ProductService>();

Create a Controller using the Service

Example: Northwind.WebApi.ProductController.cs

Can leverage the Northwind.Web.CodeTemplates.ODataControllerWithContext.t4 template (copy to project) Right click -> Add New Controller -> Web Api 2 OData Controller with actions, using Entity Framework (will use this T4 template)

public class ProductController : ODataController
{
    private readonly IProductService _productService;
    private readonly IUnitOfWorkAsync _unitOfWork;

    public ProductController(
        IUnitOfWorkAsync unitOfWork,
        IProductService productService)
    {
        _unitOfWork = unitOfWork;
        _productService = productService;
    }

    [Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
    public IQueryable<Product> GetProduct()
    {
        return _productService.Queryable();
    }

    // GET odata/Product(5)
    [Queryable]
    public SingleResult<Product> GetProduct([FromODataUri] int key)
    {
        return SingleResult.Create(
            _productService
                .Query(product => product.ProductID == key)
                .Select()
                .AsQueryable());
    }

    // PUT odata/Product(5)
    public async Task<IHttpActionResult> Put([FromODataUri] int key, Product product)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        if (key != product.ProductID)
        {
            return BadRequest();
        }

	product.ObjectState = ObjectState.Modified;
	_productService.Update(product);

        try
        {
            await _unitOfWork.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!ProductExists(key))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }

        return Updated(product);
    }

    // POST odata/Product
    public async Task<IHttpActionResult> Post(Product product)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

	product.ObjectState = ObjectState.Added;
        _productService.Insert(product);
        await _unitOfWork.SaveChangesAsync();


        return Created(product);
    }

    // PATCH odata/Product(5)
    [AcceptVerbs("PATCH", "MERGE")]
    public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Product> patch)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var product = await _productService.FindAsync(key);
        if (product == null)
        {
            return NotFound();
        }

        patch.Patch(product);

	product.ObjectState = ObjectState.Modified;
	_productService.Update(product);

        try
        {
            await _unitOfWork.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!ProductExists(key))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }

        return Updated(product);
    }

    // DELETE odata/Product(5)
    public async Task<IHttpActionResult> Delete([FromODataUri] int key)
    {
        var product = await _productService.FindAsync(key);
        if (product == null)
        {
            return NotFound();
        }

	product.ObjectState = ObjectState.Deleted;
	_productService.Delete(product);
        await _unitOfWork.SaveChangesAsync();

        return StatusCode(HttpStatusCode.NoContent);
    }

    // GET odata/Product(5)/Category
    [Queryable]
    public SingleResult<Category> GetCategory([FromODataUri] int key)
    {
        return SingleResult.Create(
			_productService
			.Query(m => m.ProductID == key)
			.Select(m => m.Category)
			.AsQueryable());
    }

    // GET odata/Product(5)/OrderDetails
    [Queryable]
    public IQueryable<OrderDetail> GetOrderDetails([FromODataUri] int key)
    {
		var product = _productService
			.Query(m => m.ProductID == key)
			.Include(m => m.OrderDetails)
			.Select()
			.Single();

        return product.OrderDetails.AsQueryable();
    }

    // GET odata/Product(5)/Supplier
    [Queryable]
    public SingleResult<Supplier> GetSupplier([FromODataUri] int key)
    {
        return SingleResult.Create(
			_productService
			.Query(m => m.ProductID == key)
			.Select(m => m.Supplier)
			.AsQueryable());
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _unitOfWork.Dispose();
        }
        base.Dispose(disposing);
    }

    private bool ProductExists(int key)
    {
	return _productService.Query(e => e.ProductID == key).Select().Any();
    }
}