Click here to Skip to main content
15,867,308 members
Articles / Web Development / ASP.NET

Converting anonymous types to any type

Rate me:
Please Sign up or sign in to vote.
4.90/5 (24 votes)
2 Aug 2009CPOL5 min read 340.1K   44   50
How to convert LINQ to SQL anonymous types to a specified type.

Introduction

This article describes how to handle conversions from an anonymous type to a specific type by using .NET 3.5 extensions. It is especially helpful when using LINQ to SQL to retrieve data in the form of lists and/or arrays.

Problem

With Microsoft LINQ to SQL, you enjoy the ability of using a strong programming language and at the same time having control over your data. Data objects are represented by classes automatically created when you link your data structure to your Visual Studio project via DBML files.

Instead of dividing your attention between SQL and programming, you can now write data retrieving procedures within your Visual Studio programming environment.

At the same time, LINK to SQL sometimes creates some challenges. One of such is the Anonymous type that is returned by LINQ to SQL queries. When you join several database tables in one query, you have to choose how to organize the returning sequence. Often enough, you have to create a sequence that returns a list or an array of Anonymous type objects. Many programmers, myself including, think that it is not the best practice to operate with such anonymous type objects at the business and/or UI layers.

Solution

Let's have a database which consists of two tables: Client and Order.

Image 1

The Client table has client specific information. In our case, it is Name and AddressID, which obviously point to some depository with addresses info for this client, but that is not our concern now. The Order table has some text describing an order in string format and a ClientID column, which points to an ID of the client who placed the order.

Say, we want to get all the clients info and also how many orders each client made.

Let's see the data by running the following queries:

Image 2

The Group By query returns the set of rows having ID, Name, Address ID, and Total Orders for all the clients from the Client table. In old days, you would use ADO.NET to retrieve and work with the data.

Addressing the problem

If you want to use LINQ to SQL, you would create a procedure to retrieve the sequence with the required info:

C#
public static List<Client> GetClientsWithTotalOrders()
{ 
   //create DataContext object, use it, discard it:
    using (CodeProjectDataContext db = new GetDataContext())
    {
        //join Client with Order:
        var res = db.Clients
            .Join(
            db.Orders,
            o => o.ID,
            c => c.ClientID,
            (c, o) => new { c }
            )

            //group by Client
            .GroupBy(o => o.c)

            //define output object
            .Select(o => new { ID = o.Key.ID, AddressID = o.Key.AddressID, 
            Name = o.Key.Name, TotalOrders = o.Count() })
            
            //output to List of objects:
            .ToList()
            ;

         //cast the output sequence to List of Clients and return:
         return (List<Client>)res;
    }
}

In this procedure, we join the Client and Order tables and grouped by Client, calculating how many orders the client made. The result is returned in the following format: ID, Address ID, Name, and Total Orders.

Let us add a property into the Client class using the ability to create partial classes, and add any property/methods we want to the classes generated by the Visual Studio Auto Designer:

C#
public int TotalOrders { get; set; }

Now, the Client class has these properties: ID, Name, AddressID, and TotalOrders. A list of objects with all these properties is returned by the LINQ to SQL GetClientsWithTotalOrders() procedure.

But, if you try to compile the procedure, you will get the error:

Error    1    Cannot convert type 'System.Collections.Generic.List<AnonymousType#1>' 
   to 'System.Collections.Generic.List<CodeProject.LinkToSql.Client>'    
   C:\Development\VS2008\Projects\CodeProject\CodeProject\LinkToSql\DbHandlers.cs
       38    23    CodeProject

Unfortunately, there is no way the compiler would recognize that the anonymous type created by the program has the same set of properties as your Client class. That means that you will have to deal with the anonymous List type after you retrieve the data. How can we convert this anonymous type into the Client type?

By writing extensions to deal with anonymous type objects.

I created two extensions to handle this:

C#
public static object ToType<T>(this object obj, T type)
{

    //create instance of T type object:
    var tmp = Activator.CreateInstance(Type.GetType(type.ToString())); 

    //loop through the properties of the object you want to covert:          
    foreach (PropertyInfo pi in obj.GetType().GetProperties()
    {
      try 
      {   

        //get the value of property and try 
        //to assign it to the property of T type object:
        tmp.GetType().GetProperty(pi.Name).SetValue(tmp, 
                                  pi.GetValue(obj, null), null)
      }
      catch { }
     }  

   //return the T type object:         
   return tmp; 
}

This extension allows to convert an anonymous type object into a specified type. If you have an object of an anonymous type and want to covert it to Client type, you need to call this extension:

C#
object obj=getSomeObjectOfAnonymoustype();
Client client=obj.ToType(typeof (Client));

Let us see how it works:

At first, it creates an empty temporary object of Client type using the Activator.CreateInstance() procedure. Then, it loops through every property info of the calling object, gets its value, and re-assigns the value to the corresponding property of the newly created Client object. Finally, it returns the Client type object having all the properties populated from the calling object.

So, for a single object, the problem is solved.

What about a list of such objects? Or arrays?

The second extension I created is to transform a List of Anonymous type objects into a List of a specific type objects:

C#
public static object ToNonAnonymousList<T>(this List<T> list, Type t)
{

   //define system Type representing List of objects of T type:
   var genericType = typeof(List<>).MakeGenericType(t);

   //create an object instance of defined type:
   var l = Activator.CreateInstance(genericType);

   //get method Add from from the list:
   MethodInfo addMethod = l.GetType().GetMethod("Add");

   //loop through the calling list:
   foreach (T item in list)
   {

      //convert each object of the list into T object 
      //by calling extension ToType<T>()
      //Add this object to newly created list:
      addMethod.Invoke(l, new object[] { item.ToType(t) });
   }

   //return List of T objects:
   return l;
}

The first row of the above code is rather interesting:

C#
var genericType = typeof(List<>).MakeGenericType(t);

When we call the MakeGenericType(t) function on typeof(List<>), it substitutes the type of List objects with the type T and returns a Type object representing the List of T objects.

After that, everything is very straightforward:

The activator creates an empty list of T objects. GetType().GetMethod("Add") returns the MethodInfo object which we will use to call method Add() of the newly created list.

Then, we loop through the original list, changing the original type of each element into T by calling our own extension ToType<T>() and finally adding this item into the list of type T. The returned result is the list of type T.

Let us update the procedure with this new extension:

C#
public static List<Client> GetClientsWithTotalOrders()
{
    //create DataContext object, use it, discard it:
    using (CodeProjectDataContext db = new GetDataContext())
    {
        //join Client with Order:
        var res = db.Clients
            .Join(
            db.Orders,
            o => o.ID,
            c => c.ClientID,
            (c, o) => new { c }
            )

            //group by Client
            .GroupBy(o => o.c)

            //define output object
            .Select(o => new { ID = o.Key.ID, AddressID = o.Key.AddressID, 
            Name = o.Key.Name, TotalOrders = o.Count() })
            
            //output to List of objects:
            .ToList()

            //apply extension  to covert into Client
            .ToNonAnonymousList(typeof(Client))
            ;

         //cast the output sequence to List of Clients and return:
         return (List<Client>)res;
    }
}

Calling ToNonAnonymousList(typeof(Client)) converts the List of anonymous type to a List of Client type.

History

  • Added on August 2, 2009
  • When I published this article, I received several comments asking why I converted the anonymous type by calling these extensions when we can just use the following syntax:

    C#
    .Select(o => new Client { ID = o.Key.ID, AddressID = o.Key.AddressID, 
       Name = o.Key.Name, TotalOrders = o.Count() })

    As you may notice, this statement creates a Client object rather than an anonymous object.

    If you research forums, you would noticed that many developers complain that this syntax does not work for many LINQ to SQL queries and it returns the following error:

    "Explicit construction of entity type 'xxxxxxxx' in query is not allowed"

    This error is due to a check added by Microsoft. "This check was added because it was supposed to be there from the beginning and was missing. Constructing entity instances manually as a projection pollutes the cache with potentially malformed objects, leading to confused programmers and lots of bug reports for us. In addition, it is ambiguous whether projected entities should be in the cache or change tracked at all. The usage pattern for entities is that they are created outside of queries and inserted into tables via the DataContext and then later retrieved via queries, never created by queries".

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
United States United States
I started as an electronic engineer and moved into programming in 1989, was fascinated by Internet and developed my first web application in 1997.
When Microsoft introduced C# I became its fan and am not disappointed by now.
As for the hobbies, except of course reading, I love skiing and horse riding.

Comments and Discussions

 
QuestionWhy do you need the type argument? Pin
Ger20014-Aug-09 6:22
Ger20014-Aug-09 6:22 
AnswerRe: Why do you need the type argument? Pin
Ger20014-Aug-09 6:24
Ger20014-Aug-09 6:24 
GeneralRe: Why do you need the type argument? Pin
Ger20014-Aug-09 6:26
Ger20014-Aug-09 6:26 
JokeRe: Why do you need the type argument? Pin
TobiasP5-Aug-09 3:24
TobiasP5-Aug-09 3:24 
AnswerRe: Why do you need the type argument? Pin
Job V3-Mar-10 5:09
Job V3-Mar-10 5:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.