【问题标题】:Using LINQ to select distinct values between two tables使用 LINQ 在两个表之间选择不同的值
【发布时间】:2015-05-16 02:15:46
【问题描述】:

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

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

所以我想从顶部数据集中获取 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 中选择 UnitNumber,前提是它们不存在于表 1 中

更好的解释。

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

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

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

【问题讨论】:

  • 这与实体框架有关吗?

标签: c# linq dataset


【解决方案1】:

如果我了解您的要求,这就是您正在寻找的。它首先从表一中获取所有内容,然后通过 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&lt;Unit&gt;,您可以将其用于许多(基于集合的)LINQ 方法,例如JoinIntersectUnionGroupBy。您也可以将它用于HashSet&lt;Unit.Unit&gt;,在这种情况下我更喜欢它。这是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&lt;T&gt; 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&lt;T&gt;.Add 返回 false 如果因为它已经在集合中而无法添加。

【讨论】:

  • 我喜欢它。我在想有一种方法可以利用 Intersect 功能。谢谢你。我将运行一些单元测试,看看哪个执行得更快。
  • @CalvinSmith:我不明白Intersect 在这里有什么帮助。如果您在Unit.Unit 中覆盖了Equals+GetHashCode,则可以使用Union。您还可以将Union 的重载与自定义IEqualityComparer&lt;Unit.Unit&gt; 一起使用。
  • 此外,它在 concat 中产生了重复的单元 65002。我不认为只是在 concat 的末尾添加一个直接的 distinct 可以解决它,但我会尝试下一个
  • @CalvinSmith:不,您不能使用Distinct,因为默认情况下它只会比较引用。两者都是具有相同UnitNumber 的不同引用。这就是为什么我提到Equals+GetHashCode。但我想知道为什么你会得到重复。上面的第二个 LINQ 查询应该只从第二个表中获取尚未从第一个表中提取的行/单元。这就是我使用“外部连接”(from outerJoin in outer.DefaultIfEmpty() where outerJoin == null) 的原因。
  • @CalvinSmith:我展示了另一种使用自定义 IEqualityComparer&lt;Unit&gt; 的方法。
【解决方案2】:

我发现当我处于 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);
}

}

【讨论】:

  • 目前尚不清楚这如何有助于从表 1 中获取所有内容以及从 table2 中获取所有内容,这是核心问题。样本完全不相关,不是吗?
  • 这并不能真正回答问题。
  • 我试图建议,而不是从直接查询数据集的转变。是的,示例是不相关的,但提供了一个查询实体的 linq 构造。它没有处理他的查询应该是什么样子。我要调查一下
猜你喜欢
  • 2013-05-02
  • 2023-03-11
  • 2012-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-29
  • 1970-01-01
相关资源
最近更新 更多