【问题标题】:Duplicate Entries in many to many table when trying to update an object from EF CodeFirst尝试从 EF CodeFirst 更新对象时,多对多表中的重复条目
【发布时间】:2015-04-16 16:35:31
【问题描述】:

这是我的两个示范班学生和课程

public class Student
{
    private readonly IStudentOps _studentOps;

    public Student(IStudentOps studentOps)
    {
        _studentOps = studentOps;
    }
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime BirthDate { get; set; }

    public Student()
    {
        Courses = new List<Course>();
    }

    public virtual IList<Course> Courses { get; set; }

    public bool Enrol(Student student)
    {
        return _studentOps.EnrolOrUpdate(student);
    }

    public IEnumerable<Course> SeeCourses()
    {
        return _studentOps.SeeCourses();
    }
}


public  class Course
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual IList<Student> Students { get; set; }
}

这是我的学生类接口和服务

public interface IStudentOps
{
    bool EnrolOrUpdate(Student student);
    IEnumerable<Course> SeeCourses();
}


public class StudentOps : IStudentOps
{

    public bool EnrolOrUpdate(Student student)
    {
        using (var context = new StudentContext())
        {
            if (student.Id == 0)
            {
                context.Students.Add(student);
                context.SaveChanges();
                return true;
            }

            var tempStudent = context.Students.FirstOrDefault(x => x.Id == student.Id);

            // ReSharper disable once InvertIf
            if (tempStudent == null) return false;
            context.Students.Attach(tempStudent);
            tempStudent.Id = student.Id;
            tempStudent.Name = student.Name;
            tempStudent.BirthDate = student.BirthDate;
            tempStudent.Courses = student.Courses;
            context.Entry(tempStudent).State = EntityState.Modified;
            context.SaveChanges();
            return true;
        }
    }

    public IEnumerable<Course> SeeCourses()
    {
        return new List<Course>() { new Course() { Name = "Social" }, new Course() { Name = "Science" } };
    }
}

上下文类

class StudentContext : DbContext
{
    public StudentContext() : 
        base("Name=StudentConn")
    {

    }
    public DbSet<Student> Students { get; set; }
    public DbSet<Course> Courses { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Student> ().HasMany(c=>c.Courses).WithMany(x=>x.Students)
            .Map(
                m =>
                {
                    m.MapLeftKey("StudentId");
                    m.MapRightKey("CourseId");
                    m.ToTable("Student_Course");
                });

    }
}

数据库中的数据

学生(表)
身份证 姓名 出生日期
21 佩德罗 2015-04-16 16:12:49.270
22 约翰 2015-04-14 16:12:49.270
23 希夫 2015-04-16 16:13:45.240

课程(表)
身份证号
26 科学
27 地理
28 测试课程
29 空

Student_Course(表)
学生 ID 课程 ID
23 28
23 29

使用 StudentOps 表中的代码,我正在尝试更新名为 Shiv 的学生以拥有课程 26(科学)和 27(地理)而不是 28(测试课程)和 29(NUll)(在多对多表中)Student_Course。但是每次我这样做时,它都会在表中插入重复的条目而不是更新它。从代码更新 Student_Course 表的最佳方法是什么?有人来帮忙吗?提前致谢

【问题讨论】:

    标签: c# .net linq entity-framework


    【解决方案1】:

    您应该能够清除tempStudent.Courses 集合并从student.Courses 重新添加集合,但如果它最终复制了课程条目,您可能需要先使用student.Courses 从数据库中获取它们。

    顺便说一句,tempStudent 应该已经通过获取它附加到上下文中,并且当您更改属性时,它应该已经被标记为已修改。除非您关闭了跟踪功能?

    另外,你不需要设置它的Id,你只是用它来获取它。

    【讨论】:

    • 嗨克洛尔斯。感谢您的评论。什么时候我会尝试清除位。但是 EF 不应该阻止代码创建重复条目吗?或者我需要为此设置任何标志。此外,当我将对象设置为 EntityState.Modified 时。我假设它检查每个属性的更改状态。我很抱歉,因为我不知道如何检查跟踪功能,因为我是 EF CodeFirst 的新手
    • Clearning 集合完成了这项工作。但它在表格上添加了带有空名称的新课程
    • 不,EF 会很高兴地创建重复项,除非您跳过几圈来阻止它。在将它们添加到 tempStudent 之前,您可以先使用 student.Courses 集合从数据库中获取它们,这样 EF 已经在跟踪它们并且不应创建重复项。
    • 是的,想法是将所有检索和更新功能放在一个上下文中。谢谢你成功了。
    【解决方案2】:

    要更新多对多关系中的实体是 EF ,您需要执行以下操作:

    1. 将实体附加到上下文
    2. 确保您要更改的集合已加载
    3. 确保作为跟踪实体加载。
    4. 致电 EF 的 .SaveChanges() 以保存更改。

    在你的情况下,你应该这样做:

    public bool EnrolOrUpdate(Student student)
        {
            using (var context = new StudentContext())
            {
                if (student.Id == 0)
                {
                    context.Students.Add(student);
                    context.SaveChanges();
                    return true;
                }
    
                var tempStudent = context.Students.FirstOrDefault(x => x.Id == student.Id);
    
                // ReSharper disable once InvertIf
                if (tempStudent == null) return false;
                context.Students.Attach(tempStudent);
                tempStudent.Id = student.Id;
                tempStudent.Name = student.Name;
                tempStudent.BirthDate = student.BirthDate;
                tempStudent.Courses = student.Courses;
                Context.Entry(tempStudent).Collection(p => p.Students).Load();
                context.Entry(tempStudent).State = EntityState.Modified;
                context.SaveChanges();
                return true;
            }
        }
    

    【讨论】:

    • 嗨,Alundra 我尝试使用上面的代码,但它仍然会创建重复项。 Klors 的回答清除了对我有用的收藏。但它会在表格中创建课程。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-19
    相关资源
    最近更新 更多