Objects are materialized using reflection. Reflection is optimized using generated IL for property or field access by creating dynamic methods, and caching these methods using dictionaries. There is a minor performance hit to reflect a type and generate the dynamic access methods. Once a type is reflected and optimized access methods are generated, value assignment is 3 orders of magnitude faster than normal reflection, but roughly 1 order of magnitude slower than direct property assignment, due to the internal dictionary look-ups to resolve the cached delegates.

The most direct and simple use of object materialization is found via methods that extend IDataReader with the following signature:

public static List<T> ReadEntities<T>(
            this IDataReader reader, 
            IDataHandler handler = null, 
            IDataModel dataModel = null, 
            int estimatedRowCount = 50)
        {...

Ignoring the optional arguments above, the most simple usage will attempt to match column names to property names on the given type. Columns that don't match property names will be ignored. The provided type is expected to have a default constructor and will fail if this is not available. Usage is very simple:

// 'someCommand' is a SqlCommand; this assumes the command is configured to execute:
using (var reader = someCommand.ExecuteReader())
{
        // Get the list of entities and return them, or otherwise do something
        return reader.ReadEntities<Customer>();
}

NOTE: The above approach buffers the full result set, which can stress memory for very large results. The following overload will materialize rows from the underlying TDS as they are enumerated (deferred execution), enabling reduced memory stress for large results:

// 'someCommand' is a SqlCommand; this assumes the command is configured to execute:
using (var reader = someCommand.ExecuteReader())
{
        // Get the list of entities and return them, or otherwise do something
        var items = reader.EnumerateEntities<Customer>();
        // Do something with the enumerable that is 'streaming'.
}

Ideally, use this approach when you can perform a stream based operation with the results, such as a transformation or additional grouping into hierarchies. Using this approach allows you to avoid redundant buffering and iteration of large result sets. If you must buffer the entities in memory as a list anyway, just "ReadEntities<>". Also, the example above assumes you will enumerate inside the using block; if you are instead returning the enumerable to calling code, you will have to ensure the reader is disposed after enumeration, because of deferred execution. The EnumerateEntities method takes an action callback to use for this purpose, as follows:

// Use the callback when returning the list of entities:
return reader.EnumerateEntities<Customer>(onCompleteCallback: () => 
    {
        reader.Close(); 
    });
      

The IDataHandler interface offers a means to inject your own mapping logic for complex object hierarchies or very hot path scenarios where exactly specified mappings are preferred. For example, if you want to maximize performance of object materialization, implement this interface to do direct assignment from a data reader, and pass an instance of this handler to the method shown above. You can also use this approach to create a hierarchy of objects from a flat data set, as needed.

The IDataModel interface offers a means to define mappings of relational data to object properties in cases where there is not a conventional name match. You can use this implementation to define keys and identity columns, which can influence query generation, if you decide to use that feature. For more regarding modeling, see Model Definition and Query Building.

The Teaq.FastReflection namespace exposes the optimized reflection API’s for general use apart from the rest of the Teaq framework. For more regarding FastReflection, see FastReflection.

NOTE: Dynamic IL generation and use will work in partial trust scenarios, but be careful to only require access to public properties of your DTO classes if they are fully trusted and Teaq is partially trusted. In most cases, this is not an issue. For more information regarding dynamic methods and partial trust, see: https://msdn.microsoft.com/en-us/library/bb384237(v=vs.110).aspx#Using_methods.

Last edited May 29, 2016 at 6:41 PM by bkjuice, version 19