【问题标题】:ef-Code-First one-to-many optional delete throws exception:primary key value cannot be deletedef-Code-First 一对多可选删除抛出异常:无法删除主键值
【发布时间】:2014-01-04 04:37:51
【问题描述】:

我查看并发现没有解决什么接缝是一件简单的事情,我得到了 3 个实体当我尝试删除 CityDistrict 时,我得到了这个异常:

System.Data.Entity.Infrastructure.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.Entity.Core.UpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlServerCe.SqlCeException: The primary key value cannot be deleted because references to this key still exist. [ Foreign key constraint name = FK_dbo.User_dbo.District_DistrictId ]

我想要达到的是: 当我删除一个城市时,所有要删除的子区,但不是用户。 当我删除一个区时,仅删除该区。 当我删除一个用户时,只有该用户被删除。

  1. 城市
    • 一个城市可以有 0 - N 个区,删除时将级联
    • 一个地区必须有一个城市并且有可选的 0 - N 个用户
  2. 用户
    • 一个用户可以有可选的 0 - 1 个区

实体用户

public class User
{
  public Guid UserId { get; set; }

  public virtual District District { get; set; }
  public Guid? DistrictId { get; set; }
 }

实体城市

public class City 
{
    public Guid CityId { get; set; }

    public virtual ICollection<District> Districts { get; set; }
}

实体区

public class District 
{
    public Guid DistrictId { get; set; }

    public City City { get; set; }
    public Guid CityId { get; set; }

    public virtual ICollection<User> Users { get; set; }
}
//EntityTypeConfiguration
   HasRequired(d => d.City)
            .WithMany(c => c.Districts)
            .HasForeignKey(d => d.CityId)
            .WillCascadeOnDelete(true); 

    HasMany(d=> d.Users)
            .WithOptional(u=> u.District)
            .HasForeignKey(u=> u.DistrictId)
            .WillCascadeOnDelete(false);

我尝试将此配置放在用户映射中,但没有改变同样的错误

HasOptional(u => u.District)
            .WithMany(d => d.Users)
            .HasForeignKey(u => u.DistrictId)
            .WillCascadeOnDelete(false);

我也在使用 Sqlce4 和 EF6 以及 Generic Unit of Work & Repositories Framework

感谢您的帮助

问候

【问题讨论】:

  • 检查您的架构并查看此 FK_dbo.User_dbo.District_DistrictId 约束的定义位置。也许您在那里不需要它,然后更新模型以从那里删除它。我认为您的模型定义了太多的关系,例如用户与 1 个地区的关系很好,然后在地区与 N 个用户定义它可能不需要或其他方式。你需要从约束中抓住它,看看你是否需要它。
  • 我首先使用代码 FK_dbo.User_dbo.District_DistrictId 是在 sqlce 数据库键中生成的键我不知道是否可以删除它,用户的配置映射也只是为了测试它是否工作,但仍然出现错误。
  • 我建议您取出两个映射,然后运行您的代码并尝试删除。它应该工作。这可能需要您删除数据库并再次为其播种,但会清除一些疑虑。然后添加一个关系,无论是否删除,都执行相同的操作。然后添加另一个并先删除,这样您就知道是哪个导致了它。通过这种方式,您可以帮助我们这里的所有人理解场景,并在过程中了解键实际发生的情况。如果您使用的是 EF 代码优先迁移,则不需要从 DB 中删除约束。只需从模型中删除键即可更新并添加新的迁移。

标签: c# entity-framework ef-code-first one-to-many


【解决方案1】:

您必须将UserDistrictId 外键设置为null,以避免在尝试删除CityDistrict 时违反约束。如果相关的Users 未加载到上下文中,EF 不会自动执行此操作。

要删除一个地区,你可以这样做:

// "Include" because you need the users to nullify their FKs to the district
var district = context.Districts.Include(d => d.Users) 
    .SingleOrDefault(d => d.DistrictId == someDistrictId);
if (district != null)
{
    // "Remove" will set the FKs of the LOADED users to null
    context.Districts.Remove(district);
    context.SaveChanges();
}

删除城市:

var city = context.Cities.Include(c => c.Districts.Select(d => d.Users))
    .SingleOrDefault(c => c.CityId == someCityId);
if (city != null)
{
    context.Cities.Remove(city);
    context.SaveChanges();
}

Cacsading delete 会关心删除这里的区。但是,我不确定在这种情况下,加载用户的DistrictId 是否会设置为null。如果它不起作用,您必须手动执行:

var city = context.Cities.Include(c => c.Districts.Select(d => d.Users))
    .SingleOrDefault(c => c.CityId == someCityId);
if (city != null)
{
    foreach (var district in city.Districts)
        foreach (var user in district.Users.ToList())
            user.DistrictId = null;

    context.Cities.Remove(city);
    context.SaveChanges();
}

【讨论】:

    【解决方案2】:

    谢谢大家,我认为 EF 足够聪明,可以处理这样的简单任务。 所以不仅要清理城市,还要清理这些地区,这就是我最终如何使用这个存储库让它工作

      var city = _unitOfWork.Repository<City>().Query()
                .Include(c => c.Districts.Select(d => d.Users))
                .Filter(c => c.CityName.Equals(cityName, StringComparison.InvariantCultureIgnoreCase))
                .Get().FirstOrDefault();
    
            if (city != null)
            {
                var districts = city.Districts.ToList();
                for (var i = districts.Count - 1; i >= 0; i--)
                {
                    var users = districts[i].Users.ToList();
                    for (var j = users.Count - 1; j >= 0; j--)
                    {
                        users[j].DistrictId = null;
                        users[j].ObjectState = ObjectState.Modified;
    
                        _unitOfWork.Repository<User>().Update(users[j]);
                    }
                    districts[i].Users.Clear();
                    _unitOfWork.Repository<District>().Delete(districts[i]);
                }
                _unitOfWork.Repository<City>().Delete(city);
    

    很糟糕,但它有效

    【讨论】:

    • 最终也不得不这样做。很可笑。
    猜你喜欢
    • 2012-05-01
    • 2014-09-26
    • 1970-01-01
    • 2014-10-27
    • 2016-12-18
    • 2013-08-03
    • 2020-04-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多