我有一个存储在数据库中的“站点”列表。列表非常大,包含大约50,000多个记录。
我正在尝试遍历每条记录并对其进行更新。这需要花费很多时间,是否有更好,更有效的方法?
using (IRISInSiteLiveEntities DB = new IRISInSiteLiveEntities())
{
var allsites = DB.Sites.ToList();
foreach( var sitedata in allsites)
{
var siterecord = DB.Sites.Find(sitedata.Id);
siterecord.CabinOOB = "Test";
siterecord.TowerOOB = "Test";
siterecord.ManagedOOB = "Test";
siterecord.IssueDescription = "Test";
siterecord.TargetResolutionDate = "Test";
DB.Entry(siterecord).State = EntityState.Modified;
}
DB.SaveChanges();
}
我已经从代码中删节了。我正在使用的正确功能代码基本上是从Excel中拉出一个列表,然后匹配站点列表中的记录,并相应地更新每个记录。 DB.Find大大降低了循环速度。
[HttpPost]
public ActionResult UploadUpdateOOBList()
{
CheckPermissions("UpdateOOBList");
string[] typesallowed = new string[] { ".xls", ".xlsx" };
HttpPostedFileBase file = Request.Files[0];
var fname = file.FileName;
if (!typesallowed.Any(fname.Contains))
{
return Json("NotAllowed");
}
file.SaveAs(Server.MapPath("~/Uploads/OOB List/") + fname);
//Create empty OOB data list
List<OOBList.OOBDetails> oob_data = new List<OOBList.OOBDetails>();
//Using ClosedXML rather than Interop Excel....
//Interop Excel: 30 seconds for 750 rows
//ClosedXML: 3 seconds for 750 rows
string fileName = Server.MapPath("~/Uploads/OOB List/") + fname;
using (var excelWorkbook = new XLWorkbook(fileName))
{
var nonEmptyDataRows = excelWorkbook.Worksheet(2).RowsUsed();
foreach (var dataRow in nonEmptyDataRows)
{
//for row number check
if (dataRow.RowNumber() >= 4 )
{
string siteno = dataRow.Cell(1).GetValue<string>();
string sitename = dataRow.Cell(2).GetValue<string>();
string description = dataRow.Cell(4).GetValue<string>();
string cabinoob = dataRow.Cell(5).GetValue<string>();
string toweroob = dataRow.Cell(6).GetValue<string>();
string manageoob = dataRow.Cell(7).GetValue<string>();
string resolutiondate = dataRow.Cell(8).GetValue<string>();
string resolutiondate_converted = resolutiondate.Substring(resolutiondate.Length - 9);
oob_data.Add(new OOBList.OOBDetails
{
SiteNo = siteno,
SiteName = sitename,
Description = description,
CabinOOB = cabinoob,
TowerOOB = toweroob,
ManageOOB = manageoob,
TargetResolutionDate = resolutiondate_converted
});
}
}
}
//Now delete file.
System.IO.File.Delete(Server.MapPath("~/Uploads/OOB List/") + fname);
Debug.Write("DOWNLOADING LIST ETC....\n");
using (IRISInSiteLiveEntities DB = new IRISInSiteLiveEntities())
{
var allsites = DB.Sites.ToList();
//Loop through sites and the OOB list and if they match then tell us
foreach( var oobdata in oob_data)
{
foreach( var sitedata in allsites)
{
var indexof = sitedata.SiteName.IndexOf(' ');
if( indexof > 0 )
{
var OOBNo = oobdata.SiteNo;
var OOBName = oobdata.SiteName;
var SiteNo = sitedata.SiteName;
var split = SiteNo.Substring(0, indexof);
if (OOBNo == split && SiteNo.Contains(OOBName) )
{
var siterecord = DB.Sites.Find(sitedata.Id);
siterecord.CabinOOB = oobdata.CabinOOB;
siterecord.TowerOOB = oobdata.TowerOOB;
siterecord.ManagedOOB = oobdata.ManageOOB;
siterecord.IssueDescription = oobdata.Description;
siterecord.TargetResolutionDate = oobdata.TargetResolutionDate;
DB.Entry(siterecord).State = EntityState.Modified;
Debug.Write("Updated Site ID/Name Record: " + sitedata.Id + "/" + sitedata.SiteName);
}
}
}
}
DB.SaveChanges();
}
var nowdate = DateTime.Now.ToString("dd/MM/yyyy");
System.IO.File.WriteAllText(Server.MapPath("~/Uploads/OOB List/lastupdated.txt"),nowdate);
return Json("Success");
}
答案 0 :(得分:2)
好像您正在使用Entity Framework(6或Core)。无论哪种情况,
var siterecord = DB.Sites.Find(sitedata.Id);
和
DB.Entry(siterecord).State = EntityState.Modified;
是多余的,因为siteData
变量来自
var allsites = DB.Sites.ToList();
这不仅将整个Site
表加载到内存中,而且EF更改跟踪器会保留对该列表中每个对象的引用。您可以使用
var siterecord = DB.Sites.Find(sitedata.Id);
Debug.Assert(siterecord == sitedata);
Find
(当数据已经在内存中时)和Entry
方法本身很快。但是问题在于默认情况下它们会自动触发DetectChanges
,从而导致二次时间复杂度-简单地说,就是很慢。
话虽如此,只需将它们删除:
if (OOBNo == split && SiteNo.Contains(OOBName))
{
sitedata.CabinOOB = oobdata.CabinOOB;
sitedata.TowerOOB = oobdata.TowerOOB;
sitedata.ManagedOOB = oobdata.ManageOOB;
sitedata.IssueDescription = oobdata.Description;
sitedata.TargetResolutionDate = oobdata.TargetResolutionDate;
Debug.Write("Updated Site ID/Name Record: " + sitedata.Id + "/" + sitedata.SiteName);
}
通过这种方式,EF仅检测一次更改(在SaveChanges
之前),并且仅更新已修改的记录字段。
答案 1 :(得分:0)
因此,首先您应该将HTTPPost设置为异步函数 更多信息https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/
然后您应该做的是创建任务并将它们添加到列表中。然后,通过调用Task.WaitAll()
等待它们完成(如果您需要/需要)。这将使您的代码在多个线程上并行运行,从而大大优化了性能。
例如,您还可以使用linq通过执行大致如下所示的操作来减小所有站点的大小
var sitedataWithCorrectNames = allsites.Where(x => x //在这里评估您的状况)
然后使用现在的foreach(sitedataWithCorrectNames中的sitedate)开始foreach(var oobdata)
SiteNo.Contains(OOBName)相同
P.S。大多数db sdk还提供异步功能,因此也请使用这些功能。 P.P.S.我没有IDE,所以我盯着代码,但是链接应该为您提供大量示例。如果您需要更多帮助,请回复。
答案 2 :(得分:0)
我遵循了Ivan Stoev的建议,并通过删除DB.Find和EntitySate Modified更改了代码-与之前的15分钟相比,现在花费了大约一分半钟。非常令人惊讶,因为我不知道您实际上并不需要更新记录。聪明。现在的代码是:
module.exports = (req, res, next) => {
if (req.path == "/business") {
res.status(201);
res.jsonp({
id: "a23e1b13-cf69-461c-aa8a-a0eb99e41350",
name: req.body['name'],
revision: "1"
});
next();
}
else {
next();
}
}