Ordering EntityCollection sets in the ADO.NET Entity Framework

Following on from a recent post regarding XML serialisation of ADO.NET Entity Framework one issue I've come across is getting entities within the XML in the correct order.

The main issue is that the EntityCollection<IEntityWithRelationships> type used to maintain references within the entity framework is that its un-ordered. This makes perfect sense of course as entities in a database have no reliable order.

Even though this makes sense its very annoying if you just want your entities to come out in a defined order within an object structure and the any XML serialised from it. You could of course order the XML once it has been serialised, either via a transform, or in code, but having data contract references makes this pretty difficult.

I've come up with a pretty hacky mechanism that forces the order that objects are returned via the EntityCollection Enumerators. The solution uses reflection and won't be guaranteed to work in future versions of the framework. Essentially it finds the HashSet object used by EntityCollection to store its values and re-orders the data structure that the HashSet loops through in its enumerator.

Once a sort order has been applied to the entity collection the only code that runs against the collection must only work via the hashsets enumerator. I.e. use a context object solely to get all the objects for the re-order process and only ever read from the collections that have been re-ordered after that. Any attempt to add or remove items will cause the HashSet object to blow up as its underlying data structures have been messed with.

The code is:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Objects.DataClasses;
using System.Collections;
using System.Reflection;

namespace Examples

{
    public static class EntityUtil
    {
        /// <summary>
        /// Reorders the entities in an entity collection by the keySelector function.
        /// </summary>
        /// <remarks>
        /// This class uses reflection to re-write the slots in the underlying hashset used by the entity
        /// collection to contain the references.
        ///
        /// The only action that should be taken on the entity collection after this method has been called
        /// is via its enumerators, as the hash set is not left in a consistant state.
        /// </remarks>
        /// <typeparam name="TSource">The type of the entity objects.</typeparam>
        /// <typeparam name="TKey">The type of the key to order by.</typeparam>
        /// <param name="source">The entity collection source to re-order.</param>
        /// <param name="keySelector">The key selector function that returns the key to order by.</param>
        public static void ReorderEntities<TSource, TKey>(
            this EntityCollection<TSource> source, Func<TSource, TKey> keySelector)
            where TSource : class, IEntityWithRelationships
        {
            HashSet<TSource> relatedEntities = (HashSet<TSource>)
                typeof(EntityCollection<TSource>).GetProperty("RelatedEntities", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(source, null);
            // re-order the sets slots to fix the ordering, (but mess up everything else probably)
            Type slotType = typeof(HashSet<TSource>).Assembly.GetType("System.Collections.Generic.HashSet`1+Slot").MakeGenericType(new Type[] { typeof(TSource) });

            FieldInfo slotField = typeof(HashSet<TSource>).GetField("m_slots", BindingFlags.Instance | BindingFlags.NonPublic);
            FieldInfo lastIndex = typeof(HashSet<TSource>).GetField("m_lastIndex", BindingFlags.Instance | BindingFlags.NonPublic);
            System.Array slots = Array.CreateInstance(slotType, relatedEntities.Count);
            int index = 0;
            foreach (TSource obj in new List<TSource>(relatedEntities).OrderBy(keySelector))
            {
                object test = Activator.CreateInstance(slotType);
                slotType.GetField("hashCode", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(test, obj.GetHashCode());
                slotType.GetField("value", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(test, obj);
                slotType.GetField("next", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(test, -1);
                slots.SetValue(test, index++);
            }
            slotField.SetValue(relatedEntities, slots);
            lastIndex.SetValue(relatedEntities, slots.Length);
        }
    }

}

 

The reflection code itself is quite interesting as it find and constructs an inner type (Slot) of a generic type. The inner type is a struct so the Activator class is used as struct's don't have a constructor to invoke.

An example use of the code is:

Customer customer;
using (ContextContainer context = new ContextContainer())
{
    // cause a load of static data types first
    customer = context.Customers
        .Include("Addresses")
        .Include("Addresses.AddressLines")
        .First(e => e.CustomerId == customerId);
}

// reorder the structure in memory
customer.Addresses.ReorderEntities(e => e.Order);

foreach (Address a in customer.Addresses)
{
    a.AddressLines.ReorderEntities(e => e.Order);

}

 

The structure will now have the correct serialisation order. FYI an example xslt snippet that works correctly with ordered referenced entites is:

<xsl:for-each select="do:Addresses/do:Address">
  <xsl:apply-templates select="self::node()[@z:Id]"/>
  <xsl:for-each select="self::node()[@z:Ref]">
    <xsl:variable name ="ref" select="@z:Ref"/>
    <xsl:apply-templates  select="//do:Address[@z:Id = $ref]"/>
  </xsl:for-each>
</xsl:for-each>

Comments

# re: Ordering EntityCollection sets in the ADO.NET Entity Framework
Gravatar The .NET Micro Framework has not been very successful, and yet it is key to the long term success of the .NET Framework as a one size fits all programming model for high level programming that still needs some performance and system-level ties...
Left by regles des jeux au casino on 4/10/2010 12:04 AM
# re: Ordering EntityCollection sets in the ADO.NET Entity Framework
Gravatar the 1970s by Peter Chen, goes a long way to solve this problem. Using entity-relationship modeling, programmers create a conceptual model of the data and write their data access code against that model, while an additional layer provides a bridge between the entity-relationship model and the actual data store.
Left by Nishu Gupta on 7/13/2010 6:18 PM
# re: Ordering EntityCollection sets in the ADO.NET Entity Framework
Gravatar the Microsoft customer community achieve success with entity architecture applications. We want to see entity-oriented applications continue to succeed on the .NET platform. We are committed to working with Microsoft to share our considerable experience with entity architectures, and approaches, as well as our successes and failures with the Entity Framework team with the hope of ensuring successful execution of entity-oriented projects within the Microsoft customer community.
Left by kuber infotek gupta on 7/13/2010 7:11 PM
# re: Ordering EntityCollection sets in the ADO.NET Entity Framework
Gravatar Microsoft shipped the first CTP of the ADO.NET Entity Framework in August of 2006. Customers immediately grasped the power of being able to work with their data through a conceptual model, rather than in terms of database schemas optimized around storage concerns like normalization and data partitioning. In February we added the framework into the CTP and Beta 1 builds of the next version of the .NET Framework and Visual Studio codenamed Orcas.
Left by Kuber Infotek on 7/14/2010 7:44 PM
# re: Ordering EntityCollection sets in the ADO.NET Entity Framework
Gravatar See how the next version of ADO.NET starts to deliver on Microsoft’s vision for an Entity Framework that can fundamentally change how you think about data. Find out what problem spaces the ADO.NET Entity Framework and Language-Integrated Query are targeting, and their innovative approach for addressing those challenges.
Left by Kuber Infotek on 7/20/2010 7:05 PM
# re: Ordering EntityCollection sets in the ADO.NET Entity Framework
Gravatar The means of accomplishing this generally take the data further out of the scope of your own concepts of how your data should be structured in your applications. It requires a solid knowledge of the database schema, table definitions, and stored procedures along with their parameters and results, views, keys, and more so that you can create your data access code.
Left by agile informatics fremont on 8/5/2010 10:10 PM
# re: Ordering EntityCollection sets in the ADO.NET Entity Framework
Gravatar The ADO.NET Entity Framework enables developers to create data access applications by programming against a conceptual application model instead of programming directly against a relational storage schema.
Left by LAX Limousine Service on 8/15/2010 11:38 PM
# My view
Gravatar Just want to say some thing about your post! I am really impressed by it. I hope to catch such interesting ideas!
Left by maison de credit on 8/24/2010 8:33 PM
# re: Ordering EntityCollection sets in the ADO.NET Entity Framework
Gravatar Selenium is a portable software testing framework for web applications. Selenium provides a record/playback tool for authoring tests without learning a test scripting language.
Left by fm stereo transmitter on 8/29/2010 6:15 PM

Leave Your Comment

Title*
Name*
Email (never displayed)
 (will show your gravatar)
Url
Comment*

Please add 7 and 3 and type the answer here:

Preview Your Comment.