运行时如何选择正确的DBSet进行查询?(EF6场景)
解决方案:基于EF6的多类型车辆查询(符合开闭原则)
以下是几种无需if-else、遵循开闭原则的实现方案,可根据项目场景选择:
方案一:泛型方法+反射调用
封装通用泛型查询逻辑,通过反射根据传入对象的实际类型动态调用对应版本:
public class TestClass { SqlTestContext context = new SqlTestContext("connection string"); public Vehicle returnExistingFromDatabase(Vehicle vehicleObject) { var entityType = vehicleObject.GetType(); // 获取泛型查询方法并动态指定类型参数 var queryMethod = typeof(TestClass).GetMethod(nameof(GetExistingEntity), BindingFlags.NonPublic | BindingFlags.Instance) .MakeGenericMethod(entityType); return (Vehicle)queryMethod.Invoke(this, new object[] { vehicleObject.Id }); } // 通用泛型查询方法,约束为实现Vehicle的实体类 private T GetExistingEntity<T>(string id) where T : class, Vehicle { return context.Set<T>().SingleOrDefault(e => e.Id == id); } }
说明:新增Vehicle实现类时,只需在DbContext中注册对应DBSet,无需修改TestClass的代码,完全符合开闭原则。
方案二:策略模式(类型映射+委托)
预先注册每种车辆类型的查询逻辑,后续扩展仅需添加映射项:
public class TestClass { SqlTestContext context = new SqlTestContext("connection string"); // 存储类型与查询委托的映射关系 private readonly Dictionary<Type, Func<string, Vehicle>> _queryStrategies; public TestClass() { _queryStrategies = new Dictionary<Type, Func<string, Vehicle>> { { typeof(Car), id => context.Car.SingleOrDefault(c => c.Id == id) }, { typeof(Bicycle), id => context.Bicycle.SingleOrDefault(b => b.Id == id) } }; } public Vehicle returnExistingFromDatabase(Vehicle vehicleObject) { if (_queryStrategies.TryGetValue(vehicleObject.GetType(), out var queryFunc)) { return queryFunc(vehicleObject.Id); } throw new ArgumentException($"未支持的车辆类型:{vehicleObject.GetType().Name}"); } }
说明:这种方式性能优于反射,逻辑直观。新增类型时仅需在构造函数的字典中添加新键值对,核心查询逻辑无需改动。
方案三:非泛型DbSet查询
直接利用EF的非泛型Set方法,结合类型转换实现查询:
public class TestClass { SqlTestContext context = new SqlTestContext("connection string"); public Vehicle returnExistingFromDatabase(Vehicle vehicleObject) { var entityType = vehicleObject.GetType(); var dbSet = context.Set(entityType); // 将非泛型DbSet转换为Vehicle可枚举集合后查询 return dbSet.Cast<Vehicle>().SingleOrDefault(e => e.Id == vehicleObject.Id); } }
说明:无需泛型或反射,但需确保所有Vehicle实现类的Id属性已正确映射到数据库列。
注意事项
- 所有
Vehicle实现类必须在SqlTestContext中注册对应的DBSet(如public DbSet<Car> Car { get; set; })。 - 确保
Vehicle接口的Id属性在实体类中通过DataAnnotations或Fluent API正确映射到数据库的id列。
内容的提问来源于stack exchange,提问作者jasu
相关产品推荐
相关产品推荐

