【问题标题】:Entity Framework: adding existing child POCO to new Parent POCO, creates new child in DB实体框架:将现有的子 POCO 添加到新的父 POCO,在 DB 中创建新的子
【发布时间】:2013-01-13 20:31:29
【问题描述】:

我想在断开连接(从上下文)模式下使用实体框架 POCO。在我的场景中,我正在创建一个新的父对象,并希望将现有的子对象附加到它,然后将其保存到数据库中。
下面的代码在保存新的学生记录时插入了新的课程记录,而我希望将现有的课程记录链接到新的学生记录。

如何在实体框架中执行此操作...

  • 对象可以从上下文中断开。 (即在一个上下文中查询,然后在另一个上下文中保存)
  • 我不需要从数据库中重新查询子记录,这样我就可以在保存到数据库时将它附加到父记录。当我已经将它作为内存中的对象时,我真的想避免对数据库进行额外的访问。

这个页面展示了一个数据库图,下面的代码是基于http://entityframeworktutorial.net/EF4_EnvSetup.aspx#.UPMZ4m-UN9Y

class Program
{
    static void Main(string[] args)
    {

        //get existing course from db as disconnected object
        var course = Program.getCourse();

        //create new student
        var stud = new Student();
        stud.StudentName = "bob";

        //assign existing course to the student
        stud.Courses.Add(course);

        //save student to db
        using (SchoolDBEntities ctx = new SchoolDBEntities())
        {
            ctx.Students.AddObject(stud);
            ctx.SaveChanges();
        }
    }

    static Course getCourse()
    {
        Course returnCourse = null;

        using (var ctx = new SchoolDBEntities())
        {
            ctx.ContextOptions.LazyLoadingEnabled = false;
            returnCourse = (from s in ctx.Courses
                            select s).SingleOrDefault();
        }

        return returnCourse;
    }
}

【问题讨论】:

    标签: c# .net entity-framework


    【解决方案1】:

    我相信有几种方法可以做到这一点。 您可以按照以下方式指定课程实体保持不变而不是添加:

    ctx.Entry(course).State = EntityState.Unchanged;
    

    或指示您的上下文,您正在使用现有实体:

    ctx.Courses.Attach(course);
    

    更多信息在这里: http://msdn.microsoft.com/en-us/data/jj592676.aspx

    编辑

    我的解决方案中有一些正在运行的示例,我验证它们按预期工作。 在所有情况下,我们在数据库中都有 ID = 2 和 Name = "Addison Wesley" 的 Publisher 记录(与示例无关,但只是为了更好地衡量)。

    方法 1 - 设置实体状态

    using (var context = new Context())
    {
        var book = new Book();
        book.Name = "Service Design Patterns";
        book.Publisher = new Publisher() {Id = 2 }; // Only ID is required
        context.Entry(book.Publisher).State = EntityState.Unchanged;
        context.Books.Add(book);
        context.SaveChanges();
    }
    

    方法 2 - 使用 Attach 方法

    using (var context = new Context())
    {
        var book = new Book();
        book.Name = "Service Design Patterns";                
        book.Publisher = new Publisher() { Id = 2 }; // Only ID is required
        context.Publishers.Attach(book.Publisher);
        context.Books.Add(book);
        context.SaveChanges();
    }
    

    方法 3 - 设置外键值

    using (var context = new Context())
    {
         var book = new Book();
         book.Name = "Service Design Patterns";
         book.PublisherId = 2; 
         context.Books.Add(book);
         context.SaveChanges();
    }
    

    对于最后一种工作方法,我需要添加额外的属性 PublisherId,它必须根据 NavigationPropertyName + 'Id' 约定命名,以便 EF 自动获取:

    public int PublisherId { get; set; }
    public Publisher Publisher { get; set; }
    

    我这里用的是EF5 Code First,不过和POCO很像

    【讨论】:

    • 第二个选项几乎可以工作,但我得到一个“无法附加对象,因为它已经在对象上下文中。” “ctx.Students.AddObject(stud);”上的错误线。我想我在做“stud.Courses.Add(course);”的同时不能这样做在它之前。我还想在 stud 对象中使用 Courses 集合。
    • 好吧,我没有方便的解决方案来测试它,但尝试先附加它,然后将它添加到学生。希望这样它不会被 EF 标记为“已添加”。
    • 这并不理想。我希望首先在对象级别发生父->子关系,因为它可能还没有准备好保存到数据库。也许我应该删除 EF 生成的实体之间的所有关系并自己手动控制它?
    • 我刚刚尝试了一些运行应用程序的示例方法,我将添加这些方法,它们可能会澄清一些解决方案。
    • @MakkyNZ - 好的,他们被添加了
    【解决方案2】:

    实体框架不允许跨上下文的关系。

    如果您将课程的阅读并将课程与学生联系起来放在同一个 using 语句中,它会起作用。

    【讨论】:

      【解决方案3】:

      我还尝试了对我有用的第二个选项。我确实喜欢首先在对象级别发生的父->子关系并保存到数据库。也许我应该删除 EF 生成的实体之间的所有关系并自己手动控制它。

      【讨论】:

        猜你喜欢
        • 2012-03-28
        • 1970-01-01
        • 2012-03-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-21
        • 1970-01-01
        相关资源
        最近更新 更多