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

时间:2015-01-27 17:10:51

标签: c# .net

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

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

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

编辑:我按照Jon在下面的优秀答案做了以下(解决了所有问题):

  • 通过为每个属性提供私人设置器,使分支不可变。

    • 在字典中用作键的每个类都应该是不可变的,或者至少使用不可变属性计算其哈希代码。
  • 实现了Object.Equals和GetHashCode的IEquatable +覆盖,后者按照这个SO答案(link

实现IEquatable只需通过测试相等的属性值来完成,

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

1 个答案:

答案 0 :(得分:0)

您的代码失败的原因是您的客户端中有两个单独的Branch实例:您在本地创建的实例(currentBranch)以及从服务器接收并由WCF隐式创建的实例(在dto.DepartmentsByBranch内)。你还没有指定这两个实例是#34;同样的东西&#34;,所以就字典而言,它从未见过你所说的currentBranch

您需要为Branch提供IEquatable<Branch>的正确实现 - 对于您用作字典键的所有类也是如此。

请注意&#34;正确实施&#34; means

  

如果您实施IEquatable<T>,您还应该覆盖基础   Object.Equals(Object)GetHashCode的类实现,以便   他们的行为与IEquatable<T>.Equals的行为一致   方法