In my app, I use SQL Server:
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(connectionString));
but when I use InMemoryProvider in my unit tests:
[TestInitialize]
public void Initialize()
{
Services = new ServiceCollection();
Services.AddDbContext<MyDbContext>(options => options.UseInMemoryDatabase("MyDbContext"), ServiceLifetime.Transient);
ServiceProvider = Services.BuildServiceProvider();
}
I'm getting:
System.NotSupportedException: The 'PersonId' on entity type 'Person' does not have a value set and no value generator is available for properties of type 'decimal'. Either set a value for the property before adding the entity or configure a value generator for properties of type 'decimal'.
Where do I configure custom ValueGenerator for PersonId property, so that it will be used only in my test project where I use InMemoryProvider?
I have MS SQL Server Database and a table with autoincrement identity column of type numeric(10, 0).
CREATE TABLE [dbo].[People](
[Person ID] [numeric](10, 0) IDENTITY(100000000,1) NOT NULL,
CONSTRAINT [PK_People] PRIMARY KEY CLUSTERED
and the EF Code
[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();
});
}
}
My application uses SQL Server, but I want to setup EF to use InMemoryProvider in tests. The Custom ValueGenerator should be used only in tests
As of EFC 2.0, the In-Memory Database Provider supports value generation only for integer types - byte
, sbyte
, short
, ushort
, int
, uint
, long
and ulong
.
So one way to resolve the issue is to map the PK property to some of these types which fits in the desired range, like int
, long
etc.
But if you insist using decimal
, then you can use the following fluent setup:
using Microsoft.EntityFrameworkCore.ValueGeneration.Internal;
// ...
modelBuilder.Entity<Person>(entity =>
{
var pb = entity.Property(e => e.PersonId).ValueGeneratedOnAdd();
if (Database.IsInMemory())
pb.HasValueGenerator<InMemoryIntegerValueGenerator<decimal>>();
});
or if you have more properties like this, you could keep the existing configuration and add the following at the end of your OnModelCreating
override:
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>());
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With