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

C# 如何创建正确的Lambda表达式来处理比较两个对象列表?,c#,lambda,C#,Lambda,我有一个列表,它是通过读取CSV文件创建的。通过从数据库表中读取,我有一个列表。设置lambda表达式的正确方法是什么: 查找交叉点(要更新的记录或不执行任何操作的记录) 在列表中查找新项目(要插入的记录) 查找列表中的项目而不是列表中的项目(要删除的记录) 现在,我正在努力解决这个问题,就像: foreach (DTO.ImportData row in Helper.ImportTracker.ImportsValid) { bool isInsert = false;

我有一个
列表
,它是通过读取CSV文件创建的。通过从数据库表中读取,我有一个
列表
。设置lambda表达式的正确方法是什么:

  • 查找交叉点(要更新的记录或不执行任何操作的记录)
  • 在列表中查找新项目(要插入的记录)
  • 查找列表中的项目而不是列表中的项目(要删除的记录)
现在,我正在努力解决这个问题,就像:

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...
}
以上部分处理我在开头列出的前两个案例

我有一段狄更斯式的时间,用正确的方式把兰博达斯组装起来

我意识到我可能必须将我所有的
列表
转换为
列表
,这样我就可以对苹果进行比较,这不是问题。我还认为我需要编写一个自定义比较器,内容如下:

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;
    }
}
现在我的头在打转!;)

我走对了吗


其他信息: 到目前为止,我的新代码已经完成了:

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  //

}
private void HandleAutos()
{
//转换为列表。。。
列表导入=自动转换.Convert(Helper.ImportTracker.ImportsValid、\u targetClientID、\u sourceClientID、DateTime.UtcNow、\u dbFeed);
//获取列表中的所有DB记录。。。
List current=\u dbFeed.Autoes.Where(a=>a.ClientID=\u targetClientID&&a.Active==true)。ToList();
//隔离所有插入、更新和删除。。。
var intersect=imports.intersect(当前,新AutoIsIn());//应该是所有具有匹配VIN和SKU的汽车//
var updates=intersect.intersect(当前,新的AutoHasChanged());//应该是已更改的解析的子集//
var inserts=imports.Except(current,new AutoIsIn());//应该是数据库中没有的所有导入//
var deletes=current.Except(imports,new AutoIsIn());//应该是导入中未包含的所有DB记录//
}
我的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;
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
命名空间RivWorks.FeedHandler.Library
{
类自动识别:IEqualityComparer
{
公共布尔等于(Model.Auto x、Model.Auto y)
{
if(Object.ReferenceEquals(x,y))返回true;
if(Object.ReferenceEquals(x,null)| | Object.ReferenceEquals(y,null))返回false;
返回x.StockNumber==y.StockNumber&&x.VIN==y.VIN;
}
public int GetHashCode(Model.Auto)
{
if(Object.ReferenceEquals(auto,null))返回0;
int hashSKU=auto.StockNumber==null?0:auto.StockNumber.GetHashCode();
int hashVIN=auto.VIN==null?0:auto.VIN.GetHashCode();
返回hashSKU^hashVIN;
}
}
类AutoHasChanged:IEqualityComparer
{
公共布尔等于(Model.Auto x、Model.Auto y)
{
if(Object.ReferenceEquals(x,y))返回true;
if(Object.ReferenceEquals(x,null)| | Object.ReferenceEquals(y,null))返回false;
退货(x.担保销售价格!=y.担保销售价格
||x.ListPrice!=y.ListPrice
||x.英里!=y.英里
||x.MSRP!=y.MSRP
||x.InternetPrice!=y.InternetPrice
||x.InvoiceCost!=y.InvoiceCost
||做
||x.模型!=y.模型
||x年!=y年
);
}
public int GetHashCode(Model.Auto)
{
if(Object.ReferenceEquals(auto,null))返回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();
返回hashMake^hashModel^hashYear^hashGSP^hashLP^hashMiles^hashMSRP^hashIP^hashIC;
}
}
}
到目前为止有什么不对劲吗


-kb

不是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
以上允许您拥有

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)
为了清楚起见,我已经明确地键入了上面的内容。这不是必需的


很抱歉使用VB-我手头有代码,现在没有时间翻译

看来你走对了方向。最好将记录加载到同一实体/DTO中,以便进行简单的操作。我手头没有代码示例,因此不会作为答案发布,但您可以扩展lambdas以在AND/OR语法中组合多个lambda。这可能非常方便。如果你想要的话,我会尽力把它挖出来。那太棒了。我确实理解使用相同的实体/DTO,这就是为什么我正在考虑为此创建一个IEqualityComparer(因为我在EF2或EF3上,而不是EF4上)…顺便说一句,我制作了一个简单的控制台应用程序来玩Intersect,Exception等,但并没有得到我预期的结果。我只想说,除了简单的LINQ之外,我仍然处在Lambdas学习曲线的陡峭部分……谢谢。在接下来的几天里仔细检查这件事。别担心VB,我会说两种语言@基思:不用担心,希望有帮助。我刚刚意识到我没有包含
参数ebinder
类-我已经编辑了我的答案以包含它。如果你有任何问题,请告诉我。我想我已经知道得够多了。翻译对你来说难吗
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)