【问题标题】:Entity Framework - Inserting records in child table when parent record already exists实体框架 - 当父记录已经存在时在子表中插入记录
【发布时间】:2011-04-04 12:01:21
【问题描述】:

下午好-

我正在为数据访问层使用 LINQ to Entity Framework 开发 Silverlight 应用程序。我的 n 层数据库模型包括大约 30 个左右的表,其中包含许多多层次的父子关系。最近开始编写我的数据服务和单元测试,并发现在父表记录已定义时将新记录插入子表的问题。这是我的父表和子表的精简版:

父表:

用户
用户 ID (PK)
电子邮件
密码
等等……

儿童桌:

用户配置文件
用户配置文件 ID (PK)
各种个人资料信息...
UserID(FK -- 用户表)

所以此时,我已经使用实体框架为 ORM 创建了各种类,并且我正在寻找为我的实体上的 CRUD 操作创建数据服务。以下是我插入新 User(没有父表引用)的方法的示例代码:

    public User CreateUser(User newUser)
    {
        using (var _context = new ProjectDatabase())
        {
            newUser.CreatedDate = DateTime.Now;
            newUser.ModifiedDate = DateTime.Now;
            _context.AddToUsers(newUser);
            _context.SaveChanges();
        }

        return newUser;
    }

这很好用。我已经为它编写了单元测试,没问题。现在,另一方面,考虑我编写的以下数据服务方法,用于将新的 UserProfile 对象插入数据库。首先,我有一个非常相似的数据服务:

    public UserProfile CreateProfile(UserProfile newUserProfile)
    {
        using (var _context = new ProjectDatabase())
        {
            newUserProfile.CreatedDate = DateTime.Now;
            newUserProfile.ModifiedDate = DateTime.Now;
            _context.AddToUserProfiles(newUserProfile);
            _context.SaveChanges();
        }

        return newUserProfile;
    }

此代码与其他代码一样,可以毫无问题地编译。但是,我的单元测试始终失败。这是我的单元测试的代码:

    [TestMethod]
    public void UserProfileCrudTest()
    {
        IProfileDataService _dataService = new ProfileDataService();

        // Pre-seeded data on a State for foreign key
        State _myState;
        using (var _context = new ProjectDatabase())
        {
            _myState = _context.States.First();
        }

        // Creating temporary User for association with new UserProfile
        User _myUser = new User()
                  {
                      Email = "test",
                      Password = "test",
                      IsActive = true,
                      NameFirst = "test",
                      NameLast = "test"
                  };

        _myUser = _dataService.CreateUser(_myUser);
        Assert.IsNotNull(_myUser);

        // Attempt to create new UserProfile, assigning foreign key
        _myUserProfile = new UserProfile()
                 { 
                     Address = "123 Main", 
                     City = "Anywhere", 
                     State = _myState, 
                     PostalCode = "63021", 
                     UserID = _myUser.UserID 
                 };

        // This call to CreateProfile throws an exception and the test fails!!
        _myUserProfile = _dataService.CreateProfile(_myUserProfile);
        Assert.IsNotNull(_myUserProfile);

        // Remaining test code... never gets this far... 
    }

所以此时,我的单元测试抛出以下异常:

测试方法 MyProject.DataServices.Tests.SecurityTests.UserProfileCrudTest 抛出异常: System.InvalidOperationException:EntityKey 属性只有在属性当前值为空时才能设置。

据我目前的研究了解,这以某种方式与导航属性相关联,该属性将新的 UserProfile 对象与父表/对象上的 User.UserProfiles 集合相关联。但我不确定解决问题需要哪些步骤。我是否需要以某种方式将父级附加到 ObjectContext?任何帮助将不胜感激!

谢谢, 杰夫·S.

【问题讨论】:

    标签: entity-framework-4 parent-child wcf-data-services


    【解决方案1】:

    你不需要做所有这些事情。使用 EF 4.0,您的代码可以像这样简单:

    public UserProfile Create(UserProfile userProfile) {
        using (var context = new ProjectDatabase()) {                     
            //add the new graph to the context:
            context.UserProfiles.AddObject(userProfile);
            context.SaveChanges();
        }
        return userProfile;
    }
    
    public void UserProfileCrudTest() {    
        User _myUser = new User() {
            Email = "test",
            ...
        };
        UserProfile _myUserProfile = new UserProfile() {
            Address = "123 Main",
            ...
        };
    
        // join the new UserProfile to the User
        _myUserProfile.User = _myUser;
        UserProfile userProfile = Create(_myUserProfile);    
    }
    

    说明:

    您所做的在典型的数据访问场景中很有意义,您必须首先插入新的 User,检索其 UserID,然后使用它来插入新的 UserProfile。但是,当 SaveChanges 看到这两个 是新的并且它们是相关的。它还使用模型的映射来确定哪个 是依赖实体(在本例中为 UserProfile),需要外键 (UserID)。 使用此信息,它以正确的顺序执行数据库插入。
    向现有用户添加新的 UserProfile:
    如果您想向现有用户添加一个新的 UserProfile 并且您已经拥有想要添加配置文件的 User 对象,您可以通过编写来关联它们:

    userProfile.User = yourUserObject
    // OR
    yourUserObject.UserProfiles.add(userProfile)
    

    然后调用 SavingChanges()。

    如果您只有 UserID,则可以像 UserProfile.UserID = userID 一样设置它。顺便说一句,我知道这是您最初尝试做的事情并且您遇到了异常,因此您需要执行以下操作:

    1. 从数据库更新您的模型,以确保您的模型与您的数据库完全同步。

    2. 逐步检查您的代码并确保您为 UserProfile 对象设置了合法的 UserID(用户表中确实存在的东西)——您甚至可以尝试对其进行硬编码。

    3. 再次运行它,如果您仍然收到异常,请发布您的堆栈跟踪,因为我运行了您的代码但我没有收到任何信息,因此您的异常可能来自完全不同的问题。

    【讨论】:

    • 感谢您的帮助,莫特萨!但采取不同的场景——在这个应用程序的生产中更有可能——用户行存在一段时间,然后用户配置文件在以后添加,仍然需要与之前创建的用户记录相关联?这就是我试图用我的单元测试来模拟的。 -杰夫
    • 没问题。根据您的代码,我认为您对插入两个全新的依赖对象更感兴趣,我的观点是您可以将它们作为一个整体保存,就像您使用 EF 将它们作为一个整体读取一样。我编辑了我的答案以解决更新方案,请试一试,让我知道它是如何为你工作的。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2016-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多