0

I'm getting a client-side error when trying to save an entity. 2 tables have a one-to-many and one-to-zero relationship at the same time and this is causing the following error:

Unable to determine the relationship represented by navigation property 'Group.LockedByUser' of type 'User'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating

These are my current contexts:

User entity

public class User
{
    [Key]
    public int UserId { get; set; }
    public string Username { get; set; }
    public int GroupId { get; set; }

    public Group GroupThisUserBelongsTo { get; set; }
    public ICollection<Group> GroupsLockedByThisUser { get; set; }
    public ICollection<Config> Configs { get; set; }
}

Group entity

public class Group
{ 
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int GroupId { get; set; }
    public string GroupName { get; set; }
    public int? LockedByUserId { get; set; }

    public User LockedByUser { get; set; }
    public ICollection<User> Users { get; set; }
}

DbContext1 extract

modelBuilder.Entity<Group>(entity =>
        {
            entity.HasOne(d => d.LockedByUser)
                .WithMany(p => p.GroupsLockedByThisUser)
                .HasForeignKey(d => d.LockedByUserId);
        }

modelBuilder.Entity<User>(entity =>
        {
            entity.HasOne(d => d.GroupThisUserBelongsTo)
                .WithMany(p => p.Users)
                .HasForeignKey(d => d.GroupId)
                .OnDelete(DeleteBehavior.ClientSetNull);
        });

Config entity

public class Config
{
    [Key]
    public int ConfigId { get; set; }
    public int UserId { get; set; }
    public string Config { get; set; }

    public User User { get; set; }
}

DbContext2 extract

 modelBuilder.Entity<Config>(entity =>
        {
            entity.HasOne(d => d.User)
                .WithMany(p => p.Configs)
                .HasForeignKey(d => d.UserId);
        });

The piece of code generating the error is as follows:

var config = new Config {
            UserId = 123456,
            Config = "test"
        };

        _dbContext2.Config.Add(config);
        _dbContext2.SaveChanges();

I really don't understand why I get a client-side error when trying to save that entity. The error isn't even from the context from which I am trying to save.

Are my foreign keys set properly?

  • Group has many User - User has one Group (FK GroupId)
  • User locks zero or many Group (FK LockedByUserId)
  • Config has one User (FK UserId)
Raphael Laurent
  • 1,941
  • 1
  • 17
  • 27
  • You've configured the Navigation Property User.Group on both relationships. Try renaming the Navigation Properties on user to "GroupThisUserBelongsTo" and "GroupsLockedByThisUser" to help you configure the navigation properties correctly. – David Browne - Microsoft May 01 '18 at 20:35
  • Thanks for your comment @DavidBrowne-Microsoft, I've edited my question with your advice. I have tried it and I still get the same error. I've tripled checked everything and I *think* I got the relationships right this time. – Raphael Laurent May 01 '18 at 21:20
  • maybe you need to remove this `.WithMany(p => p.Users)` and why don't you use normal `virtual` keyword to define relations. – Rudresha Parameshappa May 01 '18 at 21:37
  • Hey @RudreshaParameshappa, here is a link on `virtual`: https://stackoverflow.com/a/41881299/3768672. I don't need it in my case. Also I can't remove half of a relationship afaik. – Raphael Laurent May 01 '18 at 22:02

2 Answers2

0

Tried to repro the error. No repro:

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
//using Microsoft.Samples.EFLogging;
using System.Data.SqlClient;
using System.Xml.Linq;
using System.Threading.Tasks;

namespace EFCore2Test
{

    public class User
    {
        [Key]
        [DatabaseGenerated( DatabaseGeneratedOption.None)]
        public int UserId { get; set; }
        public string Username { get; set; }
        public int GroupId { get; set; }

        public Group GroupThisUserBelongsTo { get; set; }
        public ICollection<Group> GroupsLockedByThisUser { get; set; }

        public ICollection<Config> Configs { get; set; }
    }
    public class Group
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int GroupId { get; set; }
        public string GroupName { get; set; }
        public int? LockedByUserId { get; set; }

        public User LockedByUser { get; set; }
        public ICollection<User> Users { get; set; }
    }

    public class Config
    {
        [Key]
        public int ConfigId { get; set; }
        public int UserId { get; set; }
        public string ConfigName { get; set; }

        public User User { get; set; }
    }

    public class Db : DbContext
    {
        public DbSet<User> Users { get; set; }
        public DbSet<Group> Groups { get; set; }

        public DbSet<Config> Configs { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Group>()
                .HasOne(d => d.LockedByUser)
                .WithMany(p => p.GroupsLockedByThisUser)
                .HasForeignKey(d => d.LockedByUserId);            

            modelBuilder.Entity<User>()
                .HasOne(d => d.GroupThisUserBelongsTo)
                .WithMany(p => p.Users)
                .HasForeignKey(d => d.GroupId)
                .OnDelete(DeleteBehavior.ClientSetNull);

            modelBuilder.Entity<Config>()
                .HasOne(d => d.User)
                .WithMany(p => p.Configs)
                .HasForeignKey(d => d.UserId);            

            base.OnModelCreating(modelBuilder);
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("Server=(local);Database=EFCoreTest;Trusted_Connection=True;MultipleActiveResultSets=true");
            base.OnConfiguring(optionsBuilder);
        }
    }

    class Program
    {   

        static void Main(string[] args)
        {            
            using (var db = new Db())
            {
                db.Database.EnsureDeleted();

                db.Database.EnsureCreated();
                //db.ConfigureLogging(s => Console.WriteLine(s));

                var g = new Group()
                {
                    GroupName = "G"
                };

                var u = new User()
                {
                    UserId = 123456,
                    GroupThisUserBelongsTo = g
                };                

                db.Users.Add(u);
                db.SaveChanges();

                g.LockedByUser = u;
                db.SaveChanges();

            }

            using (var db = new Db())
            {
                var config = new Config
                {
                    UserId = 123456,
                    ConfigName = "test"
                };

                db.Configs.Add(config);
                db.SaveChanges();
            }


            Console.WriteLine("Hit any key to exit");
            Console.ReadKey();
        }
    }
}
David Browne - Microsoft
  • 80,331
  • 6
  • 39
  • 67
  • I tried your repro, no error. I split the context into 2 contexts like it is in my question, the error is back. EDIT: now wondering if that's not my issue, do I need all models in both contexts? Currently the second one only had Config as a DbSet – Raphael Laurent May 01 '18 at 21:59
  • https://stackoverflow.com/a/31090360/3768672 seems to indicate that I'd have better luck going back to a single context when it overlaps like that. – Raphael Laurent May 01 '18 at 22:35
  • 1
    You can't have relationships between entities in different models. – David Browne - Microsoft May 02 '18 at 12:18
0

As explained in this answer, I would need to duplicate the shared relationships.

When using DbContext2, the relationships between User and Group aren't defined.

I can solve my problem by using a single context, or by having contexts inherit a master context defining all shared relationships.

Raphael Laurent
  • 1,941
  • 1
  • 17
  • 27