One to many in Ef core Fk Viloation

232 Views Asked by At

Ok I think I may have got this wrong in the model builder. I want the case which can be one case but inside that one case they can be many relationships.

modelBuilder.Entity<RelationShips>()
              .HasOne<MISObject>(s => s.Case)
              .WithMany(g => g.RelationShip)
          .HasForeignKey(s => s.MisObjectId);

But when I attempt to save with the above

DbUpdateException: An error occurred while updating the entries. See the inner exception for details. SqlException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_RelationShips_MISobject_MisObjectId". The conflict occurred in database "MISSystem", table "dbo.MISobject", column 'Id'. The statement has been terminated.

My MISObject has a collection of relationships as such

public class MISObject {
    public int Id { get; set; }
    public ICollection<RelationShips> RelationShip { get; set; }
}

public class RelationShips {
    public int Id { get; set; }        
    public MISObject Case { get; set; }
}

Will the above not allow a one to many relationship basically one case could have 20 relationships but there could be many cases. But those 20 relationships should only belong to that case if that makes since.

I think I need to use hasmany but not to sure how in this context.

Edit 2

I think this might be what i need but im not sure how legal it is.

modelBuilder.Entity<MISObject>()
 .HasMany(c => c.RelationShip);

modelBuilder.Entity<RelationShips>()
 .HasMany(c => c.PersonOfIntrests);

modelBuilder.Entity<POI>()
  .HasMany(c => c.PersonOfIntrestsPhotos)
  .WithOne(e => e.PersonOfIntrest);
2

There are 2 best solutions below

2
On

Just use convention there is no need to use fluent config for relationships:

public class MISObject
    {
        public int Id { get; set; }
        public ICollection<RelationShips> RelationShip { get; set; }
    }
public class RelationShips
    {
        public int Id { get; set; }
        public int CaseId { get; set; }
        public MISObject Case { get; set; }
    }

without any error, EF Core makes your Tables in the database.

2
On

To make this work with the FluentAPI, you need to completely define the navigation and use a foreign key:

public class Relationship {
    public int Id { get; set; }        
    public int MisObjectId { get; set; } // <-- add foreign key ID
    
    public MisObject Case { get; set; }
}

public class Context : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<MisObject>()
            .HasMany(o => o.Relationships)
            .WithOne(r => r.Case) // <-- add other endpoint
            .HasForeignKey(r => r.MisObjectId); // <-- add FK property
    }
}

Here is a fully working console project, that demonstrates these concepts:

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace IssueConsoleTemplate
{
    public class MisObject {
        public int Id { get; set; }
        
        public ICollection<Relationship> Relationships { get; set; }
    }

    public class Relationship {
        public int Id { get; set; }        
        public int MisObjectId { get; set; } // <-- add foreign key ID
        
        public MisObject Case { get; set; }
    }

    public class Context : DbContext
    {
        public DbSet<MisObject> MisObjects { get; set; }
        public DbSet<Relationship> Relationships { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseSqlServer(@"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=So62854210")
                .UseLoggerFactory(
                    LoggerFactory.Create(
                        b => b
                            .AddConsole()
                            .AddFilter(level => level >= LogLevel.Information)))
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors();
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<MisObject>()
                .HasMany(o => o.Relationships)
                .WithOne(r => r.Case) // <-- add other endpoint
                .HasForeignKey(r => r.MisObjectId); // <-- add FK property

            // Technically not necessary, because this navigation has already been defined
            // in the previous line, but might be good practice anyway.
            modelBuilder.Entity<Relationship>()
                .HasOne(r => r.Case)
                .WithMany(o => o.Relationships)
                .HasForeignKey(r => r.MisObjectId);
        }
    }
    
    internal static class Program
    {
        private static void Main()
        {
            using (var context = new Context())
            {
                context.Database.EnsureDeleted();
                context.Database.EnsureCreated();

                var newMisObject = new MisObject();
                var newRelationships = new[]
                {
                    new Relationship {Case = newMisObject},
                    new Relationship {Case = newMisObject},
                    new Relationship {Case = newMisObject},
                };

                context.MisObjects.Add(newMisObject);
                context.Relationships.AddRange(newRelationships);
                context.SaveChanges();
            }

            using (var context = new Context())
            {
                var misObjects = context.MisObjects
                    .Include(o => o.Relationships)
                    .ToList();

                Debug.Assert(misObjects.Count == 1);
                Debug.Assert(misObjects[0].Relationships.Count == 3);
            }
        }
    }
}

Coincidentally, because the properties in this example follow EF Core naming conventions, you could even remove the OnModelCreating() method entirely here and the result would still work. See Relationships: Conventions for further information on that.