Entity Framework Core:为DbContext查询视图模型自定义LINQ方法
复用EF Core查询逻辑的解决方案(符合DRY原则)
完全理解你的痛点——重复编写嵌套视图模型的计算逻辑不仅容易出错,维护起来也像噩梦一样麻烦。好在EF Core支持通过**表达式树(Expression Trees)**来封装可被解析为SQL的复用逻辑,刚好能解决你的问题。下面一步步来实现:
1. 先封装嵌套模型的选择表达式
核心思路是用返回Expression<Func<源模型, 嵌套视图模型>>的静态方法,这样EF能把这些表达式合并到最终的查询SQL里,而不是在客户端执行。
比如针对你的NestedViewModel2,可以这样封装:
public static class NestedViewModelSelectors { // 封装NestedViewModel2的选择逻辑,包括计算逻辑 public static Expression<Func<MyDbModel1, NestedViewModel2>> SelectNestedViewModel2() { return dbm1 => !dbm1.OtherTableModel2Id.HasValue ? null : new NestedViewModel2 { SomeCalculatedValue = dbm1.OtherTableModel2.Value1 + dbm1.OtherTableModel2.Value2, SomeOtherValue = dbm1.OtherTableModel2.Value3 }; } // 同理封装NestedViewModel1的选择逻辑 public static Expression<Func<MyDbModel1, NestedViewModel1>> SelectNestedViewModel1() { return dbm1 => new NestedViewModel1 { SomeValue1 = dbm1.OtherTableModel1.Value1, SomeValue2 = dbm1.OtherTableModel1.Value2 }; } }
2. 封装DbSet的扩展方法
接下来,把整个视图模型的查询逻辑封装成DbSet的扩展方法,在里面复用上面的嵌套表达式。这里需要用到Expression.Invoke,EF Core 2.0对它的支持已经足够处理这种场景。
public static class MyDbModel1QueryExtensions { // 封装MyViewModel1的查询逻辑,带过滤条件 public static IQueryable<MyViewModel1> SelectViewModel1WithValueGreaterThan( this DbSet<MyDbModel1> dbSet, int greaterThan) { var selectNested1 = NestedViewModelSelectors.SelectNestedViewModel1(); var selectNested2 = NestedViewModelSelectors.SelectNestedViewModel2(); return dbSet .Where(dbm1 => dbm1.SomeValue > greaterThan) .Select(dbm1 => new MyViewModel1 { ValueName = dbm1.SomeValue, NestedModel1 = selectNested1.Invoke(dbm1), NestedModel2 = selectNested2.Invoke(dbm1) }); } // 如果需要其他视图模型的查询,比如MyViewModel2,同样复用嵌套表达式 public static IQueryable<MyViewModel2> SelectViewModel2List(this DbSet<MyDbModel1> dbSet) { var selectNested2 = NestedViewModelSelectors.SelectNestedViewModel2(); return dbSet .Select(dbm1 => new MyViewModel2 { AnotherValue = dbm1.AnotherField, SharedNestedModel = selectNested2.Invoke(dbm1) // 其他专属字段 }); } }
3. 使用封装后的扩展方法
现在你可以在代码里直接调用这些扩展方法,不用再重复写嵌套逻辑了:
// 获取MyViewModel1列表 var viewModel1Items = await MyDbContext.MyDbModel1 .SelectViewModel1WithValueGreaterThan(10) .ToListAsync(); // 获取MyViewModel2列表 var viewModel2Items = await MyDbContext.MyDbModel1 .SelectViewModel2List() .ToListAsync();
关键注意事项
- 必须用Expression而不是Func:EF需要解析表达式树来生成SQL,如果用普通的Func,会导致查询先拉取全量数据到客户端再映射,性能极差。
- EF Core 2.0兼容性:
Expression.Invoke在EF Core 2.0中已经支持被解析为SQL,不用担心客户端求值的问题。 - 集中维护逻辑:以后如果
NestedViewModel2的计算逻辑需要修改,只需要更新NestedViewModelSelectors.SelectNestedViewModel2()这一处即可,所有引用它的视图模型查询都会自动生效。
这种方式完美遵循了DRY原则,既保证了EF能生成高效的SQL,又大幅降低了维护成本。
内容的提问来源于stack exchange,提问作者monty
相关产品推荐
相关产品推荐

