.NET - ADO.NET Entity Framework : Metadata extension methods
- Date:
- Author: Stefan Cruysberghs
In addition to my article about querying the metadata of the ADO.NET Entity Framework I have been implementing several extension methods that utilize this metadata. The following extension methods are especially useful when building your own framework around the Entity Framework, which is really needed when you want to use the Entity Framework in combination with (WCF) services. I really hope Microsoft will make some significant improvements in Entity Framework version 2 to avoid all the plumbing, but that's another story.
GetEntitySetName() and GetEntityTypeName()
I started creating 4 overloaded GetEntitySetName() and GetEntityTypeName() extension methods for the ObjectQuery, EntityObject and EntityCollection<TEntity> classes. These methods use reflection and the Entity Framework metadata services to return a string with the name of the entity type or the entity set.
Example 1
var employees = context.Employees;
var emp = employees.First();
// -> Employees (EmployeeSet, Categories, ...)
Console.WriteLine(employees.GetEntitySetName(context));
// -> Employee (Category, ...)
Console.WriteLine(employees.GetEntityTypeName());
// -> Employees (EmployeeSet, Categories, ...)
Console.WriteLine(emp.GetEntitySetName(context));
// -> Employee (Category, ...)
Console.WriteLine(emp.GetEntityTypeName());
These methods can come in handy when you call the AttachTo() or the AddObject() methods from the ObjectContext. By calling these extension methods you can avoid having hard coded string names in your source code.
Example 2
context.AttachTo("Employees", emp);
context.AddObject("Employees", emp);
context.AttachTo(emp.GetEntitySetName(context), emp);
context.AddObject(emp.GetEntitySetName(context), emp);
Be careful if you want to use these extension methods to solve the problem of the hard coded strings in the Include() method. The name of the NavigationProperty does not have to be the same as the name of an EntitySet! A possible solution is to generate constants or an enumeration in a pre-build event. I haven't tried this yet but take a look at example 7 of my previous article.
Example 3
// OK, but we do not like hard coded strings
var employeesWithOrders = context.Employees.Include("Orders");
// Works in this case but is NOK as a general solution
var employeesWithOrders2 = context.Employees.Include(context.Orders.GetEntityTypeName());
// Works in this case but is NOK as a general solution
var employeesWithOrders3 = context.Employees.Include(new Employee().Orders.GetEntityTypeName());
// EmployeeOrders is a constant, OK
var employeesWithOrders4 = context.Employees.Include(EmployeeOrders);
A much better solution to remove the hard coded strings is the generic Include<T>() extension method that has been implemented by Rudi Breedenraedt. Check out his blog post for this really nice solution.
Furthermore I created a generic GetEntitySet<T>() method for the EntityObject class. It returns an ObjectQuery that has been created by composing an Entity SQL SELECT query with the EntitySet name. For example: you have one instance of a (detached) Employee class. Then you can call the GetEntitySet<T>() method to get the full Employees collection.
Example 4
var employees = context.Employees;
var emp = employees.First();
var allEmployees = emp.GetEntitySet(context);
Here is the class diagram and the full source of these extension methods. Notice that this source is quite easy to understand.
public static partial class EntityFrameworkExtensionMethods
{
public static string GetEntitySetName(this ObjectQuery objectQuery, ObjectContext context)
{
string entityTypeName = GetEntityTypeName(objectQuery);
return GetEntitySetName(entityTypeName, context);
}
public static string GetEntityTypeName(this ObjectQuery objectQuery)
{
return objectQuery.GetResultType().EdmType.Name;
}
ToMetadata()
As you have noticed in my previous article, the metadata services of the Entity Framework are very powerful but also extensive and quite complex. Besides, a lot of typecasting is required. Because of this I wanted to create a simpler hierarchical structure which only holds the basic metadata information. This metadata can be handy when building dynamic user interfaces or when implementing generic validation routines. Sometimes you want a list of all properties of a given object. Of course this can be done with reflection but by querying the metadata of the conceptual model you can retrieve a lot more interesting information about an entity such as it's keymembers, the (not) nullable properties, ...
So I started creating Metadata, MetaProperty, MetaType and MetaNavigationProperty classes and a ToMetadata() extension method that returns a Metadata object. This metadata object is filled by executing several queries on the metadata of your Entity Data Model. I have demonstrated this in my previous article.
The following class diagram shows which metadata is included in a Metadata object.
The following examples will give you a better idea of the possible results when calling the ToMetadata() method:
Example 5
var query = context.Employees.ToMetadata();
Example 6
var employees = from e in context.Employees
select new
{
e.EmployeeID,
Name = new
{
e.FirstName,
e.LastName
},
e.Orders
};
var query = (employees as ObjectQuery).ToMetadata();
Where and when would you call this ToMetadata() method? It could fore example be called in your service layer before serializing your entities. Together with your entities you could send this Metadata object, which is serializable, to the consumer (WinForms/WPF/Silverlight client). The consumer can use it to build dynamic user interfaces.
This metadata can also be handy when creating validation methods because you know which properties are not nullable, so they are required and should have a value. Or you could check which properties are keymembers and guids. These keymember properties could be filled automatically when inserting new entities.
I hope that this small article has shown how you can simplify some things by using the Entity Framework metadata. My ToMetadata() extension method holds a lot of code and that is why I didn't show everything in this article. The full sources and a help file of my ScipBe.Common.EntityFramework assembly can be downloaded at the components section of my website. Check it out to see how it works.