如何正确处理EF Database First多上下文列表并完成类型转换?
解决EF6多Database First上下文动态处理Bill表的问题
这个问题我太熟悉了——EF6的DbContext基类确实不会自动暴露你的实体集合,因为它根本不知道每个具体上下文里有哪些表。咱们一步步来解决这个动态处理多上下文的问题:
方案1:定义强类型接口(最优雅、安全的方案)
既然每个企业上下文都有bill表,那我们可以通过接口来统一约束这些上下文,让它们都暴露强类型的DbSet<Bill>。
步骤1:统一Bill实体
首先确保所有上下文对应的bill表实体是同一个类型(最好抽成共享类库的实体类,避免重复定义),比如:
// 共享的Bill实体类 public class Bill { public string merchant { get; set; } public decimal amount { get; set; } // 其他字段... }
步骤2:创建约束接口
定义一个包含bill集合的接口:
public interface IBillContext { DbSet<Bill> bill { get; set; } }
步骤3:让企业上下文实现接口
因为Database First生成的上下文是partial类,我们可以在单独的文件里实现接口,避免下次更新模型时覆盖代码:
// 针对Company1_Entities的接口实现 public partial class Company1_Entities : IBillContext { public DbSet<Bill> bill { get; set; } } // 同理给Company2_Entities、Company3_Entities都加上这个实现
步骤4:修改你的业务代码
把字典的类型从DbContext改成IBillContext,这样就能直接强类型访问bill集合了:
// 这里可以换成从数据库读取商户号和上下文名称的逻辑 Dictionary<string, IBillContext> lstDB = new Dictionary<string, IBillContext>(); lstDB.Add("00439837", new Company1_Entities()); lstDB.Add("00439832", new Company2_Entities()); lstDB.Add("00439839", new Company3_Entities()); using (All_Bills_Entities main_db = new All_Bills_Entities()) { foreach(var dataBaseInfo in lstDB) { IBillContext currentDB = dataBaseInfo.Value; // 直接用强类型的bill集合,还能享用到Linq智能提示 var matchedBills = currentDB.bill .Where(record => record.merchant == dataBaseInfo.Key) .ToList(); foreach (var record in matchedBills) { // 注意:不能直接跨上下文添加实体,必须映射成主库的Bill实例 var mainBill = new Bill { merchant = record.merchant, amount = record.amount, // 复制其他需要的字段... }; main_db.bill.Add(mainBill); } } main_db.SaveChanges(); // 记得最后统一保存 }
方案2:使用DbContext.Set<T>(无需修改现有上下文)
如果不想修改每个企业的上下文,也可以用DbContext的泛型Set<T>方法获取bill集合,前提是你有统一的Bill实体类:
Dictionary<string, DbContext> lstDB = new Dictionary<string, DbContext>(); lstDB.Add("00439837", new Company1_Entities()); lstDB.Add("00439832", new Company2_Entities()); lstDB.Add("00439839", new Company3_Entities()); using (All_Bills_Entities main_db = new All_Bills_Entities()) { foreach(var dataBaseInfo in lstDB) { DbContext currentDB = dataBaseInfo.Value; // 用Set<T>获取bill集合 var matchedBills = currentDB.Set<Bill>() .Where(record => record.merchant == dataBaseInfo.Key) .ToList(); foreach (var record in matchedBills) { var mainBill = new Bill { merchant = record.merchant, amount = record.amount, // 复制其他字段... }; main_db.bill.Add(mainBill); } } main_db.SaveChanges(); }
这个方案更快捷,但缺点是没有编译时检查——如果某个上下文的bill表结构和共享实体不一致,会抛出运行时错误。
方案3:动态类型(不推荐,无类型安全)
如果连统一的Bill实体都没有(非常不建议这种情况),可以用dynamic类型绕过编译检查,但风险很高:
using (All_Bills_Entities main_db = new All_Bills_Entities()) { foreach(var dataBaseInfo in lstDB) { DbContext currentDB = dataBaseInfo.Value; // 通过反射获取bill集合 dynamic bills = currentDB.GetType().GetProperty("bill").GetValue(currentDB); foreach (dynamic record in bills) { if(record.merchant == dataBaseInfo.Key) { var mainBill = new Bill { merchant = record.merchant, // 这里都是动态访问字段,拼写错误只会在运行时暴露 }; main_db.bill.Add(mainBill); } } } main_db.SaveChanges(); }
额外优化建议
- 资源管理:不要把上下文长期存在字典里,建议在循环内创建上下文并包裹在
using中,避免资源泄漏。 - 批量处理:如果数据量很大,不要一次性
ToList()加载所有数据,建议分页查询,并用AddRange批量添加实体提升性能。 - 实体映射:可以用AutoMapper等工具简化实体间的字段复制,减少重复代码。
内容的提问来源于stack exchange,提问作者Antoine Pelletier
相关产品推荐
相关产品推荐

