В моем приложении довольно глубокая иерархия объектов, и у меня возникают проблемы с сохранением объектов.В зависимости от порядка, в котором я что-то делаю, у меня возникает одна из двух ошибок:
[OptimisticConcurrencyException: оператор обновления, вставки или удаления магазина затронул неожиданное количество строк (0).Объекты могут быть изменены или удалены с момента загрузки объектов.Обновите записи ObjectStateManager.]
или
[DbUpdateException: произошла ошибка при сохранении сущностей, которые не предоставляют свойства внешнего ключа для их отношений.Свойство EntityEntries вернет значение NULL, поскольку один объект не может быть определен как источник исключения.Обработка исключений при сохранении может быть упрощена путем предоставления свойств внешнего ключа в типах объектов.Подробности смотрите в InnerException.]
Вот классы, с которыми я работаю:
public class SpecialEquipment : Entity
{
public Equipment Equipment { get; set; }
public virtual ICollection<AutoclaveValidation> Validations { get; set; }
}
public class Equipment : Entity
{
public string Model { get; set; }
public string SerialNumber { get; set; }
public Location Location { get; set; }
public EquipmentType EquipmentType { get; set; }
public ICollection<Identifier> Identifiers { get; set; }
}
public class Identifier : Entity
{
public IdentifierType Type { get; set; }
public string Value { get; set; }
}
public class Location : Entity
{
public Building Building { get; set; }
public string Room { get; set; }
}
Я пытался заполнить один SpecialEquipment
объект на основе формывходы и уже существующие объекты в базе данных, а затем сохранить специальное оборудование для проталкивания всех изменений, это выглядит так:
Building building = buildingService.GetExistingOrNew(viewModel.BuildingCode)); //checks to see if building exists already, if not, create it, save it, and re-query
Location location = locationService.GetExistingOrNew(viewModel.Room, building); //checks to see if location exists already, if not, create it, save it, and re-query
EquipmentType equipmentType = equipmentTypeService.GetOne(x => x.Name == EquipmentTypeConstants.Names.Special);
Equipment equipment = new Equipment{ EquipmentType = equipmentType, Location = location };
equipment.Identifiers = new Collection<Identifier>();
foreach (FormIdentifier formIdentifier in identifiers)
{
FormIdentifier fIdentifier = formIdentifier;
IdentifierType identifierType = identifierTypeService.GetOne(x => x.Id == fIdentifier.Key);
equipment.Identifiers.Add(new Identifier { Type = identifierType, Value = fIdentifier.Value });
}
EntityServiceFactory.GetService<EquipmentService>().Save(equipment);
SpecialEquipment specialEquipment = new SpecialEquipment();
specialEquipment.Equipment = equipment;
specialEquipmentService.Save(specialEquipment);
Этот код возвращает Store update, insert, or delete statement affected an unexpected number of rows (0)
.Если я закомментирую foreach identifiers
ИЛИ, поставьте foreach identifiers
после equipment save
и затем вызовите equipment save
после цикла, код работает.Если я закомментирую строку foreach identifiers
и save equipment
, я получу: The INSERT statement conflicted with the FOREIGN KEY constraint "SpeicalEquipment_Equipment". The conflict occurred in database "xxx", table "dbo.Equipments", column 'Id'.
Так как я могу сделать так, чтобы эти ошибки не возникали, но все же сохраняли мой объект?Есть лучший способ сделать это?Также мне не нравится сохранять объект моего оборудования, затем связывать / сохранять мои идентификаторы и / или затем мой объект специального оборудования, потому что в случае возникновения ошибки между этими этапами у меня будут потерянные данные.Кто-то может помочь?
Я должен упомянуть несколько вещей, которые не унаследованы из кода, но были ответы, которые я видел на похожие вопросы:
- Моя структура хранит контекст вHttpContext, поэтому все сервисные методы, которые я использую в своем API, используют один и тот же контекст в этом блоке кода.Таким образом, все объекты поступают / хранятся в одном контексте.
- Конструктор My Entity заполняет ID каждый раз, когда создается новый объект, ни у каких объектов нет пустого первичного ключа.
Редактировать:По запросу комментариев:
Мой метод .Save
вызывает вставку или обновление в зависимости от того, существует сущность или нет (в этом примере вставка вызывается, поскольку specialEquipment является новым):
public void Insert(TClass entity)
{
if (Context.Entry(entity).State == EntityState.Detached)
{
Context.Set<TClass>().Attach(entity);
}
Context.Set<TClass>().Add(entity);
Context.SaveChanges();
}
public void Update(TClass entity)
{
DbEntityEntry<TClass> oldEntry = Context.Entry(entity);
if (oldEntry.State == EntityState.Detached)
{
Context.Set<TClass>().Attach(oldEntry.Entity);
}
oldEntry.CurrentValues.SetValues(entity);
//oldEntry.State = EntityState.Modified;
Context.SaveChanges();
}
GetExistingOrNew для Building и location одинаковы по логике:
public Location GetExistingOrNew(string room, Building building)
{
Location location = GetOne(x => x.Building.Code == building.Code && x.Room == room);
if(location == null)
{
location = new Location {Building = building, Room = room};
Save(location);
location = GetOne(x => x.Building.Code == building.Code && x.Room == room);
}
return location;
}
Get просто передает это, где предикат к контексту в моем репозитории с singleOrDefault.Я использую сервисный уровень / уровень репозитория / объектный слой для моей платформы.