ASP.NET MVC web application with Entity Framework – Part 3.

In this part I will show what a Business Logic Layer (BLL) should contain. We will use Repository Pattern and Unit Of Work design pattern.

What are these (in few words)?

Unit Of Work does 2 really important things: it handles all the in-memory updates and sends these in-memory updates to the database as one transaction. Simple definition of the Work is performing task. Unit will hold together Works and send the changes to the database.

Why do we need Repository Pattern? Without it, the applications may access the data in the database directly. This may cause:

  • duplicated code
  • difficulty in centralizing data-related policies like caching
  • weak typing of business data

Repository Pattern gives you many objectives, like:

  • accessing data from many locations
  • implement and centralize a caching strategy for the data source
  • improves the maintainability and readability of the code by separating business logic from data access logic
  • you can use strongly typed business entities so problems can be identified at compile time instead of runtime

It provides abstraction of data so our application can work with simple abstraction. CRUD operations (Create, Read, Update, Delete) from the returning collections of repository is done through series of straightforward methods. No need of fussing with database connections, commands, readers, etc.

Add a new project to the solution called MovieInventory.BLL. This is also a Class Library. Then add references of Entity Framework, DTO and DAL.

Now we need to create a generic repository interface (the parent-child relations will be similar to DTO’s classes). This interface will be our common interface (with the type of BaseIdentity class), so every entity can access its implemented methods:

public interface IRepository where T : BaseIdentity
{
    T GetById(int id);
    IQueryable Query();
    IEnumerable GetAll();
    void Insert(T entity);
    void Update(T entity);
    void Delete(int id);
}

All implementations of the interfaces must be in internal classes!

namespace MovieInventory.BLL.Repository.Base
{
    internal abstract class Repository : IRepository where T : BaseIdentity
    {
        protected readonly MovieInventoryContext _context;
        protected DbSet TDbSet { get; private set; }

        protected Repository(MovieInventoryContext context)
        {
            _context = context;
            TDbSet = _context.GetDbSet();
        }

        public virtual T GetById(int id)//must be overridden in specific classes where foreign key exists, so we must include the other table(s)
        {
            if (id < 1) throw new ArgumentNullException(nameof(id));

            return TDbSet.Find(id);
        }

        public virtual IQueryable Query()
        {
            return TDbSet.AsQueryable();
        }

        public virtual IEnumerable GetAll()
        {
            return TDbSet.AsEnumerable();
        }

        public void Insert(T entity)
        {
            if (entity == null) throw new ArgumentNullException(nameof(entity));

            entity.LastModified = DateTime.Now;
            TDbSet.Add(entity);
            _context.SaveChanges();
        }

        public void Update(T entity)
        {
            if (entity == null) throw new ArgumentNullException(nameof(entity));

            entity.LastModified = DateTime.Now;
            TDbSet.Attach(entity);
            _context.Entry(entity).State = EntityState.Modified;
            _context.SaveChanges();
        }

        public virtual void Delete(int id)
        {
            T entity = GetById(id);
            TDbSet.Remove(entity);
            try
            {
                _context.SaveChanges();
            }
            catch (DbUpdateException ex)
            {
                if (ex.InnerException != null)
                {
                    string message = "";

                    if (ex.InnerException.Message.Contains("The DELETE statement conflicted with the REFERENCE"))
                    {
                        message = "The entry cannot be deleted, because another entry is referring to this!";
                    }

                    else if (ex.InnerException.InnerException != null)
                    {
                        if (ex.InnerException.InnerException.Message.Contains("The DELETE statement conflicted with the REFERENCE"))
                        {
                            message = "The entry cannot be deleted, because another entry is referring to this!";
                        }
                    }

                    throw new Exception(message);
                }

            }
        }

        protected bool Exists(TU entity) where TU : BaseIdentity//just for fun, maybe later you can use it
        {
            return TDbSet.Find(entity.Id) != null;
        }
    }
}

Let’s walk through the implementations and if you understand these, the rest interfaces and those’ implementations won’t be difficult to understand.

First of all we create a new instance of our MovieInventoryContext and our generic TDbSet we declared in DAL project.

Don’t forget to set the visiblity of the constructors to protected in abstract classes!

After this we can access every native methods on context, like Find(), Add(), Create(), etc.

The main difference between Query() and GetAll() methods is the returning type: IQueryable<T> and IEnumerable<T>. Depending on what you want to do with the result, you can use GetAll() if you want to send it to View as it is, or you may want to do some more operations on the result: Query(). IEnumerables are calculated in clients’ memory, while IQueryable are being executed in the database, if possible.

When overriding the methods, you can set any logic in it you want. My logic set the LostModified property to current time. So whenever you create a new record (of any entity, because this is the common interface) or update a record, the LastModified field automatically filled and no need to set it in View. Very comfortable.

What I should even  mention is the Delete method. As you can see, I handle DbUpdateException, because you may want to delete a record from Category table, but Movie table has a foreign key to this table. So if you want to delete Horror category but it is still used in Movie table, SQL throws an exception that isn’t really user-friendly and readable. The almost user-friendly message is in the InnerException of the InnerException. Don’t ask why… Secret of .NET. 🙂

You may ask why is Delete method virtual? Because there are 2 entities that we don’t want to delete physically, just set it’s visiblity flag. We can do this by overriding later in the exact repository.

IVisibleRepository<T> interface has only 2 methods: QueryVisible() and GetAllVisible(). It’s easy to find out that these methods return only “not deleted”, visible values.

public interface IVisibleRepository : IRepository where T : VisibleIdentity
{
    IQueryable QueryVisible();
    IEnumerable GetAllVisible();
}

Implementation is simple: return the base.Query() with filtering to visible values with LINQ statement: Where().

internal abstract class VisibleRepository : Repository, IVisibleRepository where T : VisibleIdentity
{
    public VisibleRepository(MovieInventoryContext context) : base(context)
    {
    }

    public virtual IQueryable QueryVisible()
    {
        return base.Query().Where(x => x.Visible);
    }

    public virtual IEnumerable GetAllVisible()
    {
        return base.Query().Where(x => x.Visible);
    }
}

Remaining 4 interfaces are for the entities: Category, Format, Inventory and Movie.

ICategoryRepository and IFormatRepository now don’t have any new methods. But later can be added as you wish, that’s why we have to use it even if it’s empty now. Remember: flexibility…

public interface IFormatRepository : IVisibleRepository
{
}
public interface ICategoryRepository : IVisibleRepository
{
}

I created some methods in IMovieRepository and IInventoryRepository, but they are not used in this tutortial. Just thought provoking.

public interface IMovieRepository : IRepository
{
    IQueryable GetAllByTitle(string title);
    IQueryable GetAllByCategoryId(int categoryId);
    IQueryable GetAllByRatingNumber(int rating);
}
public interface IInventoryRepository : IRepository
{
    IQueryable GetAllByMovieId(int movieId);
    IQueryable GetAllByFormatId(int formatId);
}

As I wrote before, the Delete() method must be overridden in FormatRepository and CategoryRepository classes, so the entities won’t be deleted physically, just flag-set and updated. I’ve also overridden the IQueryable<T> and IEnumerable<T> methods so we get alphabetically ordered lists. It’s much more user-friendly in UI:

namespace MovieInventory.BLL.Repository
{
    internal class CategoryRepository : VisibleRepository, ICategoryRepository
    {
        public CategoryRepository(MovieInventoryContext context) : base(context)
        {
        }

        public override IQueryable Query()
        {
            return base.Query().OrderBy(x => x.CategoryType);
        }

        public override IQueryable QueryVisible()
        {
            return base.QueryVisible().OrderBy(x => x.CategoryType);
        }

        public override IEnumerable GetAll()
        {
            return base.GetAll().OrderBy(x => x.CategoryType);
        }

        public override IEnumerable GetAllVisible()
        {
            return base.GetAllVisible().OrderBy(x => x.CategoryType);
        }

        public override Category GetById(int id)
        {
            if (id < 1) throw new ArgumentNullException(nameof(id)); return QueryVisible().SingleOrDefault(x => x.Id == id);
        }

        public override void Delete(int id)
        {
            Category entity = GetById(id);
            entity.Visible = false;
            Update(entity);
        }
    }
}
namespace MovieInventory.BLL.Repository
{
    internal class FormatRepository : VisibleRepository, IFormatRepository
    {
        public FormatRepository(MovieInventoryContext context) : base(context)
        {
        }

        public override IQueryable Query()
        {
            return base.Query().OrderBy(x => x.FormatType);
        }

        public override IQueryable QueryVisible()
        {
            return base.QueryVisible().OrderBy(x => x.FormatType);
        }

        public override IEnumerable GetAll()
        {
            return base.GetAll().OrderBy(x => x.FormatType);
        }

        public override IEnumerable GetAllVisible()
        {
            return base.GetAllVisible().OrderBy(x => x.FormatType);
        }

        public override Format GetById(int id)
        {
            if (id < 1) throw new ArgumentNullException(nameof(id)); return QueryVisible().SingleOrDefault(x => x.Id == id);
        }

        public override void Delete(int id)
        {
            Format entity = GetById(id);
            entity.Visible = false;
            Update(entity);
        }
    }
}

In MovieRepository and InventoryRepository classes I have overridden the GetById method, because we want to get the foreign values too, so we have to include that tables in the query. Remember: when you return with IQueryable you don’t have to include foreign tables, because IQueryable hasn’t been calculated yet. When you return entity (like here), it is calculated (in our case with SingleOrDefault) so we have to include those foreign tables that we need. In InventoryRepository we need data not only from immediate neighbor (like Format), but from indirect neighbor (neighbor of a neighbor, like Category).

namespace MovieInventory.BLL.Repository
{
    internal class MovieRepository : Repository, IMovieRepository
    {
        public MovieRepository(MovieInventoryContext context) : base(context)
        {
        }

        public override Movie GetById(int id)
        {
            return Query().Include("Category").SingleOrDefault(x => x.Id == id);
        }

        public override IQueryable Query()
        {
            return base.Query().OrderBy(x => x.Title);
        }

        public override IEnumerable GetAll()
        {
            return base.GetAll().OrderBy(x => x.Title);
        }

        public IQueryable GetAllByTitle(string title)
        {
            return string.IsNullOrEmpty(title.Trim()) ? Query() : Query().Where(x => x.Title.ToLower().Trim().Equals(title.ToLower().Trim()));
        }

        public IQueryable GetAllByCategoryId(int categoryId)
        {
            return Query().Where(x => x.CategoryId == categoryId);
        }

        public IQueryable GetAllByRatingNumber(int rating)
        {
            return Query().Where(x => x.ImdbRating == rating);
        }
    }
}
namespace MovieInventory.BLL.Repository
{
    internal class InventoryRepository : Repository, IInventoryRepository
    {
        public InventoryRepository(MovieInventoryContext context) : base(context)
        {
        }

        public override Inventory GetById(int id)
        {
            return Query()
                .Include("Format").Include("Movie").Include("Movie.Category")//eager loading
                .SingleOrDefault(x => x.Id == id);
        }

        public override IQueryable Query()
        {
            return base.Query().OrderBy(x => x.Movie.Title);
        }

        public override IEnumerable GetAll()
        {
            return base.GetAll().OrderBy(x => x.Movie.Title);
        }

        public IQueryable GetAllByMovieId(int movieId)
        {
            return Query().Where(x => x.MovieId == movieId);
        }

        public IQueryable GetAllByFormatId(int formatId)
        {
            return Query().Where(x => x.FormatId == formatId);
        }
    }
}

Now only we have to create the Unit Of Work part. It’s very simple and short. We need 4 properties (only with getter!) for each repositories and a SaveChanges() method:

namespace MovieInventory.BLL.UnitOfWork
{
    public interface IUnitOfWork : IDisposable
    {
        int SaveChanges();
        ICategoryRepository Category { get; }
        IFormatRepository Format { get; }
        IInventoryRepository Inventory { get; }
        IMovieRepository Movie { get; }
    }
}

The implementation of this interface is easy to understand: everytime we get the UnitOfWork class, we can access each repositories (remember: repositories contain all the CRUD operations) and their methods.

If you need transactions, modify base repository (Repository<T>) and remove every _context.SaveChanges(); lines, because you have to call the save in UnitOfWork class!

The implementation of IUnitOfWork interface (which is IDisposable of course):

namespace MovieInventory.BLL.UnitOfWork
{
    internal class UnitOfWork : IUnitOfWork
    {
        private readonly MovieInventoryContext _context;
        private bool _disposed;
        private ICategoryRepository _category;
        private IFormatRepository _format;
        private IInventoryRepository _inventory;
        private IMovieRepository _movie;

        public ICategoryRepository Category => _category ?? (_category = new CategoryRepository(_context));
        public IFormatRepository Format => _format ?? (_format = new FormatRepository(_context));
        public IInventoryRepository Inventory => _inventory ?? (_inventory = new InventoryRepository(_context));
        public IMovieRepository Movie => _movie ?? (_movie = new MovieRepository(_context));

        public UnitOfWork()
        {
            _context = new MovieInventoryContext();
        }

        public UnitOfWork(MovieInventoryContext context)
        {
            _context = context;
        }

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

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _context.Dispose();
                }
            }

            _disposed = true;
        }

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

We only need one more method to be able to access this internal class. This is the Manager static public class (this is Factory Pattern):

namespace MovieInventory.BLL
{
    public static class Manager
    {
        public static IUnitOfWork GetUnitOfWork()
        {
            return new UnitOfWork.UnitOfWork();
        }
    }
}

You’ll see in UI controller action, that this way it is very simple to access any CRUD operation.

Your solution structure should look something like this:

Next part is the UI with some frontend development. Very little, because I’m not a designer… 🙂