更新 EF6 中的实体子集合

海吉思

我有一个名为 Driver 的模型,其中包含一个“DriverQualifications”列表,更新时我想添加/删除/更新当前 DriverQualifications 的值。

我当前尝试通过首先清除列表并读取所有元素来更新:

public void UpdateOne(Driver val)
{
    using (var db = new COMP1690Entities())
    {
        Driver d = db.Drivers.Where((dr) => dr.Id == val.Id).Include("DriverQualifications.Qualification").FirstOrDefault();
        d.DriverQualifications.Clear();
        foreach (DriverQualification q in val.DriverQualifications)
        {
            q.Fk_Qualifications_Id = q.Qualification.Id;
            q.Qualification = null;
            d.DriverQualifications.Add(q);
        }
        d.Phone_Number = val.Phone_Number;
        db.SaveChanges();
    }
}

这会导致“违反多重性约束”。关系“COMP1690Model.DriverQualifications_ibfk_1”的角色“Drivers”具有多重性1或0..1。

我如何向数据库添加值:

    public void CreateOne(Driver val)
    {
        using (var db = new COMP1690Entities())
        {
            foreach(DriverQualification q in val.DriverQualifications)
            {
                q.Fk_Qualifications_Id = q.Qualification.Id;
                q.Qualification = null;
            }
            db.Drivers.Add(val);
            db.SaveChanges();
        }
    }

驱动型号:

public partial class Driver
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public Driver()
    {
        this.DriverQualifications = new HashSet<DriverQualification>();
        this.DriverTrainings = new HashSet<DriverTraining>();
    }

    public int Id { get; set; }
    public string Phone_Number { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<DriverQualification> DriverQualifications { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<DriverTraining> DriverTrainings { get; set; }
}

驾驶员资格模型:

public partial class DriverQualification
{
    public int Id { get; set; }
    public Nullable<System.DateTime> Expiry_Date { get; set; }
    public int Fk_Driver_Id { get; set; }
    public int Fk_Qualifications_Id { get; set; }

    public virtual Driver Driver { get; set; }
    public virtual Qualification Qualification { get; set; }
}
史蒂夫·派

在处理 EF 和引用(DriverQualification -> Qualification)时,请使用引用,而不是 FK。事实上,我通常建议不要向实体添加 FK,而是使用影子属性 (EF Core) 或.Map()在实体配置中避免它们可访问。您面临的问题是 EF 仍在跟踪引用特定 Qualification 的 DriverQualification 实体,因此将 Qualification 设置为 null 并更新 FK 并不起作用。

因此,您要传回一个驱动程序,想要重新加载该驱动程序实体,并根据传入的驱动程序更新其资格。

假设传入的驱动程序来自客户端(Web 应用程序等)并已被修改,我们不能“信任”它,或者它是参考数据,因此最好将其重新加载而不是重新附加到上下文。

编辑:我建议使用 ViewModel 而不是传递实体,即使您不打算信任它。传递实体的主要风险是在更新时很容易重新附加/使用它或引用的实体。我不得不再次检查这个答案,因为我认为在获得更新的资格时我违反了这条规则!:) 例如,将实体图传递给客户端浏览器也会比您应该公开更多有关您的域的信息。即使不显示各种列/fks/reference 数据,客户端也可以使用调试工具查看这些数据。网络上的数据也多于可能需要的数据。Automapper 可以使转置实体以快速查看模型,并且也可以使用IQueryable( ProjectTo). /编辑

为了清楚起见,我重命名了一些变量..(即 val => updatedDriver)

using (var context = new COMP1690Entities())
{
    var updatedQualificationIds = updatedDriver.DriverQualifications.Select(dq => dq.Qualification.Id).ToList();
    // Get the updated qualification entities from the DB.
    var updatedQualifications = context.Qualifications.Where(q => updatedQualificationIds.Contains(q.Id)).ToList();

    var driver = context.Drivers.Where(d => d.Id == updatedDriver.Id)
        .Include(d => d.DriverQualifications)
        .Include("DriverQualifications.Qualification").Single();

    var driverQualificationsToRemove = driver.DriverQualifications
        .Where(dq => !updatedQualificationIds.Contains(udq.Qualification.Id));

    foreach(var driverQualification in driverQualificationsToRemove)
        driver.DriverQualifications.Remove(driverQualification);

    var driverQualificationsToAdd = updatedDriverQualifications
        .Except(driver.DriverQualifications.Select(dq => dq.Qualification),
            new LamdaComparer((q1,q2) => q1.Id == q2.Id))
        .Select(q => new DriverQualification { Qualification = q })
        .ToList();

    driver.DriverQualifications.AddRange(driverQualificationsToAdd);

    driver.PhoneNumber = updatedDriver.PhoneNumber;

    context.SaveChanges();
}

这假设我们要删除不再与驱动程序关联的资格关联,并添加任何尚未关联的新资格。(保留任何不变的资格。)

您可以在此处找到的 LamdaComparer

基本上,为了避免引用/关键问题,坚持更新引用并完全忽略 FK。对于我需要进行大量“原始”更新的实体/上下文,我将仅声明 FK 并放弃添加性能参考。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何使用EF6更新多对多表格

来自分类Dev

这是在EF6中更新对象的好方法

来自分类Dev

WebApi的EF6合并实体

来自分类Dev

如何在EF6 Code First中更新虚拟属性?

来自分类Dev

EF6在查询中插入“ \ n”?

来自分类Dev

Servicestack ORMLite更新子集合

来自分类Dev

在ef中更新实体

来自分类Dev

休眠更新子集合

来自分类Dev

MongoDB并发更新(带有子集合)

来自分类Dev

MongoDB C#批量更新/替换子集合

来自分类Dev

更新CloudFunctions上的子集合

来自分类Dev

Blazor /实体框架-将子集合添加到时更新父

来自分类Dev

EF6包括实体中集合的引用

来自分类Dev

在mongo中更新或追加到子集合

来自分类Dev

Node.js /猫鼬-添加,更新和删除子集合中的项目

来自分类Dev

EF6实体的通用克隆

来自分类Dev

这是在EF6中更新对象的好方法

来自分类Dev

EF6无法删除/更新-ObjectStateManager中已经存在具有相同键的对象

来自分类Dev

EF6子实体未在多对多关系中更新

来自分类Dev

如何在EF6 Code First中更新虚拟属性?

来自分类Dev

EF6在查询中插入“ \ n”?

来自分类Dev

Servicestack ORMLite更新子集合

来自分类Dev

在ef中更新实体

来自分类Dev

使用Linq返回EF6中的相关实体

来自分类Dev

更新EF6时需要属性

来自分类Dev

在EF6中更新实体会产生主键异常

来自分类Dev

在EF6中更新外键对象

来自分类Dev

阻止在EF6中为特定实体生成表

来自分类Dev

在 EF6 中加载相关实体