C# 在客户机/服务器应用程序中将DTO对象图映射回实体框架对象图的优雅方式

C# 在客户机/服务器应用程序中将DTO对象图映射回实体框架对象图的优雅方式,c#,wcf,entity-framework,C#,Wcf,Entity Framework,我有一个客户机/服务器应用程序,其中服务器使用实体框架作为ORM。 要发送到客户机的每个实体都由DTO类表示 实体框架和DTO类之间的映射是使用AutoMapper处理的 假设我们有以下表格: Person(字符串名称,int CountryID) 国家(int CountryID、int Population、字符串名) 它们由以下EF类表示: class Person { public string Name { get; set; } public int CountryID { ge

我有一个客户机/服务器应用程序,其中服务器使用实体框架作为ORM。 要发送到客户机的每个实体都由DTO类表示

实体框架和DTO类之间的映射是使用AutoMapper处理的

假设我们有以下表格:

Person(字符串名称,int CountryID) 国家(int CountryID、int Population、字符串名)

它们由以下EF类表示:

class Person
{
 public string Name { get; set; }
 public int CountryID { get; set; }
 public Country Country { get; set;}
}

class Country
{
 public int CountryID { get; set; }
 public int Population { get; set; }
 public string Name { get; set;}
}
依次由以下DTO表示:

class PersonDTO
{
 public string Name { get; set; }
 public CountryDTO Country { get; set;}
}

class CountryDTO
{
 public int CountryID { get; set; }
 public int Population { get; set; }
 public string Name { get; set;}
}
数据库的初始状态表示一个空的Person表和一个包含一个条目的Country表:(1123,‘CountryXYZ’)

客户端的应用程序任务是创建一个新的个人实体,并将其国家/地区引用附加到可用的“CountryXYZ”国家/地区实体

为此,客户端应用程序首先请求可用的CountryDTO。 然后,它创建一个新的PersonDTO实例,并将其Country属性设置为它从服务器接收到的唯一CountryDTO。 然后将此PersonDTO实例发送回服务器。 服务器依次将PersonDTO实例映射回Person实例

最后一步是将Person实例存储在ObjectContext中,并调用ObjectContext.SaveChanges()

这种方法的问题是,只要调用ObjectContext.SaveChanges(),就会在数据库中创建一个新的国家行,而不仅仅是使用可用的国家行。 我错过了什么

我是EF的新手,我认为这个用例非常常见。。。所以我希望有一个简单的解决办法

如果问题描述不够清楚,请告诉我


谢谢

这是一个使用可能对您有用的实例

它使用T4模板来生成实体类,并且可以使用WCF对实体类进行双向转换。但是,您需要共享包含客户端和服务器上的实体的程序集


如果这两个都由您控制,并且您都使用.Net,我会这样做。

如果您知道客户端将始终使用它从您的服务器(现有服务器)收到的国家/地区,您只需修改保存逻辑即可使用:

objectContext.PersonSet.AddObject(personToSave);
objectContext.ObjectStateManager
             .ChangeObjectState(personToSave.Country, EntityState.Unchanged);
objectContext.SaveChanges();
如果使用
AddObject
方法,实体及其所有关系将标记为已添加,并将作为新对象插入数据库,除非重新配置其状态

实体还公开FK属性,以便在将DTO映射回实体而不是创建国家/地区实例时可以使用FK属性。在这种情况下,您将不需要处理更改关系的状态,因为该关系将仅通过整数列表示


如果客户可以在一次通话中同时创建
个人
国家
,则您的数据中需要一些标志,以便在现有实体或新实体之间有所区别,或者您必须查询数据库,以验证此类
国家
是否已经存在。

@ChrisWue-谢谢。结果表明,mobile不支持降价:(谢谢Ladislav。有没有一种方法可以在不遍历整棵树的情况下将对象图标记为已修改?@TwinHabity:没有。您必须始终将所有要处理的对象标记为已修改。当我尝试更改个人的引用国家/地区实例时,这种方法遇到了一个新问题。我称为PersonSet.AddObject(personToSave);将该人员及其引用的国家/地区标记为“已添加”。然后我想将其引用的国家/地区对象标记为“已修改”通过调用ObjectStateManager.ChangeObjectState。不幸的是,这会引发一个异常,告诉我对象上下文中已经有一个国家/地区实例。知道吗?当您使用
AddObject
时必须引发异常,而不是在您更改状态时。您是否为每个操作都使用新的上下文实例?只要服务器接收数据并将其转换为EF实例并调用PersonSet.AddPerson(person);此人现在可能引用一个新创建的国家/地区实例(我可以通过查看国家/地区的ID来确定是否是这种情况)。如果不是新实例,我将调用ObjectStateManager.ChangeObjectState(person.Country,EntityState.Modified)。这将引发:AcceptChanges无法继续,因为该对象的键值与ObjectStateManager中的另一个对象冲突。在调用AcceptChanges之前,请确保键值是唯一的。是否知道此处发生了什么?