From 999345276f437d6d18fefce6f2b8b7f15c8d191f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 21 May 2021 23:18:01 +0300 Subject: [PATCH] Initial article I will continue. --- .../POST.md | 209 ++++++++++++++++++ .../default-solution.png | Bin 0 -> 12023 bytes 2 files changed, 209 insertions(+) create mode 100644 docs/en/Community-Articles/2021-05-24-Removing-EfCore-Migrations/POST.md create mode 100644 docs/en/Community-Articles/2021-05-24-Removing-EfCore-Migrations/default-solution.png diff --git a/docs/en/Community-Articles/2021-05-24-Removing-EfCore-Migrations/POST.md b/docs/en/Community-Articles/2021-05-24-Removing-EfCore-Migrations/POST.md new file mode 100644 index 0000000000..c4159c2ac6 --- /dev/null +++ b/docs/en/Community-Articles/2021-05-24-Removing-EfCore-Migrations/POST.md @@ -0,0 +1,209 @@ +# Unifying DbContexts for EF Core / Removing the EF Core Migrations Project + +This article shows how to remove the `EntityFrameworkCore.DbMigrations` project from your solution to have a single `DbContext` for your database mappings and code first migrations. + +## Motivation + +If you create a new solution with **Entity Framework Core** as the database provider, you see two projects related to EF Core: + +![default-solution](default-solution.png) + +* `EntityFrameworkCore` project contains the actual `DbContext` of your application. It includes all the database mappings and your repository implementations. +* `EntityFrameworkCore.DbMigrations` project, on the other hand, contains another `DbContext` class that is only used to create and apply the database migrations. It contains the database mappings for all the modules you are using, so have a single, unified database schema. + +There were two main reasons we'd created such a separation; + +1. Your actual `DbContext` remains simple and focused. It only contains your own entities and doesn't contain anything related to the modules that are used by the application. +2. You can create your own classes that map to the tables of depending modules. For example, the `AppUser` entity (that is included in the downloaded solution) is mapped to `AbpUsers` table in the database, which is actually mapped to the `IdentityUser` entity of the [Identity Module](https://docs.abp.io/en/abp/latest/Modules/Identity). That means they share the same database table. `AppUser` includes less properties compared to `IdentityServer`. You only add the properties you need, not more. This also allows you to add new standard (type-safe) properties to the `AppUser` for your custom requirements as long as you carefully manage the database mappings. + +We've [documented the structure](https://docs.abp.io/en/abp/latest/Entity-Framework-Core-Migrations) in details. However, it has always been a problem for the developers since that structure makes your database mappings complicated when you re-use tables of the depended modules. Many developers are misunderstanding or making mistakes while mapping such classes, especially when they try to use these entities in relations to other entities. + +**So, [we've decided](https://github.com/abpframework/abp/issues/8776) to cancel that separation and remove the `EntityFrameworkCore.DbMigrations` project in the version 4.4. New startup solutions will come with a single `EntityFrameworkCore` project and single `DbContext` class.** + +If you want to make it in your solution with today, follow the steps in this article. + +> There is one **drawback** with the new design (everything in software development is a trade-off). We need to remove the `AppUser` entity, because EF Core can't map two classes to single table without an inheritance relation. I will cover this later in this article and provide suggestions to deal with it. + +> If you are using **ABP Commercial**, ABP Suite code generation won't work correctly with the new design. In this case, we suggest to wait for the next version. + +## All the Changes in one PR! + +I've created a new solution with v4.3, then made all the changes in a pull request, so you can see all the changes line by line. While this article will cover all, you may want to check the changes done in this PR **(TODO: LINK)** if you have problems with the implementation. + +## The Steps + +Our goal to enable database migrations in the `EntityFrameworkCore` project, remove the `EntityFrameworkCore.DbMigrations` project and revisit the code depending on that package. + +### 1) Add Microsoft.EntityFrameworkCore.Tools package to the EntityFrameworkCore project + +Add the following code into the `EntityFrameworkCore.csproj` file: + +````xml + + + runtime; build; native; contentfiles; analyzers + compile; contentFiles; build; buildMultitargeting; buildTransitive; analyzers; native + + +```` + +### 2) Create design time DbContext factory + +Create a class implementing `IDesignTimeDbContextFactory` inside the `EntityFrameworkCore` project: + +````csharp +using System.IO; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; + +namespace UnifiedContextsDemo.EntityFrameworkCore +{ + public class UnifiedContextsDemoDbContextFactory : IDesignTimeDbContextFactory + { + public UnifiedContextsDemoDbContext CreateDbContext(string[] args) + { + UnifiedContextsDemoEfCoreEntityExtensionMappings.Configure(); + + var configuration = BuildConfiguration(); + + var builder = new DbContextOptionsBuilder() + .UseSqlServer(configuration.GetConnectionString("Default")); + + return new UnifiedContextsDemoDbContext(builder.Options); + } + + private static IConfigurationRoot BuildConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../UnifiedContextsDemo.DbMigrator/")) + .AddJsonFile("appsettings.json", optional: false); + + return builder.Build(); + } + } +} +```` + +I basically copied from the `EntityFrameworkCore.DbMigrations` project, renamed and uses the actual `DbContext` of the application. + +### 3) Create DB schema migrator + +Copy `EntityFrameworkCore...DbSchemaMigrator` (`...` standard for your project name) class to the `EntityFrameworkCore` project and change the code in the `MigrateAsync` method to use the actual `DbContext` of the application. In my case, the final class is shown below: + +````csharp +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using UnifiedContextsDemo.Data; +using Volo.Abp.DependencyInjection; + +namespace UnifiedContextsDemo.EntityFrameworkCore +{ + public class EntityFrameworkCoreUnifiedContextsDemoDbSchemaMigrator + : IUnifiedContextsDemoDbSchemaMigrator, ITransientDependency + { + private readonly IServiceProvider _serviceProvider; + + public EntityFrameworkCoreUnifiedContextsDemoDbSchemaMigrator( + IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public async Task MigrateAsync() + { + /* We intentionally resolving the UnifiedContextsDemoMigrationsDbContext + * from IServiceProvider (instead of directly injecting it) + * to properly get the connection string of the current tenant in the + * current scope. + */ + + await _serviceProvider + .GetRequiredService() + .Database + .MigrateAsync(); + } + } +} +```` + +### 4) Move module configurations + +The migrations `DbContext` typically contains code lines like `builder.ConfigureXXX()` for each module you are using. We can move these lines to our actual `DbContext` in the `EntityFrameworkCore` project. Also, remove the database mappings for the `AppUser` (we will remove this entity). Optionally, you may move the database mappings code for your own entities from `...DbContextModelCreatingExtensions` class in the `OnModelCreating` method of the actual `DbContext`, and remove the static extension class. + +For the example solution, the final `DbContext` class is shown below: + +````csharp +using Microsoft.EntityFrameworkCore; +using UnifiedContextsDemo.Users; +using Volo.Abp.AuditLogging.EntityFrameworkCore; +using Volo.Abp.BackgroundJobs.EntityFrameworkCore; +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.FeatureManagement.EntityFrameworkCore; +using Volo.Abp.Identity.EntityFrameworkCore; +using Volo.Abp.IdentityServer.EntityFrameworkCore; +using Volo.Abp.PermissionManagement.EntityFrameworkCore; +using Volo.Abp.SettingManagement.EntityFrameworkCore; +using Volo.Abp.TenantManagement.EntityFrameworkCore; + +namespace UnifiedContextsDemo.EntityFrameworkCore +{ + [ConnectionStringName("Default")] + public class UnifiedContextsDemoDbContext + : AbpDbContext + { + public DbSet Users { get; set; } + + /* Add DbSet properties for your Aggregate Roots / Entities here. + * Also map them inside UnifiedContextsDemoDbContextModelCreatingExtensions.ConfigureUnifiedContextsDemo + */ + + public UnifiedContextsDemoDbContext( + DbContextOptions options) + : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.ConfigurePermissionManagement(); + builder.ConfigureSettingManagement(); + builder.ConfigureBackgroundJobs(); + builder.ConfigureAuditLogging(); + builder.ConfigureIdentity(); + builder.ConfigureIdentityServer(); + builder.ConfigureFeatureManagement(); + builder.ConfigureTenantManagement(); + + /* Configure your own tables/entities inside here */ + + //builder.Entity(b => + //{ + // b.ToTable(UnifiedContextsDemoConsts.DbTablePrefix + "YourEntities", UnifiedContextsDemoConsts.DbSchema); + // b.ConfigureByConvention(); //auto configure for the base class props + // //... + //}); + } + } +} +```` + +### 5) Remove `EntityFrameworkCore.DbMigrations` project from the solution + +Remove the `EntityFrameworkCore.DbMigrations` project from the solution and replace references given to that project by the `EntityFrameworkCore` project reference. + +Also, change usages of `...EntityFrameworkCoreDbMigrationsModule` to `...EntityFrameworkCoreModule` (`...` stands for your project name). + +In this example, I had to change references and usages in the `DbMigrator`, `Web` and `EntityFrameworkCore.Tests` projects. + +### 6) Remove AppUser Entity + +We need to remove the `AppUser` entity, because EF Core can't map two classes to single table without an inheritance relation. So, remove this class and all the usages. You can replace the usages with `IdentityUser` if you need to query users in your application code. + +TODO \ No newline at end of file diff --git a/docs/en/Community-Articles/2021-05-24-Removing-EfCore-Migrations/default-solution.png b/docs/en/Community-Articles/2021-05-24-Removing-EfCore-Migrations/default-solution.png new file mode 100644 index 0000000000000000000000000000000000000000..ee67b00dd98b7db89fca7db2c1dce8c4d741fabf GIT binary patch literal 12023 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 S(+4)R1JdFOVih8Wf&T|<^Dj35 literal 0 HcmV?d00001