【问题标题】:One to many in Ef core Fk ViloationEf core Fk Violation中的一对多
【发布时间】:2020-07-11 20:26:08
【问题描述】:

好的,我想我可能在模型构建器中弄错了。我想要一个案例,它可以是一个案例,但在一个案例中,它们可以是许多关系。

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

但是当我尝试用上面的方法保存时

DbUpdateException:更新条目时出错。看 有关详细信息的内部异常。 SqlException:INSERT 语句 与 FOREIGN KEY 约束冲突 “FK_RelationShips_MISobject_MisObjectId”。冲突发生在 数据库“MISSystem”,表“dbo.MISobject”,列“Id”。这 语句已终止。

我的 MISObject 有一个这样的关系集合

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; }
}

上述是否不允许一对多关系,基本上一个案例可能有20个关系,但可能有很多案例。但是这 20 条关系应该只属于那种情况,如果那样的话。

我认为我需要使用 hasmany 但不确定在这种情况下如何使用。

编辑 2

我认为这可能是我需要的,但我不确定它是否合法。

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);

【问题讨论】:

  • 你能提供一些你保存数据到数据库(创建数据库实体)的sn-p吗?如果您想允许在没有 MISObject 的情况下创建 RelationShips(我猜是基于正在发生的错误),您应该将外键设为可为空
  • 没有一个案例总是有关系,即使它只有一个关系,但一个关系可能有很多照片,例如联系电话

标签: c# entity-framework-core ef-core-3.0


【解决方案1】:

要使用 FluentAPI 进行这项工作,您需要完全定义导航并使用外键:

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
    }
}

这是一个完整的控制台项目,展示了这些概念:

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);
            }
        }
    }
}

巧合的是,由于此示例中的属性遵循 EF Core 命名约定,您甚至可以在此处完全删除 OnModelCreating() 方法,结果仍然有效。 有关详细信息,请参阅Relationships: Conventions

【讨论】:

  • 所以这允许对一个案例进行许多关系,但它是否允许与其他案例的其他关系说我希望将许多照片绑定到多个关系sl。
  • 不确定你的意思。我的回答演示了如何实现一对多关系,这是您在问题中所要求的。如果您想实现多对多关系,请查看 Relationships: Many-to-many 或在 SO 上提出另一个问题,或者只是提供另一个示例,说明您现在想要完成的工作。
【解决方案2】:

只需使用约定,无需使用流利的配置来建立关系:

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; }
    }

没有任何错误,EF Core 会在数据库中创建您的表。

【讨论】:

  • 不,@Mortaza Ghahremani 是正确的。如果您从OnModelCreating() 删除 FluentAPI 代码,那么以这种方式定义模型类应该由convention 工作。如果您不理解,请不要只对答案投反对票。相反,请先尝试更多地询问答案。
  • 这不是抄袭,我测试过,先测试再判断!
猜你喜欢
  • 1970-01-01
  • 2017-10-26
  • 2018-11-08
  • 1970-01-01
  • 2021-07-24
  • 1970-01-01
  • 2021-04-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多