I'm writing a client and service using WCF, however I suspect that there are multiple issues at the moment.
As you can see from the code below, the process is as follows: The client asks for some data, which the service generates and returns in a DTO-object. The client then attempts to do a look-up in a returned dictionary, but this throws a KeyNotFoundException.
In addition, a test on the server fails before this (if left uncommented) because the input parameter list allBranches no longer contains Branch currentBranch, which it did on the client side of the method call.
Can someone enlighten me as to what happens in this code, and why it blows up first on the server side and later on the client side?
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
EDIT: I followed Jon's excellent answer below and did the following (which fixed all problems):
Made Branch immutable by giving every property a private setter.
- Every class used as a key in a dictionary should be immutable, or at least have its hash code computed from immutable properties.
Implemented IEquatable + overrides of Object.Equals and GetHashCode, the latter as per this SO-answer (link)
Implementing IEquatable is done simply by testing for equal property values,
public bool Equals(Branch other)
{
return other != null && ((BranchId == other.BranchId) && (Name == other.Name));
}