У меня проблема с функцией поиска/группы.
контекст
У меня есть список объектов клиента (Контекст 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;
}
Проблема заключается в том, чтобы сделать группу по заявлению "динамическим", так что на основе выбранной крестики была создана группировка. Любые предложения для хорошего решения?
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>
.
Как насчет того, чтобы полагаться на критерии, построенные вокруг 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]",
};
}
}
Ваш запрос может быть переписан как:
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
является анонимным типом. Поэтому, если вы можете динамически генерировать выражение, тогда вы можете решить свою проблему.
Должен признать, что анонимный тип создается компилятором, и поэтому у вас его еще нет в коде С#.