BreezeJS快速上手指南:在CustomerManagerStandard中掌握EntityManager、元数据获取与saveChanges完整工作流
【免费下载链接】CustomerManagerStandardCustomer Manager AngularJS/BreezeJS Application.项目地址: https://gitcode.com/gh_mirrors/cu/CustomerManagerStandard
CustomerManagerStandard 是一个基于 AngularJS + BreezeJS 的客户管理应用演示项目。它用一个完整的 CRUD 场景,展示了 BreezeJS 的核心三大件:EntityManager 实体管理器、元数据获取(fetchMetadata)和 saveChanges 保存变更的完整工作流,非常适合新手理解 BreezeJS 的数据访问机制。
为什么这个项目是学习BreezeJS的好材料 🎯
多数项目里,前端直接用$http请求 REST 接口,手动管理 JSON 数据。而 CustomerManagerStandard 提供了一个"对照组":同一个功能同时实现了两套数据访问层,通过一个开关随时切换:
- 普通模式:AngularJS
$http+ RESTful 服务(customersService.js) - Breeze模式:BreezeJS EntityManager + 元数据驱动的实体管理(customersBreezeService.js)
切换开关只有 3 行,位于 config.js:
var value = { useBreeze: false // 改为 true 即启用 BreezeJS };dataService.js 则负责按配置分发:
return (config.useBreeze) ? customersBreezeService : customersService;控制器代码完全不用改——这正是 BreezeJS 封装的直观体现:业务层只关心"实体",不关心 HTTP 细节。
BreezeJS 工作流总览:三步闭环
BreezeJS 的完整生命周期可以概括为三步,本项目代码与之一一对应:
| 步骤 | 核心API | 前端代码位置 | 后端代码位置 |
|---|---|---|---|
| ① 创建 EntityManager | new breeze.EntityManager(serviceName) | customersBreezeService.js#L14-L16 | BreezeWebApiConfig.cs#L15-L19 |
| ② 获取元数据 | metadataStore.fetchMetadata() | customersBreezeService.js#L112-L120 | BreezeDataServiceController.cs#L27-L31 |
| ③ 保存变更 | entityManager.saveChanges() | customersBreezeService.js#L49-L51 | BreezeDataServiceController.cs#L76-L88 |
后端路由:breeze 前缀如何生效
BreezeWebApiConfig.cs 在应用启动时注册了一条专用路由breeze/{controller}/{action},它被插入到所有 Web API 路由的最前面。因此前端请求的breeze/breezedataservice会精确命中 BreezeDataServiceController.cs 这个标注了[BreezeController]的控制器。
第一步:创建并配置 EntityManager
EntityManager 是 BreezeJS 的"心脏"——它维护一个客户端实体缓存(Identity Map),所有查询结果、修改、删除都挂在它身上。
创建前有两行全局配置,位于 customersBreezeService.js#L10-L13:
// 使用 backingStore 模型库(AngularJS 数据绑定支持) breeze.config.initializeAdapterInstance('modelLibrary', 'backingStore', true); // 服务端 PascalCase → 客户端 camelCase 自动转换 breeze.NamingConvention.camelCase.setAsDefault();然后一行代码创建 EntityManager:
var serviceName = 'breeze/breezedataservice'; var entityManager = new breeze.EntityManager(serviceName);💡
serviceName对应后端的breeze/breezedataservice路由前缀,是前端 EntityManager 与后端 BreezeDataServiceController 的"握手约定"。
第二步:元数据获取(Metadata)揭秘
BreezeJS 与原生$http最大的区别:先要告诉客户端"实体长什么样"。元数据描述了实体类型、属性、主键、导航关系(如 Customer 的 Orders 集合、State 关联),它是createEntity、级联更新、变更追踪的前提。
本项目把元数据获取封装成了带缓存判断的函数 customersBreezeService.js#L112-L120:
function getMetadata() { var store = entityManager.metadataStore; if (store.hasMetadataFor(serviceName)) { // 已有元数据,跳过 return $q.when(true); } else { // 首次:向后端拉取 return store.fetchMetadata(serviceName); } }对应的后端端点非常简单,由EFContextProvider从 Entity Framework 的上下文中自动序列化模型 BreezeDataServiceController.cs#L27-L31:
[HttpGet] public string Metadata() { return _contextProvider.Metadata(); }元数据的典型消费者是createEntity。新建客户时,必须先有元数据才能创建实体 customersBreezeService.js#L53-L57:
factory.newCustomer = function () { return getMetadata().then(function () { return entityManager.createEntity('Customer', { firstName: '', lastName: '' }); }); };此外,项目还注册了实体构造函数与初始化器customersBreezeService.js#L172-L173,用于给Customer、Order实体注入客户端计算属性(如订单合计ordersTotal),这是元数据扩展能力的实际应用。
第三步:saveChanges 完整工作流
BreezeJS 的保存机制是"变更暂存 + 批量提交":任何属性修改、createEntity、setDeleted都只是把实体标记进 EntityManager 的变更集合,真正落库统一由saveChanges()触发。
新增与更新:只需一个调用
factory.insertCustomer = function (customer) { return entityManager.saveChanges(); };insertCustomer与updateCustomer的实现完全相同——都是saveChanges()。因为 EntityManager 自动区分了 Added / Modified 状态。customerEditController.js 中的控制器据此判断走 insert 还是 update,而底层无需区别处理。
后端接收 Save Bundle
saveChanges()会把所有变更打包成一个Save Bundle(JSON),POST 到后端的SaveChanges端点 BreezeDataServiceController.cs#L76-L88:
[HttpPost] public SaveResult SaveChanges(JObject saveBundle) { return _contextProvider.SaveChanges(saveBundle); }EFContextProvider.SaveChanges负责将 Bundle 中的各实体变更逐一应用到 Entity Framework 上下文并SaveChanges,再把新生成的 Id 等结果返回给前端——前端据此刷新实体缓存,实现服务端 → 客户端的自动同步。
删除:标记 + 级联处理
删除客户时,代码先 detach 掉其 orders(避免级联冲突),再标记客户为 Deleted,最后统一 saveChanges customersBreezeService.js#L59-L86:
var customer = entityManager.getEntityByKey('Customer', id); if (customer) { customer.orders.slice().forEach(function (order) { entityManager.detachEntity(order); }); customer.entityAspect.setDeleted(); } return entityManager.saveChanges();⚠️ 新手易错点:修改
customer.firstName = 'X'后不会自动保存!必须显式调用saveChanges()。这是 BreezeJS 与"即时写库"模式的本质区别。
附赠技能:EntityQuery 分页查询实战
BreezeJS 的查询 API 是链式的,本项目的分页查询 customersBreezeService.js#L122-L143 是标准模板:
var query = EntityQuery .from('Customers') .skip(pageIndex * pageSize) // 跳过分页 .take(pageSize) // 每页条数 .inlineCount(true); // 附带总条数inlineCount让后端一次返回"当前页数据 + 总数",前端直接渲染分页条(即界面底部的 "Showing 10 of 23 total customers")。查询条件与展开关系(如.expand('orders, state'))也是同样链式追加,完整模型定义见 Customer.cs。
常见问题速查 🛠️
- 页面空白 / 实体创建失败→ 检查是否先执行了
fetchMetadata,以及serviceName是否与后端路由一致。 - 属性名对不上→ 确认已启用
NamingConvention.camelCase.setAsDefault()。 - 改了数据没生效→ 忘了调
entityManager.saveChanges(),或变更未通过 Breeze 的实体对象操作。 - 切换数据层→ 只需把 config.js 的
useBreeze置为true,控制器零改动。
总结
CustomerManagerStandard 用最精简的客户管理场景,把 BreezeJS 的核心链路演示得清清楚楚:
- EntityManager统一管理实体缓存与变更追踪;
- 元数据获取(
fetchMetadata+ 后端Metadata())是创建实体与变更追踪的前提; - saveChanges以 Save Bundle 批量提交,新增/更新/删除共用同一入口。
掌握这条工作流后,你已具备在任何 BreezeJS 项目中搭建数据访问层的能力。动手试试:把useBreeze切换为true,对比两种模式的网络请求差异,理解会更深!
【免费下载链接】CustomerManagerStandardCustomer Manager AngularJS/BreezeJS Application.项目地址: https://gitcode.com/gh_mirrors/cu/CustomerManagerStandard
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考