使用linq到实体和自定义IEqualityComparer的明显计数

Sea*_*ean 4 c# linq linq-to-entities distinct

我的自定义比较器似乎不起作用.我想要一些不同的对象,但我每次都得到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();
    }
}
Run Code Online (Sandbox Code Playgroud)

认为它可能是GetHashCode方法,因为我不太熟悉它的工作方式.这是linq查询.我转换为AsEnumerable,因为Linq to Entities不支持distinct方法.

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();
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 6

您的Equals和GetHashCode方法采用完全不同的方法.特别是,假设Action.ToString使用除TimeOfAction之外的字段,则相等的对象可能具有不同的哈希码.他们必须协调一致,否则你将无法获得合理的结果.不等对象可以使用相同的哈希码(尽管这会妨碍性能),但是相等的对象必须提供相同的哈希码.

请注意,使用自定义比较器将强制Distinct部分在进程内而不是在数据库中完成.这可能不是问题,你只需要了解它.编辑:我还没有发现这有一个超载的Queryable.Distinct确实需要一个IEqualityComparer<T>.我的猜测是,你可以提供自定义字符串比较器和一些其他众所周知的比较器...而不仅仅是任意代码.如果它有效,它无论如何都将在本地完成.如果只是爆炸,我不会感到惊讶.

编辑:正如Marc所说,您可以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();
Run Code Online (Sandbox Code Playgroud)

当然,如果您需要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
Run Code Online (Sandbox Code Playgroud)

请注意,再次使用查询将产生第二个数据库查询.如果您需要计数与第二个查询所做的一致...或者从数据库中提取所有详细信息(使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我也简化了您的Equals方法 - 只要您发现自己:

if (condition)
{
    return true;
}
else
{
    return false;
}
Run Code Online (Sandbox Code Playgroud)

你可以简化它:

return condition;
Run Code Online (Sandbox Code Playgroud)