如何解决尝试使用 OData 和实体框架克隆 Project Server 数据库时出现问题
我在使用 Parallel.Foreach 更新我的实体时遇到问题。我拥有的程序通过使用 foreach 来更新实体可以正常工作,但是如果我使用 Parallel.Foreach 它会给我这样的错误:“参数异常:已经添加了具有相同键的项目”。我不知道为什么会这样,它不应该是线程安全的吗?或者为什么给我这个错误?如何解决这个问题?
程序本身从数据库中获取一些数据并将其复制到另一个数据库中。如果数据行存在相同的 guid(见下文),并且状态不变,则必须更新第二个匹配数据行。如果匹配,并且状态发生变化,则必须忽略修改。最后,如果在第二个数据库中没有匹配项,则将数据行插入到第二个数据库中。 (同步两个数据库)。我只是想以某种方式加快进程,这就是我首先想到并行处理的原因。
(如果重要的话,我使用 Autofac 作为 IoC 容器和依赖项注入)
这是尝试更新的代码片段:
/* @param reports: data from the first database */
public string SynchronizeData(List<Reports> reports,int statusid)
{
// reportdataindatabase - the second database data,List() actually selects all,see next code snippet
List<Reports> reportdataindatabase = unitOfWorkTAFeedBack.ReportsRepository.List().ToList();
int allcount = reports.Count;
int insertedcount = 0;
int updatedcount = 0;
int ignoredcount = 0;
// DOES NOT WORK,GIVES THE ERROR
Parallel.ForEach(reports,r =>
{
var guid = reportdataindatabase.FirstOrDefault(x => x.AssignmentGUID == r.AssignmentGUID);
if (guid == null)
{
unitOfWorkTAFeedBack.ReportsRepository.Add(r); // an insert on the repository
insertedcount++;
}
else
{
if (guid.StatusId == statusid)
{
r.ReportsID = guid.ReportsID;
unitOfWorkTAFeedBack.ReportsRepository.Update(r); // update on the repo
updatedcount++;
}
else
{
ignoredcount++;
}
}
});
/* WORKS PERFECTLY BUT RELATIVELY SLOW - takes 80 seconds to update 1287 records
foreach (Reports r in reports)
{
var guid = reportdataindatabase.FirstOrDefault(x => x.AssignmentGUID == r.AssignmentGUID); // find match between the two databases
if (guid == null)
{
unitOfWorkTAFeedBack.ReportsRepository.Add(r); // no match,insert
insertedcount++;
}
else
{
if (guid.StatusId == statusid)
{
r.ReportsID = guid.ReportsID;
unitOfWorkTAFeedBack.ReportsRepository.Update(r);
updatedcount++;
}
else
{
ignoredcount++;
}
}
} */
unitOfWorkTAFeedBack.Commit(); // this only calls SaveChanges() on DbContext object
int allprocessed = insertedcount + updatedcount + ignoredcount;
string result = "Synchronization finished. " + allprocessed + " reports processed out of " + allcount + ","
+ insertedcount + " has been inserted," + updatedcount + " has been updated and "
+ ignoredcount + " has been ignored. \n Press a button to dismiss this window." ;
return result;
}
程序在 Update 方法中的这个 Repository 类上中断(使用 Parallel.Foreach,标准 foreach 没有问题):
public class EntityFrameworkReportsRepository : IReportsRepository
{
private readonly TAFeedBackContext tAFeedBackContext;
public EntityFrameworkReportsRepository(TAFeedBackContext tAFeedBackContext)
{
this.tAFeedBackContext = tAFeedBackContext;
}
public void Add(Reports r)
{
tAFeedBackContext.Reports.Add(r);
}
public void Delete(int Id)
{
var obj = tAFeedBackContext.Reports.Find(Id);
tAFeedBackContext.Reports.Remove(obj);
}
public Reports Get(int Id)
{
var obj = tAFeedBackContext.Reports.Find(Id);
return obj;
}
public IQueryable<Reports> List()
{
return tAFeedBackContext.Reports.AsNoTracking();
}
public void Update(Reports r)
{
var entry = tAFeedBackContext.Entry(r); // The Program Breaks At This Point!
if (entry.State == EntityState.Detached)
{
tAFeedBackContext.Reports.Attach(r);
tAFeedBackContext.Entry(r).State = EntityState.Modified;
}
else
{
tAFeedBackContext.Entry(r).CurrentValues.SetValues(r);
}
}
}
解决方法
请记住,很难给出完整的答案,因为有些事情我需要澄清……但评论应该有助于构建图片。
Parallel.ForEach(reports,r => //Parallel.ForEach is not the answer..
{
//reportdataindatabase is done..before so ok here
// do you really want FirstOrDefault vs SingleOrDefault
var guid = reportdataindatabase.FirstOrDefault(x => x.AssignmentGUID == r.AssignmentGUID);
if (guid == null)
{
// this is done on the context not the DB,unresolved..(excuted)
unitOfWorkTAFeedBack.ReportsRepository.Add(r); // an insert on the repository
//insertedcount++; u would need a lock
}
else
{
if (guid.StatusId == statusid)
{
r.ReportsID = guid.ReportsID;
// this is done on the context not the DB,unresolved..(excuted)
unitOfWorkTAFeedBack.ReportsRepository.Update(r); // update on the repo
//updatedcount++; u would need a lock
}
else
{
//ignoredcount++; u would need a lock
}
}
});
这里的问题......因为reportdataindatabase可以包含两次相同的键...... 并且上下文仅在事后更新,也就是当它到达这里时..
unitOfWorkTAFeedBack.Commit();
它可能被同一个实体调用了两次 如上所述(提交)是工作所在......在并行执行上面的添加/更新不会为您节省任何实时时间,因为那部分很快..
//更新 1287 条记录需要 80 秒...看起来确实很长... //列出reportdataindatabase = unitOfWorkTAFeedBack.ReportsRepository.List().ToList();
//PS 添加如何检索报告.. 你想要类似的东西
TAFeedBackContext db = new TAFeedBackContext();
var remoteReports = DatafromAnotherPLace //include how this was retrieved;
var localReports = TAFeedBackContext.Reports.ToList(); //these are tracked.. (by default)
foreach (var item in remoteReports)
{
//i assume more than one is invalid.
var localEntity = localReports.SingleOrDefault(x => x.AssignmentGUID == item.AssignmentGUID);
if (localEntity == null)
{
//add as it doenst exist
TAFeedBackContext.Reports.Add(new Report() { *set fields* });
}
else
{
if (localEntity.StatusId == statusid) //only update if status is the passed in status.
{
//why are you modifying the remote entity
item.ReportsID = localEntity.ReportsID;
//update remove entity?,i get the impression its from a different context,//if not then cool,but you need to show how reports is retrieved
}
else
{
}
}
}
TAFeedBackContext.SaveChanges();
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。