C # LINQ to SQL: Refactoring cette méthode GetByID générique

c# expression-trees generics linq-to-sql

Question

J'ai écrit la méthode suivante.

public T GetByID(int id)
{
    var dbcontext = DB;
    var table = dbcontext.GetTable<T>();
    return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}

Fondamentalement, c'est une méthode dans une classe générique où T est une classe dans un DataContext.

La méthode extrait la table du type de T ( GetTable ) et recherche la première propriété (toujours l'ID) du paramètre entré.

Le problème, c’est que j’ai dû convertir la table des éléments en une liste pour pouvoir exécuter un GetType sur la propriété, mais cela n’est pas très pratique car tous les éléments de la table doivent être énumérés et convertis en List .

Comment puis-je refactoriser cette méthode pour éviter une ToList de ToList sur toute la table?

[Mettre à jour]

La raison pour laquelle je ne peux pas exécuter directement l' Where sur la table est que je reçois cette exception:

La méthode 'System.Reflection.PropertyInfo [] GetProperties ()' n'a aucune traduction prise en charge en SQL.

Parce que GetProperties ne peut pas être traduit en SQL.

[Mettre à jour]

Certaines personnes ont suggéré d'utiliser une interface pour T , mais le problème est que le paramètre T sera une classe générée automatiquement dans [DataContextName] .designer.cs . Je ne peux donc pas lui demander d' implémenter une interface (l'implémentation n'est pas réalisable les interfaces pour toutes ces "classes de base de données" de LINQ; et aussi, le fichier sera régénéré une fois que j’ajoute de nouvelles tables au DataContext, perdant ainsi toutes les données écrites).

Donc, il doit y avoir un meilleur moyen de faire cela ...

[Mettre à jour]

J'ai maintenant implémenté mon code comme suggéré par Neil Williams , mais j'ai toujours des problèmes. Voici des extraits du code:

Interface:

public T GetByID(int id)
{
    var dbcontext = DB;
    var table = dbcontext.GetTable<T>();
    return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}

DataContext [Voir le code]:

public T GetByID(int id)
{
    var dbcontext = DB;
    var table = dbcontext.GetTable<T>();
    return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}

Méthode générique:

public T GetByID(int id)
{
    var dbcontext = DB;
    var table = dbcontext.GetTable<T>();
    return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}

L'exception est levée sur cette ligne: return table.SingleOrDefault(e => e.ID.Equals(id)); et l'exception est:

System.NotSupportedException: The member 'MusicRepo_DataContext.IHasID.ID' has no supported translation to SQL.

[Mise à jour] Solution:

Avec l'aide de la réponse postée de Denis Troller et du lien vers l'article sur le blog Code Rant , j'ai finalement réussi à trouver une solution:

public T GetByID(int id)
{
    var dbcontext = DB;
    var table = dbcontext.GetTable<T>();
    return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}

Réponse acceptée

Ce dont vous avez besoin est de créer un arbre d’expression que LINQ to SQL peut comprendre. En supposant que votre propriété "id" soit toujours nommée "id":

public virtual T GetById<T>(short id)
{
    var itemParameter = Expression.Parameter(typeof(T), "item");
    var whereExpression = Expression.Lambda<Func<T, bool>>
        (
        Expression.Equal(
            Expression.Property(
                itemParameter,
                "id"
                ),
            Expression.Constant(id)
            ),
        new[] { itemParameter }
        );
    var table = DB.GetTable<T>();
    return table.Where(whereExpression).Single();
}

Cela devrait faire l'affaire. Il a été emprunté sans vergogne à ce blog . C’est essentiellement ce que LINQ to SQL fait lorsque vous écrivez une requête telle que

public virtual T GetById<T>(short id)
{
    var itemParameter = Expression.Parameter(typeof(T), "item");
    var whereExpression = Expression.Lambda<Func<T, bool>>
        (
        Expression.Equal(
            Expression.Property(
                itemParameter,
                "id"
                ),
            Expression.Constant(id)
            ),
        new[] { itemParameter }
        );
    var table = DB.GetTable<T>();
    return table.Where(whereExpression).Single();
}

Vous ne faites que le travail pour LTS car le compilateur ne peut pas le créer pour vous, car rien ne peut imposer que T ait une propriété "id" et vous ne pouvez pas mapper une propriété "id" arbitraire d'une interface à la base de données.

==== UPDATE ====

OK, voici une implémentation simple pour trouver le nom de la clé primaire, en supposant qu’il n’en existe qu’un (pas une clé primaire composite), et en supposant que tout est bien typé (c’est-à-dire que votre clé primaire est compatible avec le type "court" utiliser dans la fonction GetById):

public virtual T GetById<T>(short id)
{
    var itemParameter = Expression.Parameter(typeof(T), "item");
    var whereExpression = Expression.Lambda<Func<T, bool>>
        (
        Expression.Equal(
            Expression.Property(
                itemParameter,
                "id"
                ),
            Expression.Constant(id)
            ),
        new[] { itemParameter }
        );
    var table = DB.GetTable<T>();
    return table.Where(whereExpression).Single();
}

Réponse populaire

Quelques idées...

Supprimez simplement l'appel ToList (), SingleOrDefault fonctionne avec un IEnumerably que je suppose que la table est.

Mettre en cache l'appel à e.GetType (). GetProperties (). First () pour obtenir le PropertyInfo renvoyé.

Pouvez-vous simplement ajouter à T une contrainte qui les obligerait à implémenter une interface qui expose la propriété Id?




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi