在InMemoryProvider中为ValueGenerator配置自动递增的十进制字段

Lie*_*ero 2 c# entity-framework entity-framework-core

问题

在我的应用程序中,我使用SQL Server:

services.AddDbContext<MyDbContext>(options => options.UseSqlServer(connectionString));
Run Code Online (Sandbox Code Playgroud)

但是当我在单元测试中使用InMemoryProvider时:

[TestInitialize]
public void Initialize()
{
    Services = new ServiceCollection();
    Services.AddDbContext<MyDbContext>(options => options.UseInMemoryDatabase("MyDbContext"), ServiceLifetime.Transient);
    ServiceProvider = Services.BuildServiceProvider();
}
Run Code Online (Sandbox Code Playgroud)

我越来越:

System.NotSupportedException:实体类型“人”上的“人ID”没有设置值,并且没有值生成器可用于“十进制”类型的属性。在添加实体之前为属性设置一个值,或者为“十进制”类型的属性配置一个值生成器。

在哪里为PersonId属性配置自定义ValueGenerator,以便仅在使用InMemoryProvider的测试项目中使用它?

是)我有的

我有MS SQL Server数据库和一个带有类型为numeric(10,0)的自动增量标识列的表。

CREATE TABLE [dbo].[People](
    [Person ID] [numeric](10, 0) IDENTITY(100000000,1) NOT NULL,
 CONSTRAINT [PK_People] PRIMARY KEY CLUSTERED 
Run Code Online (Sandbox Code Playgroud)

和EF代码

[Column("Person ID", TypeName = "numeric(10, 0)")]
public decimal PersonId { get; set; }

public class MyDbContext
{
    public OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Person>(entity =>
        {
           entity.Property(e => e.PersonId).ValueGeneratedOnAdd();
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

我的应用程序使用SQL Server,但我想设置EF以在测试中使用InMemoryProvider。Custom ValueGenerator应该仅在测试中使用

Iva*_*oev 5

作为EFC 2.0的,所述内存数据库提供商支持值生成只对整数类型- ,bytesbyteshortushort,,int 和。 uintlongulong

因此,解决此问题的一种方法是将PK属性映射到适合所需范围的某些类型,例如intlong等等。

但是,如果您坚持使用decimal,那么可以使用以下流利的设置:

using Microsoft.EntityFrameworkCore.ValueGeneration.Internal;

// ...

modelBuilder.Entity<Person>(entity =>
{
    var pb = entity.Property(e => e.PersonId).ValueGeneratedOnAdd();
    if (Database.IsInMemory())
        pb.HasValueGenerator<InMemoryIntegerValueGenerator<decimal>>();
});
Run Code Online (Sandbox Code Playgroud)

或者,如果您有更多类似的属性,则可以保留现有配置,并在OnModelCreating覆盖的末尾添加以下内容:

using Microsoft.EntityFrameworkCore.Metadata
using Microsoft.EntityFrameworkCore.ValueGeneration.Internal;

// ...

if (Database.IsInMemory())
{
    var autoGenDecimalProperies = modelBuilder.Model.GetEntityTypes()
        .Select(t => t.FindPrimaryKey())
        .Where(pk => pk != null)
        .SelectMany(pk => pk.Properties)
        .Where(p => p.ClrType == typeof(decimal) && p.ValueGenerated != ValueGenerated.Never);

    foreach (var property in autoGenDecimalProperies)
        property.SetValueGeneratorFactory((p, t) => new InMemoryIntegerValueGenerator<decimal>());
}
Run Code Online (Sandbox Code Playgroud)