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

发布于 2024-09-16 14:35:28 字数 3315 浏览 12 评论 0原文

下午好

- 我正在使用 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:仅当属性的当前值为 null 时才能设置 EntityKey 属性。

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

谢谢, 杰夫·S。

Good afternoon-

I am developing a Silverlight application using LINQ to Entity Framework for the data access layer. My n-tier database model includes about 30 or so tables, with a number of multi-leveled parent-child relationships throughout. Recently began writing my data services and unit tests and have discovered a problem with the insert of a new record into a child table when the parent table record is already defined. Here's an abridged version of my parent and child tables:

PARENT TABLE:

Users
    UserID (PK)
    Email
    Password
    etc...

CHILD TABLE:

UserProfiles
    UserProfileID (PK)
    Various profile info...
    UserID (FK -- Users table)

So at this point, I have the Entity Framework has already been used to create my various classes for the ORM, and I am looking to create data services for the CRUD operations on my entities. The following is sample code for my method to insert a new User (which has no parent table references):

    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;
    }

This works just fine. I have written unit tests for it, no problem. Now, on the other hand, consider the following data service method that I have written to insert a new UserProfile object into the database. To begin with, I have a very similar data service:

    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;
    }

This code, just like the other, compiles without problem. However, my unit test consistently fails. Here is the code for my unit test:

    [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... 
    }

So at this point, my unit test throws the following exception:

Test method MyProject.DataServices.Tests.SecurityTests.UserProfileCrudTest threw exception:
System.InvalidOperationException: The EntityKey property can only be set when the current value of the property is null.

As I understand it from my research so far, this somehow is tying back to the navigation property that associates the new UserProfile object with the User.UserProfiles collection on the parent table/object. But I'm not sure what steps are needed to fix the problem. Do I somehow need to attach the parent to the ObjectContext? Any assistance would be GREATLY appreciated!

Thanks,
Jeff S.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

掀纱窥君容 2024-09-23 14:35:28

你不需要做所有这些事情。使用 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);    
}

说明:

您所做的在典型的数据访问场景中很有意义,您必须首先插入新的用户,检索其 UserID,然后使用它插入新的UserProfile。但是,当 SaveChanges 发现两者都存在时,它会为您完成所有这一切
是新的并且它们是相关的。它还使用模型的映射来确定哪些
是依赖实体(在本例中为 UserProfile)并且需要外键 (UserID)。
有了这些信息,它就可以按照正确的顺序执行数据库插入。

向现有用户添加新的用户配置文件:

如果您想将新的 UserProfile 添加到现有 User 并且您已经拥有要添加配置文件的 User 对象,您可以通过编写以下内容将它们关联起来:

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

然后调用SavingChanges()。

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

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

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

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

You don't need to do all that stuff. With EF 4.0 your code could be as simple as this:

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);    
}

Explanation:

What you did is well make sense in typical data access scenarios, where you would have to first insert the new User, retrieve its UserID, and then use that to insert the new UserProfile. However, SaveChanges does all of this for you when it sees that both
are new and that they are related. It also uses the model’s mappings to figure out which
is the dependent entity (in this case, UserProfile) and needs the foreign key (UserID).
With this information, it executes the database inserts in the correct order.

Adding a new UserProfile to an existing User:

If you want to add a new UserProfile to an existing User and you already have the User object you wish to add the profile to, you can just relate them by writing:

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

and then call the SavingChanges().

If you only have the UserID, you can set it just like UserProfile.UserID = userID. BTW, I am aware that this is what you originally tried to do and you got an exception, so here is what you need to do:

1. Update your Model from the database to make sure that your model is fully sync with your DB.

2. Step through your code and make sure that you are setting a legitimate UserID to UserProfile object (something that really exist in User Table) - you can even try to hard code it.

3. Run it again and if you still are getting exception, then please post your stack trace since I ran your code and I did not get any, so your exception might be coming from a completely different issue.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文