Today I spent a few hours on mysterious error. One automated test failed, and since it was actually an integration test with complex setup, tracking down the failure was not easy. The problem that I’ve eventually found can be illustrated with a couple of tests:
[Test]
public void BadClosureTest()
{
int[] numbers = new int[]
{
1, 2, 3
};
List> functions = new List>();
foreach (int n in numbers)
{
functions.Add(new Func(() => n));
}
// Will fail on first assertion!
Assert.That(functions[0].DynamicInvoke(), Is.EqualTo(1));
Assert.That(functions[1].DynamicInvoke(), Is.EqualTo(2));
Assert.That(functions[2].DynamicInvoke(), Is.EqualTo(3));
}
[Test]
public void GoodClosureTest()
{
int[] numbers = new int[]
{
1, 2, 3
};
List> functions = new List>();
foreach (int n in numbers)
{
int m = n;
functions.Add(new Func(() => m));
}
Assert.That(functions[0].DynamicInvoke(), Is.EqualTo(1));
Assert.That(functions[1].DynamicInvoke(), Is.EqualTo(2));
Assert.That(functions[2].DynamicInvoke(), Is.EqualTo(3));
}
As you can see, the only difference between these tests is a line in the second test assigning iterator “n” to a variable “m” defined within the “foreach” scope. Why does this matter?
Well, for us, procedural languages veterans the difference may be not so obvious at first glance. But if you happen to have ReSharper, it will immediately give you a warning about the first test: “Access to modified closure”. And will suggest to copy “n” to a local variable. I didn’t have ReSharper, so I came to the same conclusion in a hard way.
A lambda-expression “() => n” is not just a delegate – it’s a closure. It uses a variable that is defined outside its scope. Such variables are always treated by reference, even though they may be of a value type. So as long as the variable “n” lives, any changes applied to it will affect every closure where it is referenced.
And apparently ReSharper can warn us about improper use of closures.
By the way, if instead of “foreach” test had a “for” loop with an index, the first test would fail with IndexOutOfRangeException. Because closures were accessed after the array elements are iterated and index would be set to 3.
Don’t know who made it, but holidays hint me that it comes from Russia.
If you need to send large amount of information using a single WCF call, you will need to customize binding configuration that is by default tuned for efficient transfer of small messages. And if your data contracts happen to have types with circular (or self-) references, this behavior needs to be configured too. Here what I had to tweak in order to enable transfer of very large (up to 100 MB) collections of hierarchical data.
1. Binding configuration
The following binding attribute may need adjustments:
- maxBufferSize
- maxBufferPoolSize
- maxReceivedMessageSize
- openTimeout
- closeTimeout
- sendTimeout
- receiveTimeout
Binding element has “readerQuotas” sub-element where the following attributes may be tweaked for large amount of data transfer:
- maxDepth
- maxStringContentLength
- maxArrayLength
- maxBytesPerRead
- maxNameTableCharCount
In addition, transferMode can be set to either “Buffered” or “Streamed” depending on how data should be received by the client.
2. Behavior configuration
In case large amount of data are sent as standard collections (arrays, lists etc.), no behavior customization is needed in order to increase data transfer limits. But if the data represent more complex topology, there is an additional attribute to adjust, and this attribute belongs to behavior, not binding. It is called “maxItemsInObjectGraph” and can be specified either on serviceBehavior or endpointBehavior level. The customize behaviors element may look like this:
<behaviors>
<servicebehaviors>
<behavior name="LargeTreeBehavior">
<servicedebug includeexceptiondetailinfaults="true" />
<datacontractserializer maxitemsinobjectgraph="2147483647" />
</behavior>
</servicebehaviors>
<endpointbehaviors>
<behavior name="HighlightBehaviorLarge">
<datacontractserializer maxitemsinobjectgraph="2147483647" />
</behavior>
</endpointbehaviors>
</behaviors>
3. Serialization of object graphs with circular references
.NET 3.5 SP1 introduced a new attribute IsReference that can be used with DataContracts to enable serialization of object graphs with circular references. Previously it was not possible without writing custom code. Now it requires only a single attribute specification:
[DataContract(IsReference = true)]
public class Node
{
[DataMember]
Node Parent { get; set; }
....
}
That should be it. Now you can send graphs with millions of nodes over the wire using WCF. In may cases sending so much data in a single call is not a good idea, but that’s another topic.