Добрый день -
Я занимаюсь разработкой приложения 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? Буду признателен за любую помощь!
Спасибо, Джефф С.
Вам не нужно делать все это. С 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. Запустите его еще раз, и если вы все еще получаете исключение, опубликуйте трассировку стека, поскольку я запустил ваш код и не получил его, поэтому ваше исключение может быть вызвано совершенно другой проблемой.