Количество отличных с linq для сущностей и пользовательских IEqualityComparer - PullRequest
4 голосов
/ 08 февраля 2011

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

 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();

Ответы [ 2 ]

6 голосов
/ 08 февраля 2011

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

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

РЕДАКТИРОВАТЬ: Как говорит Марк, вы можете использовать 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), а затем выполнить Отдельная часть в процессе.


Вернуться к пользовательским компараторам равенства ...

Предполагая, что 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;
4 голосов
/ 08 февраля 2011

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

I подозреваю это частично , потому что ваш 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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...