5
votes

Une façon propre de vérifier si toutes les propriétés, sauf deux, correspondent entre deux objets?

J'ai une base de données contenant des composants avec environ 20 propriétés. Pour savoir si une mise à jour est nécessaire, je veux vérifier si toutes les propriétés des deux objets, à l'exception de DateCreated et Id, correspondent. Si tout ne correspond à aucune mise à jour, sinon, mettez à jour la base de données.

Component comp_InApp = new Component()
{
    Id = null,
    Description = "Commponent",
    Price = 100,
    DateCreated = "2019-01-30",
    // Twenty more prop
};

Component comp_InDb = new Component()
{
    Id = 1,
    Description = "Component",
    Price = 100,
    DateCreated = "2019-01-01",
    // Twenty more prop
};

// Check if all properties match, except DateCreated and Id.
if (comp_InApp.Description == comp_InDb.Description &&
    comp_InApp.Price == comp_InDb.Price
    // Twenty more prop
    )
{
    // Everything up to date.
}
else
{
    // Update db.
}

Cela fonctionne, mais ce n'est pas une manière très propre avec 20 propriétés. Existe-t-il une meilleure façon d'obtenir le même résultat d'une manière plus propre?


3 commentaires

Vous pouvez utiliser les méthodes Equals et GetHashCode , ou vous pouvez utiliser une bibliothèque d'égalité qui peut effectuer la comparaison pour vous.


Copie possible de Comparaison des propriétés d'objet en c # . Vérifiez la réponse avec 64 votes positifs. des trucs comme ça se font facilement avec certaines bibliothèques.


N'utilisez pas de chaîne pour stocker un DateHeure


4 Réponses :


3
votes

J'utilise DeepEqual lorsque je ne veux pas / n'ai pas le temps d'écrire moi-même méthodes Equals et GetHashCode .

Vous pouvez l'installer simplement à partir de NuGet avec:

    if (comp_InApp.IsDeepEqual(comp_InDb))
    {
        // Everything up to date.
    }
    else
    {
        // Update db.
    }

et l'utiliser comme:

Install-Package DeepEqual

Mais gardez à l'esprit que cela ne fonctionnera que pour votre cas lorsque vous souhaitez comparer explicitement des objets, mais pas pour le cas où vous souhaitez supprimer un formulaire objet une List ou des cas comme celui-ci, lorsque Equals et GetHashCode sont appelés.


0 commentaires

2
votes

D'une manière, créez une classe qui implémente IEqualityComparer pour encapsuler cette logique et pour éviter que vous ayez modifié la classe Comparer elle-même (si vous ne veulent cette logique Equals à tout moment). Ensuite, vous pouvez l'utiliser pour un simple Equals de deux instances de Component et même pour tout Méthodes LINQ qui l'accepte comme argument supplémentaire.

IEnumerable<Component> missingInDb = inAppList.Except( inDbList, comparer );

Votre cas d'utilisation simple:

var comparer = new ComponentComparer();
bool equal = comparer.Equals(comp_InApp, comp_InDb);

Cela fonctionne aussi si vous avez deux collections et que vous voulez connaître la différence, par exemple:

class ComponentComparer : IEqualityComparer<Component>
{
    public bool Equals(Component x, Component y)
    {
        if (object.ReferenceEquals(x, y)) return true;
        if (x == null || y == null) return false;
        return x.Price == y.Price && x.Description == y.Description;
    }

    public int GetHashCode(Component obj)
    {
        unchecked 
        {
            int hash = 17;
            hash = hash * 23 + obj.Price.GetHashCode();
            hash = hash * 23 + obj.Description?.GetHashCode() ?? 0;
            // ...
            return hash;
        }
    }
}


0 commentaires

1
votes

Voici une solution avec Reflection:

    static bool AreTwoEqual(Component inApp, Component inDb)
    {
        string[] propertiesToExclude = new string[] { "DateCreated", "Id" };

        PropertyInfo[] propertyInfos = typeof(Component).GetProperties()
                                                 .Where(x => !propertiesToExclude.Contains(x.Name))
                                                 .ToArray();

        foreach (PropertyInfo propertyInfo in propertyInfos)
        {
            bool areSame = inApp.GetType().GetProperty(propertyInfo.Name).GetValue(inApp, null).Equals(inDb.GetType().GetProperty(propertyInfo.Name).GetValue(inDb, null));

            if (!areSame)
            {
                return false;
            }
        }

        return true;
    }


0 commentaires

0
votes

Vous pouvez utiliser une réflexion mais cela peut ralentir votre application. Une autre façon de créer ce comparateur est de le générer avec des expressions Linq. Essayez ce code:

var comparator = CreateAreEqualExpression<Component>(
        c => c.Id,
        c => c.DateCreated)
    .Compile();

il générera une expression qui ressemblera à

public static Expression<Func<T, T, bool>> CreateAreEqualExpression<T>(
  params Expression<Func<T, object>>[] toExclude)
{
    var exclude = toExclude
        .Select(e =>
        {
            // for properties that is value types (int, DateTime and so on)
            var name = ((e.Body as UnaryExpression)?.Operand as MemberExpression)?.Member.Name;
            if (name != null)
                return name;

            // for properties that is reference type
            return (e.Body as MemberExpression)?.Member.Name;
        })
        .Where(n => n != null)
        .Distinct()            
        .ToArray();

    var type = typeof(T);
    var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
        .Where(p => !exclude.Contains(p.Name))
        .ToArray();

    /* rest of code is unchanged */
}

L'utilisation est simple

var comporator = CreateAreEqualExpression<Component>("Id", "DateCreated")
    .Compile(); // save compiled comparator somewhere to use it again later
var areEqual = comporator(comp_InApp, comp_InDb);


0 commentaires