版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、仓储的实现:深入篇 早在年前的时候就已经在CSAI博客发表了上一篇文章:仓储的实现:基础篇。苦于日夜奔波于工作与生活之间,一直没有能够抽空继续探讨仓储的实现细节,也让很多关注EntityFramework和领域驱动设计的朋友们备感失望。闲话不多说,现在继续考虑,如何让仓储的操作在相同的事物处理上下文中进行。DDD引入仓储模式,其目的之一就是能够通过仓储隐藏对象持久化的技术细节,使得领域模型变得更为“纯净”。由此可见,仓储的实现是需要基础结构层的组件支持的,表现为对数据库的操作。在传统的关系型数据库操作中,事务处理是一个很重要的概念,虽然从目前某些大型项目看,事务处理会降低效率,但它保证了数据的
2、完整性。关系型数据库仍然是目前数据持久化机制的主流,事务处理的实现还是很有必要的。为了迎合仓储模式,就需要对经典的ObjectContext使用方式作一些调整。比如,原本我们可以非常简单地使用using (EntitiesContainer ec = new EntitiesContainer()语句来界定LINQ to Entities的操作范围,并使用ObjectContext的SaveChanges成员方法提交事务,而在引入了仓储的实现中,就不能继续采用这种经典的使用方式。这让EntityFramework看上去变得很奇怪,也很牵强,我相信很多网友会批评我的做法,因为我把问题复杂化了。其
3、实,这应该是关注点不同罢了。关注EntityFramework的开发人员,自然觉得经典的调用方式简单明了,而从DDD的角度看呢?只能把关注点放在仓储上,而把EntityFramework当成是仓储的一种技术选型(当然从DDD角度讲,我们完全可以不选择EntityFramework,而去选择其它技术)。所以本文暂且抛开EntityFramework,继续在上文的基础上,讨论仓储的实现。前面提到,仓储的实现需要考虑事务处理,而且根据DDD的经验,针对每一个聚合根,都需要有个仓储对其进行持久化以及对象重新组装等操作。为此,我的想法是,将仓储操作“界定”在某一个事务处理上下文(Context)中,仓储
4、的实例是由Context获得的,这有点像EntityFramework中ObjectContext与EntityObject的关系那样。由于仓储是来自于transaction context,所以它知道目前处于哪个事务上下文中。我定义的这个transaction context如下:隐藏行号 复制代码 ?Transaction Contextpublic interface IRepositoryTransactionContext : IDisposable IRepository<TEntity> GetRepository<TEntity>() where TEn
5、tity : EntityObject, IAggregateRoot; void BeginTransaction(); void Commit(); void Rollback();上面第三行代码定义了一个接口方法,这个方法的主要作用就是返回一个针对指定聚合根实体的仓储实例。剩下那三行代码就很明显了,那是标准的transaction操作:启动事务、提交事务以及回滚事务。在设计上,可以根据需要,选择合适的技术来实现IRepositoryTransactionContext。我们现在讨论的是EntityFramework,所以我将给出EntityFramework的具体实现。当然,如果你不选用
6、EntityFramework,而是用NHibernate实现数据持久化,这样的设计同样能够使你达到目的。以下是基于EntityFramework的实现:EdmRepositoryTransactionContext的伪代码。隐藏行号 复制代码 ?EdmRepositoryTransactionContextinternal class EdmRepositoryTransactionContext : IRepositoryTransactionContext private ObjectContext objContext; private Dictionary<Type, obje
7、ct> repositoryCache = new Dictionary<Type, object>(); public EdmRepositoryTransactionContext(ObjectContext objContext) this.objContext = objContext; #region IRepositoryTransactionContext Members public IRepository<TEntity> GetRepository<TEntity>() where TEntity : EntityObject, I
8、AggregateRoot if (repositoryCache.ContainsKey(typeof(TEntity) return (IRepository<TEntity>)repositoryCachetypeof(TEntity); IRepository<TEntity> repository = new EdmRepository<TEntity>(this.objContext); this.repositoryCache.Add(typeof(TEntity), repository); return repository; public
9、 void BeginTransaction() / We do not need to begin a transaction here because the object context, / which would handle the transaction, was created and injected into the / constructor by Castle Windsor framework. public void Commit() this.objContext.SaveChanges(); public void Rollback() / We also do
10、 not need to perform the rollback operation because / entity framework will handle this for us, just when the execution / point is stepping out of the using scope. #endregion #region IDisposable Members public void Dispose() this.repositoryCache.Clear(); this.objContext.Dispose(); #endregionEdmRepos
11、itoryTransactionContext被定义为internal,这个设计是合理的,因为Domain层是不需要知道事务上下文的具体实现,它将会被IoC/DI容器注入到Domain层中(本系列文章采用Castle Windsor框架)。在EdmRepositoryTransactionContext的构造函数中,它需要EntityFramework的ObjectContext对象来初始化实例。同样,由于IoC/DI的使用,我们在代码中也是不需要去创建这个ObjectContext的,交给Castle Windsor就OK了。第13行的GetRepository方法简单地采用了Diction
12、ary对象来实现缓存仓储实例的效果,当然这种做法还有待改进。EdmRepositoryTransactionContext是不需要BeginTransaction的,我们将方法置空,因为EntityFramework的事务会由ObjectContext来管理,同理,Rollback也被置空。EdmRepository的实现就比较显而易见了,请参见上文。此外,我们还可以针对NHibernate实现仓储模式,只需要实现IRepositoryTransactionContext和IRepository接口即可,比如:隐藏行号 复制代码 ?NHibernateRepositoryTransaction
13、Context实现internal class NHibernateRepositoryTransactionContext : IRepositoryTransactionContext ITransaction transaction; Dictionary<Type, object> repositoryCache = new Dictionary<Type, object>(); public ISession Session get return DatabaseSessionFactory.Instance.Session; #region IReposit
14、oryTransactionContext Members public IRepository<TEntity> GetRepository<TEntity>() where TEntity : EntityObject, IAggregateRoot if (repositoryCache.ContainsKey(typeof(TEntity) return (IRepository<TEntity>)repositoryCachetypeof(TEntity); IRepository<TEntity> repository = new N
15、HibernateRepository<TEntity>(this.Session); this.repositoryCache.Add(typeof(TEntity), repository); return repository; public void BeginTransaction() transaction = DatabaseSessionFactory.Instance.Session.BeginTransaction(); public void Commit() transaction.Commit(); public void Rollback() trans
16、action.Rollback(); #endregion #region IDisposable Members public void Dispose() transaction.Dispose(); ISession dbSession = DatabaseSessionFactory.Instance.Session; if (dbSession != null && dbSession.IsOpen) dbSession.Close(); #endregion隐藏行号 复制代码 ?NHibernateRepository实现internal class NHibern
17、ateRepository<TEntity> : IRepository<TEntity> where TEntity : EntityObject, IAggregateRoot ISession session; public NHibernateRepository(ISession session) this.session = session; #region IRepository<TEntity> Members public void Add(TEntity entity) this.session.Save(entity); public
18、TEntity GetByKey(int id) return (TEntity)this.session.Get(typeof(TEntity), id); public IEnumerable<TEntity> FindBySpecification(Func<TEntity, bool> spec) throw new NotImplementedException(); public void Remove(TEntity entity) this.session.Delete(entity); public void Update(TEntity entity
19、) this.session.SaveOrUpdate(entity); #endregion在NHibernateRepositoryTransactionContext中使用了一个DatabaseSessionFactory的类,该类主要用于管理NHibernate的Session对象,具体实现如下(该实现已被用于我的Apworks应用开发框架原型中):隐藏行号 复制代码 ?DatabaseSessionFactory实现/ <summary>/ Represents the factory singleton for database session./ </summa
20、ry>internal sealed class DatabaseSessionFactory #region Private Static Fields / <summary> / The singleton instance of the database session factory. / </summary> private static readonly DatabaseSessionFactory databaseSessionFactory = new DatabaseSessionFactory(); #endregion #region Pri
21、vate Fields / <summary> / The session factory instance. / </summary> private ISessionFactory sessionFactory = null; / <summary> / The session instance. / </summary> private ISession session = null; #endregion #region Constructors / <summary> / Privately constructs the d
22、atabase session factory instance, configures the / NHibernate framework by using the assemblies listed in the configured spaces(paths) / that are decorated by <see cref=EntityVisibleAttribute/>. / </summary> private DatabaseSessionFactory() Configuration nhibernateConfig = new Configurat
23、ion(); nhibernateConfig.Configure(); nhibernateConfig.AddAssembly(typeof(IAggregateRoot).Assembly); sessionFactory = nhibernateConfig.BuildSessionFactory(); #endregion #region Public Properties / <summary> / Gets the singleton instance of the database session factory. / </summary> public
24、 static DatabaseSessionFactory Instance get return databaseSessionFactory; / <summary> / Gets the singleton instance of the session. If the session has not been / initialized or opened, it will return a newly opened session from the session factory. / </summary> public ISession Session g
25、et ISession result = session; if (result != null && result.IsOpen) return result; return OpenSession(); #endregion #region Public Methods / <summary> / Always opens a new session from the session factory. / </summary> / <returns>The newly opened session.</returns> pub
26、lic ISession OpenSession() this.session = sessionFactory.OpenSession(); return this.session; #endregion简单小结一下。通过上面的例子可以看到,仓储的实现是不能依赖于任何技术细节的,因为领域模型并不关心技术问题。这并不是DDD一书中怎么说,我们就得怎么做。事实上的确如此,因为DDD的思想,使得我们应该把关注点放在业务分析与领域建模上,而仓储实现的分离正是这一思想的具体表现形式。不管怎么样,采用其它的手段也罢,我们还是应该遵循“将关注点放在领域”这一宗旨。接下来看如何在领域层结合IoC框架使用仓储
27、。仍然以Castle Windsor为例。配置文件如下(超长部分我用省略号去掉了): 隐藏行号 复制代码 ?Castle Windsor配置<castle> <components> <!- Object Context for Entity Data Model -> <component id=ObjectContext service=System.Data.Objects.ObjectContext, System.Data.Entity, Version=,. type=EasyCommerce.Domain.Model.Ent
28、itiesContainer, EasyCommerce.Domain/> <component id=GeneralRepository service=EasyCommerce.Domain.IRepository1EasyCommerce.Domain.Model.Customer, . type=EasyCommerce.Infrastructure.Repositories.EdmRepositories.EdmRepository1EasyCo.> <objContext>$ObjectContext</objContext> </c
29、omponent> <component id=TransactionContext service=EasyCommerce.Domain.IRepositoryTransactionContext, EasyCommerce.Domain. type=EasyCommerce.Infrastructure.Repositories.EdmRepositories.EdmRepositoryTransactionContext, .> </component> </components></castle>以下是调用代码:隐藏行号 复制代码
30、 ?调用方代码TestMethodpublic void TestCreateCustomer() IWindsorContainer container = new WindsorContainer(new XmlInterpreter(); using (IRepositoryTransactionContext tx = container.GetService<IRepositoryTransactionContext>() tx.BeginTransaction(); try Customer customer = Customer.CreateCustomer(daxn
31、et, 12345, new Name FirstName = Sunny, LastName = Chen , new Address(), new Address(), DateTime.Now.AddYears(-29); IRepository<Customer> customerRepository = tx.GetRepository<Customer>(); customerRepository.Add(customer); tx.Commit(); catch tx.Rollback(); throw; 测试结果及数据库数据结果: 【注意】:在使用NHi
32、bernate的仓储实现时,由于NHibernate的延迟加载特性,需要将实体的属性设置为virtual,以便由NHibernate产生Proxy Class进而实现延迟加载;但是由EntityFramework自动生成的源代码并不会将实体属性设置为virtual,而采用partial class也无法解决这个问题。因此需要在代码生成技术上做文章。我的做法是,针对edmx产生一个基于T4的代码生成模板,然后修改这个模板,分别在WritePrimitiveTypeProperty和WriteComplexTypeProperty方法中的适当位置加上virtual关键字:隐藏行号 复制代码 ?Wr
33、itePrimitiveTypeProperty private void WritePrimitiveTypeProperty(EdmProperty primitiveProperty, CodeGenerationTools code) MetadataTools ef = new MetadataTools(this);#> / <summary> / <#=SummaryComment(primitiveProperty)#> / </summary><#=LongDescriptionCommentElement(primitiveP
34、roperty, 1)#> EdmScalarPropertyAttribute(EntityKeyProperty=<#=code.CreateLiteral(ef.IsKey(primitiveProperty)#>, IsNullable=<#=code.CreateLiteral(ef.IsNullable(primitiveProperty)#>) DataMemberAttribute() <#=code.SpaceAfter(NewModifier(primitiveProperty)#><#=Accessibility.ForProperty(primitiveProperty)#> virtual <#=code.Escape(primitiveProperty.TypeUsage)#> <#=code.Escape(primitiveProperty)#> <#=code.SpaceAfter(Accessibility.ForGetter(primitiveProperty)#>get <#+ if (ef.ClrType(primitiveProperty.TypeUsage) = typeof(byte) #> re
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 木栈道设施维护方案
- 2024年合同在国际商务中的法宝
- 2024年企业品牌建设与市场营销合同
- 中小学环保教育管理制度
- 2024年企业知识产权贯标合同
- 2024年专项工程落水管安装委托合同
- 2024年强力深孔项目可行性研究报告
- 2024年农业用地租凭合同协议
- 朝花夕拾主题 研究报告
- 2024年区块链技术知识产权转让合同
- 苏科版(2024新版)七年级上册数学期中学情评估测试卷(含答案)
- 部编版《道德与法治》三年级上册第10课《父母多爱我》教学课件
- 大语言模型赋能自动化测试实践、挑战与展望-复旦大学(董震)
- 期中模拟检测(1-3单元)2024-2025学年度第一学期西师大版二年级数学
- 气管插管操作规范(完整版)
- 2024-2025学年外研版英语八年级上册期末作文范文
- 四级劳动关系协调员试题库含答案
- 运城中学2023-2024学年八年级上学期期中考试数学试卷(含解析)
- 2025年广东省高中学业水平考试春季高考数学试题(含答案解析)
- 2024年重庆市渝北区数据谷八中小升初数学试卷
- 2024年AI大模型场景探索及产业应用调研报告-前瞻
评论
0/150
提交评论