-
Notifications
You must be signed in to change notification settings - Fork 214
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
The specified type 'System.String' must be a non-interface reference type to be used as an entity type. #3121
Comments
EF's ToJson() is about mapping entity types, i.e. .NET types, and not primitives (like a string). So you cannot use ToJson() to map a string array. You can continue mapping the property via Note: it should still be possible to use EF's new primitive collection support to map to |
I'll go ahead and close this as the question has been answered, but if you need further clarifications don't hesitate to post back here. |
Hi @roji, I totally understand that using Postgres array type column is a better approach but since the system I am upgrading to is in production, it seems that the only way to complete the upgrade and use The other problem is that if I keep using |
So again, ToJson() does not (and will not) work with primitive collections (e.g.
Can you post a minimal, runnable code sample which worked with EF 6 but fails with EF 8? |
Probably I know what the problem I have with I use a custom Later when I try to query database EF 8 cannot call collection functions for string type. User entity public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<string> Roles { get; set; } = [];
} Custom MapJsonb helper function. public static PropertyBuilder<TProperty> MapJsonb<TProperty>(this PropertyBuilder<TProperty> propertyBuilder, JsonSerializerSettings jsonSerializerSettings = null)
{
var converter = new ValueConverter<TProperty, string>(
v => Serializer.Serialize(v, jsonSerializerSettings),
v => Serializer.Deserialize<TProperty>(v, jsonSerializerSettings));
var comparer = new ValueComparer<TProperty>(
(l, r) => Serializer.Serialize<TProperty>(l, jsonSerializerSettings) == Serializer.Serialize<TProperty>(r, jsonSerializerSettings),
v => v == null ? 0 : Serializer.Serialize<TProperty>(v, jsonSerializerSettings).GetHashCode(),
v => Serializer.Deserialize<TProperty>(Serializer.Serialize<TProperty>(v, jsonSerializerSettings), jsonSerializerSettings));
propertyBuilder.HasConversion(converter);
propertyBuilder.Metadata.SetValueConverter(converter);
propertyBuilder.Metadata.SetValueComparer(comparer);
propertyBuilder.HasColumnType("jsonb");
return propertyBuilder;
} User model definition. protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>(entity =>
{
entity.HasKey(x => x.Id);
entity.Property(x => x.Roles).MapJsonb();
});
} As a result of the custom value converter I get this. [DbContext(typeof(MyDbContext))]
partial class MyDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Ef8UpgradeApi.Entities.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Roles")
.IsRequired()
.HasColumnType("jsonb");
b.HasKey("Id");
b.ToTable("Users");
});
#pragma warning restore 612, 618
}
} However, if I don't use my converter then I get b.Property<List<string>>("Roles")
.IsRequired()
.HasColumnType("jsonb"); |
@dremlin2000 I'm a bit lost...
Why is that a problem? OTOH I'm not sure why you'd need a value converter here, given that you can map In any case, above you indicate that there was some breaking change, i.e. that some code that worked in EF6 doesn't work in EF8:
Is that really the case? If so, then can you please post a minimal, runnable code sample as I asked above? To summarize, at the moment I'm not sure exactly what you're asking or what problems you're running into... |
It is a problem because EF 8 cannot translate collection functions to SQL statement. What I mean here is that the problem is in the custom value converter rather that EF8 itself. |
As I asked several times before, can you please post a code sample of what works in EF6 but not in EF8? Querying over a value-converted List shouldn't have worked in EF6 either, as far as I'm aware. |
@roji, you can use the code snippets with As I already noted if I run the code below with EF6 then it works but it does not with EF8. await dbContext.Users
.Where(x=> x.Roles.Contains("MyRole"))
.ToArrayAsync() I get the following exception when I run it with EF8.
|
@dremlin2000 above you've posted various incomplete snippets - I've asked for a minimal, runnable code sample which I can simply run, and which shows the code working on EF 6 and not on 8. You're basically asking me to do work, and piece together a repro from your remarks and snippets, which includes quite a bit of guesswork on my side (which takes time). I've tried to do this, and have not been able to reproduce the problem: when a string[] property is value-converted to a string and mapped to jsonb, querying over it with Contains fails for me (with EF 6). Please take a look at my attempt below, and tweak it as needed to show it working in EF6 but failing in EF8. Attempted reproawait using var context = new BlogContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();
_ = await context.Users.Where(x => x.Roles.Contains("MyRole")).ToListAsync();
public class BlogContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseNpgsql("Host=localhost;Username=test;Password=test")
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.Property(u => u.Roles)
.HasColumnType("jsonb")
.HasConversion(
r => JsonSerializer.Serialize(r, JsonSerializerOptions.Default),
s => JsonSerializer.Deserialize<string[]>(s, JsonSerializerOptions.Default));
}
}
public class User
{
public int Id { get; set; }
public string[] Roles { get; set; }
} In the future, when posting an issue, please always include a minimal, runnable console program that shows what you're doing. This saves everyone a lot of time. |
I encountered the same problem. When I map |
@choby as above, we need a minimal, runnable sample to be able to help here. |
it's here : https://github.com/choby/ReproduceStringListMapToJsonbInEF8 |
@choby that looks like your whole solution - can you please post a minimal sample, ideally just a small console program that shows the problem happening? |
I think it's small and simple enough. It only contains the dependent environment and required code to reproduce the problem. |
@choby there are 7 projects in that solution - I don't even know where to look for your problematic query... you're asking me to spend my time to understand your solution and narrow it down - can you help out by just distilling the problem to a minimal console program? I have tons of issues to investigate from users, and this adds more work to my plate. |
Hi there,
I am upgrading a .net project from EF Core 6 to version 8 and got a breaking change related to JSON column types.
In the current version I have been using
for entity configuration which translates Roles column to JSON.
However, now if I try to use
then I get a following exception
Does it mean that EF 8 does not support JSON string arrays?
The text was updated successfully, but these errors were encountered: