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.
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:
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:
public static List<Client> GetClientsWithTotalOrders()
{
using (CodeProjectDataContext db = new GetDataContext())
{
var res = db.Clients
.Join(
db.Orders,
o => o.ID,
c => c.ClientID,
(c, o) => new { c }
)
.GroupBy(o => o.c)
.Select(o => new { ID = o.Key.ID, AddressID = o.Key.AddressID,
Name = o.Key.Name, TotalOrders = o.Count() })
.ToList()
;
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:
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:
public static object ToType<T>(this object obj, T type)
{
var tmp = Activator.CreateInstance(Type.GetType(type.ToString()));
foreach (PropertyInfo pi in obj.GetType().GetProperties()
{
try
{
tmp.GetType().GetProperty(pi.Name).SetValue(tmp,
pi.GetValue(obj, null), null)
}
catch { }
}
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:
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:
public static object ToNonAnonymousList<T>(this List<T> list, Type t)
{
var genericType = typeof(List<>).MakeGenericType(t);
var l = Activator.CreateInstance(genericType);
MethodInfo addMethod = l.GetType().GetMethod("Add");
foreach (T item in list)
{
addMethod.Invoke(l, new object[] { item.ToType(t) });
}
return l;
}
The first row of the above code is rather interesting:
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:
public static List<Client> GetClientsWithTotalOrders()
{
using (CodeProjectDataContext db = new GetDataContext())
{
var res = db.Clients
.Join(
db.Orders,
o => o.ID,
c => c.ClientID,
(c, o) => new { c }
)
.GroupBy(o => o.c)
.Select(o => new { ID = o.Key.ID, AddressID = o.Key.AddressID,
Name = o.Key.Name, TotalOrders = o.Count() })
.ToList()
.ToNonAnonymousList(typeof(Client))
;
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:
.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".