c# - Cómo usar el IEqualityComparer
linq (5)
Tengo algunas campanas en mi base de datos con el mismo número. Quiero obtener todos ellos sin duplicación. Luego creo una clase de comparación para hacer este trabajo, pero la ejecución de la función hace un gran retraso de la función sin diferencia, de 0.6 segundos a 3.2 segundos.
¿Lo estoy haciendo bien o tengo que usar otro método?
reg.AddRange((from a in this.dataContext.reglements
join b in this.dataContext.Clients on a.Id_client equals b.Id
where a.date_v <= datefin && a.date_v >= datedeb
where a.Id_client == b.Id
orderby a.date_v descending
select new Class_reglement
{
nom = b.Nom,
code = b.code,
Numf = a.Numf,
}).AsEnumerable().Distinct(new Compare()).ToList());
class Compare : IEqualityComparer<Class_reglement>
{
public bool Equals(Class_reglement x, Class_reglement y)
{
if (x.Numf == y.Numf)
{
return true;
}
else { return false; }
}
public int GetHashCode(Class_reglement codeh)
{
return 0;
}
}
La inclusión de su clase de comparación (o más específicamente, la llamada AsEnumerable
que necesitaba usar para que funcione) significó que la lógica de clasificación pasó de estar basada en el servidor de la base de datos a estar en el cliente de la base de datos (su aplicación). Esto significa que su cliente ahora necesita recuperar y luego procesar una mayor cantidad de registros, lo que siempre será menos eficiente que realizar la búsqueda en la base de datos donde se pueden usar los índices apropiados.
Debería intentar desarrollar una cláusula WHERE que satisfaga sus requisitos en su lugar, consulte Cómo usar un IEqualityComparer con una cláusula LINQ to Entities Except para obtener más detalles.
No es de extrañar, teniendo en cuenta su implementación GetHashCode
que siempre devuelve el mismo valor. Distinct
depende de una buena función hash para trabajar de manera eficiente.
Al implementar interfaces de clases, primero debe leer su documentation , de lo contrario no sabrá qué contrato debe implementar. 1
En su código, la solución es reenviar GetHashCode
a Class_reglement.Numf.GetHashCode
e implementarlo apropiadamente allí.
Aparte de eso, su método Equals
está lleno de código innecesario. Podría reescribirse de la siguiente manera (la misma semántica, ¼ del código, más legible):
public bool Equals(Class_reglement x, Class_reglement y)
{
return x.Numf == y.Numf;
}
Además, la llamada a la ToList
es innecesaria y requiere mucho tiempo: AddRange
acepta cualquier IEnumerable
por lo que no es necesario convertirlo en una List
. AsEnumerable
también es redundante aquí ya que procesar el resultado en AddRange
causará esto de todos modos.
1 La implementación del código sin saber lo que realmente hace se llama programación de culto a la carga . Es una práctica sorprendentemente extendida. Fundamentalmente no funciona.
Pruebe este código:
public class GenericCompare<T> : IEqualityComparer<T> where T : class
{
private Func<T, object> _expr { get; set; }
public GenericCompare(Func<T, object> expr)
{
this._expr = expr;
}
public bool Equals(T x, T y)
{
var first = _expr.Invoke(x);
var sec = _expr.Invoke(y);
if (first != null && first.Equals(sec))
return true;
else
return false;
}
public int GetHashCode(T obj)
{
return obj.GetHashCode();
}
}
Ejemplo de su uso sería
collection = collection
.Except(ExistedDataEles, new GenericCompare<DataEle>(x=>x.Id))
.ToList();
Solo código, con implementación de GetHashCode
y validación NULL
:
public class Class_reglementComparer : IEqualityComparer<Class_reglement>
{
public bool Equals(Class_reglement x, Class_reglement y)
{
if (x is null || y is null))
return false;
return x.Numf == y.Numf;
}
public int GetHashCode(Class_reglement product)
{
//Check whether the object is null
if (product is null) return 0;
//Get hash code for the Numf field if it is not null.
int hashNumf = product.hashNumf == null ? 0 : product.hashNumf.GetHashCode();
return hashNumf;
}
}
Ejemplo: lista de Class_reglement distinto por Numf
List<Class_reglement> items = items.Distinct(new Class_reglementComparer());
IEquatable<T>
puede ser una forma mucho más fácil de hacerlo con marcos modernos.
Obtienes una bonita función bool Equals(T other)
simple bool Equals(T other)
y no hay problemas con el casting o la creación de una clase separada.
public class Person : IEquatable<Person>
{
public Person(string name, string hometown)
{
this.Name = name;
this.Hometown = hometown;
}
public string Name { get; set; }
public string Hometown { get; set; }
// can''t get much simpler than this!
public bool Equals(Person other)
{
return this.Name == other.Name && other.Hometown == other.Hometown;
}
public override int GetHashCode()
{
return Name.GetHashCode(); // see other links for hashcode guidance
}
}
Tenga en cuenta que TIENE que implementar GetHashCode
si usa esto en un diccionario o con algo parecido a Distinct
.
PD. No creo que ningún método personalizado de Equals trabaje con el marco de la entidad directamente en el lado de la base de datos (creo que lo sabes porque haces AsEnumerable), pero este es un método mucho más simple para hacer un igual simple para el caso general.
Si las cosas no parecen funcionar (como duplicar los errores clave al hacer ToDictionary) ponga un punto de interrupción dentro de Equals para asegurarse de que se está accionando y asegúrese de tener GetHashCode
definido (con la palabra clave override).