当前位置: 首页 > news >正文

BreezeJS快速上手指南:在CustomerManagerStandard中掌握EntityManager、元数据获取与saveChanges完整工作流

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前端代码位置后端代码位置
① 创建 EntityManagernew breeze.EntityManager(serviceName)customersBreezeService.js#L14-L16BreezeWebApiConfig.cs#L15-L19
② 获取元数据metadataStore.fetchMetadata()customersBreezeService.js#L112-L120BreezeDataServiceController.cs#L27-L31
③ 保存变更entityManager.saveChanges()customersBreezeService.js#L49-L51BreezeDataServiceController.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,用于给CustomerOrder实体注入客户端计算属性(如订单合计ordersTotal),这是元数据扩展能力的实际应用。

第三步:saveChanges 完整工作流

BreezeJS 的保存机制是"变更暂存 + 批量提交":任何属性修改、createEntitysetDeleted都只是把实体标记进 EntityManager 的变更集合,真正落库统一由saveChanges()触发。

新增与更新:只需一个调用

factory.insertCustomer = function (customer) { return entityManager.saveChanges(); };

insertCustomerupdateCustomer的实现完全相同——都是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 的核心链路演示得清清楚楚:

  1. EntityManager统一管理实体缓存与变更追踪;
  2. 元数据获取fetchMetadata+ 后端Metadata())是创建实体与变更追踪的前提;
  3. saveChanges以 Save Bundle 批量提交,新增/更新/删除共用同一入口。

掌握这条工作流后,你已具备在任何 BreezeJS 项目中搭建数据访问层的能力。动手试试:把useBreeze切换为true,对比两种模式的网络请求差异,理解会更深!

【免费下载链接】CustomerManagerStandardCustomer Manager AngularJS/BreezeJS Application.项目地址: https://gitcode.com/gh_mirrors/cu/CustomerManagerStandard

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

http://www.cnnetsun.cn/news/4173426.html

相关文章:

  • 嵌入式学习路线全解析:从51单片机到STM32,新手避坑指南与核心技能构建
  • 数学建模实战:线性回归的核心假设、特征工程与模型诊断全解析
  • Vortigern 样式方案拆解:CSS Modules + PostCSS-Assets 完整配置指南
  • 深入react-native-app-tour源码:findNodeHandle与NativeModules如何打通JS与原生App Tour视图
  • 为什么DebugKit是Android开发者必备的悬浮调试神器?完整概览与功能解析
  • noteForOpenGL PBO像素缓冲对象:Pack/Unpack机制与CPU-GPU数据通道完整指南
  • 函数设计四大核心特性:从内置函数到模板重载的工程实践
  • OpCore-Simplify 快速上手指南:从硬件报告到 OpenCore EFI
  • Android开发者必学:从file_operations入门Linux驱动开发
  • 如何测试行级权限控制?用 pytest 与 pytest-mock 构建 fastapi-permissions 单元测试完全指南
  • 数学建模实战指南:从思维转变到模型落地的全流程解析
  • 开发者知识体系重构:从碎片化学习到系统化升级的工程实践
  • 5分钟跑通pymavlink:mavlink_connection连接Pixhawk并接收心跳的保姆级实战
  • 多智能体集群架构:构建公平、自适应的心理健康支持系统
  • 彻底解决链接器报错:从原理到实战的完整指南
  • RogueViz引擎深度剖析:HyperRogue背后的非欧几何游戏引擎
  • 30 分钟跑通 openAUTOSAR 经典平台:3 个核心模块与 1 个必踩的坑
  • 人形机器人落地实战:工业、商用、家庭三大场景技术评估与集成指南
  • RESTful API设计最佳实践与Python工程化实战指南
  • 花多少钱能买齐OpenArm的零件?BOM成本完整拆解与低价采购攻略
  • cargo-call-stack 源码解析指南:用 nom 手写 LLVM IR 解析器,构建全程序调用图
  • 从数学建模到量化交易:基于MCM赛题的策略开发全流程解析
  • 泰拉瑞亚灾厄Mod完整安装指南:从版本选择到汉化排错
  • 2026年硬盘盒选购指南:从SATA到NVMe协议,实测16款主流产品
  • unicode-segmentation如何实现UAX29标准:剖析GraphemeCursor状态机与GB规则判定逻辑
  • personal-jekyll-theme源码架构全解析:Jekyll布局、Liquid模板与组件化设计实战
  • HyperRogue的.tes镶嵌文件格式完全指南:定义并加载你的自定义几何
  • 计算机考研408核心考点:虚拟内存地址转换机制深度解析与真题实战
  • 机器人应用泛化:从汽车产线到千行百业的技术变革与实践指南
  • ESP-FC 低成本飞行控制器完整指南:约 5 美元打造自己的 ESP32 四轴飞控