为什么我的ASP.NET MVC 4应用程序创建新实体而不是更新旧实体?

时间:2014-04-17 23:08:29

标签: c# asp.net asp.net-mvc entity-framework asp.net-mvc-4

编辑:我选择的解决方案可能不是最好的,但绝对有效。我将在下周完成我的代码(一旦这个项目完成),当我理解出了什么问题时,我会更新我的问题。

我正在使用带有实体5的ASP.NET MVC 4框架。以下是一些代码:

要在数据库中实例化和保存(新)的类:

    public class ClassCancellation
    {
        [Key]
        public int Id { get; set; }
        public Faculty Professor { get; set; }
        public DateTime CancelledOn { get; set; }
        public Course Course { get; set; }
        [Required]
        public ClassDate ClassCancelled { get; set; }
        public Message CancellationMessage { get; set; }
        [Required]
        public List<Student> Students { get; set; }
    }

它是从名为CancellationFull的视图模型(使用AutoMapper)映射的:

public class CancellationForList
{
    [Key]
    public int Id { get; set; }
    public CourseForList Course { get; set; }
    public ClassDateForList ClassCancelled { get; set; }
}

public class CancellationFull : CancellationForList
{
    public CancellationFull()
    {
        this.Students = new List<StudentForList>();
    }
    public FacultyForList Professor { get; set; }
    public MessageForList CancellationMessage { get; set; }
    public DateTime CancelledOn { get; set; }
    public List<StudentForList> Students { get; set; }
}

这是将CancellationFull转换为ClassCancellation然后将其保存到数据库的repo方法:

    public CancellationFull createClassCancellation(CancellationFull c) 
    {
        ClassCancellation newCancellation = Mapper.Map<ClassCancellation>(c);

        dc.ClassCancellations.Add(newCancellation);
        dc.SaveChanges();

        return Mapper.Map<CancellationFull>(dc.ClassCancellations.FirstOrDefault(cc => cc.Id == newCancellation.Id));
    }

为什么,为了爱上帝为什么,当提供每个现有实体对应物的Faculty(主键)时,数据库是否为CourseId创建了新对象?它也可能对Student个对象做同样的事情,但我没有仔细看过。

ClassCancellation实例保存到数据库之前,调试器显示类型Professor的属性Faculty和类型Course的{​​{1}}具有正确的属性Course主键 - 即我尝试使用对新ClassCancellation对象的引用更新的那些类型的现有实体的主键。

让我疯了。随意请求澄清!

编辑:

以下是从表单数据构建CancellationFull视图模型的逻辑,以及从各自的存储库中检索到的现有对象的视图模型:

     newCancellation = new CancellationFull();
     newCancellation.CancelledOn = DateTime.Now;
     newCancellation.ClassCancelled = repoClass.getClassDateForListById(Int32.Parse(classIds[i]));
     newCancellation.Course = repoCourse.getForList(newCancellation.ClassCancelled.Course.Id);
     newCancellation.CancellationMessage = repoMessage.getMessageForList(newMessage.Id);
     newCancellation.Professor = repoFac.getFacultyForList((int)Session["facId"]);

     var students = repoStudent.getStudentsForListByCourse(newCancellation.Course.Id);

     foreach ( var student in students )
     {
         newCancellation.Students.Add(student);
     }

     repoCancellation.createClassCancellation(newCancellation);

以下是其中一个repo方法的示例(其余的非常相似):

    public CourseForList getForList(int? id)
    {
        return Mapper.Map<CourseForList>(dc.Courses.FirstOrDefault(c => c.Id == id));
    }

3 个答案:

答案 0 :(得分:3)

我发现最简单的解决方案是更新模型,清除任何相关实体,然后重新添加它们。

即:

 newCancellation.Students.Clear();

 foreach ( var student in students )
 {
     newCancellation.Students.Add(student);
 }

答案 1 :(得分:2)

尝试使用Attach()代替Add()

dc.ClassCancellations.Attach(newCancellation);
dc.SaveChanges();

Add()用于数据库中尚不存在的新对象。 Attach()用于创建与数据库中已存在的实体的关系。

修改

如果没有看到您的代码,我建议您附加的最佳解决方案是创建一个&#39; stub&#39;实例,然后将其附加到您的newCancellation

var existingCourse = new Course{ Id = newCancellation.ClassCancelled.Course.Id };
db.Courses.Attach(existingCourse);
newCancellation.Course = existingCourse;

答案 2 :(得分:1)

问题在于您有多个上下文或工作单元。将newCancellation添加到dc上下文时,它还会在对象图中添加未在dc上下文中跟踪的任何相关实体。我认为你最好的选择是:

dc.ClassCancellations.Add(newCancellation);
dc.Entry(newCancellation.Course).State = EntityState.Unchanged;
dc.Entry(newCancellation.Faculty).State = EntityState.Unchanged;

有关说明和其他选项,请参阅Julie Lerman's article on this issue

在我看来,EF应该识别具有自动编号密钥的实体,如果分配了密钥则不插入它们。