尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Microsoft NLayerApp案例理论与实践 - 基础结构层(数据访问部分)
Unit Of WorkPoEAAUnit Of WorkUoW模式在企业应用架构中被广泛使用它能够将Domain Model中对象状态的变化收集起来并在适当的时候在同一数据库连接和事务处理上下文中一次性将对象的变更提交到数据中。在没有引入UoW之前你可以在每次增加、删除对象或者更改对象状态后直接调用数据库以保存对象的变化但这样做会导致应用程序对数据库这一外部技术架构的频繁访问严重影响了系统性能。这就好像我们打开Notepad进行文字编辑一样我们完全可以每输入一个字符就按下CtrlS保存一次但这样做非常耗时也没必要我们通常的做法可能是每完成一个段落的编辑输入字符、删除字符或者更改字符等再保存一次那么Notepad就会在我们编辑段落的时候跟踪段落及其中字符的变化情况最后一次性将这些变更写到硬盘上。从UoW的模式描述上看它有点像数据库事务Transaction因为它们都具有“提交”和“回滚”的操作。但从语义上讲它并不能等同于数据库事务。我觉得应该这样理解我们可以将UoW看成是一个事务对象但它不是数据库事务它的事务性体现在能够在一个原子操作中将对象一次性提交给持久化机制或者如果在提交过程中出现问题它还能将对象返回到提交前的状态。不仅如此UoW还具有跟踪领域对象变化的功能它能够跟踪某一个业务步骤范围内领域对象的变化情况正如上面的例子中每个段落的编辑就可以看成是一个业务步骤那么在这个业务步骤中编辑段落的过程中UoW会对领域对象进行跟踪而在业务步骤完成之时完成段落编辑之时UoW就会对跟踪到的变更做一次性提交。从上面的分析让我们大致了解到UoW与仓储一样本身应该是属于Domain Model的它的设计应该是技术无关的也就是常说的POCO或者IPOCO因为它跟踪的是Domain Model中领域对象的变化情况当然一个更好的设计应该是使用Separated InterfacePoEAA模式将UoW接口与仓储的接口一起设计在Domain Model中。从UoW的实现上来看NLayerApp采用了Entity Framework的一些特性并基于Entity Framework的模型利用T4自动化产生代码。目前我们不要去关心在NLayerApp中是如何使用T4产生这些代码的我们需要关心的是为什么需要产生这些代码。有关Visual Studio中的模型项目、Domain Specific LanguageDSL以及T4代码自动化生成我们在此将不作讨论。有兴趣的朋友可以参考我前面的文章《在Visual Studio 2010中使用Modeling Project定制DSL以及自动化代码生成》。以下是NLayerApp中与UoW相关的类关系图在了解NLayerApp的UoW执行机制之前首先让我们了解一下NLayerApp中与UoW相关的三个接口。IObjectWithChangeTracker接口该接口下只定义了一个ObjectChangeTracker的属性在NLayerApp中所有的实体都要实现IObjectWithChangeTracker接口以向外界主要是UoW和仓储提供ObjectChangeTracker实例。ObjectChangeTracker的主要功能就是记录当前实体中的状态变化。比如实体的当前状态、变更前所有属性的原始数据、向集合属性添加的所有对象、从集合属性中删除的所有对象等等。当仓储通过Unit Of Work来注册已变更的实体时Unit Of Work会使用ObjectChangeTracker所提供的信息来向Entity Framework进行变更注册。INotifyPropertyChanged接口NLayerApp的实体不仅实现了IObjectWithChangeTracker接口同时还实现了INotifyPropertyChanged接口。实现这个接口的主要目的就是为了在实体的某个属性发生变化时能及时地将这种变化记录在ObjectChangeTracker中。因此只要客户程序通过实体的属性来改变实体的状态时实体本身就会将状态变化记录到ObjectChangeTracker中。IRepository接口IRepository接口是定义在Domain Model层的接口之所以在此提及是因为对象的持久化过程是通过仓储完成的而持久化又离不开UoW。在NLayerApp中IRepository接口有一个IUnitOfWork的属性因此所有的仓储都必须实现这个属性以便Repository能够在UoW中记录对象的变更信息。从NLayerApp的源代码可以看到其实仓储本身并不负责将实体保存到数据库的这一具体任务它只是通过IObjectWithChangeTracker接口将需要保存的对象设置为相应的状态并向UoW注册对象变更剩下的与数据库打交道的任务则是由UoW完成的通过这些信息我们可以了解到NLayerApp中的实体都是各自管理自己的变更记录称之为“自跟踪实体”Self-Tracking EntitiesSTE。其实从DDD的角度来看STE并不是一个很好的设计因为它给Domain Model带来了太多技术关注点。例如在实现STE的时候当你向Customer添加一个Order时你需要首先判断Customer的ObjectChangeTracker中是否已经将该Order标记为“删除”状态了如果是这样的话那么你需要将这个Order从ObjectChangeTracker的“删除”列表中移去。类似这样的业务逻辑本不应该放在Domain Model中。此外NLayerApp为了迎合Entity Framework的需求所实现的STE也并非纯粹的与技术无关的。UoW的实现也是如此比如在上面的类图中我们可以很明显地看到MainModuleUnitOfWork是ObjectContext的子类。现在我们将思路串联起来以修改Customer为例从整个架构服务端的最上层Distributed Service层开始看看Unit Of Work与仓储是如何协作的。1、DistributedServices.MainModule项目MainModuleService类通过使用位于应用层的CustomerManagementService实现Customer信息的变更12345678910111213141516publicvoidChangeCustomer(Customer customer){try{//Resolve root dependency and perform operationICustomerManagementService customerService IoCFactory.Instance.CurrentContainer.ResolveICustomerManagementService();customerService.ChangeCustomer(customer);}catch(ArgumentNullException ex){// ......}}上述代码通过IoCFactory从IoC容器中获得ICustomerManagementService的具体实现有关NLayerApp中IoC容器的实现请参考前一篇文章。2、Application.MainModule项目CustomerManagementService类实现了ICustomerManagementService接口同时实现了ChangeCustomer方法。在该方法中首先通过CustomerRepository的UnitOfWork属性获得UoW然后调用仓储的Modify方法以将要更改的Customer实体注册到UoW中同时改变了Customer实体的状态。最后使用UoW的CommitAndRefreshChanges方法将变更的实体对象提交到数据库123456789101112publicvoidChangeCustomer(Customer customer){if(customer (Customer)null)thrownewArgumentNullException(customer);IUnitOfWork unitOfWork _customerRepository.UnitOfWorkasIUnitOfWork;_customerRepository.Modify(customer);unitOfWork.CommitAndRefreshChanges();}值得一提的是在CustomerManagementService中CustomerRepository以构造器注入的方式获得实例化的12345678910111213141516171819/// summary/// Create new instance/// /summary/// param namecustomerRepositoryCustomer repository dependency,/// intented to be resolved with dependency injection/param/// param namecountryRepositoryCountry repository dependency,/// intended to be resolved with dependency injection/parampublicCustomerManagementService(ICustomerRepository customerRepository,ICountryRepository countryRepository){if(customerRepository (ICustomerRepository)null)thrownewArgumentNullException(customerRepository);if(countryRepository (ICountryRepository)null)thrownewArgumentNullException(countryRepository);_customerRepository customerRepository;_countryRepository countryRepository;}3、Infrastructure.Data.Core项目Repository类的Modify方法首先将当前状态不是Deleted的实体设置为“Modified”同时在UoW中通过RegisterChanges调用以向UoW注册该实体12345678910111213141516171819202122publicvirtualvoidModify(TEntity item){//check argumentsif(item (TEntity)null)thrownewArgumentNullException(item, Resources.Messages.exception_ItemArgumentIsNull);//Set modifed state if change tracker is enabled and state is not deletedif(item.ChangeTracker !null((item.ChangeTracker.State ObjectState.Deleted) ! ObjectState.Deleted)){item.MarkAsModified();}//apply changes for item object_CurrentUoW.RegisterChanges(item);_TraceManager.TraceInfo(string.Format(CultureInfo.InvariantCulture,Resources.Messages.trace_AppliedChangedItemRepository,typeof(TEntity).Name));}4、Infrastructure.Data.MainModule项目MainModuleUnitOfWork类的RegisterChanges方法简单地利用Entity Framework所提供的机制向Entity Framework注册对象状态变更。这是Entity Framework技术实现的细节内容我们在此也不去深入分析其中的实现方式了12345publicvoidRegisterChangesTEntity(TEntity item)whereTEntity :class, IObjectWithChangeTracker{this.CreateObjectSetTEntity().ApplyChanges(item);}5、Infrastructure.Data.MainModule项目MainModuleUnitOfWork类的CommitAndRefreshChanges方法通过Entity Framework将变更提交到数据库同时将实体对象的状态设置为“未更改”1234567891011121314151617181920212223publicvoidCommitAndRefreshChanges(){try{//Default option is DetectChangesBeforeSavebase.SaveChanges();//accept all changes in STE entities attached in contextIEnumerableIObjectWithChangeTracker steEntities (fromentryinthis.ObjectStateManager.GetObjectStateEntries(~EntityState.Detached)whereentry.Entity !null(entry.EntityasIObjectWithChangeTracker !null)selectentry.EntityasIObjectWithChangeTracker);steEntities.ToList().ForEach(ste ste.MarkAsUnchanged());}catch(OptimisticConcurrencyException ex){//......}}整个执行过程我们可以使用下面的序列图来表示
RELATED

相关推荐

物理AI在工业中的典型应用与优势——基于EdgePLC工业AI边缘控制器

物理AI在工业中的典型应用与优势——基于EdgePLC工业AI边缘控制器

一、什么是物理AI?物理AI(Physical AI) 是指将人工智能模型部署于工业现场的边缘设备上,使机器不仅具备数据分析和决策能力,更能自主感知物理环境、理解复杂工况并做出实时物理调整的新一代智能技术范式。与传统AI运行…

📅 2026/9/11 18:02:59
草酸与烟酸对消化及糖代谢的影响解析

草酸与烟酸对消化及糖代谢的影响解析

您的问题非常专业,涉及食品化学、营养学与人体代谢的交叉领域。我将根据现有的科学常识,为您梳理和介绍食物中常见的几类酸性物质及其对消化系统和糖类代谢的潜在影响。首先需要澄清一个关键点:您提到的“烟酸”可能存在误解。在食品科学中&a…

📅 2026/9/8 7:45:23
项目从1个模块拆成8个微服务,然后我又合了回去

项目从1个模块拆成8个微服务,然后我又合了回去

摘要:我们项目从 1 个 SpringBoot 单体拆成了 8 个微服务,用了半年。然后在接下来的一年里,分布式事务、调试地狱、运维成本翻倍,团队被折磨得够呛。最后我做了一个决定:合回去。不是退回到大泥球,而是用模…

📅 2026/8/23 23:49:29
MORE NEWS

更多资讯

📰

多变量LSTM结合气象特征的PM2.5时序预测实战

简介:基于LSTM的多变量时间序列PM2.5预测项目源码,配套说明文档与数据集,是一个经导师指导、评审96.5分的高分毕业设计,适合计算机、人工智能等专业学生用于毕业设计、课程设计或期末大作业。压缩包共6个文件,包括3个P…

📰

LeetCode-Go 题解:542. 01 Matrix 三种解法(BFS / DFS / DP)求解最近 0 距离

LeetCode-Go 题解:542. 01 Matrix 三种解法(BFS / DFS / DP)求解最近 0 距离 【免费下载链接】LeetCode-Go ✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解 项目地址: https://gitcode.com/GitH…

📰

pm-skills 实战:用 outcome-roadmap 技能把功能清单路线图重构为结果导向路线图

pm-skills 实战:用 outcome-roadmap 技能把功能清单路线图重构为结果导向路线图 【免费下载链接】pm-skills PM Skills Marketplace: 100 agentic skills, commands, and plugins — from discovery to strategy, execution, launch, and growth. 项目地址: https…

📰

D2 v0.6.8 版本解读:非浏览器渲染、原子写入与一批稳健性修复

D2 v0.6.8 版本解读:非浏览器渲染、原子写入与一批稳健性修复 【免费下载链接】d2 D2 is a modern diagram scripting language that turns text to diagrams. 项目地址: https://gitcode.com/GitHub_Trending/d2/d2 D2 是一种把文本描述直接转换为图表的现代…

📰

claude-task-master 配置驱动 Mock 系统实战:用 Mock 工厂重构跨标签任务测试

claude-task-master 配置驱动 Mock 系统实战:用 Mock 工厂重构跨标签任务测试 【免费下载链接】claude-task-master An AI-powered task-management system you can drop into Cursor, Lovable, Windsurf, Roo, and others. 项目地址: https://gitcode.com/GitHub…

📰

智能家居与物联网实战:把传感器固件写成YAML:从看懂ESPHome到完成第一次可检查编译

智能家居与物联网实战:把传感器固件写成YAML:从看懂ESPHome到完成第一次可检查编译 [!NOTE] 手写固件能理解底层,ESPHome则让常见设备功能用声明式配置表达。真正入门不该从复制一长段别人家的配置开始,而应先弄清板型、框架、组件和编译产物。本课用没有真实引脚操作的模板…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬