Linq Any() 超时
Linq Any() times out
我正在尝试使用 Linq 比较两个数据表。非常简单的表格,只有一列,但有大约 44,000 行。我使用以下内容,但是当我跟踪它时,当它到达 if (dr.Any()) 时,它就在那里,下一行或异常永远不会执行:
public static DataTable GetTableDiff(DataTable dt1, DataTable dt2)
{
DataTable dtDiff = new DataTable();
try
{
var dr = from r in dt1.AsEnumerable() where !dt2.AsEnumerable().Any(r2 => r["FacilityID"].ToString().Trim().ToLower() == r2["FacilityID"].ToString().Trim().ToLower()) select r;
if (dr.Any())
dtDiff = dr.CopyToDataTable();
}
catch (Exception ex)
{
}
return dtDiff;
}
我在 web.config 中设置了最大请求长度以确保这不是问题,但没有改变:
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" maxRequestLength="1048576" />
我不认为 44,000 行太大,是吗?
加入表 O(N1+N2) 而不是进行 O(N1*N2) 搜索(当前对于 dt1 中的每一行,您正在扫描 dt2 中的所有行):
var diff = from r1 in dt1.AsEnumerable()
join r2 in dt2.AsEnumerable()
on r1.Field<string>("FacilityID").Trim().ToLower()
equals r2.Field<string>("FacilityID").Trim().ToLower() into g
where !g.Any() // get only rows which do not have joined rows from dt2
select r1;
加入后,您还将只计算每个键(设施 ID)一次。
另一个选项是创建简单的行比较器:
public class FacilityIdComparer : IEqualityComparer<DataRow>
{
public bool Equals(DataRow x, DataRow y) => GetFacilityID(x) == GetFacilityID(y);
public int GetHashCode(DataRow row) => GetFacilityID(row)?.GetHashCode() ?? 0;
private string GetFacilityID(DataRow row)
=> row.Field<string>("FacilityID")?.Trim().ToLower();
}
然后获取新行是使用 LINQ Except
方法的一种方式:
var diff = dt2.AsEnumerable().Except(dt1.AsEnumerable(), new FacilityIdComparer());
它也适用于搜索路口
我会使用一种不同的、更轻量级的方法,因为您只需从一个 table 中获取行,而您只需要那些具有新 FacilityId
:
的行
public static DataTable GetTableDiff(DataTable dtNew, DataTable dtOld)
{
DataTable dtDiff = dtNew.Clone(); // no data only columns and constraints
var oldFacilityIds = dtOld.AsEnumerable().Select(r => r.Field<string>("FacilityID").Trim());
var oldFacilityIDSet = new HashSet<string>(oldFacilityIds, StringComparer.CurrentCultureIgnoreCase);
var newRows = dtNew.AsEnumerable()
.Where(r => !oldFacilityIDSet.Contains(r.Field<string>("FacilityID").Trim()));
foreach (DataRow row in newRows)
dtDiff.ImportRow(row);
return dtDiff;
}
我正在尝试使用 Linq 比较两个数据表。非常简单的表格,只有一列,但有大约 44,000 行。我使用以下内容,但是当我跟踪它时,当它到达 if (dr.Any()) 时,它就在那里,下一行或异常永远不会执行:
public static DataTable GetTableDiff(DataTable dt1, DataTable dt2)
{
DataTable dtDiff = new DataTable();
try
{
var dr = from r in dt1.AsEnumerable() where !dt2.AsEnumerable().Any(r2 => r["FacilityID"].ToString().Trim().ToLower() == r2["FacilityID"].ToString().Trim().ToLower()) select r;
if (dr.Any())
dtDiff = dr.CopyToDataTable();
}
catch (Exception ex)
{
}
return dtDiff;
}
我在 web.config 中设置了最大请求长度以确保这不是问题,但没有改变:
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" maxRequestLength="1048576" />
我不认为 44,000 行太大,是吗?
加入表 O(N1+N2) 而不是进行 O(N1*N2) 搜索(当前对于 dt1 中的每一行,您正在扫描 dt2 中的所有行):
var diff = from r1 in dt1.AsEnumerable()
join r2 in dt2.AsEnumerable()
on r1.Field<string>("FacilityID").Trim().ToLower()
equals r2.Field<string>("FacilityID").Trim().ToLower() into g
where !g.Any() // get only rows which do not have joined rows from dt2
select r1;
加入后,您还将只计算每个键(设施 ID)一次。
另一个选项是创建简单的行比较器:
public class FacilityIdComparer : IEqualityComparer<DataRow>
{
public bool Equals(DataRow x, DataRow y) => GetFacilityID(x) == GetFacilityID(y);
public int GetHashCode(DataRow row) => GetFacilityID(row)?.GetHashCode() ?? 0;
private string GetFacilityID(DataRow row)
=> row.Field<string>("FacilityID")?.Trim().ToLower();
}
然后获取新行是使用 LINQ Except
方法的一种方式:
var diff = dt2.AsEnumerable().Except(dt1.AsEnumerable(), new FacilityIdComparer());
它也适用于搜索路口
我会使用一种不同的、更轻量级的方法,因为您只需从一个 table 中获取行,而您只需要那些具有新 FacilityId
:
public static DataTable GetTableDiff(DataTable dtNew, DataTable dtOld)
{
DataTable dtDiff = dtNew.Clone(); // no data only columns and constraints
var oldFacilityIds = dtOld.AsEnumerable().Select(r => r.Field<string>("FacilityID").Trim());
var oldFacilityIDSet = new HashSet<string>(oldFacilityIds, StringComparer.CurrentCultureIgnoreCase);
var newRows = dtNew.AsEnumerable()
.Where(r => !oldFacilityIDSet.Contains(r.Field<string>("FacilityID").Trim()));
foreach (DataRow row in newRows)
dtDiff.ImportRow(row);
return dtDiff;
}