我有一个类ReportConfigurationManager,它管理针对UserReport实体的CRUD操作。感兴趣的两个操作是“Get”和“SaveUpdate”。在这两种情况下,我将操作包装在using语句中,以便将DbContext放置在查询的末尾。
现在最终这些方法将构成WCF服务的一部分,但也可以在服务内部调用它们。我目前的困难是让一组单元测试工作,直接调用ReportConfigurationManager。
我可以创建一个新的UserReport并保存它(由于实体有几个已经存在于数据库中的嵌套对象,这需要我一段时间才能解决 - 我需要在调用之前将这些对象依次“附加”到上下文中添加UserReport以使其正确保存。
我现在的问题是更新。
尽管有
context.Configuration.ProxyCreationEnabled = false;
context.Configuration.AutoDetectChangesEnabled = false;
在使用ReportConfigurationManager的所有方法上,当我附加一个UserReport时,它失败了经典的“ObjectStateManager中已经存在一个具有相同键的对象”(我认为禁用更改跟踪是为了处理这个?)
所以现在我已经切换到使用以下代码here
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;
}
我担心的是我的代码看起来很费力 - 在保存更新的副本之前,我需要获取旧对象的副本吗?而且我也不相信我的Save New逻辑。
这种方法是正确的,还是有更好的方法来编写上述内容?
其他事情的进一步细节:
因为我将通过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。
使用Ladislav的回答中的代码,当我尝试附加UserReport时失败,可能是因为我附加的对象之一已经存在于数据库中。
答案 0 :(得分:1)
是的还有另一种方式。首先,您应该知道EF不支持部分附加的对象图,因此Attach
和Add
都有附加或添加图中尚未被上下文跟踪的所有实体的副作用。这将大大简化您的插入代码。
public UserReport SaveUpdateUserReport(UserReport userReport)
{
using (var context = new ReportDataEF())
{
context.Configuration.ProxyCreationEnabled = false;
context.Configuration.AutoDetectChangesEnabled = false;
// Now all entities in the graph are attached in unchanged state
context.ReportTopTypes.Attach(userReport);
if (userReport.Id > 0 &&
context.UserReports.Any(ur => ur.Id == userReport.Id))
{
context.Entry(userReport).State = EntityState.Modified;
}
else
{
context.Entry(userReport).State = EntityState.Added;
}
context.SaveChanges();
}
return userReport;
}
这相当于您的原始代码。您不再次加载用户报告 - 您只需检查其在DB中的存在。此代码存在很多问题 - 例如,如果您更改了任何其他相关对象,则它将不会持久保存到数据库,因为其当前状态为Unchanged
。如果你需要改变关系,那就更复杂了。