实体框架 - 当父记录已存在时,在子表中插入记录

jef*_*uss 9 parent-child entity-framework-4 wcf-data-services

下午好-

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

父母表:

用户
    UserID(PK)
    电子邮件
    密码
    等...

儿童桌:

UserProfiles
    UserProfileID(PK)
    各种个人资料信息...
    用户ID(FK - 用户表)

所以在这一点上,我已经使用Entity Framework为ORM创建了各种类,我希望为我的实体上的CRUD操作创建数据服务.以下是我的方法插入新用户(没有父表引用)的示例代码:

    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;
    }
Run Code Online (Sandbox Code Playgroud)

这很好用.我已经为它编写了单元测试,没问题.另一方面,现在考虑我编写的以下数据服务方法,以将新的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;
    }
Run Code Online (Sandbox Code Playgroud)

这个代码就像其他代码一样编译没有问题.但是,我的单元测试始终失败.这是我的单元测试的代码:

    [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... 
    }
Run Code Online (Sandbox Code Playgroud)

所以在这一点上,我的单​​元测试抛出以下异常:

测试方法MyProject.DataServices.Tests.SecurityTests.UserProfileCrudTest引发异常: System.InvalidOperationException:只有在属性的当前值为null时才能设置EntityKey属性.

正如我从目前为止的研究中所理解的那样,这在某种程度上回到了导航属性,该属性将新的UserProfile对象与父表/对象上的User.UserProfiles集合相关联.但我不确定解决问题需要哪些步骤.我是否需要将父级附加到ObjectContext?任何帮助都将非常感谢!

谢谢,杰夫S.

Mor*_*avi 8

你不需要做那些事情.使用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);    
}
Run Code Online (Sandbox Code Playgroud)

说明:

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

userProfile.User = yourUserObject
// OR
yourUserObject.UserProfiles.add(userProfile)
Run Code Online (Sandbox Code Playgroud)

然后调用SavingChanges().

如果您只有UserID,则可以像UserProfile.UserID = userID一样设置它.顺便说一句,我知道这是你最初尝试做的事情而且你有一个例外,所以这就是你需要做的事情:

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

2.逐步执行代码并确保将正确的UserID设置为UserProfile对象(用户表中确实存在的东西) - 您甚至可以尝试对其进行硬编码.

3.再次运行它,如果你仍然遇到异常,那么请发布你的堆栈跟踪,因为我运行你的代码并且我没有得到任何,所以你的异常可能来自一个完全不同的问题.