名称解释
主键映射是为了保证一个业务事务(请求)内只访问或修改一份领域模型。基本的思路是在内存中维护一个映射表,映射表的键为领域模型的主键,值为加载的领域模型。工作原理如下:
- 根据主键加载:先判断映射中有没有,有就直接从映射中返回,没有就从数据库加载,然后添加进映射再返回。
- 根据查询加载:先从数据库加载所有满足条件的领域模型集合,然后遍历这个集合,用1的算法处理每个加载的领域模型,返回新的集合(集合.Map算法)。
总之,主键映射会保证整个业务事务发生的过程你只会引用到一个内存引用,你自己实现主键映射也要保证这个语义。
图片示意
不用主键映射会出现什么问题?
using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Linq; using System.Transactions; using System.Threading; namespace TSTest { [TestClass] public class 工作单元测试 { [TestInitialize] public void 初始化数据() { using (var context = new TestEntities()) { foreach (var table in context.Tables) { context.Tables.Remove(table); } context.Tables.Add(new Table() { Id = Guid.NewGuid(), Name = "李妞妞", Age = 26 }); context.SaveChanges(); } } /// <summary> /// 因为EntityFramework的DbContext封装了主键映射,所以这里模拟一次用了两个主键映射。 /// 用多个或没有用主键映射都可能导致一些逻辑异常 /// </summary> [TestMethod] public void 两个主键映射_或_没有主键映射_都会出现的问题_测试() { using (var context = new TestEntities()) { var user = context.Tables.First(); this.修改年龄(); //因为两处的修改逻辑修改的是不同的内存实例,如果后续的逻辑中有用到Age属性,就会出现业务逻辑异常。 //正常的期望是这里会变成26 Assert.AreEqual(26, user.Age); user.Name = "段光伟"; context.SaveChanges(); } } private void 修改年龄() { using (var context = new TestEntities()) { var user = context.Tables.First(); user.Age = 28; context.SaveChanges(); } } } }
主键映射的生命周期管理
参考此文:DDD:管理“工作单元实例”的两种模式