Implement the infrastructure persistence layer with Entity Framework Cadre

When you use relational databases such as SQL Server, Oracle, or PostgreSQL, a recommended approach is to implement the persistence layer based on Entity Framework (EF). EF supports LINQ and provides strongly typed objects for your model, every bit well equally simplified persistence into your database.

Entity Framework has a long history as part of the .Internet Framework. When y'all apply .NET, you should also utilize Entity Framework Core, which runs on Windows or Linux in the aforementioned way as .NET. EF Core is a complete rewrite of Entity Framework that's implemented with a much smaller footprint and of import improvements in performance.

Introduction to Entity Framework Core

Entity Framework (EF) Core is a lightweight, extensible, and cross-platform version of the popular Entity Framework data access technology. It was introduced with .NET Core in mid-2016.

Since an introduction to EF Cadre is already bachelor in Microsoft documentation, here we simply provide links to that information.

Additional resources

  • Entity Framework Core
    https://docs.microsoft.com/ef/cadre/

  • Getting started with ASP.Net Core and Entity Framework Cadre using Visual Studio
    https://docs.microsoft.com/aspnet/core/data/ef-mvc/

  • DbContext Class
    https://docs.microsoft.com/dotnet/api/microsoft.entityframeworkcore.dbcontext

  • Compare EF Core & EF6.x
    https://docs.microsoft.com/ef/efcore-and-ef6/alphabetize

Infrastructure in Entity Framework Core from a DDD perspective

From a DDD signal of view, an important capability of EF is the ability to use POCO domain entities, also known in EF terminology as POCO lawmaking-starting time entities. If you lot utilize POCO domain entities, your domain model classes are persistence-ignorant, post-obit the Persistence Ignorance and the Infrastructure Ignorance principles.

Per DDD patterns, y'all should encapsulate domain beliefs and rules inside the entity class itself, and then it can control invariants, validations, and rules when accessing any collection. Therefore, it is not a good practice in DDD to allow public admission to collections of child entities or value objects. Instead, you want to expose methods that control how and when your fields and belongings collections tin can exist updated, and what behavior and actions should occur when that happens.

Since EF Cadre one.i, to satisfy those DDD requirements, you can accept plain fields in your entities instead of public properties. If y'all do not desire an entity field to be externally attainable, yous tin can just create the aspect or field instead of a holding. Yous can also use private property setters.

In a similar manner, yous tin can now have read-but access to collections by using a public holding typed as IReadOnlyCollection<T>, which is backed by a private field member for the collection (like a List<T>) in your entity that relies on EF for persistence. Previous versions of Entity Framework required drove properties to support ICollection<T>, which meant that whatever developer using the parent entity course could add or remove items through its holding collections. That possibility would be against the recommended patterns in DDD.

You lot can use a private collection while exposing a read-simply IReadOnlyCollection<T> object, as shown in the post-obit code example:

              public grade Order : Entity {     // Using private fields, immune since EF Cadre 1.i     individual DateTime _orderDate;     // Other fields ...      private readonly List<OrderItem> _orderItems;     public IReadOnlyCollection<OrderItem> OrderItems => _orderItems;      protected Order() { }      public Lodge(int buyerId, int paymentMethodId, Accost accost)     {         // Initializations ...     }      public void AddOrderItem(int productId, string productName,                              decimal unitPrice, decimal discount,                              cord pictureUrl, int units = ane)     {         // Validation logic...          var orderItem = new OrderItem(productId, productName,                                       unitPrice, disbelieve,                                       pictureUrl, units);         _orderItems.Add(orderItem);     } }                          

The OrderItems property tin can only exist accessed equally read-only using IReadOnlyCollection<OrderItem>. This type is read-only then information technology is protected confronting regular external updates.

EF Core provides a way to map the domain model to the concrete database without "contaminating" the domain model. It is pure .NET POCO code, because the mapping action is implemented in the persistence layer. In that mapping activeness, you need to configure the fields-to-database mapping. In the post-obit instance of the OnModelCreating method from OrderingContext and the OrderEntityTypeConfiguration class, the call to SetPropertyAccessMode tells EF Cadre to admission the OrderItems property through its field.

              // At OrderingContext.cs from eShopOnContainers protected override void OnModelCreating(ModelBuilder modelBuilder) {    // ...    modelBuilder.ApplyConfiguration(new OrderEntityTypeConfiguration());    // Other entities' configuration ... }  // At OrderEntityTypeConfiguration.cs from eShopOnContainers course OrderEntityTypeConfiguration : IEntityTypeConfiguration<Guild> {     public void Configure(EntityTypeBuilder<Society> orderConfiguration)     {         orderConfiguration.ToTable("orders", OrderingContext.DEFAULT_SCHEMA);         // Other configuration          var navigation =               orderConfiguration.Metadata.FindNavigation(nameof(Order.OrderItems));          //EF access the OrderItem collection property through its bankroll field         navigation.SetPropertyAccessMode(PropertyAccessMode.Field);          // Other configuration     } }                          

When you lot utilize fields instead of properties, the OrderItem entity is persisted as if it had a Listing<OrderItem> property. However, it exposes a single accessor, the AddOrderItem method, for calculation new items to the order. As a effect, beliefs and information are tied together and will be consistent throughout whatever awarding code that uses the domain model.

Implement custom repositories with Entity Framework Core

At the implementation level, a repository is just a grade with data persistence code coordinated by a unit of measurement of work (DBContext in EF Core) when performing updates, every bit shown in the following class:

              // using directives... namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositories {     public form BuyerRepository : IBuyerRepository     {         private readonly OrderingContext _context;         public IUnitOfWork UnitOfWork         {             get             {                 render _context;             }         }          public BuyerRepository(OrderingContext context)         {             _context = context ?? throw new ArgumentNullException(nameof(context));         }          public Heir-apparent Add together(Buyer buyer)         {             return _context.Buyers.Add together(buyer).Entity;         }          public async Task<Heir-apparent> FindAsync(string buyerIdentityGuid)         {             var buyer = await _context.Buyers                 .Include(b => b.Payments)                 .Where(b => b.FullName == buyerIdentityGuid)                 .SingleOrDefaultAsync();              return buyer;         }     } }                          

The IBuyerRepository interface comes from the domain model layer as a contract. However, the repository implementation is done at the persistence and infrastructure layer.

The EF DbContext comes through the constructor through Dependency Injection. Information technology is shared betwixt multiple repositories within the same HTTP request scope, thanks to its default lifetime (ServiceLifetime.Scoped) in the IoC container (which tin can also exist explicitly set with services.AddDbContext<>).

Methods to implement in a repository (updates or transactions versus queries)

Within each repository form, y'all should put the persistence methods that update the state of entities contained by its related aggregate. Remember there is one-to-one human relationship betwixt an aggregate and its related repository. Consider that an aggregate root entity object might have embedded child entities within its EF graph. For case, a buyer might have multiple payment methods as related child entities.

Since the approach for the ordering microservice in eShopOnContainers is also based on CQS/CQRS, most of the queries are not implemented in custom repositories. Developers have the freedom to create the queries and joins they need for the presentation layer without the restrictions imposed by aggregates, custom repositories per amass, and DDD in full general. Most of the custom repositories suggested past this guide have several update or transactional methods merely just the query methods needed to get information to exist updated. For example, the BuyerRepository repository implements a FindAsync method, because the awarding needs to know whether a particular heir-apparent exists before creating a new buyer related to the order.

However, the real query methods to get information to send to the presentation layer or client apps are implemented, as mentioned, in the CQRS queries based on flexible queries using Dapper.

Using a custom repository versus using EF DbContext directly

The Entity Framework DbContext class is based on the Unit of measurement of Piece of work and Repository patterns and tin can exist used directly from your code, such as from an ASP.Internet Cadre MVC controller. The Unit of measurement of Work and Repository patterns result in the simplest code, every bit in the Crud itemize microservice in eShopOnContainers. In cases where you desire the simplest code possible, yous might desire to directly use the DbContext grade, as many developers exercise.

However, implementing custom repositories provides several benefits when implementing more than complex microservices or applications. The Unit of Work and Repository patterns are intended to encapsulate the infrastructure persistence layer so it is decoupled from the application and domain-model layers. Implementing these patterns can facilitate the use of mock repositories simulating admission to the database.

In Figure 7-18, you can encounter the differences between not using repositories (straight using the EF DbContext) versus using repositories, which makes it easier to mock those repositories.

Diagram showing the components and dataflow in the two repositories.

Figure 7-18. Using custom repositories versus a plain DbContext

Figure 7-18 shows that using a custom repository adds an abstraction layer that can be used to ease testing by mocking the repository. In that location are multiple alternatives when mocking. You could mock just repositories or you could mock a whole unit of work. Usually mocking just the repositories is enough, and the complexity to abstract and mock a whole unit of measurement of piece of work is usually not needed.

Later, when we focus on the application layer, you volition see how Dependency Injection works in ASP.Internet Core and how information technology is implemented when using repositories.

In short, custom repositories permit you to examination code more easily with unit tests that are not impacted by the data tier country. If you run tests that also admission the actual database through the Entity Framework, they are non unit of measurement tests but integration tests, which are a lot slower.

If you were using DbContext straight, y'all would have to mock it or to run unit tests by using an in-memory SQL Server with predictable data for unit of measurement tests. But mocking the DbContext or controlling faux data requires more work than mocking at the repository level. Of form, you could always test the MVC controllers.

EF DbContext and IUnitOfWork instance lifetime in your IoC container

The DbContext object (exposed as an IUnitOfWork object) should be shared amidst multiple repositories inside the same HTTP request scope. For example, this is true when the functioning being executed must deal with multiple aggregates, or merely considering you are using multiple repository instances. It is also important to mention that the IUnitOfWork interface is part of your domain layer, non an EF Core type.

In lodge to practice that, the case of the DbContext object has to take its service lifetime set to ServiceLifetime.Scoped. This is the default lifetime when registering a DbContext with services.AddDbContext in your IoC container from the ConfigureServices method of the Startup.cs file in your ASP.Net Core Web API project. The following lawmaking illustrates this.

              public IServiceProvider ConfigureServices(IServiceCollection services) {     // Add framework services.     services.AddMvc(options =>     {         options.Filters.Add(typeof(HttpGlobalExceptionFilter));     }).AddControllersAsServices();      services.AddEntityFrameworkSqlServer()       .AddDbContext<OrderingContext>(options =>       {           options.UseSqlServer(Configuration["ConnectionString"],                                sqlOptions => sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().                                                                                     Assembly.GetName().Name));       },       ServiceLifetime.Scoped // Note that Scoped is the default option                              // in AddDbContext. It is shown here only for                              // pedagogic purposes.       ); }                          

The DbContext instantiation mode should not be configured as ServiceLifetime.Transient or ServiceLifetime.Singleton.

The repository instance lifetime in your IoC container

In a similar way, repository's lifetime should ordinarily exist set every bit scoped (InstancePerLifetimeScope in Autofac). It could too be transient (InstancePerDependency in Autofac), just your service will be more efficient in regards memory when using the scoped lifetime.

              // Registering a Repository in Autofac IoC container builder.RegisterType<OrderRepository>()     .Equally<IOrderRepository>()     .InstancePerLifetimeScope();                          

Using the singleton lifetime for the repository could cause you lot serious concurrency issues when your DbContext is set to scoped (InstancePerLifetimeScope) lifetime (the default lifetimes for a DBContext).

Additional resources

  • Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC Awarding
    https://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-piece of work-patterns-in-an-asp-internet-mvc-awarding

  • Jonathan Allen. Implementation Strategies for the Repository Pattern with Entity Framework, Dapper, and Chain
    https://world wide web.infoq.com/manufactures/repository-implementation-strategies

  • Cesar de la Torre. Comparing ASP.NET Core IoC container service lifetimes with Autofac IoC container example scopes
    https://devblogs.microsoft.com/cesardelatorre/comparing-asp-net-cadre-ioc-service-life-times-and-autofac-ioc-instance-scopes/

Table mapping

Table mapping identifies the table data to be queried from and saved to the database. Previously yous saw how domain entities (for example, a production or lodge domain) can be used to generate a related database schema. EF is strongly designed around the concept of conventions. Conventions address questions like "What will the name of a table be?" or "What property is the primary key?" Conventions are typically based on conventional names. For instance, it is typical for the primary fundamental to exist a property that ends with Id.

Past convention, each entity will be set up to map to a table with the same name equally the DbSet<TEntity> holding that exposes the entity on the derived context. If no DbSet<TEntity> value is provided for the given entity, the grade name is used.

Data Annotations versus Fluent API

In that location are many additional EF Cadre conventions, and nigh of them can be changed by using either data annotations or Fluent API, implemented inside the OnModelCreating method.

Data annotations must be used on the entity model classes themselves, which is a more intrusive mode from a DDD signal of view. This is because you are contaminating your model with data annotations related to the infrastructure database. On the other paw, Fluent API is a convenient way to modify most conventions and mappings within your information persistence infrastructure layer, and then the entity model will be clean and decoupled from the persistence infrastructure.

Fluent API and the OnModelCreating method

As mentioned, in social club to change conventions and mappings, yous can use the OnModelCreating method in the DbContext course.

The ordering microservice in eShopOnContainers implements explicit mapping and configuration, when needed, every bit shown in the following code.

              // At OrderingContext.cs from eShopOnContainers protected override void OnModelCreating(ModelBuilder modelBuilder) {    // ...    modelBuilder.ApplyConfiguration(new OrderEntityTypeConfiguration());    // Other entities' configuration ... }  // At OrderEntityTypeConfiguration.cs from eShopOnContainers grade OrderEntityTypeConfiguration : IEntityTypeConfiguration<Order> {     public void Configure(EntityTypeBuilder<Order> orderConfiguration)     {         orderConfiguration.ToTable("orders", OrderingContext.DEFAULT_SCHEMA);          orderConfiguration.HasKey(o => o.Id);          orderConfiguration.Ignore(b => b.DomainEvents);          orderConfiguration.Property(o => o.Id)             .UseHiLo("orderseq", OrderingContext.DEFAULT_SCHEMA);          //Address value object persisted as endemic entity type supported since EF Cadre two.0         orderConfiguration             .OwnsOne(o => o.Accost, a =>             {                 a.WithOwner();             });          orderConfiguration             .Property<int?>("_buyerId")             .UsePropertyAccessMode(PropertyAccessMode.Field)             .HasColumnName("BuyerId")             .IsRequired(simulated);          orderConfiguration             .Holding<DateTime>("_orderDate")             .UsePropertyAccessMode(PropertyAccessMode.Field)             .HasColumnName("OrderDate")             .IsRequired();          orderConfiguration             .Belongings<int>("_orderStatusId")             .UsePropertyAccessMode(PropertyAccessMode.Field)             .HasColumnName("OrderStatusId")             .IsRequired();          orderConfiguration             .Property<int?>("_paymentMethodId")             .UsePropertyAccessMode(PropertyAccessMode.Field)             .HasColumnName("PaymentMethodId")             .IsRequired(simulated);          orderConfiguration.Property<string>("Clarification").IsRequired(false);          var navigation = orderConfiguration.Metadata.FindNavigation(nameof(Order.OrderItems));          // DDD Patterns comment:         //Ready as field (New since EF 1.1) to admission the OrderItem drove property through its field         navigation.SetPropertyAccessMode(PropertyAccessMode.Field);          orderConfiguration.HasOne<PaymentMethod>()             .WithMany()             .HasForeignKey("_paymentMethodId")             .IsRequired(fake)             .OnDelete(DeleteBehavior.Restrict);          orderConfiguration.HasOne<Heir-apparent>()             .WithMany()             .IsRequired(false)             .HasForeignKey("_buyerId");          orderConfiguration.HasOne(o => o.OrderStatus)             .WithMany()             .HasForeignKey("_orderStatusId");     } }                          

You could set all the Fluent API mappings inside the same OnModelCreating method, but it's advisable to partition that code and take multiple configuration classes, ane per entity, every bit shown in the case. Peculiarly for large models, it is advisable to have separate configuration classes for configuring unlike entity types.

The code in the example shows a few explicit declarations and mapping. Yet, EF Core conventions practice many of those mappings automatically, so the bodily code you would need in your instance might be smaller.

The Hi/Lo algorithm in EF Core

An interesting aspect of code in the preceding example is that it uses the How-do-you-do/Lo algorithm as the cardinal generation strategy.

The Hullo/Lo algorithm is useful when you lot demand unique keys before committing changes. Equally a summary, the Howdy-Lo algorithm assigns unique identifiers to table rows while not depending on storing the row in the database immediately. This lets you start using the identifiers right away, equally happens with regular sequential database IDs.

The Hi/Lo algorithm describes a mechanism for getting a batch of unique IDs from a related database sequence. These IDs are safe to utilize considering the database guarantees the uniqueness, so there volition be no collisions between users. This algorithm is interesting for these reasons:

  • It does not break the Unit of Work design.

  • It gets sequence IDs in batches, to minimize round trips to the database.

  • It generates a human readable identifier, dissimilar techniques that use GUIDs.

EF Core supports HiLo with the UseHiLo method, equally shown in the preceding case.

Map fields instead of properties

With this feature, available since EF Core 1.1, you tin can directly map columns to fields. It is possible to not use backdrop in the entity class, and just to map columns from a table to fields. A common use for that would be private fields for any internal land that do not demand to be accessed from outside the entity.

You tin can do this with unmarried fields or also with collections, like a List<> field. This signal was mentioned earlier when nosotros discussed modeling the domain model classes, but here you lot can see how that mapping is performed with the PropertyAccessMode.Field configuration highlighted in the previous code.

Utilise shadow properties in EF Core, hidden at the infrastructure level

Shadow properties in EF Core are properties that practise not exist in your entity class model. The values and states of these backdrop are maintained purely in the ChangeTracker class at the infrastructure level.

Implement the Query Specification pattern

As introduced earlier in the design section, the Query Specification pattern is a Domain-Driven Pattern pattern designed every bit the place where you can put the definition of a query with optional sorting and paging logic.

The Query Specification blueprint defines a query in an object. For instance, in order to encapsulate a paged query that searches for some products you can create a PagedProduct specification that takes the necessary input parameters (pageNumber, pageSize, filter, etc.). Then, within whatever Repository method (usually a List() overload) it would accept an IQuerySpecification and run the expected query based on that specification.

An example of a generic Specification interface is the following lawmaking, which is similar to code used in the eShopOnWeb reference application.

              // GENERIC SPECIFICATION INTERFACE // https://github.com/dotnet-architecture/eShopOnWeb  public interface ISpecification<T> {     Expression<Func<T, bool>> Criteria { get; }     List<Expression<Func<T, object>>> Includes { go; }     Listing<string> IncludeStrings { get; } }                          

So, the implementation of a generic specification base course is the following.

              // GENERIC SPECIFICATION IMPLEMENTATION (Base CLASS) // https://github.com/dotnet-compages/eShopOnWeb  public abstract form BaseSpecification<T> : ISpecification<T> {     public BaseSpecification(Expression<Func<T, bool>> criteria)     {         Criteria = criteria;     }     public Expression<Func<T, bool>> Criteria { get; }      public Listing<Expression<Func<T, object>>> Includes { get; } =                                            new List<Expression<Func<T, object>>>();      public List<string> IncludeStrings { go; } = new Listing<string>();      protected virtual void AddInclude(Expression<Func<T, object>> includeExpression)     {         Includes.Add(includeExpression);     }      // string-based includes permit for including children of children     // e.g. Basket.Items.Product     protected virtual void AddInclude(string includeString)     {         IncludeStrings.Add(includeString);     } }                          

The following specification loads a single basket entity given either the basket'due south ID or the ID of the buyer to whom the basket belongs. Information technology will eagerly load the basket's Items drove.

              // SAMPLE QUERY SPECIFICATION IMPLEMENTATION  public grade BasketWithItemsSpecification : BaseSpecification<Handbasket> {     public BasketWithItemsSpecification(int basketId)         : base of operations(b => b.Id == basketId)     {         AddInclude(b => b.Items);     }      public BasketWithItemsSpecification(string buyerId)         : base of operations(b => b.BuyerId == buyerId)     {         AddInclude(b => b.Items);     } }                          

And finally, you can meet below how a generic EF Repository can use such a specification to filter and eager-load data related to a given entity type T.

              // GENERIC EF REPOSITORY WITH SPECIFICATION // https://github.com/dotnet-architecture/eShopOnWeb  public IEnumerable<T> List(ISpecification<T> spec) {     // fetch a Queryable that includes all expression-based includes     var queryableResultWithIncludes = spec.Includes         .Aggregate(_dbContext.Set<T>().AsQueryable(),             (current, include) => current.Include(include));      // modify the IQueryable to include any string-based include statements     var secondaryResult = spec.IncludeStrings         .Aggregate(queryableResultWithIncludes,             (current, include) => current.Include(include));      // return the result of the query using the specification'southward criteria expression     return secondaryResult                     .Where(spec.Criteria)                     .AsEnumerable(); }                          

In improver to encapsulating filtering logic, the specification can specify the shape of the information to be returned, including which properties to populate.

Although we don't recommend returning IQueryable from a repository, it's perfectly fine to utilize them inside the repository to build upward a ready of results. You tin see this approach used in the List method to a higher place, which uses intermediate IQueryable expressions to build up the query'south list of includes before executing the query with the specification's criteria on the last line.

Learn how the specification blueprint is applied in the eShopOnWeb sample.

Additional resources

  • Table Mapping
    https://docs.microsoft.com/ef/core/modeling/relational/tables

  • Use HiLo to generate keys with Entity Framework Core
    https://www.talkingdotnet.com/utilise-hilo-to-generate-keys-with-entity-framework-core/

  • Backing Fields
    https://docs.microsoft.com/ef/core/modeling/backing-field

  • Steve Smith. Encapsulated Collections in Entity Framework Core
    https://ardalis.com/encapsulated-collections-in-entity-framework-core

  • Shadow Properties
    https://docs.microsoft.com/ef/core/modeling/shadow-properties

  • The Specification blueprint
    https://deviq.com/specification-design/