Hugoware

The product of a web developer with a little too much caffeine

Posts Tagged ‘Anonymous Types

Anonymous Types As Object Templates

with 6 comments

I’ve got a bit of an addiction to intellisense. Dynamic types are interesting but I still prefer being able to see a nice neat list of properties on each of my objects.

Since anonymous types are ‘real’ classes then you can actually create them at runtime. This means you can use them as templates for other objects if you can provide all of the required parameters.

Below is a simple class that allows you to perform queries against a database and use an anonymous type to provide a template for the records to return.

/// <summary>
/// Helps working with database connections
/// </summary>
public class DataConnection {

    #region Constructors

    /// <summary>
    /// Creates a new DataConnection for the connection string
    /// </summary>
    public DataConnection(string connection) {
        this.ConnectionString = connection;
    }

    #endregion

    #region Properties

    /// <summary>
    /// The connection string to use with this DataConnection
    /// </summary>
    public string ConnectionString { get; private set; }

    #endregion

    #region Performing Queries

    /// <summary>
    /// Performs a query for the connection without any parameters
    /// </summary>
    public IEnumerable<T> Query<T>(string query, T template)
        where T : class {
        return this.Query(query, (object)null, template);
    }

    /// <summary>
    /// Performs a query for the connection with the provided paramters
    /// </summary>
    public IEnumerable<T> Query<U, T>(string query, U parameters, T template)
        where T : class 
        where U : class {

        //prepare the connection and command
        Type type = template.GetType();

        //create the connection - (thanks @johnsheehan about the 'using' tip)
        using (SqlConnection connection = new SqlConnection(this.ConnectionString)) {
            using (SqlCommand command = new SqlCommand(query, connection)) {

                //assign each of the properties
                if (parameters != null) {
                    this._UsingProperties(
                        parameters,
                        (name, value) => command.Parameters.AddWithValue(name, value));
                }

                //execute the query
                connection.Open();
                SqlDataReader reader = command.ExecuteReader();

                //start reading each of the records
                List<T> results = new List<T>();
                Dictionary<string, int> fields = null;
                while (reader.Read()) {

                    //prepare the fields for the first time if needed
                    fields = fields == null ? this._ExtractFields(reader) : fields;

                    //generate the record
                    T record = this._CreateAnonymousResult(reader, fields, type, template);
                    results.Add(record);
                }

                //return the results for the query
                return results.AsEnumerable();
            }

        }

    }

    #endregion

    #region Helpers

    //creates a new anonymous type from 
    private T _CreateAnonymousResult<T>(SqlDataReader reader, Dictionary<string, int> fields, Type templateType, T template)
        where T : class {

        //create a container to queue up each record
        List<object> values = new List<object>();

        //use the template to find values
        this._UsingProperties(template, (name, @default) => {

            //try and find the field name
            if (fields.ContainsKey(name)) {

                //check if this is a value
                int index = fields[name];
                object value = reader[index];
                Type convert = @default.GetType();

                //try and copy the va;ue
                if (value != null) {
                    values.Add(Convert.ChangeType(value, convert));
                }
                //not the same type, use the default
                else {
                    value.Equals(@default);
                }

            }
            //no field was found, just use the default
            else {
                values.Add(@default);
            }

        });

        //with each of the values, invoke the anonymous constructor
        //which accepts each of the values on the template
        try {
            return Activator.CreateInstance(templateType, values.ToArray()) as T;
        }
        catch (Exception e) {
            Console.WriteLine(e.Message);
            return template;
        }

    }

    //reads a set of records to determine the field names and positions
    private Dictionary<string, int> _ExtractFields(SqlDataReader reader) {
        Dictionary<string, int> fields = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
        for (int i = 0; i < reader.FieldCount; i++) {
            fields.Add(reader.GetName(i), i);
        }
        return fields;
    }

    //performs an action with each of the properties on an object
    private void _UsingProperties(object obj, Action<string, object> with) {
        
        //if there isn't anything to work with, just cancel
        if (obj == null) { return; }

        //get the type and peform an action for each property
        Type type = obj.GetType();
        foreach (PropertyInfo prop in type.GetProperties()) {
            with(prop.Name, prop.GetValue(obj, null));
        }

    }

    #endregion

}

This class allows us to perform basic queries against a database and then provide a template of the records to return. This means that if the information is found then the database value is used but if it is missing then the template value is used instead.

In order to create an anonymous type we need to know the type and each of the properties in the order they appear. After collecting this information we simply need to use the Activator.CreateInstance method to instantiate the class and we’re set! (method _CreateAnonymousResult)

This means we can write a ‘dynamic’ query with results that have intellisense!

//set up the connection
DataConnection data = new DataConnection(CONNECTION_DATA);

//perform the query
var results = data.Query(
    "select * from orders where orderId > @orderId",
    new { orderId = 11000 },
    new {
        orderId = -1,
        shipName = "missing",
        shipRegion = "none"
    });

//access properties
var record = results.First();
int id = record.orderId; //intellisense!

I’ve actually used this same approach before in CSMongo as a way to provide intellisense without knowing anything about the database in advance.

Of course, this is just some code I hacked out this evening. You aren’t going to be able to do a lot with it but it is a good example of how you can reuse anonymous types in your code.

Advertisements

Written by hugoware

August 30, 2010 at 12:19 am

Simplify Using Anonymous Types

with 7 comments

New Version Available:Check out this post about the AnonymousType class Remixed!.

When you’re using ASP.NET MVC you pass information from the Controller to the View. Your information is found in the ViewDataDictionary where you can cast the info back into whatever form it originally was. Additionally, you can even specify a model type for the page so that no casting is required to access your information.

It may be a little inconvenient, but it’s not bad. This just means you need to either define the class in advance or limit yourself to simple values.

If you’re writing LINQ queries and creating anonymous types then you’re in a tougher situation. I’ve seen people try and come up with solutions for accessing an anonymous type after its been created, but nothing seemed all that intuitive.

In the next version of C# we’re going to see the new dynamic declaration which will most likely solve this problem. For now, Reflection is about the only way you solve this issue.

On a recent project I got a little tired of trying to manually do this each time. I ended up writing a wrapper class (included at the end of the post). The example below shows how you can use this class AnonymousType.

//Controller
//==================
this.ViewData["details"] = new {
  name = "Jim",
  age = 30
};


//View
//==================
AnonymousType details = new AnonymousType(this.ViewData["details"]);

//access properties
string name = details.Get<string>("name");
int age = details.Get<int>("age");

//supply a default type in case the property doesn't exist
bool fake = details.Get<bool>("someFakeProperty", false);

//Use the properties by name - maps to the argument name
details.Use((string name, int age) => { 
  Console.WriteLine("{0} is {1} years old", name, age);
});

//An wrong property name (wrong type or incorrect case) will cause an error
details.Use((int NAME) => { /* Error! */ });

It’s really just Reflection, but it streamlines using unknown types inside of a View.

Now, if you are thinking “What about methods? or Fields?” then you’re like me. Now, if your second thought was “Well wait, if it has methods then it’s a defined class, we should just cast it instead.”, then you’re much smarter than me. Unfortunately, I spent an hour or so writing ComplexAnonymousType that would call methods (even match correct signatures), access fields, the works… then to realize how much of a wasted effort it was. 🙂

Anyways, try it out if interested. Let me know what you think!

Don’t forget! Reflection is slower than the normal means of accessing properties. While the time spent is negligible in small amounts, if you’re going to be using this code below with a lot of items, it may be wiser to define a class instead.

Source Code

Download AnonymousType.cs

New Version Available:Check out this post about the AnonymousType class Remixed!.

.

Written by hugoware

May 13, 2009 at 11:41 am