Bug description
I have ended up with a rather complex/annoying set of relationships between 2 types (See the attached code).
I have tried reducing it further, but that doesn't seem possible without causing the issue to disappear.
If I explicitly set the ProviderClrType annotation on the WorkspaceId property, when the issue stops occuring.
If I don't use the multi-column (foreign)keys, then the issue does not occur.
The issue still occurs when specifying all the columns to be int rather than Guid. (Setting ProviderClrType still works to 'fix' the issue.)
The issue seems to occur on all versions between EFCore 8.0.0 up to and including EFCore version 11.0.0-preview.3.26207.106. (I only have dotnet 11 preview 3 available in my package manager, not preview 4 yet, so I have not tested preview 4).
The issue seems to occur no matter the database provider. I have tested "InMemory", "Sqlite" and "Postgres".
Your code
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
await using var context = new ReproContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();
public class ReproContext : DbContext
{
public DbSet<ChatMessage> Messages => Set<ChatMessage>();
public DbSet<ChatSession> Sessions => Set<ChatSession>();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
optionsBuilder
.UseInMemoryDatabase("Repro")
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ChatSession>(session =>
{
// session.Property(s => s.WorkspaceId).HasAnnotation(CoreAnnotationNames.ProviderClrType, typeof(Guid));
session.HasKey(s => new { s.WorkspaceId, s.Id });
session
.HasOne(s => s.ParentMessage)
.WithMany(m => m.ChildChatSessions)
.HasForeignKey(s => new { s.WorkspaceId, s.ParentMessageId })
.HasPrincipalKey(m => new {m.WorkspaceId, m.Id})
.IsRequired(false)
.OnDelete(DeleteBehavior.Restrict);
session
.HasOne(s => s.ParentSession)
.WithMany(s => s.ChildSessions)
.HasForeignKey(s => new { s.WorkspaceId, s.ParentSessionId })
.HasPrincipalKey(s => new {s.WorkspaceId, s.Id})
.IsRequired(false)
.OnDelete(DeleteBehavior.Restrict);
});
modelBuilder.Entity<ChatMessage>(message =>
{
// message.Property(s => s.WorkspaceId).HasAnnotation(CoreAnnotationNames.ProviderClrType, typeof(Guid));
message.HasKey(m => new { m.WorkspaceId, m.Id });
message
.HasOne(m => m.Session)
.WithMany(s => s.Messages)
.HasForeignKey(m => new { m.WorkspaceId, m.SessionId })
.HasPrincipalKey(s => new { s.WorkspaceId, s.Id })
.OnDelete(DeleteBehavior.Restrict);
message
.HasOne(m => m.PreviousMessage)
.WithMany(m => m.FollowingMessages)
.HasForeignKey(m => new { m.WorkspaceId, m.PreviousMessageId })
.HasPrincipalKey(m => new { m.WorkspaceId, m.Id })
.OnDelete(DeleteBehavior.Restrict);
});
}
}
public class ChatMessage
{
public Guid WorkspaceId { get; set; }
public Guid Id { get; set; }
public Guid SessionId { get; set; }
public ChatSession Session { get; set; } = null!;
public Guid? PreviousMessageId { get; set; }
public ChatMessage? PreviousMessage { get; set; }
public ICollection<ChatMessage> FollowingMessages { get; set; } = null!;
public ICollection<ChatSession> ChildChatSessions { get; set; } = null!;
}
public class ChatSession
{
public Guid WorkspaceId { get; set; }
public Guid Id { get; set; }
public Guid? ParentMessageId { get; set; }
public ChatMessage? ParentMessage { get; set; }
public Guid? ParentSessionId { get; set; }
public ChatSession? ParentSession { get; set; }
public ICollection<ChatSession> ChildSessions { get; set; } = null!;
public ICollection<ChatMessage> Messages { get; set; } = [];
}
Stack traces
Unhandled exception. System.InvalidOperationException: A relationship cycle involving the property 'ChatMessage.WorkspaceId' was detected. This prevents Entity Framework from determining the correct configuration. Review the foreign keys defined on the property and the corresponding principal property and either remove one of them or specify 'ValueConverter' explicitly on one of the properties.
at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.GetConversion(Boolean throwOnValueConverterConflict, Boolean throwOnProviderClrTypeConflict)
at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.GetValueConverter()
at Microsoft.EntityFrameworkCore.Storage.TypeMappingInfo..ctor(IReadOnlyList`1 principals, Nullable`1 fallbackUnicode, Nullable`1 fallbackSize, Nullable`1 fallbackPrecision, Nullable`1 fallbackScale)
at Microsoft.EntityFrameworkCore.Storage.TypeMappingSource.FindMapping(IProperty property)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.ElementMappingConvention.<ProcessModelFinalizing>g__Validate|4_0(IConventionTypeBase typeBase)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.ElementMappingConvention.ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext`1 context)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelFinalizing(IConventionModelBuilder modelBuilder)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelFinalizing(IConventionModelBuilder modelBuilder)
at Microsoft.EntityFrameworkCore.Metadata.Internal.Model.FinalizeModel()
at Microsoft.EntityFrameworkCore.Infrastructure.ModelRuntimeInitializer.Initialize(IModel model, Boolean designTime, IDiagnosticsLogger`1 validationLogger)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, ModelCreationDependencies modelCreationDependencies, Boolean designTime)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, ModelCreationDependencies modelCreationDependencies, Boolean designTime)
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel(Boolean designTime)
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__8_4(IServiceProvider p)
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(ServiceIdentifier serviceIdentifier, 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()
at Microsoft.EntityFrameworkCore.DbContext.get_ContextServices()
at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<System.IServiceProvider>.get_Instance()
at Microsoft.EntityFrameworkCore.Infrastructure.Internal.InfrastructureExtensions.GetService(IInfrastructure`1 accessor, Type serviceType)
at Microsoft.EntityFrameworkCore.Infrastructure.Internal.InfrastructureExtensions.GetService[TService](IInfrastructure`1 accessor)
at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.get_Dependencies()
at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.EnsureDeletedAsync(CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in /home/rasmus/projects/EfCoreReproduction/EfCoreReproduction/Program.cs:line 9
at Program.<Main>$(String[] args) in /home/rasmus/projects/EfCoreReproduction/EfCoreReproduction/Program.cs:line 10
at Program.<Main>(String[] args)
Verbose output
EF Core version
10.0.8
Database provider
Microsoft.EntityFrameworkCore.InMemory, Microsoft.EntityFrameworkCore.Sqlite, Npgsql.EntityFrameworkCore.PostgreSQL
Target framework
.net 10
Operating system
Nixos
IDE
Commandline
Bug description
I have ended up with a rather complex/annoying set of relationships between 2 types (See the attached code).
I have tried reducing it further, but that doesn't seem possible without causing the issue to disappear.
If I explicitly set the
ProviderClrTypeannotation on theWorkspaceIdproperty, when the issue stops occuring.If I don't use the multi-column (foreign)keys, then the issue does not occur.
The issue still occurs when specifying all the columns to be
intrather than Guid. (Setting ProviderClrType still works to 'fix' the issue.)The issue seems to occur on all versions between EFCore 8.0.0 up to and including EFCore version
11.0.0-preview.3.26207.106. (I only have dotnet 11 preview 3 available in my package manager, not preview 4 yet, so I have not tested preview 4).The issue seems to occur no matter the database provider. I have tested "InMemory", "Sqlite" and "Postgres".
Your code
Stack traces
Verbose output
EF Core version
10.0.8
Database provider
Microsoft.EntityFrameworkCore.InMemory, Microsoft.EntityFrameworkCore.Sqlite, Npgsql.EntityFrameworkCore.PostgreSQL
Target framework
.net 10
Operating system
Nixos
IDE
Commandline