Skip to content
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

Can't map Dictionary<string,object> (e.g. to JSON) since it's detected as a property bag #26903

Open
roji opened this issue Dec 6, 2021 · 11 comments
Assignees
Labels
area-model-building customer-reported punted-for-7.0 Originally planned for the EF Core 7.0 (EF7) release, but moved out due to resource constraints. type-bug
Milestone

Comments

@roji
Copy link
Member

roji commented Dec 6, 2021

With (PostgreSQL) native JSON support, it's useful to map Dictionary directly to JSON columns. This works e.g. for Dictionary<string,string>, but fails for Dictionary<string,object> since the property is detected as a property bag:

public class Blog
{
    public int Id { get; set; }

    [Column(TypeName = "jsonb")]
    public Dictionary<string, object> JsonProperty { get; set; }
}

The exception:

The navigation 'Blog.JsonProperty' must be configured in 'OnModelCreating' with an explicit name for the target shared-type entity type, or excluded by calling 'EntityTypeBuilder.Ignore'.

We may want to stop detecting property bags if column has an explicit store type. Following on how type converters work, this can be worked around by configuring the property with .Metadata.SetProviderClrType(null).

/cc @AndriySvyryd

Originally filed by @ColinZeb in npgsql/efcore.pg#2134

@ajcvickers
Copy link
Member

Note from triage: @roji to give this a try on current main.

@roji
Copy link
Member Author

roji commented Dec 10, 2021

Confirmed that the exception still occurs on latest main (590c783):

Unhandled exception. System.InvalidOperationException: The navigation 'Blog.JsonProperty' must be configured in 'OnModelCreating' with an explicit name for the target shared-type entity type, or excluded by calling 'EntityTypeBuilder.Ignore'.
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidatePropertyMapping(IModel model, IDiagnosticsLogger`1 logger) in /home/roji/projects/efcore/src/EFCore/Infrastructure/ModelValidator.cs:line 244
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger) in /home/roji/projects/efcore/src/EFCore/Infrastructure/ModelValidator.cs:line 48
   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger) in /home/roji/projects/efcore/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:line 49
   at Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.NpgsqlModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger) in /home/roji/projects/efcore.pg/src/EFCore.PG/Infrastructure/NpgsqlModelValidator.cs:line 27
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelRuntimeInitializer.Initialize(IModel model, Boolean designTime, IDiagnosticsLogger`1 validationLogger) in /home/roji/projects/efcore/src/EFCore/Infrastructure/ModelRuntimeInitializer.cs:line 84
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, ModelCreationDependencies modelCreationDependencies, Boolean designTime) in /home/roji/projects/efcore/src/EFCore/Infrastructure/ModelSource.cs:line 72
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel(Boolean designTime) in /home/roji/projects/efcore/src/EFCore/Internal/DbContextServices.cs:line 86
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model() in /home/roji/projects/efcore/src/EFCore/Internal/DbContextServices.cs:line 113
   at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__8_4(IServiceProvider p) in /home/roji/projects/efcore/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs:line 276
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass2_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies() in /home/roji/projects/efcore/src/EFCore/DbContext.cs:line 430
   at Microsoft.EntityFrameworkCore.DbContext.get_ContextServices() in /home/roji/projects/efcore/src/EFCore/DbContext.cs:line 412
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider() in /home/roji/projects/efcore/src/EFCore/DbContext.cs:line 366
   at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<System.IServiceProvider>.get_Instance() in /home/roji/projects/efcore/src/EFCore/DbContext.cs:line 2115
   at Microsoft.EntityFrameworkCore.Infrastructure.Internal.InfrastructureExtensions.GetService[TService](IInfrastructure`1 accessor) in /home/roji/projects/efcore/src/EFCore/Infrastructure/Internal/InfrastructureExtensions.cs:line 25
   at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor) in /home/roji/projects/efcore/src/EFCore/Infrastructure/AccessorExtensions.cs:line 42
   at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.get_Dependencies() in /home/roji/projects/efcore/src/EFCore/Infrastructure/DatabaseFacade.cs:line 31
   at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.EnsureDeletedAsync(CancellationToken cancellationToken) in /home/roji/projects/efcore/src/EFCore/Infrastructure/DatabaseFacade.cs:line 193
   at Program.<Main>$(String[] args) in /home/roji/projects/test/Program.cs:line 11
   at Program.<Main>$(String[] args) in /home/roji/projects/test/Program.cs:line 12
   at Program.<Main>(String[] args)

@ajcvickers ajcvickers added this to the 7.0.0 milestone Dec 10, 2021
@ajcvickers ajcvickers added propose-punt punted-for-7.0 Originally planned for the EF Core 7.0 (EF7) release, but moved out due to resource constraints. and removed propose-punt labels Jul 6, 2022
@ajcvickers ajcvickers modified the milestones: 7.0.0, Backlog Jul 7, 2022
@Mfolmer

This comment was marked as resolved.

@roji

This comment was marked as resolved.

@Mfolmer

This comment was marked as resolved.

@SomePrettyUsername

This comment was marked as off-topic.

@roji roji changed the title Can't map Dictionary<string,object> to JSON Can't map Dictionary<string,object> (e.g. to JSON) since it's detected as a property bag Dec 11, 2022
@roji

This comment was marked as off-topic.

@AndriySvyryd
Copy link
Member

Related to #28871

@Dean-ZhenYao-Wang
Copy link

With (PostgreSQL) native JSON support, it's useful to map Dictionary directly to JSON columns. This works e.g. for Dictionary<string,string>, but fails for Dictionary<string,object> since the property is detected as a property bag:

public class Blog
{
    public int Id { get; set; }

    [Column(TypeName = "jsonb")]
    public Dictionary<string, object> JsonProperty { get; set; }
}

The exception:

The navigation 'Blog.JsonProperty' must be configured in 'OnModelCreating' with an explicit name for the target shared-type entity type, or excluded by calling 'EntityTypeBuilder.Ignore'.

We may want to stop detecting property bags if column has an explicit store type. Following on how type converters work, this can be worked around by configuring the property with .Metadata.SetProviderClrType(null).

/cc @AndriySvyryd

Originally filed by @ColinZeb in npgsql/efcore.pg#2134
This problem can be solved in the following way:
[NotMapped] public Dictionary<string, object> Contents { get { return string.IsNullOrWhiteSpace(ContentsJson) ? new Dictionary<string, object>() : System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(ContentsJson); } set { ContentsJson = JsonSerializer.Serialize(value, Extention.jsonSerializerOptions); } } [JsonIgnore] public string? ContentsJson { get; set; }

@onionhammer
Copy link

Any chance this is coming in EF core 9?

@AndriySvyryd
Copy link
Member

This issue is in the Backlog milestone. This means that it is not planned for the next release. We will re-assess the backlog following this release and consider this item at that time. However, keep in mind that there are many other high priority features with which it will be competing for resources, see Release planning process. Make sure to vote (👍) for this issue if it is important to you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-model-building customer-reported punted-for-7.0 Originally planned for the EF Core 7.0 (EF7) release, but moved out due to resource constraints. type-bug
Projects
None yet
Development

No branches or pull requests

7 participants