У меня есть класс ReportConfigurationManager, который управляет операциями CRUD в отношении объекта UserReport. Интересуют две операции: «Получить» и «Сохранить обновление». В обоих случаях я заключаю операцию в оператор using, чтобы DbContext располагался в конце запроса.
Со временем эти методы станут частью службы WCF, но их также можно будет вызывать внутри службы. Мои нынешние трудности связаны с получением набора модульных тестов для работы, которые напрямую вызывают ReportConfigurationManager.
Я могу создать новый UserReport и сохранить его (это заняло у меня некоторое время, чтобы решить, поскольку сущность имеет несколько вложенных объектов, которые уже существуют в базе данных — мне нужно было «прикрепить» каждый из них по очереди к контексту, прежде чем вызывать «Добавить» на UserReport, чтобы он правильно сохранялся.
Мои проблемы сейчас связаны с обновлениями.
Несмотря на наличие
context.Configuration.ProxyCreationEnabled = false;
context.Configuration.AutoDetectChangesEnabled = false;
во ВСЕХ методах, которые используют ReportConfigurationManager, когда я пришел, чтобы прикрепить UserReport, он потерпел неудачу с классическим «объектом с тем же ключом, который уже существует в ObjectStateManager» (я думал, что отключение отслеживания изменений предназначено для обработки этого?).
Итак, теперь я переключился на использование следующего кода, который я нашел здесь
public UserReport SaveUpdateUserReport(UserReport userReport)
{
using (var context = new ReportDataEF())
{
context.Configuration.ProxyCreationEnabled = false;
context.Configuration.AutoDetectChangesEnabled = false;
if (userReport.Id > 0)
{
{
UserReport oldReport = context.UserReports.Where(ur => ur.Id == userReport.Id).FirstOrDefault();
context.Entry(oldReport).CurrentValues.SetValues(userReport);
}
}
else
{
//Need to attach everything to prevent EF trying to create duplicates in the database
context.ReportTopTypes.Attach(userReport.ReportTopType);
context.ReportWindows.Attach(userReport.ReportWindow);
context.ReportSortOptions.Attach(userReport.ReportSortOption);
foreach (var col in userReport.ReportColumnGroups)
{
context.ReportColumnGroups.Attach(col);
}
context.ReportTemplates.Attach(userReport.ReportTemplate);
//just add the new data
context.UserReports.Add(userReport);
}
context.SaveChanges();
}
return userReport;
}
Меня беспокоит то, что мой код кажется трудоемким - мне нужно получить копию старого объекта, прежде чем я смогу сохранить обновленную копию? И меня тоже не убеждает моя логика «Сохранить новое».
Итак, правильный ли это подход, или есть лучший способ написать вышеизложенное?
Более подробная информация о других событиях:
Потому что я буду отправлять графы объектов через WCF. Я реализовал Eager Loading:
public static DbQuery<ReportTemplate> IncludeAll(this DbQuery<ReportTemplate> self)
{
return self
.Include("ReportColumnGroups.ReportColumns.ReportColumnType")
.Include("ReportColumnGroups.ReportColumnType")
.Include("ReportSortOptions.ReportSortColumns.ReportColumn.ReportColumnType")
.Include("ReportSortOptions.ReportSortColumns.ReportSortType");
}
public static DbQuery<UserReport> IncludeAll(this DbQuery<UserReport> self)
{
return self
.Include("ReportTemplate")
.Include("ReportTopType")
.Include("ReportWindow")
.Include("ReportSortOption.ReportSortColumns.ReportColumn.ReportColumnType")
.Include("ReportSortOption.ReportSortColumns.ReportSortType")
.Include("ReportColumnGroups.ReportColumns.ReportColumnType")
.Include("ReportColumnGroups.ReportColumnType");
}
public static DbQuery<ReportSortOption> IncludeAll(this DbQuery<ReportSortOption> self)
{
return self
.Include("ReportSortColumns.ReportColumn.ReportColumnType")
.Include("ReportSortColumns.ReportSortType");
}
public static DbQuery<ReportColumnGroup> IncludeAll(this DbQuery<ReportColumnGroup> self)
{
return self
.Include("ReportColumn.ReportColumnType")
.Include("ReportColumnType");
}
public static DbQuery<ReportColumn> IncludeAll(this DbQuery<ReportColumn> self)
{
return self
.Include("ReportColumnType");
}
public static DbQuery<ReportSortColumn> IncludeAll(this DbQuery<ReportSortColumn> self)
{
return self
.Include("ReportColumn.ReportColumnType")
.Include("ReportSortType");
}
У меня есть набор статических кэшированных данных, которые я получаю следующим образом:
using (var context = new ReportDataEF())
{
context.Configuration.ProxyCreationEnabled = false;
context.Configuration.AutoDetectChangesEnabled = false;
reportConfigurationData = new ReportingMetaData()
{
WatchTypes = context.WatchTypes.ToList(),
ReportTemplates = context.ReportTemplates.IncludeAll().ToList(),
ReportTopTypes = context.ReportTopTypes.ToList(),
ReportWindows = context.ReportWindows.ToList(),
ReportSortOptions =
context.ReportSortOptions.IncludeAll().ToList()
};
}
и я получаю UserReports следующим образом:
public UserReport GetUserReport(int userReportId)
{
using (var context = new ReportDataEF())
{
context.Configuration.ProxyCreationEnabled = false;
context.Configuration.AutoDetectChangesEnabled = false;
var visibleReports =
context.UserReports.IncludeAll().Where(ur => ur.Id == userReportId).FirstOrDefault();
return visibleReports;
}
}
Тест, который меня интересует, получает существующий UserReport из БД, обновляет его свойства ReportTemplate и ReportColumnGroups объектами из класса статических данных, а затем пытается сохранить обновленный UserReport.
Используя код из ответа Ладислава, это не удается, когда я пытаюсь прикрепить UserReport, предположительно потому, что один из прикрепленных к нему объектов уже существует в базе данных.