C# 在动态列数上联接2个数据表
我正在尝试以动态列数连接两个数据表。我已经得到了下面的代码。问题在于join的ON语句。如何根据“joinColumnNames”列表中有多少列名称使其成为动态的 我在想我需要构建某种表达式树,但是我找不到任何关于如何使用多个联接列和DataRow对象(每个列都没有属性)来实现这一点的示例C# 在动态列数上联接2个数据表,c#,linq,datatable,expression-trees,C#,Linq,Datatable,Expression Trees,我正在尝试以动态列数连接两个数据表。我已经得到了下面的代码。问题在于join的ON语句。如何根据“joinColumnNames”列表中有多少列名称使其成为动态的 我在想我需要构建某种表达式树,但是我找不到任何关于如何使用多个联接列和DataRow对象(每个列都没有属性)来实现这一点的示例 private DataTable Join(List<string> joinColumnNames, DataTable pullX, DataTable pullY) { DataT
private DataTable Join(List<string> joinColumnNames, DataTable pullX, DataTable pullY)
{
DataTable joinedTable = new DataTable();
// Add all the columns from pullX
foreach (string colName in joinColumnNames)
{
joinedTable.Columns.Add(pullX.Columns[colName]);
}
// Add unique columns from PullY
foreach (DataColumn col in pullY.Columns)
{
if (!joinedTable.Columns.Contains((col.ColumnName)))
{
joinedTable.Columns.Add(col);
}
}
var Join = (from PX in pullX.AsEnumerable()
join PY in pullY.AsEnumerable() on
// This must be dynamic and join on every column mentioned in joinColumnNames
new { A = PX[joinColumnNames[0]], B = PX[joinColumnNames[1]] } equals new { A = PY[joinColumnNames[0]], B = PY[joinColumnNames[1]] }
into Outer
from PY in Outer.DefaultIfEmpty<DataRow>(pullY.NewRow())
select new { PX, PY });
foreach (var item in Join)
{
DataRow newRow = joinedTable.NewRow();
foreach (DataColumn col in joinedTable.Columns)
{
var pullXValue = item.PX.Table.Columns.Contains(col.ColumnName) ? item.PX[col.ColumnName] : string.Empty;
var pullYValue = item.PY.Table.Columns.Contains(col.ColumnName) ? item.PY[col.ColumnName] : string.Empty;
newRow[col.ColumnName] = (pullXValue == null || string.IsNullOrEmpty(pullXValue.ToString())) ? pullYValue : pullXValue;
}
joinedTable.Rows.Add(newRow);
}
return joinedTable;
}
私有数据表联接(列出joinColumnNames、DataTable pullX、DataTable pullY)
{
DataTable joinedTable=新DataTable();
//添加pullX中的所有列
foreach(joinColumnNames中的字符串colName)
{
joinedTable.Columns.Add(pullX.Columns[colName]);
}
//从PullY中添加唯一的列
foreach(pullY.Columns中的数据列col)
{
如果(!joinedTable.Columns.Contains((col.ColumnName)))
{
joinedTable.Columns.Add(col);
}
}
var Join=(来自pullX.AsEnumerable()中的PX)
在上的pullY.AsEnumerable()中加入PY
//这必须是动态的,并对joinColumnNames中提到的每一列进行联接
新的{A=PX[joinColumnNames[0]],B=PX[joinColumnNames[1]}等于新的{A=PY[joinColumnNames[0]],B=PY[joinColumnNames[1]}
进入外部
从Outer.DefaultIfEmpty(pullY.NewRow())中的PY
选择新的{PX,PY});
foreach(联接中的变量项)
{
DataRow newRow=joinedTable.newRow();
foreach(joinedTable.Columns中的DataColumn列)
{
var pullXValue=item.PX.Table.Columns.Contains(col.ColumnName)?item.PX[col.ColumnName]:string.Empty;
var pullYValue=item.PY.Table.Columns.Contains(col.ColumnName)?item.PY[col.ColumnName]:string.Empty;
newRow[col.ColumnName]=(pullXValue==null | | string.IsNullOrEmpty(pullXValue.ToString())?pullYValue:pullXValue;
}
joinedTable.Rows.Add(newRow);
}
返回连接表;
}
添加使用3个联接列(国家、公司和日期ID)显示输入/输出的特定示例:
拉动X:
Country Company DateId Sales
United States Test1 Ltd 20160722 $25
Canada Test3 Ltd 20160723 $30
Italy Test4 Ltd 20160724 $40
India Test2 Ltd 20160725 $35
国家公司DateId销售
美国测试1有限公司20160722 25美元
加拿大测试3有限公司20160723$30
意大利测试4有限公司20160724美元40
印度测试2有限公司20160725$35
拉动Y:
Country Company DateId Downloads
United States Test1 Ltd 20160722 500
Mexico Test2 Ltd 20160723 300
Italy Test4 Ltd 20160724 900
国家公司DateId下载
美国测试1有限公司20160722500
墨西哥测试2有限公司20160723 300
意大利测试4有限公司20160724900
结果:
Country Company DateId Sales Downloads
United States Test1 Ltd 20160722 $25 500
Canada Test3 Ltd 20160723 $30
Mexico Test2 Ltd 20160723 300
Italy Test4 Ltd 20160724 $40 900
India Test2 Ltd 20160725 $35
国家公司DateId销售下载
美国测试1有限公司20160722 25 500美元
加拿大测试3有限公司20160723$30
墨西哥测试2有限公司20160723 300
意大利测试4有限公司20160724$40 900
印度测试2有限公司20160725$35
var Join=
来自pullX.AsEnumerable()中的PX
在pullY.AsEnumerable()中加入PY
在string.Join(“\0”上,joinColumnNames.Select(c=>PX[c]))
等于string.Join(“\0”,joinColumnNames.Select(c=>PY[c]))
进入外部
从Outer.DefaultIfEmpty(pullY.NewRow())中的PY
选择新的{PX,PY};
另一种方法是在数据集中既有数据表
,又使用数据关系
因为您使用的是LINQ to对象,所以不需要使用表达式树。您可以使用自定义相等比较器解决问题
创建一个相等比较器,该比较器可以根据特定列的值比较两个DataRow
对象之间的相等。以下是一个例子:
public class MyEqualityComparer : IEqualityComparer<DataRow>
{
private readonly string[] columnNames;
public MyEqualityComparer(string[] columnNames)
{
this.columnNames = columnNames;
}
public bool Equals(DataRow x, DataRow y)
{
return columnNames.All(cn => x[cn].Equals(y[cn]));
}
public int GetHashCode(DataRow obj)
{
unchecked
{
int hash = 19;
foreach (var value in columnNames.Select(cn => obj[cn]))
{
hash = hash * 31 + value.GetHashCode();
}
return hash;
}
}
}
公共类MyEqualityComparer:IEqualityComparer
{
私有只读字符串[]列名称;
公共MyEqualityComparer(字符串[]列名称)
{
this.columnNames=columnNames;
}
公共布尔等于(数据行x、数据行y)
{
返回columnNames.All(cn=>x[cn].Equals(y[cn]);
}
public int GetHashCode(DataRow obj)
{
未经检查
{
int hash=19;
foreach(columnNames.Select中的var值(cn=>obj[cn]))
{
hash=hash*31+value.GetHashCode();
}
返回散列;
}
}
}
然后您可以使用它使连接如下所示:
public class TwoRows
{
public DataRow Row1 { get; set; }
public DataRow Row2 { get; set; }
}
private static List<TwoRows> LeftOuterJoin(
List<string> joinColumnNames,
DataTable leftTable,
DataTable rightTable)
{
return leftTable
.AsEnumerable()
.GroupJoin(
rightTable.AsEnumerable(),
l => l,
r => r,
(l, rlist) => new {LeftValue = l, RightValues = rlist},
new MyEqualityComparer(joinColumnNames.ToArray()))
.SelectMany(
x => x.RightValues.DefaultIfEmpty(rightTable.NewRow()),
(x, y) => new TwoRows {Row1 = x.LeftValue, Row2 = y})
.ToList();
}
public类两行
{
公共数据行行1{get;set;}
公共数据行第2行{get;set;}
}
私有静态列表LeftOuterJoin(
列出你的名字,
数据表leftTable,
数据表(右表)
{
返回左表
.可计算的()
.GroupJoin(
rightTable.AsEnumerable(),
l=>l,
r=>r,
(l,rlist)=>new{LeftValue=l,RightValues=rlist},
新的MyQualityComparer(joinColumnNames.ToArray())
.SelectMany(
x=>x.RightValues.DefaultIfEmpty(rightTable.NewRow()),
(x,y)=>新的两行{Row1=x.LeftValue,Row2=y})
.ToList();
}
请注意,我使用的是方法语法,因为我不认为您可以使用自定义相等比较器
请注意,该方法执行左外部联接,而不是完全外部联接。根据您提供的示例,您似乎需要一个完整的外部联接。要做到这一点,你需要做两个左外连接(见此)。下面是完整方法的外观:
private static DataTable FullOuterJoin(
List<string> joinColumnNames,
DataTable pullX,
DataTable pullY)
{
var pullYOtherColumns =
pullY.Columns
.Cast<DataColumn>()
.Where(x => !joinColumnNames.Contains(x.ColumnName))
.ToList();
var allColumns =
pullX.Columns
.Cast<DataColumn>()
.Concat(pullYOtherColumns)
.ToArray();
var allColumnsClone =
allColumns
.Select(x => new DataColumn(x.ColumnName, x.DataType))
.ToArray();
DataTable joinedTable = new DataTable();
joinedTable.Columns.AddRange(allColumnsClone);
var first =
LeftOuterJoin(joinColumnNames, pullX, pullY);
var resultRows = new List<DataRow>();
foreach (var item in first)
{
DataRow newRow = joinedTable.NewRow();
foreach (DataColumn col in joinedTable.Columns)
{
var value = pullX.Columns.Contains(col.ColumnName)
? item.Row1[col.ColumnName]
: item.Row2[col.ColumnName];
newRow[col.ColumnName] = value;
}
resultRows.Add(newRow);
}
var second =
LeftOuterJoin(joinColumnNames, pullY, pullX);
foreach (var item in second)
{
DataRow newRow = joinedTable.NewRow();
foreach (DataColumn col in joinedTable.Columns)
{
var value = pullY.Columns.Contains(col.ColumnName)
? item.Row1[col.ColumnName]
: item.Row2[col.ColumnName];
newRow[col.ColumnName] = value;
}
resultRows.Add(newRow);
}
var uniqueRows =
resultRows
.Distinct(
new MyEqualityComparer(
joinedTable.Columns
.Cast<DataColumn>()
.Select(x => x.ColumnName)
.ToArray()));
foreach (var uniqueRow in uniqueRows)
joinedTable.Rows.Add(uniqueRow);
return joinedTable;
}
private静态数据表FullOuterJoin(
列出你的名字,
数据表pullX,
数据表(pullY)
{
var pullYOtherColumns=
短柱
.Cast()
.Where(x=>!joinColumnNames.Contains(x.ColumnName))
.ToList();
var allColumns=
pullX.Columns
.Cast()
.Concat(pullYOtherColumns)
.ToArray();
氯乙烯=
所有列
.Select(x=>newdatacolumn(x.ColumnName,x.DataTyp
private static DataTable FullOuterJoin(
List<string> joinColumnNames,
DataTable pullX,
DataTable pullY)
{
var pullYOtherColumns =
pullY.Columns
.Cast<DataColumn>()
.Where(x => !joinColumnNames.Contains(x.ColumnName))
.ToList();
var allColumns =
pullX.Columns
.Cast<DataColumn>()
.Concat(pullYOtherColumns)
.ToArray();
var allColumnsClone =
allColumns
.Select(x => new DataColumn(x.ColumnName, x.DataType))
.ToArray();
DataTable joinedTable = new DataTable();
joinedTable.Columns.AddRange(allColumnsClone);
var first =
LeftOuterJoin(joinColumnNames, pullX, pullY);
var resultRows = new List<DataRow>();
foreach (var item in first)
{
DataRow newRow = joinedTable.NewRow();
foreach (DataColumn col in joinedTable.Columns)
{
var value = pullX.Columns.Contains(col.ColumnName)
? item.Row1[col.ColumnName]
: item.Row2[col.ColumnName];
newRow[col.ColumnName] = value;
}
resultRows.Add(newRow);
}
var second =
LeftOuterJoin(joinColumnNames, pullY, pullX);
foreach (var item in second)
{
DataRow newRow = joinedTable.NewRow();
foreach (DataColumn col in joinedTable.Columns)
{
var value = pullY.Columns.Contains(col.ColumnName)
? item.Row1[col.ColumnName]
: item.Row2[col.ColumnName];
newRow[col.ColumnName] = value;
}
resultRows.Add(newRow);
}
var uniqueRows =
resultRows
.Distinct(
new MyEqualityComparer(
joinedTable.Columns
.Cast<DataColumn>()
.Select(x => x.ColumnName)
.ToArray()));
foreach (var uniqueRow in uniqueRows)
joinedTable.Rows.Add(uniqueRow);
return joinedTable;
}