LINQ除了获取所有行

时间:2017-10-01 12:07:50

标签: c# entity-framework linq

我在包含数据库表对象的列表中使用Except方法,我有数据库中现有行的列表,以及来自应用程序的值列表,我必须检查这2个列表之间的增量并删除/插入到DB,我使用除了,但它返回所有值。代码:

public void UpdatePoints(IEnumerable<TBL_Points> selectedPoints, int id)
{

    List<TBL_Points> plistPointsFromDB = Context.TBL_Points.Where(x => x.Code == id).ToList();         
    List<TBL_Points> pRemoveItems = plistPointsFromDB .Except<TBL_Points>(selectedPoints.ToList()).ToList();
    List<TBL_Points> pAddItems = selectedPoints.ToList().Except<TBL_Points>(plistPointsFromDB).ToList();
}

pRemoveItems获取plistPointsFromDB的所有值, 并pAddItems获取selectedPoints

中的所有值

3 个答案:

答案 0 :(得分:1)

Except,除非另有指定,否则使用默认的比较器,该比较器调用EqualsGetHashCode。这些,除非重写比较引用相等,这意味着如果你有两个不同的实例,即使具有相同的值,它们也不相等。你需要:

  • 覆盖Equals
  • GetHashCodeTBL_Points
  • 或指定IEqualityComparer<TBL_Points>

了解更多信息:

也不需要这么多ToList()次电话。

var fromDB = Context.TBL_Points.Where(x => x.Code == id).ToList(); 
var remove = fromDB.Except(selectedPoints);
var add = selectedPoints.Except(fromDB);

我离开的那个是执行对DB的查询。对于其他情况,可以使用IEnumerable<T>

答案 1 :(得分:1)

前面的答案说明了如果要检查实际内容而不是相等的参考,需要指定一个特殊的比较器。

可能应该提到一个补充:

如果您不是特别需要List<T>Array<T>,那么您应该避免拨打.ToList<T>().ToArray<T>()。当您使用其中任何一个时,您将复制现有集合中的所有项目并将其复制到新集合中。当你处理大型集合时,你的记忆不会被这个逗乐。处理新分配的内存所需的垃圾收集也会对整体性能造成巨大损失。由于我们的初级软件开发人员没有意识到这一点,我们之前在我们的软件中造成了内存泄漏。

如果你可以使用IEnumerable<T>,那么该集合将被延迟流式传输,因此没有(或几乎没有)额外的内存被分配。

有时,由于访问索引或其他原因,您需要将集合转换为List<T>Array<T>。即使这样,你也应该将内存副本减少到最低限度。

答案 2 :(得分:0)

CLR引用类型的默认相等语义是按对象标识进行比较。除非两个引用引用相同的对象实例,否则它们将被视为不同。

特别是在需要设置操作的情况下,我们可能需要自定义此行为,为对象提供 value 语义,而不是 reference 语义。 / p>

有多种方法可以实现这一目标。我们可以在算法级别定义它,将自定义比较传递给Except方法。或者我们可以在类型级别定义它,为所讨论的set元素类型的所有实例定义相等性。哪种方法最好将部分取决于对象的使用方式以及是否可以修改定义集合元素类型的类的源代码。

在他的回答中,Gilad Green提供了有关这些方法的性质和用例的Microsoft文档的非常有用的参考。

在下面的示例中,我们使用自定义比较器来定义相等。

sealed class PointComparer: EqualityComparer<TBL_Points>
{
    public override bool Equals(TBL_Points x, TBL_Points y) => x.Code == y.Code;

    public override int GetHashCode(TBL_Points point) => point.Code.GetHashCode();
}

我们现在需要实例化这个比较器并将其传递给Except,如此

var pointsToAdd = selectedPoints
    .AsEnumerable()
    .Except(plistPointsFromDB, new PointComparer())
    .ToList();

请注意,我清理了命名(除了类型本身的命名仍然很糟糕)并删除了不必要的显式类型。

另一种方法是为类型TBL_Points本身定义相等性(同样需要重命名)

class TBL_Points: IEquatable<TBL_Points>
{
    public bool Equals(TBL_Points other) => Code == other?.Code;

    public sealed override bool Equals(object obj) => obj is TBL_Points o && Equals(o);

    public sealed override int GetHashCode() => Code.GetHashCode();

    public static bool operator ==(TBL_Points x, TBL_Points y) => x?.Equals(y);

    public static bool operator !=(TBL_Points x, TBL_Points y) => !(x == y);
}

上面定义了该类型的所有用法的相等语义。这很方便,因为我们不再需要创建比较对象的实例并将其传递给算法。这也确保了所有用途的一致性。