Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/316.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在动态列数上联接2个数据表_C#_Linq_Datatable_Expression Trees - Fatal编程技术网

C# 在动态列数上联接2个数据表

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

我正在尝试以动态列数连接两个数据表。我已经得到了下面的代码。问题在于join的ON语句。如何根据“joinColumnNames”列表中有多少列名称使其成为动态的

我在想我需要构建某种表达式树,但是我找不到任何关于如何使用多个联接列和DataRow对象(每个列都没有属性)来实现这一点的示例

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;
}