-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ValueConverters not considered with Sequence generators #30749
Comments
Duplicate of #11597. |
I don't consider this a duplicate, as it specifically doesn't work with sequences, but the value converters DO work reading and writing ordinary properties. |
@Soundman32 Does the issue still happen with EF Core 7.0? If so, then please attach a small, runnable project or post a small, runnable code listing that reproduces what you are seeing so that we can investigate. |
I've started creating a small example, but when I create a migration, I get: SQL Server sequences cannot be used to generate values for the property 'Id' on entity type 'Order' because the property type is 'OrderId'. Sequences can only be used with integer properties. Can you confirm that value converters with sequences are supported? |
@Soundman32 Here's an example, and output showing the sequences, tables, and SQL. using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Microsoft.Extensions.Logging;
using (var context = new SomeDbContext())
{
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();
await context.AddRangeAsync(
new Category("Foods") { Products = { new("Marmite"), new("Toast"), new("Butter") } },
new Category("Beverages") { Products = { new("Tea"), new("Milk") } });
await context.SaveChangesAsync();
}
using (var context = new SomeDbContext())
{
var categoriesAndProducts = await context.Categories.Include(category => category.Products).ToListAsync();
Console.WriteLine();
foreach (var category in categoriesAndProducts)
{
Console.WriteLine($"Category {category.Id.Value} is '{category.Name}'");
foreach (var product in category.Products)
{
Console.WriteLine($" Product {product.Id.Value} is '{product.Name}'");
}
}
Console.WriteLine();
}
public readonly struct ProductId
{
public ProductId(int value) => Value = value;
public int Value { get; }
}
public readonly struct CategoryId
{
public CategoryId(int value) => Value = value;
public int Value { get; }
}
public class Product
{
public Product(string name) => Name = name;
public ProductId Id { get; set; }
public string Name { get; set; }
public CategoryId CategoryId { get; set; }
public Category Category { get; set; } = null!;
}
public class Category
{
public Category(string name) => Name = name;
public CategoryId Id { get; set; }
public string Name { get; set; }
public List<Product> Products { get; } = new();
}
public class SomeDbContext : DbContext
{
public DbSet<Product> Products => Set<Product>();
public DbSet<Category> Categories => Set<Category>();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(@"Data Source=(LocalDb)\MSSQLLocalDB;Database=AllTogetherNow")
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.UseKeySequences();
modelBuilder.Entity<Product>().Property(product => product.Id).ValueGeneratedOnAdd();
modelBuilder.Entity<Category>().Property(category => category.Id).ValueGeneratedOnAdd();
}
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Properties<ProductId>().HaveConversion<ProductIdConverter>();
configurationBuilder.Properties<CategoryId>().HaveConversion<CategoryIdConverter>();
}
private class ProductIdConverter : ValueConverter<ProductId, int>
{
public ProductIdConverter()
: base(v => v.Value, v => new(v))
{
}
}
private class CategoryIdConverter : ValueConverter<CategoryId, int>
{
public CategoryIdConverter()
: base(v => v.Value, v => new(v))
{
}
}
}
|
Thanks @ajcvickers My project has 100s of id types, so adding each one manually to ConfigureConventions would be difficult to manage. I've taken your example and added a custom ValueConverterSelector, removed the explicit
|
Note for triage: validation in the model building process doesn't account for converters that will be selected later. We could probably fix this, but it looks like at least the @Soundman32 You might have more look doing this using some other form of bulk configuration. |
Note from triage: we don't plan to change this behavior unless there is a case where doing so would result in a working solution. Currently, that doesn't appear to be the case. |
My code wraps primary key values in a type. I've configured the usual TypeConverter/ValueConverter and reading and writing to the database works perfectly. This project uses sequences to generate PK values, and when I configure it, I get this exception when a new record is created:
I suspect this line in:
If that line is replaced with
This will then run the sequence generator, but I then get a later InvalidCastException when it tries to assign the value to the type.
Include provider and version information
EF Core version: 6
Database provider: Oracle (but duplicated in Sql Server too)
Target framework: NET6
Operating system: Windows 10/11
IDE: Visual Studio 2022 17.5.4
The text was updated successfully, but these errors were encountered: