使用 LINQ to select 两个表之间的不同值

Using LINQ to select distinct values between two tables

我有一个类似于以下信息的数据集。

我想做的是用尽可能多的可用信息填充一个对象。

所以我想从顶部数据集中获取 UnitNum 65002,其余从单列 table。

我的代码:

foreach (DataRow row in dsUnits.Tables[0].Rows)
    {
        var unit = new Unit.Unit 
        {
            UnitNum = row["UnitNumber"].NullSafeToString(),
            CustCode = row["CustCode"].NullSafeToString(),
            Year = row["Year"].NullSafeToString(),
            Make = row["Make"].NullSafeToString(),
            Model = row["Model"].NullSafeToString()
        };

        UnitsInvolvedInBreakdown.Add(unit);
    }

    foreach (DataRow row in dsUnits.Tables[1].Rows)
    {
        if (UnitsInvolvedInBreakdown.Where(x => x.UnitNum == row["UnitNumber"].ToString()).Count() == 0)
        {
            var unit = new Unit.Unit
            {
                UnitNum = row["UnitNumber"].ToString()
            };

            UnitsInvolvedInBreakdown.Add(unit);
        }
    }

这对我来说似乎效率很低,我尝试了下面的代码但没有结果,

 var q = dsUnits.Tables[0].AsEnumerable().Except(dsUnits.Tables[1].AsEnumerable());

我的问题基本上是,有没有一种方法可以使用 linq 到 table 0 中的 select UnitNumbers,前提是它们不存在于 table 1

更好的解释。

单元号将在table 1.它可能在table 0.

如果它在 table 0 中,我想从那里获取信息..我有更多信息。

如果它不在 table 0 中,我想从 table 1 中获取信息,因为我必须尽我所能。但我不想重复。

如果我理解了您的要求,这就是您要找的。它首先从 table 中获取所有内容,然后通过 Linq Left-Outer-Join 获取所有不在 table 中但在 table 中的内容:

var unitsFrom1 = dsUnits.Tables[0].AsEnumerable()
    .Select(row => new Unit.Unit
    {
        UnitNum = row["UnitNumber"].NullSafeToString(),
        CustCode = row["CustCode"].NullSafeToString(),
        Year = row["Year"].NullSafeToString(),
        Make = row["Make"].NullSafeToString(),
        Model = row["Model"].NullSafeToString()
    });

var unitsFrom2Notin1 = 
    from row in dsUnits.Tables[1].AsEnumerable()
    join u1 in unitsFrom1
    on row.Field<string>("UnitNumber") equals u1.UnitNum into outer
    from outerJoin in outer.DefaultIfEmpty()
    where outerJoin == null
    select new Unit.Unit
    {
        UnitNum = row["UnitNumber"].NullSafeToString()
    };

现在您可以连接两者:

IEnumerable<Unit.Unit> result = unitsFrom1.Concat(unitsFrom2Notin1);

这是一种更易于维护并且在任何情况下都应该有效的不同方法。您可以实现自定义 IEqualityComparer<Unit>,您可以将其用于许多(基于集合的)LINQ 方法,例如 JoinIntersectUnionGroupBy。您也可以将它用于 HashSet<Unit.Unit>,在这种情况下我更喜欢。这是 UnitComparer:

的可能实现
public class UnitComparer : IEqualityComparer<Unit>
{
    public bool Equals(Unit x, Unit y)
    {
        if (x == null && y == null) return true; 
        if (x == null || y == null) return false; 

        return x.UnitNum == y.UnitNum;
    }

    public int GetHashCode(Unit obj)
    {
        return obj == null || obj.UnitNum == null ? 0 : obj.UnitNum.GetHashCode();
    }
}

这里是你需要的简单循环,从第一个开始,所有不在第一个的从第二个开始。请注意我使用的 HashSet<T> constructor

var uniqueUnits = new HashSet<Unit.Unit>(new Unit.UnitComparer());
foreach (DataRow row in dsUnits.Tables[0].Rows)
{
    Unit.Unit unit = new Unit.Unit
    {
        UnitNum = row["UnitNumber"].NullSafeToString(),
        CustCode = row["CustCode"].NullSafeToString(),
        Year = row["Year"].NullSafeToString(),
        Make = row["Make"].NullSafeToString(),
        Model = row["Model"].NullSafeToString()
    };
    uniqueUnits.Add(unit);
}
foreach (DataRow row in dsUnits.Tables[1].Rows)
{
    Unit.Unit unit = new Unit.Unit
    {
        UnitNum = row["UnitNumber"].NullSafeToString()
    };
    uniqueUnits.Add(unit);
}

HashSet<T>.Add returns false 如果因为已经在集合中而无法添加。

我发现当我处于 linq 思维模式时,数据集往往会让我失望。我发现通过 类 构建实体然后使用 linq 查询实体更容易。由于时间不够,我复制了一个 MSDN 示例。您可以根据需要修改它。

var query =
    from contact in contacts
    from order in orders
    where contact.ContactID == order.Contact.ContactID
        && order.TotalDue < totalDue
    select new
    {
        ContactID = contact.ContactID,
        LastName = contact.LastName,
        FirstName = contact.FirstName,
        OrderID = order.SalesOrderID,
        Total = order.TotalDue
    };

foreach (var smallOrder in query)
{
    Console.WriteLine("Contact ID: {0} Name: {1}, {2} Order ID: {3} Total Due:  ",
        smallOrder.ContactID, smallOrder.LastName, smallOrder.FirstName,
        smallOrder.OrderID, smallOrder.Total);
}

}