据我了解,您的问题源于您有一个由 DTO 制成的本地明确声明的对象图。我的意思是你已经在你的Country 模型上声明了public Unit Unit { get; set; }(不知道你为什么要声明它们virtual,但这与手头的问题没有直接关系),而不是尝试一种保证简单性的方法对象图转化为单个对象图节点的退化情况。
例如,考虑以public UnitID Unit { get; set; } 的形式在模型上定义每个“引用”属性,其中UnitID 实际上可能是int 或Guid 或您用来唯一标识的任何内容并区分Unit 模型。无论您在哪里有对另一个模型的引用或一组引用,都将其替换为其标识符类型而不是其实际类型。这种策略非常适合一组持久的模型,例如从/到具有每个模型的身份密钥的数据库。这样做可以让您简化序列化,而不必担心循环引用,因为它们现在是不可能的。从技术上讲,不再有引用(即直接引用;它们现在是间接引用)。我们现在只是在您的域模型设计中添加一个间接层。现在让我们适应那个间接层。
既然你声称你对贫血域模型方法没问题,那么这应该很适合。您在模型设计中支付间接 (IMHO) 的次要成本,并用它换取基于接口的数据检索方法的主要 (IMHO) 好处:
public interface IUnitRepository {
Unit GetUnit(UnitID id);
IEnumerable<Unit> GetUnits(IEnumerable<UnitID> ids);
// etc.
}
在您的消费者代码(即使用此接口的代码和您的Unit 域模型)中,通过执行接口调用以获取间接指向的底层模型,遍历隐含对象图看起来稍微复杂一些参考文献。
之前:
Country ct = ...; // I assume you have this reference already
Unit ut = ct.Unit;
之后:
// Somewhere earlier in your code, i.e. not *every* time this type of code appears
IUnitsRepository repo = new SomeUnitsRepositoryImpl();
Country ct = ...; // I assume you have this reference already
Unit ut = repo.GetUnit(ct.UnitID);
如果这在语法上困扰您,您可以定义一组扩展方法,键入Country 的形式:
public static Unit Unit(this Country c, IUnitsRepository repo) {
return repo.GetUnit(c.UnitID);
}
扩展方法后:
IUnitsRepository repo = new SomeUnitsRepositoryImpl();
Country ct = ...; // I assume you have this reference already
Unit ut = ct.Unit(repo);
基于接口的方法为您带来一系列经典的好处,例如关注点分离、可测试性、消费者-生产者隔离等等。此外,您现在可以通过接口的实现类型更直接地控制对象的生命周期。我的意思是你不应该假设你的Unit GetUnit(UnitID id) 方法的实现是天真的。此方法现在可以使用与SomeUnitsRepositoryImpl 实例绑定的Dictionary<UnitID, Unit> 执行一些本地内存缓存。
有点啰嗦,但希望对您有所帮助。好像从所提供的详细信息的数量来看并不明显,我目前正在我的工作地点玩弄这种设计,以处理我们的记录数据库系统。 :) 我真的很喜欢它为我提供的所有灵活性,而代价是在域模型的设计中添加一层间接性。