使用DTO时,Automapper&;Nhibernate反映正在更新的域对象中DTO子集合的更改
我对这个设计不是很熟悉,但我希望能得到一些指导 我有一个后端服务,它向WPF智能客户端发送DTO。在WPF智能客户端上,用户将更改、删除和修改项目,然后将更改发送回(客户端-->服务器)。例如,目前我正在处理客户详细信息表单,用户可以在datagrid中添加、删除和更改属于客户的类别。当DTO被发送回服务器时,我希望加载与DTO中的ID相关的域对象,并将对DTO所做的更改应用于域对象,包括所有子集合 我尝试在下面的代码中使用UpdateCustomer方法做类似的事情。然而,我认为我偏离了目标。当代码运行时,不是以{个人、公司、NGO、政府}列表结束,而是以{个人、B2B、公司、NGO、政府}列表结束,因为它显然没有从原始列表中删除B2B条目 我想到的一个选项是循环DTO集合,并将其与来自域对象的集合进行比较,然后根据修改的内容添加、删除和更新。然而,这似乎真的很麻烦 我需要做什么才能将DTO中的更改应用到我的Domain对象中的子集合 非常感谢您的帮助,我们将不胜感激 亚历克斯使用DTO时,Automapper&;Nhibernate反映正在更新的域对象中DTO子集合的更改,nhibernate,dns,automapper,dto,Nhibernate,Dns,Automapper,Dto,我对这个设计不是很熟悉,但我希望能得到一些指导 我有一个后端服务,它向WPF智能客户端发送DTO。在WPF智能客户端上,用户将更改、删除和修改项目,然后将更改发送回(客户端-->服务器)。例如,目前我正在处理客户详细信息表单,用户可以在datagrid中添加、删除和更改属于客户的类别。当DTO被发送回服务器时,我希望加载与DTO中的ID相关的域对象,并将对DTO所做的更改应用于域对象,包括所有子集合 我尝试在下面的代码中使用UpdateCustomer方法做类似的事情。然而,我认为我偏离了目标。
公共类客户
{
公共虚拟整数Id{get;set;}
公共虚拟IList类别{get;private set;}
公共虚拟字符串代码{get;set;}
公共虚拟字符串描述{get;set;}
公众客户()
{
类别=新列表();
}
公共虚拟void AddCategory(字符串categoryName)
{
类别。添加(新类别(categoryName));
}
}
公共类类别
{
公共虚拟字符串CategoryName{get;private set;}
公共虚拟客户客户{get;set;}
公共虚拟整数Id{get;set;}
受保护类别(){}
公共类别(字符串名称)
{
CategoryName=名称;
}
}
}
公共void SetUpAutoMapper()
{
CreateMap();
CreateMap();
CreateMap();
CreateMap();
assertConfigurationsValid();
}
公共存储客户()
{
var customer=新客户{Code=“TESTCUST”,Description=“TEST customer”};
客户。添加类别(“个人”);
客户。添加类别(“B2B”);
客户。添加类别(“医疗保健”);
客户。添加类别(“NGO”);
保存(客户);
}
公共CustomerTo GetCustomer(int customerId)
{
var customer=repository.GetCustomer(customerId);
var customerDto=Mapper.Map(客户);
退回客户;
}
公共作废UpateCustomer(CustomerTo customerToUpdate)
{
/*假设传入的dto已对其执行以下操作
-----添加新类别----
customerToUpdate.Categories.Add(newcategorydto{CategoryName=“Government”});
---更新现有类别---
customerToUpdate.Categories[2].CategoryName=“公司”;
---删除类别---
customerToUpdate.Categories.RemoveAt(1)*/
var customer=repository.GetCustomer(customerToUpdate.Id);
/*在这一点上,我如何确保子集合更改是正确的
传播到从数据库检索的基础客户对象中*/
var customer=Mapper.Map(customerToUpdate);
保存(客户);
}
公共类CustomerDto
{
公共int Id{get;set;}
公共字符串代码{get;set;}
公共字符串说明{get;set;}
公共列表类别{get;set;}
}
公共类类别
{
公共int Id{get;set;}
公共字符串CategoryName{get;set;}
}
Mapper.CreateMap()
.ForMember(dest=>dest.Categories,opt=>opt.MapFrom(src=>src.Categories));
或
Mapper.CreateMap();
类似这样的东西也告诉automapper映射列表。我最近做了类似的事情,但使用EF作为数据层。我不知道nhibernate是否知道同样的方法是否有效 基本步骤如下:
- 确保从db加载目标集合并将其附加到对象图以进行更改跟踪
.ForMember(dest=>dest.Categories,opt=>opt.UseDestinationValue())
- 然后创建一个自定义IObjectMapper,用于将IList映射到IList,其中T:Entity
- 自定义IOObject映射器使用了来自
foreach(source.ChildCollection中的var child) { var targetChild=target.ChildCollection.SingleOrDefault(c=>c.Equals(child));//覆盖等于或用Id比较替换比较 如果(targetChild==null) { target.ChildCollection.Add(Mapper.Map(child)); } 其他的 { Mapper.Map(子对象,targetChild); } }
- 最后,检查sourceCollection中是否存在targetCollection中的所有Id的最后一条逻辑,如果不存在,则将其删除
它最终不是那么多代码,可以在其他操作中重用。我最初确实尝试过类似的方法,但我
public class Customer
{
public virtual int Id { get; set; }
public virtual IList<Category> Categories { get; private set; }
public virtual string Code { get; set; }
public virtual string Description { get; set; }
public Customer()
{
Categories = new List<Category>();
}
public virtual void AddCategory(string categoryName)
{
Categories.Add(new Category(categoryName));
}
}
public class Category
{
public virtual string CategoryName { get; private set; }
public virtual Customer Customer {get;set;}
public virtual int Id { get; set; }
protected Category(){}
public Category(string name)
{
CategoryName = name;
}
}
}
public void SetUpAutoMapper()
{
Mapper.CreateMap<Category, CategoryDto>();
Mapper.CreateMap<Customer, CustomerDto>();
Mapper.CreateMap<CategoryDto, Category>();
Mapper.CreateMap<CustomerDto, Customer>();
Mapper.AssertConfigurationIsValid();
}
public void SaveCustomer()
{
var customer = new Customer{Code="TESTCUST",Description="TEST CUSTOMER"};
customer.AddCategory("Individual");
customer.AddCategory("B2B");
customer.AddCategory("Healthcare");
customer.AddCategory("NGO");
repository.Save(customer);
}
public CustomerDto GetCustomer(int customerId)
{
var customer = repository.GetCustomer(customerId);
var customerDto = Mapper.Map<Customer,CustomerDto>(customer);
return customerDto;
}
public void UpateCustomer(CustomerDto customerToUpdate)
{
/*imagine that the dto incoming has had the following operations performed on it
-----add new category----
customerToUpdate.Categories.Add(new CategoryDto {CategoryName = "Government"});
---update existing category---
customerToUpdate.Categories[2].CategoryName = "Company";
---remove category---
customerToUpdate.Categories.RemoveAt(1);*/
var customer = repository.GetCustomer(customerToUpdate.Id);
/* How in this bit do I ensure that the child collection changes are
propogated into the underlying customer object retrieved from the database*/
var customer = Mapper.Map<CustomerDto,Customer>(customerToUpdate);
repository.Save(customer);
}
public class CustomerDto
{
public int Id { get; set; }
public string Code { get; set; }
public string Description { get; set; }
public List<CategoryDto> Categories { get; set; }
}
public class CategoryDto
{
public int Id { get; set; }
public string CategoryName { get; set; }
}
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Customer" table="Customer">
<id name="Id" column="CustomerId">
<generator class="native"/>
</id>
<property name="Code" />
<property name="Description" />
<bag name="Categories" table="Categories" cascade="all" inverse="false">
<key column="FK_CustomerID" />
<one-to-many class="Category"/>
</bag>
</class>
<class name="Category" table="Categories">
<id name="Id" column="CategoryId">
<generator class="native"/>
</id>
<many-to-one name="Customer" column="FK_CustomerId" not-null="true" class="Customer"></many-to-one>
<property name="CategoryName" />
</class>
</hibernate-mapping>
Mapper.CreateMap<Customer, CustomerDto>()
.ForMember(dest => dest.Categories, opt => opt.MapFrom(src =>src.Categories));
Mapper.CreateMap<IList<Category>, IList<CategoryDto>>();
foreach (var child in source.ChildCollection)
{
var targetChild = target.ChildCollection.SingleOrDefault(c => c.Equals(child)); // overwrite Equals or replace comparison with an Id comparison
if (targetChild == null)
{
target.ChildCollection.Add(Mapper.Map<SourceChildType, TargetChildType>(child));
}
else
{
Mapper.Map(child, targetChild);
}
}