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

Many-to-Many relationship (with itself) #6052

Closed
ariksman opened this issue Jul 12, 2016 · 4 comments
Closed

Many-to-Many relationship (with itself) #6052

ariksman opened this issue Jul 12, 2016 · 4 comments

Comments

@ariksman
Copy link

@ariksman ariksman commented Jul 12, 2016

Steps to reproduce

Fluent api configurations:

modelBuilder.Entity<SocketSocket>()
                .HasKey(ss => new { ss.SocketToId, ss.SocketFromId});
            modelBuilder.Entity<SocketSocket>()
                .HasOne(ss => ss.SocketTo)
                .WithMany(s => s.ConnectedToSockets)
                .HasForeignKey(ss => ss.SocketToId);
            modelBuilder.Entity<SocketSocket>()
                .HasOne(ss => ss.SocketFrom)
                .WithMany(s => s.ConnectedToSockets)
                .HasForeignKey(ss => ss.SocketFromId);

Entities:

public class Socket : ModelBase
{
    // many-to-many relation, with help of intermediate table
    public ObservableCollection<SocketSocket> ConnectedToSockets { get; set; } = new ObservableCollection<SocketSocket>();
}
public class SocketSocket : ModelBase
    {
        public int SocketSocketId { get; set; }

        // intermediate table between Socket1 and SocketSocket
        public int SocketToId { get; set; }
        public Socket SocketTo { get; set; }

        // intermediate table between Socket2 and Socketsocket
        public int SocketFromId { get; set; }
        public Socket SocketFrom { get; set; }
    }

The issue

Many to many relationship modelling with itself, socket <-> socket
The fluent api configuration mentioned above worked fine on RC2, but after updating I am receiving the following exception.

Exception message:
System.InvalidOperationException was unhandled by user code
  HResult=-2146233079
  Message=Cannot create a relationship between 'Socket.ConnectedToSockets' and 'SocketSocket.SocketFrom', because there already is a relationship between 'Socket.ConnectedToSockets' and 'SocketSocket.SocketTo'. Navigation properties can only participate in a single relationship.
  Source=Microsoft.EntityFrameworkCore
  StackTrace:
       at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalRelationshipBuilder.GetOrCreateRelationshipBuilder(EntityType principalEntityType, EntityType dependentEntityType, Nullable`1 navigationToPrincipal, Nullable`1 navigationToDependent, IReadOnlyList`1 dependentProperties, IReadOnlyList`1 principalProperties, Nullable`1 isRequired, Nullable`1 principalEndConfigurationSource, Nullable`1 configurationSource, List`1 removedNavigations, List`1 removedForeignKeys, List`1 addedForeignKeys, Boolean& existingRelationshipInverted)
       at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalRelationshipBuilder.ReplaceForeignKey(InternalEntityTypeBuilder principalEntityTypeBuilder, InternalEntityTypeBuilder dependentEntityTypeBuilder, Nullable`1 navigationToPrincipal, Nullable`1 navigationToDependent, IReadOnlyList`1 dependentProperties, IReadOnlyList`1 principalProperties, Nullable`1 isUnique, Nullable`1 isRequired, Nullable`1 deleteBehavior, Boolean oldRelationshipInverted, Nullable`1 principalEndConfigurationSource, Nullable`1 configurationSource, Boolean runConventions)
       at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalRelationshipBuilder.ReplaceForeignKey(Nullable`1 configurationSource, InternalEntityTypeBuilder principalEntityTypeBuilder, InternalEntityTypeBuilder dependentEntityTypeBuilder, Nullable`1 navigationToPrincipal, Nullable`1 navigationToDependent, IReadOnlyList`1 dependentProperties, IReadOnlyList`1 principalProperties, Nullable`1 isUnique, Nullable`1 isRequired, Nullable`1 deleteBehavior, Nullable`1 principalEndConfigurationSource, Boolean oldRelationshipInverted, Boolean runConventions)
       at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalRelationshipBuilder.Navigations(Nullable`1 navigationToPrincipal, Nullable`1 navigationToDependent, Nullable`1 configurationSource, Boolean runConventions)
       at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalRelationshipBuilder.Navigations(Nullable`1 navigationToPrincipal, Nullable`1 navigationToDependent, Nullable`1 configurationSource)
       at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalRelationshipBuilder.PrincipalToDependent(PropertyInfo property, ConfigurationSource configurationSource)
       at Microsoft.EntityFrameworkCore.Metadata.Builders.ReferenceNavigationBuilder.WithManyBuilder(PropertyIdentity collection)
       at Microsoft.EntityFrameworkCore.Metadata.Builders.ReferenceNavigationBuilder.WithManyBuilder(PropertyInfo navigationProperty)
       at Microsoft.EntityFrameworkCore.Metadata.Builders.ReferenceNavigationBuilder`2.WithMany(Expression`1 navigationExpression)
       at FxPlugin.Persistence.DatabaseContext.OnModelCreating(ModelBuilder modelBuilder) in C:\Users\ariksman\Documents\Visual Studio 2015\Projects\FxEditorDatabaseStructure\FxEditorDatabaseStructure\Persistence\DatabaseContext.cs:line 137
       at Microsoft.EntityFrameworkCore.Infrastructure.ModelCustomizer.Customize(ModelBuilder modelBuilder, DbContext dbContext)
       at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
       at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.<>c__DisplayClass14_0.<GetModel>b__0(Object k)
       at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
       at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
       at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
       at Microsoft.EntityFrameworkCore.Internal.LazyRef`1.get_Value()
       at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
       at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServiceCollectionExtensions.<>c.<AddEntityFramework>b__0_4(IServiceProvider p)
       at Microsoft.Extensions.DependencyInjection.ServiceLookup.FactoryService.Invoke(ServiceProvider provider)
       at Microsoft.Extensions.DependencyInjection.ServiceProvider.ScopedCallSite.Invoke(ServiceProvider provider)
       at Microsoft.Extensions.DependencyInjection.ServiceLookup.ConstructorCallSite.Invoke(ServiceProvider provider)
       at Microsoft.Extensions.DependencyInjection.ServiceProvider.ScopedCallSite.Invoke(ServiceProvider provider)
       at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass12_0.<RealizeService>b__0(ServiceProvider provider)
       at Microsoft.Extensions.DependencyInjection.ServiceProvider.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.Storage.DatabaseProviderServices.GetService[TService]()
       at Microsoft.EntityFrameworkCore.Storage.Internal.SqliteDatabaseProviderServices.get_Creator()
       at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServiceCollectionExtensions.<>c.<AddEntityFramework>b__0_11(IServiceProvider p)
       at Microsoft.Extensions.DependencyInjection.ServiceLookup.FactoryService.Invoke(ServiceProvider provider)
       at Microsoft.Extensions.DependencyInjection.ServiceProvider.ScopedCallSite.Invoke(ServiceProvider provider)
       at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass12_0.<RealizeService>b__0(ServiceProvider provider)
       at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
       at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
       at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
       at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.EnsureCreated()
       at FxPlugin.Persistence.UnitOfWork.Migrate() in C:\Users\user\Documents\Visual Studio 2015\Projects\FxEditorDatabaseStructure\FxEditorDatabaseStructure\Persistence\UnitOfWork.cs:line 111
       at FxPlugin.Persistence.UnitOfWork..ctor(DbContextOptions contextOptions) in C:\Users\user\Documents\Visual Studio 2015\Projects\FxEditorDatabaseStructure\FxEditorDatabaseStructure\Persistence\UnitOfWork.cs:line 38
       at lambda_method(Closure , IBuilderContext )
       at Microsoft.Practices.ObjectBuilder2.DynamicBuildPlanGenerationContext.<>c__DisplayClass1.<GetBuildMethod>b__0(IBuilderContext context)
       at Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlan.BuildUp(IBuilderContext context)
       at Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context)
       at Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context)
  InnerException: 

Further technical details

EF Core version: RTM
Operating system: Windows 10
Visual Studio version: VS 2015

Other details about my project setup:

<package id="Microsoft.EntityFrameworkCore" version="1.0.0" targetFramework="net452" />
<package id="SQLite" version="3.12.2" targetFramework="net452" />
<package id="Microsoft.Data.Sqlite" version="1.0.0" targetFramework="net452" />
@ariksman ariksman changed the title Many-to-Many relationship Many-to-Many relationship (with itself) Jul 12, 2016
@smitpatel

This comment has been minimized.

Copy link
Member

@smitpatel smitpatel commented Jul 12, 2016

As exception msg said, Navigation property can only participate in one relationship. You are using navigation Socket.ConnectedToSockets for both the relationships which is incorrect.

@ariksman

This comment has been minimized.

Copy link
Author

@ariksman ariksman commented Jul 13, 2016

Exception is quite clear, however this approach didn't cause exception on the RC2 and created following table:

CREATE TABLE "SocketSockets" (
    "SocketToId" INTEGER NOT NULL,
    "SocketFromId" INTEGER NOT NULL,
    "SocketSocketId" INTEGER NOT NULL,
    CONSTRAINT "PK_SocketSockets" PRIMARY KEY ("SocketToId", "SocketFromId"),
    CONSTRAINT "FK_SocketSockets_Sockets_SocketFromId" FOREIGN KEY ("SocketFromId") REFERENCES "Sockets" ("SocketId") ON DELETE CASCADE,
    CONSTRAINT "FK_SocketSockets_Sockets_SocketSocketId" FOREIGN KEY ("SocketSocketId") REFERENCES "Sockets" ("SocketId") ON DELETE CASCADE
)

May I ask how I can achieve similar results with RTM?

@smitpatel

This comment has been minimized.

Copy link
Member

@smitpatel smitpatel commented Jul 13, 2016

It did not throw exception in RC2 because, EF core was using last one wins approach but that could cause the model to be a lot different from what is expected by user. See #5540 and discussion in the corresponding issue.
As for similar results, navigations are part of domain model and not database model. Simply if you don't specify navigation in .WithMany() call, it would create same schema. But they to set relationship in domain you need to work with foreign key property values. Or you can add 2 navigations 1 for each relationship, or decide which one relationship will use the current navigation.

@ariksman

This comment has been minimized.

Copy link
Author

@ariksman ariksman commented Jul 13, 2016

Ok, thank you for the clarification.

@ariksman ariksman closed this Jul 13, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.