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:
DbSet
comme premier argument 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; } }
3 Réponses :
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.
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(); }
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); }