如何复用WPF ComboBox填充方法?支持传入模型类与显示属性
实现LookupItem数据服务的复用方案
当然可以实现代码复用,通过泛型结合表达式树就能让这个方法支持任意实体类和自定义的DisplayMember属性,同时保证EF Core能正确将查询翻译为SQL执行。
步骤1:定义实体约束接口(可选但推荐)
先定义一个接口,确保所有需要生成Lookup的实体都包含Id属性,编译时就能避免错误:
public interface IHasId { int Id { get; } }
然后让你的实体类(比如Grade)实现这个接口:
public class Grade : IHasId { public int Id { get; set; } public string GradeName { get; set; } // 其他实体属性 }
步骤2:重构为通用方法
编写泛型方法,接收实体类型和DisplayMember的选择表达式,通过构建表达式树让EF Core识别查询逻辑:
public async Task<IEnumerable<LookupItem>> GetLookupAsync<TEntity>(Expression<Func<TEntity, string>> displayMemberSelector) where TEntity : class, IHasId { // 构建表达式树,生成EF可识别的Select逻辑 var parameter = Expression.Parameter(typeof(TEntity), "entity"); // 获取实体的Id属性表达式 var idProperty = Expression.Property(parameter, nameof(IHasId.Id)); // 复用传入的DisplayMember选择表达式 var displayMemberExpr = Expression.Invoke(displayMemberSelector, parameter); // 构建LookupItem的初始化表达式 var lookupItemInit = Expression.MemberInit( Expression.New(typeof(LookupItem)), Expression.Bind(typeof(LookupItem).GetProperty(nameof(LookupItem.Id)), idProperty), Expression.Bind(typeof(LookupItem).GetProperty(nameof(LookupItem.DisplayMember)), displayMemberExpr) ); // 生成最终的Select Lambda表达式 var selectExpr = Expression.Lambda<Func<TEntity, LookupItem>>(lookupItemInit, parameter); using (var ctx = _contextCreator()) { return await ctx.Set<TEntity>().AsNoTracking() .Select(selectExpr) .ToListAsync(); } }
步骤3:调用通用方法
现在可以用同一个方法处理不同实体的Lookup生成:
// 生成Grade的Lookup数据(对应原GetGradeLookupAsync逻辑) var gradeLookup = await GetLookupAsync<Grade>(g => g.GradeName); // 如果有其他实体(比如Student),只要实现IHasId就能直接用 var studentLookup = await GetLookupAsync<Student>(s => s.StudentName);
关键说明
- 用
Expression<Func<>>而非普通Func<>:确保EF Core能将查询翻译为SQL,避免全表加载到内存再处理,保证性能。 - 泛型+接口约束:既支持任意实体,又能在编译时检查实体是否符合要求,减少运行时错误。
内容的提问来源于stack exchange,提问作者SND69
相关产品推荐
相关产品推荐

