2011-02-08 1 views
4

Мой заказчик, похоже, не работает. Я хочу подсчет различных объектов, но каждый раз получаю счет 1. Несмотря на то, что, глядя на базу данных, ясно видно, что существует более 1 экземпляра запроса с различными значениями «TimeOfAction».Подсчет различий с linq для сущностей и пользовательских IEqualityComparer

class TimeComparer : IEqualityComparer<Action> 
{ 
    public bool Equals(Action a, Action b) 
    { 
     if (a.TimeOfAction == b.TimeOfAction) 
      return true; 
     else 
      return false; 
    } 

    public int GetHashCode(Action obj) 
    { 
     return obj.ToString().ToLower().GetHashCode(); 
    } 
} 

Думайте, что это может быть метод GetHashCode, поскольку я не слишком хорошо знаком с тем, как он должен работать. Вот запрос linq. Я преобразован в AsEnumerable, поскольку Linq to Entities не поддерживает отдельный метод.

DBEntities db = new DBEntities(); 

     IEnumerable<Action> query = 
        from action in db.Action.AsEnumerable() 
        where action.TimeOfAction > new DateTime(2010, 11, 1, 0, 0, 0) 
        where action.TimeOfAction < new DateTime(2011, 2, 7, 0, 0, 0) 
        where action.EntityName == "seant" 
        select action; 

var count = query. 
      Distinct(new TimeComparer()).Count(); 

ответ

6

Ваши Equals и методы GetHashCode принимают полностью разные подходы. В частности, равные объекты могут иметь разные хэш-коды, предполагая, что Action.ToString использует поля, отличные от TimeOfAction. Они должны быть выровнены, или у вас не будет абсолютно никаких шансов получить разумные результаты. Это нормально для неравных объектов иметь один и тот же хэш-код (хотя это будет препятствовать производительности), но равные объекты должен дать тот же хеш-код.

Обратите внимание, что использование настраиваемого компаратора приведет к тому, что отличительная часть будет выполняться в процессе, а не в базе данных. Это может не быть проблемой, вам просто нужно это понять. EDIT: я не заметил, что есть перегрузка Queryable.Distinct, которая делает принимает IEqualityComparer<T>. Я предполагаю, что вы можете предоставить пользовательские сопоставления строк и несколько других известных компараторов ... а не только произвольный код. Если это сработает, все равно это будет сделано локально. Я бы не удивился, если бы он просто взорвался.

EDIT: Как говорит Марк, вы можете использовать Select(x => x.TimeOfAction).Distinct().Count(), чтобы сделать это в базе данных. Вам также нужно удалить звонок до AsEnumerable. Я предполагаю, что это потому, что что-то еще не работает. Вы можете попробовать это:

DBEntities db = new DBEntities(); 
IQueryable<DateTime> query = 
      from action in db.Action 
      where action.TimeOfAction > new DateTime(2010, 11, 1, 0, 0, 0) 
      where action.TimeOfAction < new DateTime(2011, 2, 7, 0, 0, 0) 
      where action.EntityName == "seant" 
      select action.TimeOfAction; 
var count = query.Distinct().Count(); 

Конечно, если вам необходимо query что-то еще, что Вы должны были бы сохранить первоначальную версию тоже:

DBEntities db = new DBEntities(); 
IQueryable<Action> query = 
      from action in db.Action 
      where action.TimeOfAction > new DateTime(2010, 11, 1, 0, 0, 0) 
      where action.TimeOfAction < new DateTime(2011, 2, 7, 0, 0, 0) 
      where action.EntityName == "seant" 
      select action; 

var count = query.Select(x => x.TimeOfAction).Distinct().Count(); 
// Use query here as well to get at full action details 

Обратите внимание, что с помощью запроса снова будет производить второй запрос к базе данных , Вам нужно будет посмотреть, что происходит в транзакциях, если вам нужно, чтобы подсчет был согласован с тем, что делает второй запрос ... или вытащить все данные из базы данных (используя вызов ToList), а затем сделать Distinct часть в процессе.


Назад к пользовательским сравнительным анализаторам ...

Предполагая TimeOfAction является DateTime или какой-либо другой тип, который имеет разумную хэш-код, вы можете изменить свой класс:

class TimeComparer : IEqualityComparer<Action> 
{ 
    public bool Equals(Action a, Action b) 
    { 
     return a.TimeOfAction == b.TimeOfAction; 
    } 

    public int GetHashCode(Action obj) 
    { 
     return obj.TimeOfAction.GetHashCode(); 
    } 
} 

Обратите внимание, что я также упрощенным ваш метод Equals - в любое время вы оказываетесь с:

if (condition) 
{ 
    return true; 
} 
else 
{ 
    return false; 
} 

вы можете упростить его:

return condition; 
+0

Хорошая деталь (по-прежнему использует 'IEnumerable <>' хотя?). –

+0

@Marc: Ой, да :) –

4

Во-первых, обратите внимание, что не будет работать на сервере db, так что это не имеет отношения к EF.

Я подозреваю это частично, потому что ваш GetHashCode не согласен с Equals; Вы в идеале должны иметь что-то вроде:

public int GetHashCode(Action obj) 
{ 
    return obj.TimeOfAction.GetHashCode(); 
} 

, так это то, что ваши Equals заботится о.

Кроме того, однако, отметить, что весь запрос может быть переписан (и вероятно, будет работать на сервере БД, если вы используете IQueryable<Action> query (и удалить AsEnumerable()), а не IEnumerable<Action> query) с:

var count = query.Select(x => x.TimeOfAction).Distinct().Count(); 
+0

+1 для последнего бита, вот что я собирался добавить в свой ответ. Я оставлю свой ответ по крайней мере некоторое время, так как я думаю, что вопрос об упрощении метода Equals заслуживает внимания. Я рад за то, что вы включили его в свой ответ, но потом я удалю мой :) –

+0

... обратите внимание на вызов AsEnumerable. Он должен будет удалить это тоже :) –

+0

@Jon - на самом деле, я * думаю * OP ссылается на неявный приведение, но я добавил его в любом случае –

Смежные вопросы