Как создать правильное лямбда-выражение для сравнения двух списков объектов? - PullRequest
1 голос
/ 15 апреля 2011

У меня есть List<imports>, который создается путем чтения файла CSV. У меня есть List<table>, читая из таблицы базы данных. Как правильно настроить лямбда-выражения на:

  • Найти пересечение (Записи для ОБНОВЛЕНИЯ или Записи без ДЕЙСТВИЯ)
  • Найти новые предметы в списке (Записи в INSERT)
  • Найти элементы в Списке , а не в Списке (Записи в УДАЛИТЬ)

Прямо сейчас я прохожу свой путь через это как:

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...
}

В приведенном выше разделе рассматриваются первые 2 случая, которые я перечислил в начале.

У меня есть время, когда я оборачиваюсь вокруг правильного способа собрать лямбды для этого.

Я понимаю, что мне, возможно, придется конвертировать все свои 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

1 Ответ

0 голосов
/ 16 апреля 2011

Не решение проблемы ОП, но в ответ на комментарии ОП ...

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

Редактировать: Добавлено отсутствие параметра 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)

Я явно напечатал вышесказанное для ясности. Это не обязательно.

Извиняюсь за то, что нахожусь в VB - у меня есть код под рукой, и у меня нет времени переводить прямо сейчас

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...