31

When I try to register a user on my .NET Core 2.1 website (using identity) I get the following error:

"InvalidOperationException: Unable to determine the relationship represented by navigation property 'City.ConnectionStartCity' of type 'ICollection'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.".

The reason this happens probably has nothing to do with identity, but registering and logging in is currently the only way I know how to trigger it.

I still want the properties City en ICollection<Connection> in my classes so I don't want to use the [NotMapped] attribute.

I searched on the internet and found that this is caused by a many-many relationship, I feel like this is not the case tho.

Class Connection:

public partial class Connection
{
    public Connection()
    {
        ConnectionRoute = new HashSet<ConnectionRoute>();
    }

    public int Id { get; set; }
    public int StartCityId { get; set; }
    public int EndCityId { get; set; }
    public int AantalMinuten { get; set; }
    public double Prijs { get; set; }

    public Stad StartCity { get; set; }
    public Stad EndCity { get; set; }
    public ICollection<ConnectionRoute> ConnectionRoute{ get; set; }
}

Class City:


public partial class City
{
    public City()
    {
        AspNetUsers = new HashSet<AspNetUsers>();
        Hotel = new HashSet<Hotel>();
        ConnectionStartCity = new HashSet<Connection>();
        ConnectionEndCity= new HashSet<Connection>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public string Country { get; set; }

    public ICollection<AspNetUsers> AspNetUsers { get; set; }
    public ICollection<Hotel> Hotel { get; set; }
    public ICollection<Connection> ConnectionStartCity { get; set; }
    public ICollection<Connection> ConnectionEndCity { get; set; }
}

Class treinrittenContext (dbContext) extract:


public virtual DbSet<City> City{ get; set; }

public virtual DbSet<Connection> Connection{ get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        ...

        modelBuilder.Entity<City>(entity =>
        {
            entity.Property(e => e.Country)
                .IsRequired()
                .HasMaxLength(255)
                .IsUnicode(false);

            entity.Property(e => e.Name)
                .IsRequired()
                .HasMaxLength(255)
                .IsUnicode(false);

            entity.HasMany(p => p.ConnectionStartcity)
                .WithOne(d => d.StartCity)
                .HasForeignKey(d => d.StartCityId);

            entity.HasMany(p => p.ConnectionEndCity)
                .WithOne(d => d.EndCity)
                .HasForeignKey(d => d.EndCityId);
        });

        ...

        modelBuilder.Entity<Connection>(entity =>
        {
            entity.HasOne(d => d.StartCity)
                .WithMany(p => p.ConnectionStartCity)
                .HasForeignKey(d => d.StartCityId)
                .OnDelete(DeleteBehavior.ClientSetNull)
                .HasConstraintName("FK_Verbinding_BeginStad");

            entity.HasOne(d => d.EndCity)
                .WithMany(p => p.ConnectionEndCity)
                .HasForeignKey(d => d.EndCityId)
                .OnDelete(DeleteBehavior.ClientSetNull)
                .HasConstraintName("FK_Verbinding_EindStad");
        });
       
        ...
    }

I expect this to work (since in my eyes it's a one-many relation), but it doesn't.

Djordje Nedovic
  • 559
  • 8
  • 20
Lukas Nys
  • 415
  • 1
  • 4
  • 6
  • You should think about your naming as well, collections should be plurial (meervoud) you DbSets for example should be Connections & Cities as well as your properties ConnectionStartCity & ConnectionEndCity. As for the error, could be because you are missing the [Required] Annotation on your StartCity & EndCity. – Dimitri Apr 11 '19 at 09:52
  • @Dimitri, thank you for the tip on naming conventions. Adding [Required] annotations to StartCity and EndCity in Connection did not solve it. – Lukas Nys Apr 11 '19 at 10:09

4 Answers4

29

UPDATE

You have multiple options here:

Option 1 with result 1

enter image description here

City Class becomes:

public partial class City
{
    public City()
    {           
        Connections = new HashSet<Connection>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public string Country { get; set; }

    public ICollection<Connection> Connections { get; set; }
}

Connection Class becomes:

public partial class Connection
{
    public Connection()
    {
    }

    public int Id { get; set; }

    public int StartCityId { get; set; }
    public int EndCityId { get; set; }

    public int AantalMinuten { get; set; }
    public double Prijs { get; set; }     
}

Your OnModelCreating becomes:

modelBuilder.Entity<City>().HasMany(city => city.Connections)
                           .WithRequired().HasForeignKey(con => con.EndCityId);

modelBuilder.Entity<City>().HasMany(city => city.Connections)
                           .WithRequired().HasForeignKey(con => con.StartCityId);

OR you can do something like this as well wchich would be option 2 with results 2:

enter image description here

City Class becomes:

public partial class City
{
    public City()
    {           
        Connections = new HashSet<Connection>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public string Country { get; set; }

    public ICollection<Connection> Connections { get; set; }
}

Connection Class becomes:

public partial class Connection
{
    public Connection()
    {
    }

    public int Id { get; set; }

    public virtual ICollection<City> Cities { get; set; }

    public int AantalMinuten { get; set; }
    public double Prijs { get; set; }     
}

And you don't have to do anything in your OnModelCreating.

Dimitri
  • 1,185
  • 2
  • 15
  • 37
  • 1
    I just realised that I left some words untranslated in my question. That's on me sorry! I updated it, so now it should make more sense. City is just a table for cities (in my case London, Brussels, ...), connection should be a connection between 2 cities (for example "London to Brussels" or "Brussels to Paris"). So in City I'd like to have a list of connections where the city is the starting point and a list of connections where city is the ending point. Full database model (Stad = City and Verbinding = Connection): https://gyazo.com/8211bb2789e0f7a864aee1a6b9e21192 – Lukas Nys Apr 11 '19 at 12:08
  • When I try the first option, I get this error while building: "Cannot create a relationship between 'City.Connection' and 'Connection.EndCity', because there already is a relationship between 'City.Connection' and 'Conncection.StartCity'. Navigation properties can only participate in a single relationship." With the second solution, I lose which city is the starting point and which one is the ending point. In this case the order of them is important. – Lukas Nys Apr 11 '19 at 12:50
  • 1
    Collection Cities should be virtual. – CrazyBaran Apr 11 '19 at 13:02
  • I agree on second solution not being applicable for your case. As for the error, strange, not happening on my side. Do you have migrations enabled? If so, update your model. If not, can you show me what your onmodelcreating has and eventually how your classes look like now? – Dimitri Apr 11 '19 at 13:09
  • @Dimitri Error I get: https://gyazo.com/6f6c006ca3d6979bc4134590e363435c. Class City (Stad): https://gyazo.com/7f1dae57a8c33ef2d3a19f7879a956eb. Class Connection (Verbinding): https://gyazo.com/1a4a9a8485ccabd5fba81a8f98eaef77. OnModelCreating (part of City = Stad): https://gyazo.com/75434895014a3d355b5770b6710cc9ae. OnModelCreating (part of Connection = Verbinding): https://gyazo.com/16c7d3d8894bb906ab0f30afee61e1f5 – Lukas Nys Apr 11 '19 at 13:28
  • @LukasNys Can you include the Entity ConnectionRoute (VerbindingRoute) please? – Dimitri Apr 11 '19 at 13:31
  • @Dimitri Class ConnectionRoute (VerbindingRoute): https://gyazo.com/5946a8d704e1bd491f287527f09edf70. OnModelCreating (part of ConnectionRoute): https://gyazo.com/c0485f0e04174daeb3e268720382c6a6. – Lukas Nys Apr 11 '19 at 13:40
  • @LukasNys Seems good to me, do you have EF Migrations enabled? – Dimitri Apr 11 '19 at 13:56
  • @Dimitri since this way of working has been causing a lot of problems, I attempted your 2nd solution but with an extra field "isEnd". Now I get the following error: https://gyazo.com/076f24c3f97ff1c0b4977010be59dc90. Any clues as to what causes this? – Lukas Nys Apr 11 '19 at 14:14
  • @LukasNys Have a look at the first answer here => https://stackoverflow.com/questions/28531201/entitytype-identityuserlogin-has-no-key-defined-define-the-key-for-this-entit P.S. I would try to have the first attempt working thoug :) – Dimitri Apr 11 '19 at 14:19
  • Why is City Collection virtual? – Hamza Dahmoun Apr 14 '20 at 13:54
  • @HamzaDahmoun: Lazy Loading: Any virtual ICollections will be lazy-loaded unless you specifically mark them otherwise. (https://en.wikipedia.org/wiki/Lazy_loading) – Dimitri Apr 16 '20 at 07:16
  • 1
    @Dimitri Thanks for your response – Hamza Dahmoun Apr 16 '20 at 15:32
1

In my case, problem occurred because when of overriding OnModelCreating in DbContext and not calling base.OnModelCreating(builder).

0
public class Entity
    {
        public int UserId { get; set; }
        [NotMapped]
        public virtual User User { get; set; }
    }

Just Added Like this.

ihsan güç
  • 241
  • 3
  • 7
0

In my case I had a custom name for the ForeignKey fields and Entity Framework did not like my ParentId convention.

I was able to resolve this by using nameof(Parent):

public partial class Child
{
    public int Id { get; set; }

    public string Name { get; set; }

    [ForeignKey(nameof(Parent))]
    public int IdOfParent{ get; set; }

    public Parent? Parent{ get; set; }

}
Shawn J. Molloy
  • 2,457
  • 5
  • 41
  • 59
Zahra
  • 2,231
  • 3
  • 21
  • 41