Background
I recently came across the need to be able to assign values of one type to values of another type that I had no control over. A vendor had provided an assembly which exposed a proxy that accepted message types that had a vastly different structure to the types we used internally. A quick view of the assembly through Reflector revealed it to be very badly written (and I do mean horrible). I wrote this little utility to allow our software to map message types we used internally to message types exposed by the third party assembly. While I implemented this for my specific needs, I thought I might as well share it with the rest of the world in the event that somebody, somewhere might face a similar problem and find it useful.
Design Goals
I wanted a simple utility with a single call to be able to assign ("map") the values of one type to another. I wanted it to be both flexible and extensible. I had considered juggling with delegates and serialization and various other tricks with Generics before I came up with what, I think, is a simple solution. I didn't want to jump through too many hoops to get the mapping done, and wanted to have as simple a call as possible at the point where the mapper was needed. I decided to list the goals as:
- Flexible
- Extensible
- Simple
- DRY (i.e., Don't Repeat Yourself)
- Support for IoC (although not a pre-requisite)
In the end, I ended up with something like this in my client code:
Mapper mapper = new Mapper(provider);
TypeB typeB = mapper.Map<TypeB>(typeA);
How it Works
Under the hood, Mapper
uses a handful of interfaces and Generics.
IMappable
Very simple, empty interface. Allows a class to be the input to the mapping. This would be the type which you do have control over. It's an empty interface deliberately so that there are no conditions to satisfy in order to use it.
public interface IMappable {}
IMappingProvider
Provides the service contract for components which will provide the mapping implementation. The method expects a type argument to be supplied for its usage. This is the heart of the entire utility. It's important that the generic type is part of the method and not part of the interface declaration.
public interface IMappingProvider
{
T Map<T>(IMappable input);
}
ConcreteMappingProvider
Implements the specific mapping between IMappable
to one or more types. Note: This ConcreteMappingProvider
is given here only as a demonstration on very basic types. You'll notice that it implements the IMappingProvider
interface with the generic <T>
type expected as the result type. The interface implementation (public method) passes the work on to a private method which will return the concrete type (i.e., the type which you have no control over). It asks the private method to return the result as an object
; this is because casting directly from the concrete type to <T>
is illegal. Casting to <T>
from object
, however, is legal. Thus, you should have the expected type returned with no boxing overhead.
public class ConcreteMappingProvider : IMappingProvider
{
public T Map<T>(IMappable input)
{
object result = map(input as TypeA);
return (T)result;
}
private TypeB map(TypeA input)
{
TypeB result = new TypeB();
result.StringX = input.StringA;
result.intY = input.IntB;
result.StringZ = input.GuidC.ToString();
return result;
}
}
Mapper
Simple facade to call the specific implementation of IMappingProvider
. This the facade that you call as needed to return the specifically mapped type that you are after.
public class Mapper
{
public IMappingProvider MappingProvider { get; private set; }
public Mapper(IMappingProvider provider)
{
this.MappingProvider = provider;
}
public T Map<T>(IMappable input)
{
return MappingProvider.Map<T>(input);
}
}
Sample Types
These are the sample types used in this article. Notice that TypeB
does not inherit from IMappable
. This would be the type that you have no control over.
public class TypeA : IMappable
{
public string StringA { get; set; }
public int IntB { get; set; }
public Guid GuidC { get; set; }
}
public class TypeB
{
public string StringX { get; set; }
public int intY { get; set; }
public string StringZ { get; set; }
}
Dependency Injection
Although DI is not essential to using this utility, I made provision for it by separating the specific implementation from the facade. Using the DI container/framework of your choice, you can register/resolve the specific providers as you see fit. I use the Windsor container, so the download sample looks similar to:
static void Main(string[] args)
{
var container = new WindsorContainer();
container.Register(
Component
.For<IMappingProvider>()
.ImplementedBy<ConcreteMappingProvider>()
);
TypeA typeA = new TypeA()
{
StringA = "some string",
IntB = 123,
GuidC = Guid.NewGuid()
};
IMappingProvider provider = container.Resolve<IMappingProvider>();
Mapper mapper = new Mapper(provider);
TypeB typeB = mapper.Map<TypeB>(typeA);
Console.ReadLine();
}
Extending the Solution
To extend the solution to suit your needs, the least you'd have to do is write your own component which implements IMappingProvider
. While this provider could run into a few hundred lines of code (depending on what you need to map), that is just about all you'd need to do.
Conclusion
As mentioned above, this is a lightweight type mapper which is flexible, simple, and extensible. I hope it finds a happy home in your own code.
History
Initial submission.