使用LINQ在两个表之间选择不同的值

时间:2015-03-13 14:33:04

标签: c# linq dataset

我有一个类似于以下信息的DataSet。

DataSet

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

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

我的代码:

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从表0中选择UnitNumbers,只有在表1中不存在

更好的解释。

单位编号将在表1中。它可能在表0中。

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

如果它不在表0中,我想从表1中获取信息,因为我必须得到我能得到的信息。但我不想重复。

2 个答案:

答案 0 :(得分:1)

如果我理解了你的要求,这就是你要找的东西。它首先从表1中获取所有表,然后是表1中的所有表,但是在表2中通过Linq Left-Outer-Join:

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如果无法添加,则会返回false,因为它已经在集合中。

答案 1 :(得分:0)

我发现当我处于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: ${4} ",
        smallOrder.ContactID, smallOrder.LastName, smallOrder.FirstName,
        smallOrder.OrderID, smallOrder.Total);
}

}