Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using Reflection to Create a Factory Class for Dynamic Method Invocation using Templates

0.00/5 (No votes)
5 Mar 2010 1  
How to call templates dynamically with Reflection

Introduction

My group is using Entity Framework and we are trying to decouple ourselves from our legacy framework which returns tightly bound data contracts to our customers. We use Extension Methods to convert entities into data contracts. This is actually more complicated than it sounds, because we also have a concept of a business entity which is embedded into our data contracts. Neither of them implements interfaces, so converting from one to the other and back requires a series of loops and logic switches.

Additionally, we use classes called repositories in our data access layer which call the Entity Framework and then map from Entity Framework entities to data contracts and business entities via Extension Methods.

Anyway, here is the problem:

  • Our entity repositories are tightly coupled to data contracts and entities, and only return data contracts.
  • Our data contracts and business entities are tightly coupled and don't return interfaces.
  • We already have a great deal of Extension Methods that we want to reuse.

Here is the proposal:

  • I want to create a factory in between entity repositories and the mapper classes so we can decouple our entity repositories from our data contracts and business entities.
  • I want to reuse the existing Extension Methods.
  • I want to write new mapper classes targeted at specific contracts which are not Extension Methods.
  • I want to account for overloaded methods.

I also want the factory method to be as generic and flexible as possible, I don't want the factory to know about mappers and entities and return types. I decided to use Reflection, but in a very optimized way, so that I can maintain good performance. If you want to know more about performance pitfalls of applications and Reflection, there is a very good article by Joel Pobar in the MSDN magazine; you can read it here.

Using the Code

Approach A

First, the factory method to reuse the data extensions. Since the legacy EF mapper class is a class of Extension Methods with an overloaded TODC() method, there is no way of passing in the method to invoke, so I have to check the input and output type of the method to see if it matches the input and output type of the contracts. Let's create the method signature of my factory class.

/// <summary> 
/// Factory class for mapping entity framework entities to their output types 
/// <summary> 
public class EntityFactory 
{   
    public OutputType Create<OutputType, InputType>(InputType entity, 
                      Type mapperTemplate) where OutputType : class 
    {}    
}

OK, fair enough; I have a generic output type, and input type, a mapper template type, and some constraints. Note: mapperTemplate is the static extension class with the Extension Methods that I want to reuse.

Using Reflection, I will find the methods in the mapper template class and then check the return type and the input parameters. I will need to use Reflection's MemberInfo to do this. As you can see below, once I find the method, I invoke it and return the result. This works, and is optimized, but not as optimized as it could be due to the fact that I am reusing an existing Extension Method class.

Note: Stay with me if this seems messy; it gets cleaner later on in the post.

public OutputType Create<OutputType, InputType>(
       InputType entity, Type mapperTemplate) where OutputType : class
{ 
   Type[] inputTypes = new Type[1] { typeof(InputType) }; 

   foreach (MemberInfo mi in mapperTemplate.GetMethods()) 
   { 
      if (((MethodInfo)mi).ReturnType == typeof(OutputType)) 
      { 
          MethodInfo method = 
                 mapperTemplate.GetMethod(((MethodInfo)mi).Name,inputTypes); 

          if (method != null) 
          { 
             return method.Invoke(null, new object[] { entity }) as OutputType; 
          } 
      } 
  } 

  return null; 
}

You'll notice that after I get the matching return type, I attempt to retrieve the method by name and by passing in the method parameter types. If the method is returned, then I invoke the method; otherwise, I try again. If you don't pass in the method parameter types, you may get back an overloaded method by the same name, and the attempt to invoke will cause Reflection to throw an Ambiguous Match Exception.

Here is how I call the method above from my unit tests:

  • DAL is the namespace for the Data Access Layer.
  • DC is the namespace for my Data Contracts.
List<MyItem> myItems = new List<MyItem>();
var results = this.repository.SearchResults(criteria);

foreach (var item in results) 
{
   myItems.Add(entityFactory.Create<DC::Item, DAL::ItemTypeTemplate>(
               results, typeof  (DAL.EFExtensionMappers))
   ); 
}

Approach B

Next, I want my factory method to use the new mapper template classes. Since this class will have unique method names, I can just pass the method name and the template class and call Invoke. Pretty simple, and very optimized.

public OutputType Create<OutputType, InputType>(
       InputType entity, Type mapperTemplate, string methodName) 
       where OutputType : class 
{ 
    Type[] inputTypes = new Type[1] { typeof(InputType) }; 
    MethodInfo mi = mapperTemplate.GetMethod(methodName, inputTypes); 
    return mi.Invoke(null, new object[] { entity }) as OutputType; 
}

Notice that I pass in the input parameter types again to avoid the Ambiguous Match Exception caused by the overloaded methods.

Here is how I call the method above from my unit tests:

  • DAL is the namespace for the Data Access Layer.
  • DC is the namespace for my Data Contracts.
List<MyItem> myItems = new List<MyItem>();
var results = this.repository.SearchResults(criteria); 

foreach (var item in results) 
{ 
    myItems.Add(entityFactory.Create<DC::Item, DAL::ItemTypeTemplate>(
                results, typeof(DAL.NewtemMappers), "GetItems") 
    ); 
}

Up to this point, I have only been able to pass to methods with one argument. Now, I want to pass to methods with multiple arguments. Here is how I handle this one:

public OutputType Create<OutputType>(Type[] inputTypes, 
     object[] arguments, Type mapperTemplate, string methodName) 
     where OutputType : class 
{ 
   MethodInfo mi = mapperTemplate.GetMethod(methodName, inputTypes); 
   return mi.Invoke(null, arguments) as OutputType; 
}

Finally, here is how I call this method:

List<MyItem> myItems = new List<MyItem>(); 
var args = new object[] { results, "test" }; 
Type[] parms = new Type[2] { typeof(DAL::AgreementTypeTemplate), typeof(String) }; 

foreach (var item in results) 
{ 
  myItems.Add(entityFactory.Create<DC:: Item >(parms, args, 
              typeof(DAL. ItemTypeTemplate), "GetItems") 
  ); 
}

I hope you found this type of dynamic factory helpful.

Read much more on my blog at http://caloia.wordpress.com.

Cheers!

History

  • 5th March, 2010: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here