J'ai le suivant Classe EF dérivé d'une base de données (simplifié)
class Product
{
public string ProductId;
public string ProductName;
public string CategoryId;
public string CategoryName;
}
ProductId
est la clé primaire de la table.
Pour une mauvaise décision de conception prise par le concepteur de base de données (je ne peux pas la modifier), j'ai CategoryId
et CategoryName
dans ce tableau.
J'ai besoin d'un DropDownList avec (distinct) CategoryId
en tant que Valeur et CategoryName
en tant que Texte . J'ai donc appliqué le code suivant:
product.Select(m => new {m.CategoryId, m.CategoryName}).Distinct();
ce qui logiquement devrait créer un objet anonyme avec CategoryId
et CategoryName
comme propriétés. Distinct()
garantit qu'il n'y a pas de doublons (CategoryId
, CategoryName
).
Mais en réalité ça ne marche pas. Autant que je sache, Distinct()
fonctionne uniquement lorsqu'il n'y a qu'un seul champ dans la collection, sinon il les ignore ... est-ce correct? Y a-t-il une solution de contournement? Merci!
METTRE À JOUR
Désolé product
est:
List<Product> product = new List<Product>();
J'ai trouvé un autre moyen d'obtenir le même résultat que Distinct()
:
product.GroupBy(d => new {d.CategoryId, d.CategoryName})
.Select(m => new {m.Key.CategoryId, m.Key.CategoryName})
Je suppose que vous utilisez distinct comme un appel de méthode sur une liste. Vous devez utiliser le résultat de la requête en tant que source de données pour votre DropDownList, par exemple en le matérialisant via ToList
.
var distinctCategories = product
.Select(m => new {m.CategoryId, m.CategoryName})
.Distinct()
.ToList();
DropDownList1.DataSource = distinctCategories;
DropDownList1.DataTextField = "CategoryName";
DropDownList1.DataValueField = "CategoryId";
Distinct () garantit l’absence de doublons (CategoryId, CategoryName).
- exactement ça
Les types anonymes "magiquement" implémentent Equals
et GetHashcode
Je suppose une autre erreur quelque part. Sensibilité à la casse? Classes mutable? Des champs non comparables?
Distinct méthode retourne des éléments distincts d'une séquence.
Si vous regardez son implémentation avec Reflector, vous verrez qu'il crée DistinctIterator
pour votre type anonyme. Distinct iterator ajoute des éléments à Set
lors de l'énumération de la collection. Cet énumérateur ignore tous les éléments déjà présents dans Set
. Set
utilise les méthodes GetHashCode
et Equals
pour définir si l'élément existe déjà dans Set
.
Comment GetHashCode
et Equals
sont-ils implémentés pour le type anonyme? Comme indiqué sur msdn :
Les méthodes Equals et GetHashCode sur les types anonymes sont définies en termes des méthodes Equals et GetHashcode des propriétés, deux instances de même type anonyme ne sont égaux que si toutes leurs propriétés sont égal.
Donc, vous devriez certainement avoir des objets anonymes distincts, lors de l'itération sur une collection distincte. Et le résultat ne dépend pas du nombre de champs que vous utilisez pour votre type anonyme.
Ceci est ma solution, il supporte keySelectors de différents types:
public static IEnumerable<TSource> DistinctBy<TSource>(this IEnumerable<TSource> source, params Func<TSource, object>[] keySelectors)
{
// initialize the table
var seenKeysTable = keySelectors.ToDictionary(x => x, x => new HashSet<object>());
// loop through each element in source
foreach (var element in source)
{
// initialize the flag to true
var flag = true;
// loop through each keySelector a
foreach (var (keySelector, hashSet) in seenKeysTable)
{
// if all conditions are true
flag = flag && hashSet.Add(keySelector(element));
}
// if no duplicate key was added to table, then yield the list element
if (flag)
{
yield return element;
}
}
}
Pour l'utiliser:
list.DistinctBy(d => d.CategoryId, d => d.CategoryName)
Répondre au titre de la question (ce qui a attiré les gens ici) et ignorer que l'exemple utilise des types anonymes ....
Cette solution fonctionnera également pour les types non anonymes. Il ne devrait pas être nécessaire pour les types anonymes.
Classe d'assistance:
/// <summary>
/// Allow IEqualityComparer to be configured within a lambda expression.
/// From https://stackoverflow.com/questions/98033/wrap-a-delegate-in-an-iequalitycomparer
/// </summary>
/// <typeparam name="T"></typeparam>
public class LambdaEqualityComparer<T> : IEqualityComparer<T>
{
readonly Func<T, T, bool> _comparer;
readonly Func<T, int> _hash;
/// <summary>
/// Simplest constructor, provide a conversion to string for type T to use as a comparison key (GetHashCode() and Equals().
/// https://stackoverflow.com/questions/98033/wrap-a-delegate-in-an-iequalitycomparer, user "orip"
/// </summary>
/// <param name="toString"></param>
public LambdaEqualityComparer(Func<T, string> toString)
: this((t1, t2) => toString(t1) == toString(t2), t => toString(t).GetHashCode())
{
}
/// <summary>
/// Constructor. Assumes T.GetHashCode() is accurate.
/// </summary>
/// <param name="comparer"></param>
public LambdaEqualityComparer(Func<T, T, bool> comparer)
: this(comparer, t => t.GetHashCode())
{
}
/// <summary>
/// Constructor, provide a equality comparer and a hash.
/// </summary>
/// <param name="comparer"></param>
/// <param name="hash"></param>
public LambdaEqualityComparer(Func<T, T, bool> comparer, Func<T, int> hash)
{
_comparer = comparer;
_hash = hash;
}
public bool Equals(T x, T y)
{
return _comparer(x, y);
}
public int GetHashCode(T obj)
{
return _hash(obj);
}
}
Utilisation la plus simple:
List<Product> products = duplicatedProducts.Distinct(
new LambdaEqualityComparer<Product>(p =>
String.Format("{0}{1}{2}{3}",
p.ProductId,
p.ProductName,
p.CategoryId,
p.CategoryName))
).ToList();
L'utilisation la plus simple (mais pas aussi efficace) consiste à mapper une représentation sous forme de chaîne afin d'éviter le hachage personnalisé. Les chaînes égales ont déjà des codes de hachage égaux.
public List<ItemCustom2> GetBrandListByCat(int id)
{
var OBJ = (from a in db.Items
join b in db.Brands on a.BrandId equals b.Id into abc1
where (a.ItemCategoryId == id)
from b in abc1.DefaultIfEmpty()
select new
{
ItemCategoryId = a.ItemCategoryId,
Brand_Name = b.Name,
Brand_Id = b.Id,
Brand_Pic = b.Pic,
}).Distinct();
List<ItemCustom2> ob = new List<ItemCustom2>();
foreach (var item in OBJ)
{
ItemCustom2 abc = new ItemCustom2();
abc.CategoryId = item.ItemCategoryId;
abc.BrandId = item.Brand_Id;
abc.BrandName = item.Brand_Name;
abc.BrandPic = item.Brand_Pic;
ob.Add(abc);
}
return ob;
}