Book Image

Entity Framework Core Cookbook - Second Edition

By : Ricardo Peres
Book Image

Entity Framework Core Cookbook - Second Edition

By: Ricardo Peres

Overview of this book

Entity Framework is a highly recommended Object Relation Mapping tool used to build complex systems. In order to survive in this growing market, the knowledge of a framework that helps provide easy access to databases, that is, Entity Framework has become a necessity. This book will provide .NET developers with this knowledge and guide them through working efficiently with data using Entity Framework Core. You will start off by learning how to efficiently use Entity Framework in practical situations. You will gain a deep understanding of mapping properties and find out how to handle validation in Entity Framework. The book will then explain how to work with transactions and stored procedures along with improving Entity Framework using query libraries. Moving on, you will learn to improve complex query scenarios and implement transaction and concurrency control. You will then be taught to improve and develop Entity Framework in complex business scenarios. With the concluding chapter on performance and scalability, this book will get you ready to use Entity Framework proficiently.
Table of Contents (15 chapters)
Entity Framework Core Cookbook - Second Edition
Credits
About the Author
About the Reviewer
www.PacktPub.com
Preface
Index

Implementing the repository pattern


This recipe is an implementation of the Repository Pattern, which allows us to abstract the underlying data source and the queries used to obtain the data.

Getting ready

We will be using the NuGet Package Manager to install the Entity Framework Core 1 package, Microsoft.EntityFrameworkCore. We will also be using a SQL Server database for storing the data, so we will also need Microsoft.EntityFrameworkCore.SqlServer.

To mock interfaces and base classes, we will use Moq.

Finally, xunit is the package we will be using for the unit tests and dotnet-text-xunit adds tooling support for Visual Studio. Note that the UnitTests project is a .NET Core App 1.0 (netcoreapp1.0), that Microsoft.EntityFrameworkCore.Design is configured as a build dependency, and Microsoft.EntityFrameworkCore.Tools is set as a tool.

Open Using EF Core Solution from the included source code examples.

Execute the database setup script from the code samples included for this recipe. This can be found in the DataAccess project within the Database folder.

How to do it…

  1. Create a new file in the DataAccess project, with this content:

    using System.Linq;
    namespace DataAccess
    {
        public interface IRepository<out T> where T : class
        {
            IQueryable<T> Set<T>() where T : class;
            void RollbackChanges();
            void SaveChanges();
        }
    }
  2. In the DataAccess project, add a new C# interface named IBlogRepository with the following code:

    using System.Linq;
    namespace DataAccess
    {
        public interface IBlogRepository : IRepository<Blog>
        {
        }
    }
  3. In the DataAccess project, create a new C# class named BlogRepository containing the following code:

    using System.Data.Entity;
    using System.Linq;
    using BusinessLogic;
    namespace DataAccess
    {
        public class BlogRepository : IBlogRepository
        {
            private readonly IDbContext _context;
            public BlogRepository(IDbContext context)
            {
                _context = context;
            }
            public IQueryable<Blog> Set()
            {
                return _context.Set<Blog>();
            }
        }
    }
  4. We'll add a new unit test in the UnitTests project that defines a test for using the repository with the following code:

    using System.Linq;
    using BusinessLogic;
    using DataAccess;
    using Moq;
    using Xunit;
    namespace UnitTests
    {
        public class RepositoryTest : BaseTest
        {
            [Fact]
            public void ShouldAllowGettingASetOfObjectsGenerically()
            {
                //Arrange
                var data = new[] { new Blog { Id = 1, Title = "Title" },              newBlog { Id = 2, Title = "No Title" } }.AsQueryable();
                var mock = new Mock<IDbContext>();
                mock.Setup(x => x.Set<Blog>()).Returns(data);
                var context = mock.Object;
                var repository = new BlogRepository(context);
                //Act
                var blogs = repository.Set();
                //Assert
                Assert.Equal(data, blogs);
            }
        }
    }
  5. In the BlogController class of the UI project, update the usage of BlogContext so it uses IBlogRepository with the following code:

    using BusinessLogic;
    using DataAccess;
    using System.Linq;
    using Microsoft.AspNet.Mvc;
    namespace UI.Controllers
    {
        public class BlogController : Controller
        {
            private readonly IBlogRepository _repository;
            public BlogController(IBlogRepository repository)
            {
                _repository = repository;
            }
            public IActionResult Index()
            {
                var blog = _repository.Set().First();
                return View(blog);
            }
        }
    }
  6. Finally, we need to register the IBlogRepository service for dependency injection so that it can be passed automatically to the HomeController's constructor. We do that in the Startup.cs file in the UI project, in the ConfigureServices method:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        services.AddSingleton<IConfiguration>(_ => Configuration);
        services.AddScoped<IDbContext>(_ => new BlogContext(Configuration["Data:Blog:ConnectionString"]));
        services.AddScoped<IBlogRepository>(_ => new BlogRepository(_.GetService<IDbContext>()));
    }

How it works…

We start off with a test that defines what we hope to accomplish. We use mocking (or verifiable fake objects) to ensure that we get the behavior that we expect. The test states that any BlogRepository function will communicate with the context to connect for the data. This is what we are hoping to accomplish, as doing so allows us to layer tests and extension points into the domain.

The usage of the repository interface is a key part of this flexible implementation as it will allow us to leverage mocks, and test the business layer, while still maintaining an extensible solution. The interface to the context is a straightforward API for all database communication. In this example, we only need to read data from the database, so the interface is very simple.

Even in this simple implementation of the interface, we see that there are opportunities to increase reusability. We could have created a method or property that returned the list of blogs, but then we would have had to modify the context and interface for every new entity. Instead, we set up the Set method to take a generic type, which allows us to add entities to the usage of the interface without modifying the interface. We will only need to modify the implementation.

Notice that we constrained the IRepository interface to accept only the reference types for T, using the where T : class constraint. We did this because value types cannot be stored using Entity Framework; if you had a base class, you could use it here to constrain the usage of the generic even further. Importantly, not all reference types are valid for T, but the constraint is as close as we can get using C#. Interfaces are not valid because Entity Framework cannot construct them when it needs to create an entity. Instead, it will produce a runtime exception, as they are valid reference types and therefore the compiler won't complain.

Once we have the context, we need to wrap it with an abstraction. IBlogRepository will allow us to query the data without allowing direct control over the database connection. We can hide the details of the specific implementation, the actual context object, while surfacing a simplified API for gathering data. We can also introduce specific operations for the Blog entity here.

The other interface that we abstracted is the IDbContext interface. This abstraction allows us to intercept operations just before they are sent to the database. This makes the untestable part of the application as thin as possible. We can, and will, test right up to the point of database connection.

We had to register the two interfaces, IDbContext and IBlogRepository, in the ASP.NET dependency resolver. This is achieved at startup time, so that any code that requires these services can use them. You will notice that the registration for IBlogRepository makes use of the IDbContext registration. This is OK, because it is a requirement for the actual implementation of BlogRepository to rely on IDbContext to actually retrieve the data.

There's more…

Keeping the repository implementation clean requires us to leverage some principles and patterns that are at the core of object-oriented programming, but not specific to using Entity Framework. These principles will not only help us to write clean implementations of Entity Framework, but can also be leveraged by other areas of our code.

Dependency Inversion Principle

Dependency inversion is another SOLID principle. This states that all of the dependencies of an object should be clearly visible and passed in, or injected, to create the object. The benefit of this is twofold: the first is exposing all of the dependencies so the effects of using a piece of code are clear to those who will use the class. The second benefit is that by injecting these dependencies at construction, they allow us to unit test by passing in mocks of the dependent objects. Granular unit tests require the ability to abstract dependencies, so we can ensure only one object is under test.

Repository and caching

This repository pattern gives us the perfect area for implementing a complex or global caching mechanism. If we want to persist a value into the cache at the point of retrieval, and not retrieve it again, the repository class is the perfect location for such logic. This layer of abstraction allows us to move beyond simple implementations and start thinking about solving business problems quickly, and later extend to handle more complex scenarios as they are warranted by the requirements of the specific project. You can think of repository as a well-tested 80%+ solution. Put off anything more until the last responsible moment.

Mocking

The usage of mocks is commonplace in tests because mocks allow us to verify underlying behavior without having more than one object under test. This is a fundamental piece of the puzzle for test-driven development. When you test at a unit level, you want to make sure that the level directly following the one you are testing was called correctly while not actually executing the specific implementation. This is what mocking buys us.

Where generic constraint

There are times when we need to create complex sets of queries that will be used frequently, but only by one or two objects. When this situation occurs, we want to reuse that code without needing to duplicate it for each object. This is where the where constraint helps us. It allows us to limit generically defined behavior to an object or set of objects that share a common interface or base class. The extension possibilities are nearly limitless.

See also

In this chapter:

  • Implementing the unit of work pattern

  • Creating mock database connections