C# 对象引用用作客户端和WCF服务器上字典中的键
我正在使用WCF编写客户机和服务,但是我怀疑目前存在多个问题 从下面的代码中可以看到,流程如下:客户机请求一些数据,服务生成这些数据并在DTO对象中返回。然后,客户端尝试在返回的字典中进行查找,但这会引发KeyNotFoundException 此外,在此之前,服务器上的测试失败(如果未注释),因为输入参数列表AllBranchs不再包含Branch currentBranch,它在方法调用的客户端执行了此操作 有人能告诉我这段代码中发生了什么,以及为什么它首先在服务器端爆炸,然后在客户端爆炸C# 对象引用用作客户端和WCF服务器上字典中的键,c#,.net,C#,.net,我正在使用WCF编写客户机和服务,但是我怀疑目前存在多个问题 从下面的代码中可以看到,流程如下:客户机请求一些数据,服务生成这些数据并在DTO对象中返回。然后,客户端尝试在返回的字典中进行查找,但这会引发KeyNotFoundException 此外,在此之前,服务器上的测试失败(如果未注释),因为输入参数列表AllBranchs不再包含Branch currentBranch,它在方法调用的客户端执行了此操作 有人能告诉我这段代码中发生了什么,以及为什么它首先在服务器端爆炸,然后在客户端爆炸
Shared class definitions
------------------------
[DataContract(IsReference = true)]
public class Branch
{
public Branch(int branchId, string name)
{
BranchId = branchId;
Name = name;
}
[DataMember]
public int BranchId { get; set; }
[DataMember]
public string Name { get; set; }
}
[DataContract]
public class Department
{
public string Name { get; set; }
// a few other properties, both primitives and complex objects
}
[DataContract]
public class MyDto
{
[DataMember]
public IDictionary<Branch, List<Department>> DepartmentsByBranch { get; set; }
[DataMember]
public Branch CurrentBranch { get; set; }
// lots of other properties, both primitives and complex objects
}
Server-side
--------------------------------
public CreateData(List<Branch> allBranches, Branch currentBranch)
{
// BOOM: On the server side, currentBranch is no longer contained in allBranches (presumably due to serialization and deserialization)
if (!branches.Contains(branchToOpen))
{
throw new ArgumentException("allBranches no longer contain currentBranch!");
}
// Therefore, I should probably not do the following, expecting to use currentBranch as a key in departmentsByBranch later on
var departmentsByBranch = branches.ToDictionary(branch => branch, branch => new List<Department>());
return new MyDto
{
DepartmentsByBranch = departmentsByBranch,
CurrentBranch = departmentsByBranch,
};
}
Client-side (relevant code only)
--------------------------------
var service = new ServiceProxy(); // using a binding defined in app.config
var allBranches = new List<Branch>
{
new Branch(0, "First branch"),
new Branch(1, "Second branch"),
// etc...
};
var currentBranch = allBranches[0];
MyDto dto = service.CreateData(allBranches, currentBranch);
var currentDepartments = dto.DepartmentsByBranch[currentBranch]; // BOOM: Generates KeyNotFoundException
代码失败的原因是您的客户端中有两个独立的
分支
实例:一个是您在本地创建的(currentBranch
),另一个是从服务器接收并由WCF隐式创建的(在dto.DepartmentsByBranch
内部)。您没有指定这两个实例是“同一件事”,因此就词典而言,它从未见过您所谈论的currentBranch
您需要为Branch
提供IEquatable
的正确实现,这同样适用于用作字典键的所有类
请注意,“适当实施”
如果您实现了IEquatable
,您还应该覆盖基
类Object.Equals(Object)
和GetHashCode
的实现,以便
他们的行为与IEquatable.Equals的行为一致
方法
客户端和服务器在不同的机器上运行,它们不共享内存。客户机上的引用始终与服务器上的任何引用不同。@JohnSaunders是的,我意识到了这一点,但我(天真地)希望当执行在服务器上输入CreateData时,WCF会确保currentBranch仍然是与AllBranchs中的一个元素相同的对象。。。我觉得很傻。Jons在下面的回答确保Branch.Equals不再测试引用相等,而是我自己版本的Equals(它只是测试属性相等)。谢谢你的提示!为什么不在DataContract属性中简单地使用IsReference来避免服务器上CreateData中的ArgumentException?@larslovlie:该属性确定序列化程序的功能(它将为每个序列化的图形创建一个对象实例)。它不会(也不能)让序列化程序意识到应用程序内存中已经存在一个等效实例,并与之协调。
public bool Equals(Branch other)
{
return other != null && ((BranchId == other.BranchId) && (Name == other.Name));
}