A couple of days ago I hit a regression bug in .NET 3.5 SP1, in which when you have a generic class that implements ISerializable and has static variables – you can not serialize it using a BinaryFormatter without your application either hanging (x86) or raising an exception (x64 – a TargetInvocationException containing an OutOfMemoryException). This only happens if you use a reference type as a generic argument.
It’s already well known, but I have yet to find a workaround documented anywhere. You could simply install the hotfix, but well, I wouldn’t if I were you – it hasn’t been thoroughly tested yet. Moreover, you might not even be able to do so due to either internal politics, strict IT rules or the fact that you simply do not have control over the hosting server.
Let’s take the simplest class that causes the issue:
[Serializable]
public class MyClass<T> : ISerializable
{
private static int list = 0;
public MyClass()
{
}
protected MyClass(SerializationInfo info, StreamingContext context)
{
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
}
}
When using the class as such:
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, new MyClass<string>());
stream.Position = 0;
MyClass<string> item = (MyClass<string>)formatter.Deserialize(stream);
}
The last line will hit the bug.
To work around this issue, simply move your static variables into a new subclass:
[Serializable]
public class MyClass<T> : ISerializable
{
private static class KB957543
{
public static int list = 0;
}
public MyClass()
{
}
protected MyClass(SerializationInfo info, StreamingContext context)
{
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
}
}
You can still access all of your static variables and you don’t hit the bug.
Note that when you use anonymous methods or lambdas, they are cached as static variables of the type, meaning that you will have to manually type all of your lambdas.
Here’s an example of such a type that is prone to the bug:
[Serializable]
public class MyClass<T> : ISerializable
{
public MyClass()
{
}
public static string Concat(IEnumerable<int> numbers)
{
return string.Join(", ", numbers.Select(i => i.ToString()).ToArray());
}
protected MyClass(SerializationInfo info, StreamingContext context)
{
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
}
}
If we look through Reflector, we can see that there is a cached delegate in our type:
Since this is a static member, it makes the type susceptible to the bug and we now need to manually create the cached member ourselves:
[Serializable]
public class MyClass<T> : ISerializable
{
private static class KB957543
{
public static readonly Func<int, string> ToString = i => i.ToString();
}
public MyClass()
{
}
public static string Concat(IEnumerable<int> numbers)
{
return string.Join(", ", numbers.Select(KB957543.ToString).ToArray());
}
protected MyClass(SerializationInfo info, StreamingContext context)
{
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
}
}
When index fragmentation becomes too high, indexes will be very inefficient. Other than planning a good index design, you should rebuild / reorganize your indexes every once in a while.
SELECT 'ALTER INDEX [' + ix.name + '] ON [' + s.name + '].[' + t.name + '] ' +
CASE WHEN ps.avg_fragmentation_in_percent > 40 THEN 'REBUILD' ELSE 'REORGANIZE' END +
CASE WHEN pc.partition_count > 1 THEN ' PARTITION = ' + cast(ps.partition_number as nvarchar(max)) ELSE '' END
FROM sys.indexes AS ix INNER JOIN sys.tables t
ON t.object_id = ix.object_id
INNER JOIN sys.schemas s
ON t.schema_id = s.schema_id
INNER JOIN (SELECT object_id, index_id, avg_fragmentation_in_percent, partition_number
FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, NULL)) ps
ON t.object_id = ps.object_id AND ix.index_id = ps.index_id
INNER JOIN (SELECT object_id, index_id, COUNT(DISTINCT partition_number) AS partition_count
FROM sys.partitions
GROUP BY object_id, index_id) pc
ON t.object_id = pc.object_id AND ix.index_id = pc.index_id
WHERE ps.avg_fragmentation_in_percent > 10 AND
ix.name IS NOT NULL
The above query will give you a list of recommended index rebuild / reorganize statements for your database, according Pinal Dave’s 10-40 rule, although you are welcome to tweak it to your liking. It supports non-partitioned as well as partitioned indexes. If you want a more intense check for fragmentation, change the last NULL in the dm_db_index_physical_stats call to 'SAMPLED' or even 'DETAILED' (include quotes).
It’s a handy little tool for database administrators and saves a lot of the hassle of monitoring index fragmentation.
Update: Added multi-schema support as suggested by MJ12 and another check for null index names.

I just filed a new bug with Microsoft Connect. I certainly hope this one doesn’t get shrugged off like many of my other bugs did.
Want to reproduce it yourselves? Just create a table with a character that can only be valid inside the context of brackets (like a comma or braces) and then try to select from it. Don’t keep the CREATE clauses in the same query window or it might just work. See the screenshot below.
This is very frustrating because this means that not only is there no IntelliSense for these objects, but IntelliSense now gets in the way of actual querying as it will autocomplete incorrect names.