如何创建正确的Lambda表达式来处理两个对象列表的比较?

本文关键字:两个 对象 列表 处理 比较 Lambda 创建 何创建 表达式 | 更新日期: 2023-09-27 17:50:11

我有一个通过读取CSV文件创建的List<imports>。我有一个List<table>从数据库表读取。设置lambda表达式的正确方法是:

  • 查找交叉点(要更新的记录或未处理的记录)
  • 在List (Records to INSERT)
  • 中查找新项目
  • 在List (Records to DELETE)
  • 中查找List 中不的项目

现在我正在努力解决这个问题:

foreach (DTO.ImportData row in Helper.ImportTracker.ImportsValid)
{
    bool isInsert = false;
    bool isUpdate = false;
    Model.Auto auto = null;
    // Get auto(s) for this SKU + VIN + ClientID...
    var autos = _dbFeed.Autoes.Where(a => a.StockNumber == row.Stock && a.VIN == row.VIN && a.ClientID == _targetClientID && a.SourceClientID == _sourceClientID).ToList();
    if (autos.Count > 1)        // ERROR...
    {
        Helper.ImportTracker.ImportsInvalid.Add(row);
        continue;
    }
    else if (autos.Count == 1)  // UPDATE...
    {
        auto = autos[0];
        if (auto.GuaranteedSalePrice != row.GuaranteedSalePrice ||
            auto.ListPrice != row.ListPrice ||
            auto.Miles != row.Miles ||
            auto.Active != row.Active ||
            auto.MSRP != row.MSRP ||
            auto.InternetPrice != row.Internet_Price ||
            auto.InvoiceCost != row.Invoice ||
            auto.Make != row.Make ||
            auto.Model != row.Model ||
            auto.Year != row.Year 
            )
        {
            Helper.ImportTracker.Updates.Add(row);
            isUpdate = true;
        }
        else
        {
            isUpdate = false;
            auto = null;
        }
    }
    else                        // INSERT...
    {
        isInsert = true;
        auto = new Model.Auto();
        _dbFeed.Autoes.AddObject(auto);
        Helper.ImportTracker.Inserts.Add(row);
    }
    // Fill in the data...
    if (auto != null)
    {
        ...
    }
    // left out for readability - this section just maps the import 
    // data to the table row and saves to the DB...
}

上面的部分处理我在开头列出的前两种情况。

我有一个狄更斯的时间包裹我的头在正确的方式把lambdas在一起。

我意识到我可能不得不把我所有的List<import>转换成List<table>,这样我就可以比较苹果和苹果,这不是问题。我还认为我需要写一个自定义比较器沿着行:

class TableComparer : IEqualityComparer<table>
{
    public bool Equals(table x, table y)
    {
        if (Object.ReferenceEquals(x, y)) return true;
        if (Object.ReferenceEquals(x, null) ||
            Object.ReferenceEquals(y, null))
                return false;
            return x.SKU == y.SKU && x.VIN == y.VIN && x.ClientID == y.ClientID;
    }
    public int GetHashCode(table table)
    {
        if (Object.ReferenceEquals(table, null)) return 0;
        int hashSKU = SKU == null ? 0 : SKU.GetHashCode();
        int hashVIN = VIN == null ? 0 : VIN.GetHashCode();
        int hashClientID = ClientID.GetHashCode();
        return hashClientID ^ hashSKU ^ hashVIN;
    }
}

然后我可以做:

var UpdateAutos = autos.Intersect(new TableComparer(imports));
var InsertAutos = imports.Except(new TableComparer(autos));
var DeleteAutos = autos.Except(new TableComparer(imports));

现在我头晕目眩!;)

我讲对了吗?


额外的信息:到目前为止,我的新代码是:

private void HandleAutos()
{
    // convert to List<auto>...
    List<Model.Auto> imports = AutoConvert.Convert(Helper.ImportTracker.ImportsValid, _targetClientID, _sourceClientID, DateTime.UtcNow, _dbFeed);
    // get all DB records in List<auto>...
    List<Model.Auto> current = _dbFeed.Autoes.Where(a => a.ClientID == _targetClientID && a.Active == true).ToList();
    // isolate all Inserts, Updates and Deletes...
    var intersect = imports.Intersect(current, new AutoIsIn());         // should be all autos with matching VIN & SKU  //
    var updates = intersect.Intersect(current, new AutoHasChanged());   // should be a subset of changed resords        //
    var inserts = imports.Except(current, new AutoIsIn());              // should be all the imports not in the DB      //
    var deletes = current.Except(imports, new AutoIsIn());              // should be all the DB records not in imports  //
}

我的Comparer类是这样的

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace RivWorks.FeedHandler.Library
{
    class AutoIsIn : IEqualityComparer<Model.Auto>
    {
        public bool Equals(Model.Auto x, Model.Auto y)
        {
            if (Object.ReferenceEquals(x, y)) return true;
            if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null)) return false;
            return x.StockNumber == y.StockNumber && x.VIN == y.VIN;
        }
        public int GetHashCode(Model.Auto auto)
        {
            if (Object.ReferenceEquals(auto, null)) return 0;
            int hashSKU = auto.StockNumber == null ? 0 : auto.StockNumber.GetHashCode();
            int hashVIN = auto.VIN == null ? 0 : auto.VIN.GetHashCode();
            return hashSKU ^ hashVIN;
        }
    }
    class AutoHasChanged : IEqualityComparer<Model.Auto>
    {
        public bool Equals(Model.Auto x, Model.Auto y)
        {
            if (Object.ReferenceEquals(x, y)) return true;
            if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null)) return false;
            return (x.GuaranteedSalePrice != y.GuaranteedSalePrice 
                 || x.ListPrice != y.ListPrice 
                 || x.Miles != y.Miles 
                 || x.MSRP != y.MSRP 
                 || x.InternetPrice != y.InternetPrice 
                 || x.InvoiceCost != y.InvoiceCost 
                 || x.Make != y.Make 
                 || x.Model != y.Model 
                 || x.Year != y.Year
                 );
        }
        public int GetHashCode(Model.Auto auto)
        {
            if (Object.ReferenceEquals(auto, null)) return 0;
            int hashMake = auto.Make == null ? 0 : auto.Make.GetHashCode();
            int hashModel = auto.Model == null ? 0 : auto.Model.GetHashCode();
            int hashYear = auto.Year.GetHashCode();
            int hashGSP = auto.GuaranteedSalePrice.GetHashCode();
            int hashLP = !auto.ListPrice.HasValue ? 0 : auto.ListPrice.GetHashCode();
            int hashMiles = !auto.Miles.HasValue ? 0 : auto.Miles.GetHashCode();
            int hashMSRP = !auto.MSRP.HasValue ? 0 : auto.MSRP.GetHashCode();
            int hashIP = !auto.InternetPrice.HasValue ? 0 : auto.InternetPrice.GetHashCode();
            int hashIC = !auto.InvoiceCost.HasValue ? 0 : auto.InvoiceCost.GetHashCode();
            return hashMake ^ hashModel ^ hashYear ^ hashGSP ^ hashLP ^ hashMiles ^ hashMSRP ^ hashIP ^ hashIC;
        }
    }
}

到目前为止有什么问题吗?

kb

如何创建正确的Lambda表达式来处理两个对象列表的比较?

不是OPs问题的解决方案,而是对OPs评论的回应…

Public Module ExpressionExtensions
    <System.Runtime.CompilerServices.Extension()> _
    Public Function Compose(Of T)(ByVal first As Expressions.Expression(Of T), ByVal second As Expressions.Expression(Of T), ByVal merge As Func(Of Expressions.Expression, Expressions.Expression, Expressions.Expression)) As Expressions.Expression(Of T)
        ' build parameter map (from parameters of second to parameters of first)
        Dim map = first.Parameters.[Select](Function(f, i) New With {f, .s = second.Parameters(i)}).ToDictionary(Function(p) p.s, Function(p) p.f)
        ' replace parameters in the second lambda expression with parameters from the first
        Dim secondBody = ParameterRebinder.ReplaceParameters(map, second.Body)
        ' apply composition of lambda expression bodies to parameters from the first expression 
        Return Expressions.Expression.Lambda(Of T)(merge(first.Body, secondBody), first.Parameters)
    End Function
    <System.Runtime.CompilerServices.Extension()> _
    Public Function [And](Of T)(ByVal first As Expressions.Expression(Of Func(Of T, Boolean)), ByVal second As Expressions.Expression(Of Func(Of T, Boolean))) As Expressions.Expression(Of Func(Of T, Boolean))
        Return first.Compose(second, AddressOf Expressions.Expression.And)
    End Function
    <System.Runtime.CompilerServices.Extension()> _
    Public Function [Or](Of T)(ByVal first As Expressions.Expression(Of Func(Of T, Boolean)), ByVal second As Expressions.Expression(Of Func(Of T, Boolean))) As Expressions.Expression(Of Func(Of T, Boolean))
        Return first.Compose(second, AddressOf Expressions.Expression.[Or])
    End Function
End Module

Edit: add Missing ParameterRebinder

Public Class ParameterRebinder
    Inherits Expressions.ExpressionVisitor
    Private ReadOnly map As Dictionary(Of Expressions.ParameterExpression, Expressions.ParameterExpression)
    Public Sub New(ByVal map As Dictionary(Of Expressions.ParameterExpression, Expressions.ParameterExpression))
        Me.map = If(map, New Dictionary(Of Expressions.ParameterExpression, Expressions.ParameterExpression)())
    End Sub
    Public Shared Function ReplaceParameters(ByVal map As Dictionary(Of Expressions.ParameterExpression, Expressions.ParameterExpression), ByVal exp As Expressions.Expression) As Expressions.Expression
        Return New ParameterRebinder(map).Visit(exp)
    End Function
    Protected Overloads Overrides Function VisitParameter(ByVal p As Expressions.ParameterExpression) As Expressions.Expression
        Dim replacement As Expressions.ParameterExpression = Nothing
        If map.TryGetValue(p, replacement) Then
            p = replacement
        End If
        Return MyBase.VisitParameter(p)
    End Function
End Class

Dim A as System.Func(Of MyType, Boolean) = Function(x) x.SomeField = SomeValue
Dim B as System.Func(Of MyType, Boolean) = A.Or(Function(x) x.SomeOtherField = SomeOtherValue)
Dim C as System.Func(Of MyType, Boolean) = A.And(Function(x) x.SomeOtherField = SomeOtherValue)
为了清楚起见,我明确地键入了上面的代码。这不是必需的。