我的Users - Rules
表与many-to-many
关系。我想删除某个用户的某些规则并在同一步/数据库事务中更新该用户的一个字段,所以我在BL中有以下伪代码:
this.UnitOfWork.ExecuteTransaction(() =>
{
// delete rules
foreach (var rule in ruleList)
repoRules.DeleteRulesForUser(user, rule);
// update user field
repoUser.UpdateUserField(user);
}
在EF存储库中:
public void DeleteRulesForUser(User user, Rule rule)
{
// check user
EFUser u = this.dbContext.EFUsers.Find(user.UID);
if (u == null)
throw new DBModuleException();
// check rule
EFRule r = this.dbContext.EFRules.Find(rule.UID);
if (r == null)
throw new DBModuleException();
// detach previous entities, so that we can attach with relation
this.dbContext.ObjectContext.Detach(u);
this.dbContext.ObjectContext.Detach(r);
// prepare entities
var efUser = new EFUser { US_UID = user.UID };
var efRule = new EFRule { RU_UID = rule.UID };
efRule.Users.Add(efUser);
// attach
this.dbContext.EFRules.Attach(efRule);
// delete relation
efRule.Users.Remove(efUser);
}
public void UpdateUserField(User user)
{
// get updating user
EFUser efUser = this.dbContext.EFUsers.FirstOrDefault(u => u.UID == user.UID);
if (efUser == null)
throw new DBModuleException("Not found!");
// perform update
efUser.US_Field = user.Field;
}
我的UnitOfWork实现(仅限EF6或更高版本):
public override void ExecuteTransaction(Action transAction)
{
// make sure to make IsolationLevel flexible later
using (var dbTransaction = this.dbContext.Database
.BeginTransaction(IsolationLevel.ReadCommitted))
{
try
{
transAction();
// save changes if not done yet
this.dbContext.SaveChanges();
// commit transaction
dbTransaction.Commit();
}
catch (System.Data.DataException ex)
{
// rollback
dbTransaction.Rollback();
// handling exception ceremony comes here
}
}
我的问题是DeleteRulesForUser
仅使用UID(在方法中指定)附加该用户,但后来在UpdateUserField
EF中为该用户提供的只是UID,而不是具有完整数据的用户,因为FirstOrDefault
调用数据库。我假设这是EF缓存的工作方式(?)。无论如何,更新操作失败了,我想学习如何最好地解决这个问题。
我看到有时需要加载只有UID的实体,但有时需要加载完整的数据。
我正在考虑在DeleteRulesForUser
方法结束时分离实体,但我认为这对性能没有好处,因为BL中有时存在for循环。我也想也许我应该重写我的Update方法,但是还不知道这样做的好方法。
你会推荐什么?
仅供参考:我禁用了LazyLoading。
注意:BL不使用EF类,而是使用专有域类(稍后添加了EF支持)。不要批评现有的架构,我想要解决这个问题。
EDIT1:在我阅读更多内容时,也许AutoDetectChangesEnabled
可以提供帮助,但我需要更多信息才能找到。
答案 0 :(得分:1)
如果您需要删除数以千计的规则,我建议删除Entity Framework并使用ADO.NET和sql语句(或存储过程),这样更快更简单。请参阅Batch update/delete EF5。
也没有理由附加或分离物体。如果您使用EF,请在需要时立即创建上下文并尽快处理。
答案 1 :(得分:0)
没有理由从对象状态管理器中分离对象。仅仅通过查看代码,它对你要做的事情没有多大意义。也许您对EF没有太多经验,但在使用之前我会建议使用reading up on how EF works。
您只需加载用户实体,并在批处理操作的结果集中包含规则:var dbUser = ctx.Users.Include(x => x.Rules).FirstOrDefault(x => x.UID == user.UID);
现在您拥有完全填充的用户对象以及与之关联的所有规则,您可以找到要删除的规则:var ruleToDelete = dbUser.Rules.FirstOrDefault(x => x.UID == rule.UID);
。
现在您拥有要删除的用户的规则,并且不会花费您返回数据库的费用。现在您可以删除规则:ctx.Rules.Remove(ruleToDelete);
现在,当您致电ctx.SaveChanges()
时,更改将被推送回数据库。您需要了解实体框架会自动为您在事务中包装所有insert
,update
和delete
语句。此外,EF不支持批量insert
,update
和delete
操作。
//Get the user from the db, should check for null after this
var dbUser = ctx.Users.Include(x => x.Rules).FirstOrDefault(x => x.UID == user.UID);
//Get the rule from the user object you want to remove
var ruleToDelete = dbUser.Rules.FirstOrDefault(x => x.UID == rule.UID);
//Set the state of the object to "Deleted" in the state manager so it will issue a delete statement
ctx.Rules.Remove(ruleToDelete);
//Save the changes to the database in a transaction
ctx.SaveChanges()