1
votes

Clause IQueryable WHERE générique LINQ

Disons que j'ai un DBContext avec 3 DBSets:

var _A = GetCode(TheDB.As, "123");
var _B = GetCode(TheDB.Bs, "123");
var _C = GetCode(TheDB.Cs, "123");

Je veux écrire une méthode générique qui:

  1. Prend de manière générique l'un des trois DbSet comme premier argument
  2. Prend une valeur de chaîne comme deuxième argument
  3. Sans transtypage en IEnumerable , renvoie le premier enregistrement dans le DbSet fourni où le champ Code correspond à la chaîne fournie

Jusqu'à présent, j'ai cette méthode:

public static T GetCode<T>(IQueryable<T> set, string code) where T : class
{
    var Prop = typeof(T).GetProperty("Code");
    return set.Where(x => (string)Prop.GetValue(x) == code).FirstOrDefault();
}

Quand j'essaye de l'appeler en utilisant cette ligne:

public class DatabaseContext : DbContext
{
    public DbSet<A> As { get; set; }
    public DbSet<B> Bs { get; set; }
    public DbSet<C> Cs { get; set; }
}

class A
{
    public string Text { get; set; }
    public string Code { get; set; }
}

class B
{
    public string Name { get; set; }
    public string Code { get; set; }
}

class C
{
    public string Book { get; set; }
    public string Code { get; set; }
}


0 commentaires

3 Réponses :


2
votes

L'approche la plus simple (et sans risque de type) serait de simplement créer une interface comme IHaveCode et de lui limiter le paramètre générique:

interface IHaveCode
{
     string Code { get; set; }
}
class A: IHaveCode
{
    public string Text { get; set; }
    public string Code { get; set; }
}

class B: IHaveCode
{
    public string Name { get; set; }
    public string Code { get; set; }
}

class C: IHaveCode
{
    public string Book { get; set; }
    public string Code { get; set; }
}

public static T GetCode<T>(IQueryable<T> set, string code) where T : IHaveCode
{
    return set.Where(x => x.Code == code).FirstOrDefault();
}

Si pour une raison quelconque cela ne vous convient pas alors vous devrez construire un arbre d'expression vous-même.


0 commentaires

0
votes

Une méthode plus générique que celle de Guru Stron, qui est également utilisable pour les DbSets de classes sans propriété Code , serait de fournir la propriété que vous souhaitez utiliser dans votre comparaison.

// Get a C by Book title:
string bookTitle = ...
C fetchedC = dbContext.Cs.GetFirstPropertyOrDefault(c => c.Book);

Usage:

string comparisonValue = ...
A fetchedA = dbContext.As.GetFirstPropertyMachOrDefault(a => a.Code, comparisonValue);

Mais maintenant que vous l'avez rendu générique, vous pouvez également l'utiliser pour récupérer d'autres propriétés; p >

public static T GetFirstPropertyMatchOrDefault<T>(
    this IQueryable<T> source,
    Expression<Func<T, string>> propertySelector,
    string comparisonValue)
{
    return source.Where(sourceElement => propertySelector(sourceElement) == comparisonValue)
                 .FirstOrDefault();
}


0 commentaires

0
votes

Vous pouvez implémenter la méthode GetCode en utilisant Arbres d'expression .

public static T GetCode<T>(IQueryable<T> set, string code) where T : class {
    PropertyInfo codeProp = typeof(T).GetProperty("Code");
    if(codeProp == null)
        throw new ArgumentException($"{typeof(T).FullName} does not have the Code property");
    ParameterExpression param = Expression.Parameter(typeof(T));
    Expression getCode = Expression.MakeMemberAccess(param, codeProp);
    Expression codeVal = Expression.Constant(code);
    Expression body = Expression.Equal(getCode, codeVal);
    Expression<Func<T, bool>> predicate = Expression.Lambda<Func<T, bool>>(body, param);
    return set.FirstOrDefault(predicate);
}


0 commentaires