我开始使用AutoMapper并且出现了一些疑问。 将dto映射到域模型的正确方法在哪里? 我这样做:
DTO:
public class PersonInsert
{
[Required]
public string Name { get; set; }
public string LastName { get; set; }
}
动作:
[HttpPost]
public ActionResult Insert(PersonInsert personInsert)
{
if (ModelState.IsValid)
{
new PersonService().Insert(personInsert);
return RedirectToAction("Insert");
}
return View("Insert");
}
服务:
public class PersonService
{
public int Insert(PersonInsert personInsert)
{
var person = Mapper.Map<PersonInsert, Person>(personInsert);
return new PersonRepository().Insert(person);
}
}
存储库:
public class PersonRepository
{
internal int Insert(Person person)
{
_db.Person.Add(person);
_db.SaveChanges();
return person.Id;
}
}
那么,这是正确的吗?我的服务应该知道域名吗?或者我应该只在存储库中进行绑定?在DTO中使用[必需]是否正确?
答案 0 :(得分:4)
我几乎不会从DTO创建实体 - 我在下面解释原因。我会使用一个请求对象来允许工厂方法来构建实体:
请求:
public class InsertPersonRequest
{
[Required]
public string Name { get; set; }
public string LastName { get; set; }
}
动作:
[HttpPost]
public ActionResult Insert(InsertPersonViewModel viewModel)
{
if (ModelState.IsValid)
{
InsertPersonRequest request = InsertPersonViewModelMapper.CreateRequestFrom(viewModel);
new PersonService().Insert(request );
return RedirectToAction("Insert");
}
return View("Insert");
}
服务:
public class PersonService
{
public int Insert(InsertPersonRequest request)
{
var person = Person.Create(request.name, request.LastName);
return new PersonRepository().Insert(person);
}
}
存储库保持不变。
这样,创建Person的所有逻辑都位于person的Factory方法中,因此业务逻辑被封装在域派生字段,默认字段等中。
您正在做的问题是必须在UI中创建DTO,然后所有字段都映射到实体 - 这是业务逻辑渗透到服务层的可靠方法,用户界面,或任何不应该的用户界面。
请再次阅读 - 这是我一次又一次看到的一个非常严重的错误。
但是,我会在服务层中使用AutoMapper来返回DTO:
服务:
public class PersonService
{
public PersonDto GetById(intid)
{
var person = new PersonRepository().GetById(id);
var personDto = Mapper.Map<Person, PersonDto>(person);
return personDto
}
}
答案 1 :(得分:2)
这是对的吗?
我个人认为让您的服务进行映射没有任何问题
在DTO中使用
[Required]
是否正确
不,DTO应该没有任何业务逻辑。它们应该纯粹用于跨应用程序的不同层/层传输数据。
DataAnnotations通常用于ViewModels
用于客户端/服务器端验证,因此,我会在您的模型中添加另一个分隔,并为您的ViewModel
操作引入Insert
,例如
public class PersonViewModel
{
[Required]
public string Name { get; set; }
public string LastName { get; set; }
}
public class PersonDto
{
public string Name { get; set; }
public string LastName { get; set; }
}
动作:
[HttpPost]
public ActionResult Insert(PersonViewModel personViewModel)
{
if (ModelState.IsValid)
{
var personDto = Mapper.Map<PersonViewModel, PersonDto>(personViewModel);
new PersonService().Insert(personDto);
...
}
...
}
}
服务:
public class PersonService
{
public int Insert(PersonDto personDto)
{
var person = Mapper.Map<PersonDto, Person>(personDto);
return new PersonRepository().Insert(person);
}
}
在这种情况下看起来有些过分(考虑到唯一的区别是[Required]
属性)。但是,在典型的MVC应用程序中,您需要确保ViewModel
与商业模式之间的清晰分离。
答案 2 :(得分:1)
在ASP.NET MVC中,DTO的典型用法是viewmodel的一部分。 Viewmodel是一个将一个到几个DTO组合成一个类的类,用于视图呈现和将值发布回服务器。
您所做的是正确的,没有问题,但数据注释应该驻留在视图模型上,而不是DTO。除非你将DTO称为视图模型,否则就可以了。
请阅读以下有关ASP.NET MVC世界中的模型(域模型)与ViewModel的帖子:
希望这有帮助
答案 3 :(得分:1)
我想说您的PersonService
可以被视为您的架构的域层(或域的正上方的应用层)的一部分,而控制器和DTO位于该层之上。这意味着您不应在PersonService
签名中引用DTO,而应在此处使用域Person
类。所以Mapping代码应该进入Controller。这可确保您的域逻辑不受Web服务合同更改的影响,这可能只是使用PersonService
的一种方式。
我还会为您的PersonService
引入一个存储库接口,因为PersonService
不需要知道具体的数据访问实现。
对于[Required]
属性,我没有看到在DTO上出现此问题,因为它只是说明了您的Web服务方法的数据协定。任何调用您的Web服务的人都应遵守此数据协定。当然,这个要求通常也会反映在您的域代码中的某个地方,可能是通过抛出异常等。
答案 4 :(得分:1)
我认为在DTO上进行注释是很好的,例如[必需],MaxLength,Range等。
您的DTO可以来自任何(可能不受信任的)来源(不只是您的网站,而是来自其他端点,WCF服务等)。所有请求都将汇集到您的服务/业务层,因此您需要在执行业务逻辑(简单保护检查)之前验证输入。在DTO上添加注释只是描述执行手头任务所需的输入。传递带注释的对象不符合验证。
但是,我相信您应该验证服务/业务层中的DTO信息是否正确(注释是一种检查此方法的好方法)。
我对这种情况的看法:)。