Introduction
Many features in Signum Framework (as many othe code) depend on an undocumented feature of the .Net Framework.
After strong debate we decided to rely in this feature because of the following reasons:
- Maintainability problems that circumventing this feature will cause (OrderAttribute in almost any field).
- How widely extended the use of this feature is in the community.
- And, due to that, the very small probabilities that this will change in the future.
However, we think we should be honest about that, explain the feature and document who is using it.
The Problem
In many situations Signum.Framework uses reflection to retrieve Type information. Often, many properties or fields are retrieved at once using
Type.GetFields or
Type.GetProperties.
The order this elements get retrieved is, usually, the order they are declared in the source code. This order, however, is not guaranteed by Microsoft, as the documentation stands:
"The GetFields method does not return fields in a particular order, such as alphabetical or declaration order. Your code must not depend on the order in which fields are returned, because that order varies."A similar sentence can be found in GetProperties documentation.
Actually, Microsoft guys are quite right about that, if you stress CLR Reflection cache enough, the order can vary (We have tried with Classes with thousands of properties and some calls to GetProperty before is called GetProperties).
The Solution
Fortunately, it seems that the ordering uncertainty is caused by Reflection cache subsystem, but the CLR has MetadataToken value that correlates with the order they are placed in the code.
This property of the MetadataToken, however,
is not guaranteed by Microsoft, but it's used as an answer by many in MSDN forums:
1 2.
Even in our stress test, sorting by MetadataToken solves the problem, so we are pretty sure that this solution works properly, saving you to decorate every single property and field with a Order attribute. We have centralized this functionality in Signun.Utilities.Reflection.MemberEntryFactory class, so if this stops being true some day (CLR changes for example) we are in a good position to fix it.
Also, if you are concerned about performance, this class is also responsible for creating
DynamicMethod for getting/setting properties and fields, so the performance overhead of using reflection should be minimal.
Who is using the hack
Here is a small list of Signum Framework functionality affected by the order you declare your Properties/Fields:
Limitations
This MetadataToken has some limitations thought:
- Heterogeneous Members: It can not be used to know if a Property has been declared after or before a Field. It can't only be used to compare Properties with Properties and Fields with Fields.
- Inherited Members: If some Fields/Properties comes from base classes, it's not deffined how MetadataToken is going to behave. SchemaBuilder handles this particular problem, so members from base classes appear first in the tables, (ID, ToStr...)
- Partial classes: It's not clearly defined which file is going to be before and witch after.