Linq / EF Dynamic Group by с необязательным параметром

1

У меня проблема с функцией поиска/группы.

контекст

У меня есть список объектов клиента (Контекст Entity Framework), и я хочу найти все возможные дубликаты в этом списке. Критерии, если объект является дубликатом, должны быть динамическими. Пусть говорят по выбору пользователя.

Модель

Возьмем следующие части, как указано.

Мой клиентский класс

public class Customer
{
    public int CustomerId { get; set; }
    public string SearchName { get; set; }
    public string Mail { get; set; }
    public DateTime? Birthday { get; set; }
    public string CardNumber { get; set; }
    public DateTime Created { get; set; }
}

Возможные критерии дублирования: SearchName, Mail, Birthday и CardNumber.

Следующая функция возвращает соответствующий результат:

    public IList<Customer> GetPossibleDuplicates()
    {
        IList<Customer> list;

        list =
            (from s in this.Context.Customers
                group s by new
                           {
                               s.SearchName, 
                               s.CardNumber
                           }
                into g where g.Count() > 1 select g)
                .SelectMany(g => g)
                .OrderBy(o => o.SearchName)
                .ThenBy(c => c.Created)
                .ToList();


        return list;
    }

Проблема заключается в том, чтобы сделать группу по заявлению "динамическим", так что на основе выбранной крестики была создана группировка. Любые предложения для хорошего решения?

Теги:
linq
entity-framework

3 ответа

0
Лучший ответ

Enumerable.GroupBy разрешает параметр IEqualityComparer. См. Страницу MSDN.

Этот сравнитель может быть динамической частью запроса. Вы заявляете, что группировка основана на критериях отбора, так что это даст вам:

public IList<Customer> GetPossibleDuplicates()
{
    var comparer = SomeMethodReturningAnEqualityComparerBasedOnSelectionCriteria();
    return this.Context.Customers
            .GroupBy(customer => customer, comparer)
            .SelectMany(g => g)
            .OrderBy(o => o.SearchName)
            .ThenBy(c => c.Created)
            .ToList();
}

Метод, возвращающий компаратор, вернет IEqualityComparer<Customer>.

  • 0
    Спасибо Энди! Подсказка приводит к лучшему решению. Но с IEqualityComparer я могу только проверить на равенство в памяти коллекции правильно? Так что с несколькими тысячами клиентов, кажется, не очень хорошо, верно? (This.Context является контекстом Entity Framework)
  • 0
    Вы правы. Мой ответ был для коллекции в памяти, и я не думал, что это Entity Framework. Вы можете даже получить ошибку времени выполнения, если Entity Framework попытается преобразовать ее в оператор SQL.
Показать ещё 1 комментарий
0

Как насчет того, чтобы полагаться на критерии, построенные вокруг IComparable

Func<Customer, IComparable> criteria

и ваш запрос, инкапсулированный в методе, будет выглядеть следующим образом:

public IList<Customer> FindByCriteria(Func<Customer, IComparable> criteria)
{
    var query = from customer in FindAll()
                group customer by criteria(customer)
                into groups
                where groups.Count() > 1
                from item in groups
                orderby item.SearchName, item.Created
                select item;

    return query.ToList();
}

Затем вы можете играть со своей собственной структурой данных или даже с Tuple

void Main()
{
    var repository = new Repository();

    //Find all
    repository.FindAll().Dump();

    // Find by mail
    var mail = new Func<Customer, IComparable>(customer =>
    {
        return Tuple.Create(customer.Mail);
    });
    repository
        .FindByCriteria(mail)
        .Dump();

    // Find by mail and card number
    var multi = new Func<Customer, IComparable>(customer =>
    {
        return Tuple.Create(customer.CardNumber, customer.Mail);
    });
    repository
        .FindByCriteria(multi)
        .Dump();
}

Где приведен полный код репозитория

public class Repository
{
    public IList<Customer> FindByCriteria(Func<Customer, IComparable> criteria)
    {
        var query = from customer in FindAll()
                    group customer by criteria(customer)
                    into groupings
                    where groupings.Count() > 1
                    from grouping in groupings
                    orderby grouping.SearchName, grouping.Created
                    select grouping;

        return query.ToList();
    }

    public IEnumerable<Customer> FindAll()
    {
        yield return new Customer
        {
            CustomerId = 1,
            SearchName = "John",
            CardNumber = "0000 0000 0000 0000 1",
            Mail = "[email protected]",
        };

        yield return new Customer
        {
            CustomerId = 2,
            SearchName = "Jim",
            CardNumber = "0000 0000 0000 0000 2",
            Mail = "[email protected]",
        };

        yield return new Customer
        {
            CustomerId = 3,
            SearchName = "Jack",
            CardNumber = "0000 0000 0000 0000 3",
            Mail = "[email protected]",
        };

        yield return new Customer
        {
            CustomerId = 4,
            SearchName = "Jane",
            CardNumber = "0000 0000 0000 0000 3",
            Mail = "[email protected]",
        };

        yield return new Customer
        {
            CustomerId = 4,
            SearchName = "Joan",
            CardNumber = "0000 0000 0000 0000 3",
            Mail = "[email protected]",
        };
    }
}
-2

Ваш запрос может быть переписан как:

var result = this.Context.Customers
    .GroupBy(x => new { x.SearchName, x.CardNumber })
    .Where(x => x.Count() > 1)
    .SelectMany(x => x)
    .OrderBy(x => x.SearchName)
    .ThenBy(x => x.Created)
    .ToList();

Вы можете видеть, что .GroupBy(...) принимает выражение типа Expression<Func<Customer, TKey>> где TKey является анонимным типом. Поэтому, если вы можете динамически генерировать выражение, тогда вы можете решить свою проблему.

Должен признать, что анонимный тип создается компилятором, и поэтому у вас его еще нет в коде С#.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню