在客户端和 WCF 服务器上的字典中用作键的对象引用

Object references used as keys in dictionary on client and WCF server

我正在使用 WCF 编写客户端和服务,但我怀疑目前存在多个问题。

从下面的代码可以看出,过程如下:客户端请求一些数据,服务生成这些数据并在 DTO 对象中 returns。然后客户端尝试在返回的字典中进行查找,但这会抛出 KeyNotFoundException。

此外,在此之前服务器上的测试失败(如果未注释),因为输入参数列表 allBranches 不再包含分支 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

编辑:我按照下面乔恩的出色回答进行了以下操作(解决了所有问题):

只需测试相等的 属性 值即可实现 IEquatable,

public bool Equals(Branch other)
    {
        return other != null && ((BranchId == other.BranchId) && (Name == other.Name));
    }

您的代码失败的原因是您的客户端中有两个单独的 Branch 实例:一个您在本地创建的实例 (currentBranch) 和一个从服务器接收并隐式创建的实例通过 WCF(在 dto.DepartmentsByBranch 内)。您没有指定这两个实例是 "the same thing",因此就字典而言,它从未见过您正在谈论的 currentBranch

您需要为 Branch 提供 IEquatable<Branch> 的正确实现 - 这同样适用于您用作字典键的所有 类。

注意 "proper implementation" means

If you implement IEquatable<T>, you should also override the base class implementations of Object.Equals(Object) and GetHashCode so that their behavior is consistent with that of the IEquatable<T>.Equals method.