EF.Core use the same model to map to multiple tables

In EntityFramework Core, we can use to configure the mapping model attributes or Fluent API. One day, I met a new demand, the system will generate a large amount of data every day, create a new table to store data every day. For example, a database as follows:

All tables have the same structure. So, how to change the map in order to avoid creating multiple models do?

In this article, I'll show you how to change the mapping to handle this situation. You can also use this method to extend more usage.

Creating .NET Core 3.1 project

Now, we can use the .NET Core 3.1, it is a LTS version of .NET Core in the future you can easily upgrade it to .NET 5.

Assuming you have installed the latest .NET Core SDK on your computer. If not, you can download from https://dotnet.microsoft.com/download. You can then use dotnet CLIto create a project. For this example, I'll use .NET Core 3.1.

Let's create a file called DynamicModelDemonew .NET Core Console project:

dotnet new console --name DynamicModelDemo

Then create a new solution with the following command:

dotnet new sln --name DynamicModelDemo

Next, use the following command to add the item you just created to the solution:

dotnet sln add "DynamicModelDemo/DynamicModelDemo.csproj"

Then you can open the solution with Visual Studio.

Create a model

The model is very simple. Add a project named in the ConfigurableEntity.csnew file:

using System;

namespaceDynamicModelDemo
{
    publicclassConfigurableEntity
    {
        publicint Id { get; set; }
        publicstring Title { get; set; }
        publicstring Content { get; set; }
        public DateTime CreateDateTime { get; set; }
    }
}

We will use the CreateDateTimeproperty to determine which model should be mapped to tables.

Add EntityFramework Core

Navigate to the project directory and add the required EF.Core packages using the following command:

dotnet add package Microsoft.EntityFrameworkCore.SqlSever
dotnet add package Microsoft.EntityFrameworkCore.Design

If you have not installed ef tool, run the following command to install:

dotnet tool install --global dotnet-ef

This allows you to create or migrate applications to update the database by using dotnet ef migration tool.

Creating DbContext

Add to a project called DynamicContext.csthe new class file. Shown as follows:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using System;

namespaceDynamicModelDemo
{
    publicclassDynamicContext : DbContext
    {
        public DbSet<ConfigurableEntity> Entities { get; set; }

        #region OnConfiguring
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            => optionsBuilder
                .UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=DynamicContext;Trusted_Connection=True;");
        #endregion

        #region OnModelCreating
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<ConfigurableEntity>(b =>
            {
                b.HasKey(p => p.Id);
            });
        }
        #endregion
    }
}

At present, this is only the basic configuration of EF.Core. It uses the default mapping, which means that the model will be mapped to the name Entitiesof the table. So, if we want based on their CreateDateTimemap the model to a different table properties, how to do it?

You may know that we can use ToTable()to change the name of the table method, but how to OnModelCreatingchange the table names of all model approach it? When the EF model, only once OnModelCreating. So in this way it can not be achieved.

In this case, we need IModelCacheKeyFactoryto change the default mapping, through which we can customize the model caching mechanism so that EF can create different models according to their attributes.

IModelCacheKeyFactoryWhat is?

This is Microsoft's official documentation explains:

EF uses IModelCacheKeyFactory to generate cache keys for models.

By default, EF is assumed that for any given type of context model is the same. But for our program, the model will be different as it is mapped to a different table. Therefore, we need to replace our implementation IModelCacheKeyFactoryservices, the implementation would be more cache key to map the model to the correct table.

Note that this procedure typically provides the interface and other extended by the database, the application code is generally not used. But for our scenario, this is a viable approach.

achieveIModelCacheKeyFactory

We need CreateDateTimeto distinguish between tables. In DynamicContextadding a property class:

public DateTime CreateDateTime { get; set; }

Add a project named in the DynamicModelCacheKeyFactory.csnew class file. Code as follows:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

namespaceDynamicModelDemo
{
    publicclassDynamicModelCacheKeyFactory : IModelCacheKeyFactory
    {
        public object Create(DbContext context)
            => context is DynamicContext dynamicContext
                ? (context.GetType(), dynamicContext.CreateDateTime)
                : (object)context.GetType();
    }
}

While creating a model cache key, this implementation will be considered CreateDateTimeproperty.

applicationIModelCacheKeyFactory

Next, we can register a new context IModelCacheKeyFactory:

#region OnConfiguring
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            => optionsBuilder
                .UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=DynamicContext;Trusted_Connection=True;")
                .ReplaceService<IModelCacheKeyFactory, DynamicModelCacheKeyFactory>();
#endregion

So that we can in OnModelCreatingeach mapping table name of the method:

#region OnModelCreating
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
      modelBuilder.Entity<ConfigurableEntity>(b =>
            {
                b.ToTable(CreateDateTime.ToString("yyyyMMdd"));
                b.HasKey(p => p.Id);
            });
}
#endregion

CreateDateTimeFrom DynamicContextthe property.

We can create DynamicContexta specified time CreateDateTimeattributes:

var context = new DynamicContext { CreateDateTime = datetime };

If datetimeis 2020/03/27, then contextthe model will be mapped to the name 20200327of the table.

Create a database

Before verification code, we need to create the database. However, EF migration is not the best solution for this situation, because as time goes by, the system will generate more tables. We just use it to create some sample to verify the mapping table. Indeed, the system should have another dynamically generated tables daily basis.

Run the following command to create the first migration:

dotnet ef migrations add InitialCreate

You will see Migrationsgenerates two files in the folder. Open the xxx_InitialCreate.csfile, and update Up method with the following code:

protected override void Up(MigrationBuilder migrationBuilder)
{
      for (int i = 0; i < 30; i++)
      {
           var index = i;
           migrationBuilder.CreateTable(
               name: DateTime.Now.AddDays(-index).ToString("yyyyMMdd"),
               columns: table => new
               {
                    Id = table.Column<int>(nullable: false)
                            .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(nullable: true),
                    Content = table.Column<string>(nullable: true),
                    CreateDateTime = table.Column<DateTime>(nullable: false)
               },
               constraints: table =>
               {
                    table.PrimaryKey($"PK_{DateTime.Now.AddDays(-index):yyyyMMdd}", x => x.Id);
               });
        }
    }

Changes is to ensure that the database can have enough tables for testing. Please note that we should not use this approach in a production environment .

Next, we can use this command to create and update the database:

dotnet ef database update

You will see that it generates the last 30 days of the table in the database.

Verify Mapping

Now verify the new map. By updating the code Program.csin Mainthe method:

static void Main(string[] args)
{
    DateTime datetime1 = DateTime.Now;
    using (var context = new DynamicContext { CreateDateTime = datetime1 })
    {
        context.Entities.Add(new ConfigurableEntity { Title = "Great News One", Content = $"Hello World! I am the news of {datetime1}", CreateDateTime = datetime1 });
        context.SaveChanges();
    }
    DateTime datetime2 = DateTime.Now.AddDays(-1);
    using (var context = new DynamicContext { CreateDateTime = datetime2 })
    {
        context.Entities.Add(new ConfigurableEntity { Title = "Great News Two", Content = $"Hello World! I am the news of {datetime2}", CreateDateTime = datetime2 });
        context.SaveChanges();
    }

    using (var context = new DynamicContext { CreateDateTime = datetime1 })
    {
        var entity = context.Entities.Single();
          // Writes news of today
        Console.WriteLine($"{entity.Title} {entity.Content} {entity.CreateDateTime}");
    }

    using (var context = new DynamicContext { CreateDateTime = datetime2 })
    {
        var entity = context.Entities.Single();
        // Writes news of yesterday
        Console.WriteLine($"{entity.Title} {entity.Content} {entity.CreateDateTime}");
    }
}

You will see the following output:

Now, we can transfer CreateDateTimeproperty to use the same DbContextto represent the different models.

Released six original articles · won praise 19 · Views 100,000 +

Guess you like

Origin blog.csdn.net/lixiaoer757/article/details/105303868