【发布时间】:2026-01-25 06:10:02
【问题描述】:
我为自己编写了一个漂亮的简单小领域模型,其对象图如下所示:
-- Customer
-- Name : Name
-- Account : CustomerAccount
-- HomeAddress : PostalAddress
-- InvoiceAddress : PostalAddress
-- HomePhoneNumber : TelephoneNumber
-- WorkPhoneNumber : TelephoneNumber
-- MobilePhoneNumber : TelephoneNumber
-- EmailAddress : EmailAddress
此结构完全与我必须使用的旧数据库不一致,因此我定义了一个平面 DTO,其中包含客户图中每个元素的数据 - 我有数据库中的视图和存储过程允许我使用这种平面结构在两个方向上与数据进行交互,这一切都很好&花花公子:)
将域模型扁平化为 DTO 以进行插入/更新很简单,但我遇到的问题是获取 DTO 并从中创建域模型......我的第一个想法是实现一个访问者,它会访问客户图中的每个元素,并根据需要从 DTO 中注入值,有点像这样:
class CustomerVisitor
{
public CustomerVisitor(CustomerDTO data) {...}
private CustomerDTO Data;
public void VisitCustomer(Customer customer)
{
customer.SomeValue = this.Data.SomeValue;
}
public void VisitName(Name name)
{
name.Title = this.Data.NameTitle;
name.FirstName = this.Data.NameFirstName;
name.LastName = this.Data.NameLastName;
}
// ... and so on for HomeAddress, EmailAddress etc...
}
这就是理论,当它像这样简单地布置时,它似乎是一个合理的想法:)
但要使这个工作,整个对象图需要在访问者 erm 访问之前构建,否则我会得到 NRE 的左右和中心。
我想要做的是让访问者在访问每个元素时分配对象到图表,目标是对数据丢失的对象使用特殊情况模式DTO,例如。
public void VisitMobilePhoneNumber(out TelephoneNumber mobileNumber)
{
if (this.Data.MobileNumberValue != null)
{
mobileNumber = new TelephoneNumber
{
Value = this.Data.MobileNumberValue,
// ...
};
}
else
{
// Assign the missing number special case...
mobileNumber = SpecialCases.MissingTelephoneNumber.Instance;
}
}
老实说,我认为这会起作用,但 C# 给我一个错误:
myVisitor.VisitHomePhone(out customer.HomePhoneNumber);
因为你不能以这种方式传递 ref/out 参数:(
所以我只剩下访问独立元素并在完成后重建图形:
Customer customer;
TelephoneNumber homePhone;
EmailAddress email;
// ...
myVisitor.VisitCustomer(out customer);
myVisitor.VisitHomePhone(out homePhone);
myVisitor.VisitEmail(out email);
// ...
customer.HomePhoneNumber = homePhone;
customer.EmailAddress = email;
// ...
在这一点上,我意识到我离访问者模式很远,离工厂更近了,我开始怀疑我是否从一开始就错误地处理了这个问题..
还有其他人遇到过这样的问题吗?你是怎么克服的?有没有非常适合这种场景的设计模式?
很抱歉发布了这样一个冗长的问题,并且阅读到这里做得很好:)
编辑为了回应 Florian Greinacher 和 gjvdkamp 的有用回答,我选择了一个相对简单的工厂实现,如下所示:
class CustomerFactory
{
private CustomerDTO Data { get; set; }
public CustomerFactory(CustomerDTO data) { ... }
public Customer CreateCustomer()
{
var customer = new Customer();
customer.BeginInit();
customer.SomeFoo = this.Data.SomeFoo;
customer.SomeBar = this.Data.SomeBar
// other properties...
customer.Name = this.CreateName();
customer.Account = this.CreateAccount();
// other components...
customer.EndInit();
return customer;
}
private Name CreateName()
{
var name = new Name();
name.BeginInit();
name.FirstName = this.Data.NameFirstName;
name.LastName = this.Data.NameLastName;
// ...
name.EndInit();
return name;
}
// Methods for all other components...
}
然后我写了一个 ModelMediator 类来处理数据层和领域模型之间的交互...
class ModelMediator
{
public Customer SelectCustomer(Int32 key)
{
// Use a table gateway to get a customer DTO..
// Use the CustomerFactory to construct the domain model...
}
public void SaveCustomer(Customer c)
{
// Use a customer visitor to scan for changes in the domain model...
// Use a table gateway to persist the data...
}
}
【问题讨论】:
-
Automapper 之类的东西会为您工作吗? automapper.codeplex.com
-
@bentayloruk 感谢您的回复,我已经评估了 automapper(和 valueinjecter),但不幸的是,这两个都不合适,因为我的域对象实现了 ISupportInitialize,我需要在注入任何属性之前 BeginInit() DTO,然后是 EndInit() - 我认为 Automapper 不可能做到这一点:(
-
如果你使用的是 .net 4,那么我建议 Lazy
类会帮助你一点:) -
@MattDavey imho CustomerDTO 数据应该在 CreateCustomer 方法中接收,而不是在工厂构造函数中。
-
@MattDavey 我个人偏向于 DI,因此我通常避免使用静态并坚持使用 DI 传递给构造函数的常规实例(在本例中为 ModelMediator)。这就是说,它本质上是静态的(没有要维护的状态),将其更改为意味着更少的实例。只有在特定情况下才真正有所不同。这是一个完全不同的问题:)
标签: c# factory-pattern dto visitor-pattern domain-model