Entity Framework 6:从视图读取数据并保存至对应数据表的方案
嘿,这个场景我之前在项目里碰过好多次,给你几个实用的方案,你可以根据自己的项目复杂度来选:
方案1:维护独立的视图实体和数据表实体
这是最直观也最不容易出错的方案,核心是把“读”和“写”的实体分开:
- 首先用Code First from DB工具,分别生成视图对应的实体(比如叫
OrderSummaryView)和目标数据表的实体(比如叫Order)。视图实体保留所有计算字段,数据表实体只包含可写入的字段。 - 读取展示数据时,直接查询视图实体:
var orderList = db.OrderSummaryView.ToList();,拿到带计算字段的完整数据。 - 需要保存时,把视图实体的属性映射到数据表实体上——可以手动写转换逻辑,也用
AutoMapper简化:
// 手动转换示例 var orderToSave = new Order { Id = viewEntity.Id, CustomerId = viewEntity.CustomerId, OrderDate = viewEntity.OrderDate, // 只复制数据表存在的字段,跳过计算字段(比如TotalAmount、DiscountRate这类) }; db.Orders.AddOrUpdate(orderToSave); db.SaveChanges();
- 关键要确保两个实体的
[Table]特性分别指向视图和数据表,EF会自动处理不同实体的读写操作。
方案2:修改视图实体,标记计算字段为不可映射
如果不想维护两个实体,可以在自动生成的视图实体上做改造:
- 找到实体里的计算字段,给它们加上
[NotMapped]特性,这样EF会忽略这些字段,不会尝试把它们写入数据库。 - 把实体的
[Table]特性从视图名修改为目标数据表的名称。 - 读取数据时,因为实体现在映射到数据表了,所以需要用原生SQL查询视图来获取带计算字段的数据:
var viewData = db.Database.SqlQuery<OrderSummaryView>("SELECT * FROM OrderSummaryView").ToList();
- 保存时直接使用这个实体即可,EF会自动跳过标记为
[NotMapped]的计算字段,只把可写入字段保存到数据表。
提示:这个方案要注意,实体的可写入字段必须和数据表结构完全匹配,否则保存会抛出字段不匹配的错误。另外,记得把实体从自动生成的目录移出来,避免后续重新生成时覆盖你的修改。
方案3:用DTO做中间层(适合架构清晰的项目)
如果你的项目已经在使用DTO(数据传输对象)模式,这个方案会让代码职责更清晰:
- 生成视图实体用于读取原始数据,生成数据表实体用于写入数据库,再创建一个
OrderDto,包含所有需要展示的字段(包括计算字段)。 - 读取时,把视图实体的数据映射到
OrderDto,再传递给前端展示;保存时,把前端传回的OrderDto(或者业务层构造的DTO)映射到数据表实体,执行保存操作。 - 用
AutoMapper可以一键配置映射规则,减少重复代码:
// 配置映射 Mapper.Initialize(cfg => { cfg.CreateMap<OrderSummaryView, OrderDto>(); cfg.CreateMap<OrderDto, Order>(); }); // 使用示例 var viewEntity = db.OrderSummaryView.FirstOrDefault(o => o.Id == 1); var dto = Mapper.Map<OrderDto>(viewEntity); // 保存时 var orderToSave = Mapper.Map<Order>(dto); db.Orders.AddOrUpdate(orderToSave); db.SaveChanges();
额外注意事项
- 无论用哪个方案,都要保证视图和数据表的主键一致,这样才能准确对应到要更新的记录。
- 如果视图的计算字段是基于数据表的实时数据,保存后重新查询视图就能看到最新的计算结果,不需要额外处理。
- 要是用方案2或方案1,记得不要让Code First的自动生成工具覆盖你手动修改的实体代码——最好把这些实体移到独立的文件夹,手动维护。
内容的提问来源于stack exchange,提问作者Bjorn W
相关产品推荐
相关产品推荐

