Entity Framework - Вставка записей в дочернюю таблицу, когда родительский запись уже существует

Добрый день -

Я занимаюсь разработкой приложения Silverlight с использованием LINQ to Entity Framework для уровня доступа к данным. Моя n-уровневая модель базы данных включает в себя около 30 или около того таблиц с множеством многоуровневых родительских и дочерних отношений. Недавно начал писать свои службы данных и модульные тесты и обнаружил проблему со вставкой новой записи в дочернюю таблицу, когда запись родительской таблицы уже определена. Вот' E-mail
Пароль
и т.д. ...

ТАБЛИЦА РЕБЕНКОВ:

UserProfiles
UserProfileID (PK)
Различная информация о профиле ...
UserID (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;
    }

Это работает просто отлично. Я написал для него юнит-тесты, никаких проблем. Теперь, с другой стороны, рассмотрим следующий метод обслуживания данных, который я написал для вставки нового объекта 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... 
    }

Итак, в этот момент мой модульный тест выдает следующее исключение: System.InvalidOperationException : Свойство EntityKey может быть установлено только в том случае, если текущее значение свойства равно нулю.

Насколько я понимаю из моих исследований, это как-то связано со свойством навигации, которое связывает новый объект UserProfile с коллекцией User.UserProfiles в родительской таблице / объекте. Но я не уверен, какие шаги необходимы для решения проблемы. Нужно ли каким-то образом прикреплять родительский элемент к ObjectContext? Буду признателен за любую помощь!

Спасибо, Джефф С.

9
задан marc_s 3 October 2010 в 19:49
поделиться

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

Объяснение:

То, что вы сделали, имеет смысл в типичных сценариях доступа к данным, когда вам нужно сначала вставить нового Пользователя, получить его UserID, а затем использовать его для вставки нового UserProfile. Однако SaveChanges сделает все это за вас, когда увидит, что оба являются новыми и что они связаны. Он также использует сопоставления модели, чтобы выяснить, какие является зависимой сущностью (в данном случае UserProfile) и нуждается во внешнем ключе (UserID). С этой информацией он выполняет вставки в базу данных в правильном порядке.
Добавление нового профиля пользователя к существующему пользователю:
Если вы хотите добавить новый профиль пользователя к существующему пользователю и у вас уже есть объект пользователя, к которому вы хотите добавить профиль, вы можете просто связать их, написав:

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

а затем вызовите функцию SavingChanges().

Если у вас есть только UserID, вы можете установить его точно так же, как UserProfile.UserID = userID. Кстати, я знаю, что это то, что вы изначально пытались сделать, и вы получили исключение, так что вот что вам нужно сделать:

1. Обновите свою модель из базы данных, чтобы убедиться, что ваша модель полностью синхронизирована с вашей БД.

2. Пройдите свой код и убедитесь, что вы устанавливаете законный идентификатор пользователя для объекта UserProfile (что-то, что действительно существует в таблице пользователей) - вы даже можете попытаться жестко запрограммировать его.

3. Запустите его еще раз, и если вы все еще получаете исключение, опубликуйте трассировку стека, поскольку я запустил ваш код и не получил его, поэтому ваше исключение может быть вызвано совершенно другой проблемой.

8
ответ дан 4 December 2019 в 22:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: