0

我正在使用带有 Pomelo.EntityFrameworkCore.MySql 的 Blazor 服务器,并且在一次完成所有操作时遇到了保存具有复杂关系的表的问题。

示例:假设实体“国家”拥有实体“城市”的集合,每个“城市”都有实体“街道”的集合。

public class Country
{
                public Guid Id { get; set; }
                public string Name { get; set; }
                public virtual ICollection<City> Cities { get; set; }
}
 
public class City
{
                public Guid Id { get; set; }
                public string Name { get; set; }
                public virtual ICollection<Street> Streets { get; set; }
}
 
public class Street
{
                public Guid Id { get; set; }
                public string Name { get; set; }
}

连接国家和城市的表格:

CountryCityId CountryId CityId

表,将上面的表行连接到 Street:

CountryCityId StreetId

什么工作正常: 创建城市,向其中添加街道列表并保存城市:

unitOfWork.RepositoryOf<City>().Add(City);
await unitOfWork.SaveAsync();

将城市添加到城市的国家列表和另一个保存:

Country.Cities.Add(City);
unitOfWork.RepositoryOf<Country>().Update(Country);
await unitOfWork.SaveAsync();

什么不起作用: 创建城市,向其中添加街道列表,然后添加它:

City.Add(Streets);
Country.Cities.Add(City);

并一次保存:

unitOfWork.RepositoryOf<Country>().Update(Country);
await unitOfWork.SaveAsync();

我收到如下错误:

efcore Cannot add or update a child row: a foreign key constraint fails

对我来说,似乎 orm 在插入外键应该指向的行之前尝试使用外键插入行。有什么线索吗?有什么明显的,我应该看看吗?

4

1 回答 1

0

典型实现

典型的实现如下所示,并在其实体上使用外键属性Street(引用City.Id):

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

namespace IssueConsoleTemplate
{
    public class City
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public virtual ICollection<Street> Streets { get; set; } = new HashSet<Street>();
    }
 
    public class Street
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public Guid CityId { get; set; } // <-- foreign key, referencing City.Id
    }
    
    public class Context : DbContext
    {
        public DbSet<City> Cities { get; set; }
        public DbSet<Street> Streets { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            var connectionString = "server=127.0.0.1;port=3306;user=root;password=;database=So68982686";
            var serverVersion = ServerVersion.AutoDetect(connectionString);

            optionsBuilder
                .UseMySql(connectionString, serverVersion)
                .UseLoggerFactory(
                    LoggerFactory.Create(
                        b => b
                            .AddConsole()
                            .AddFilter(level => level >= LogLevel.Information)))
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors();
        }
    }

    internal static class Program
    {
        private static void Main()
        {
            using (var context = new Context())
            {
                context.Database.EnsureDeleted();
                context.Database.EnsureCreated();

                var city = new City { Name = "Berlin" };
                city.Streets.Add(new Street { Name = "Potsdamer Platz" });
                context.Cities.Add(city);

                context.SaveChanges();
            }
            
            using (var context = new Context())
            {
                var city = context.Cities
                    .Include(c => c.Streets)
                    .First();
                
                Trace.Assert(city.Name == "Berlin");
                Trace.Assert(city.Streets.Count == 1);
                Trace.Assert(city.Streets.First().Name == "Potsdamer Platz");
            }
        }
    }
}

将生成以下 SQL:

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (5ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE DATABASE `So68982686`;

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      ALTER DATABASE CHARACTER SET utf8mb4;

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (22ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE `Cities` (
          `Id` char(36) COLLATE ascii_general_ci NOT NULL,
          `Name` longtext CHARACTER SET utf8mb4 NULL,
          CONSTRAINT `PK_Cities` PRIMARY KEY (`Id`)
      ) CHARACTER SET=utf8mb4;

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (31ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE `Streets` (
          `Id` char(36) COLLATE ascii_general_ci NOT NULL,
          `Name` longtext CHARACTER SET utf8mb4 NULL,
          `CityId` char(36) COLLATE ascii_general_ci NOT NULL,
          CONSTRAINT `PK_Streets` PRIMARY KEY (`Id`),
          CONSTRAINT `FK_Streets_Cities_CityId` FOREIGN KEY (`CityId`) REFERENCES `Cities` (`Id`) ON DELETE CASCADE
      ) CHARACTER SET=utf8mb4;

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (25ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE INDEX `IX_Streets_CityId` ON `Streets` (`CityId`);

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (10ms) [Parameters=[@p0='08d996c3-9074-4359-8d0f-e9df957a75d4', @p1='Berlin' (Size = 4000)], CommandType='Text', CommandTimeout='30']
      INSERT INTO `Cities` (`Id`, `Name`)
      VALUES (@p0, @p1);

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (1ms) [Parameters=[@p2='08d996c3-9080-47d8-8cb3-5b8ee8c7e720', @p3='08d996c3-9074-4359-8d0f-e9df957a75d4', @p4='Potsdamer Platz' (Size = 4000)], CommandType='Text', CommandTimeout='30']
      INSERT INTO `Streets` (`Id`, `CityId`, `Name`)
      VALUES (@p2, @p3, @p4);

info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
      Entity Framework Core 6.0.0-dev initialized 'Context' using provider 'Pomelo.EntityFrameworkCore.MySql:6.0.0-rc.1' with options: SensitiveDataLoggingEnabled DetailedErrorsEnabled ServerVersion 8.0.25-mysql

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT `t`.`Id`, `t`.`Name`, `s`.`Id`, `s`.`CityId`, `s`.`Name`
      FROM (
          SELECT `c`.`Id`, `c`.`Name`
          FROM `Cities` AS `c`
          ORDER BY `c`.`Id`
          LIMIT 1
      ) AS `t`
      LEFT JOIN `Streets` AS `s` ON `t`.`Id` = `s`.`CityId`
      ORDER BY `t`.`Id`

该代码按预期工作,您可以根据需要一次性保存尽可能多的不同实体。


如果同一个Street可以是多个City实体的一部分,这意味着您想要多对多关系,我也可以为您发布一些该场景的示例代码。

于 2021-10-24T07:57:15.487 回答