Mappers

A mapper is the primary object within the Mapping Application Block. A mapper represents a domain object and its public properties through its collection of Maps. The goal behind the MAB is to serialize domain objects to a single Data Transfer Object (DTO) type and deserialize from DTO's back to domain objects and all described through configuration. The DTO type used is the DataSet to match the DTO type used by the QAB and many other .NET components such as ADO.NET. A mapper primarily holds a domain object type name and a set of maps to match DTO column names with domain object public properties. The MAB uses "reflection" to create instances of Domain objects and to populate public fields and properties. Although the developer is pretty much free to do anything he likes with a domain object the one caveat to full flexibility and total independence from the MAB is that a domain object must contain a parameterless constructor. There can, of course, be any number of constructors to a domain object but the MAB needs to use a parameterless one.

The MAB supports two different mapper types: Basic Mapper and Super Mapper plus the Custom Mapper for user extensions to other mapper types. The MapperFactory is used to create instances of mapper objects based on the mapper name:

[C#]
// create a mapper with a configured name of "Mapper Name"
IMapper mapper = MapperFactory.CreateMapper("Mapper Name");

Once you have a mapper object you can map to and from a domain object with the ToDomainObject<T>() and FromDomainObject() methods. If the DTO contains multiple rows or you wish to send multiple objects back to data store then you can also use the collection methods: ToDomainObjectCollection<T>() and FromDomainObjectCollection(). The "To" methods take a DTO object (DataSet) as a parameter and the "From" methods take a domain object as a parameter. The "To" methods always return strongly typed domain objects whereas the "From" methods return a DataSet:

[C#]
// create a mapper with a configured name of "MyObject Mapper"
IMapper mapper = MapperFactory.CreateMapper("MyObject Mapper");

// get a MyObject object from a DataSet. If the DataSet contains more than one row then only the first row will be read
DataSet objectData1 = GetObjectData(); // some method that returns datasets
MyObject myObject = mapper.ToDomainObject<MyObject>(objectData1);

// now get a collection of MyObject objects from a DataSet.
DataSet objectData2 = GetObjectData(); // some method that returns datasets
Collection<MyObject> myObjects = mapper.ToDomainObjectCollection<MyObject>(objectData2);

The "From" methods work in the reverse taking a domain object and serializing to a DataSet:

[C#]
// create a mapper with a configured name of "MyObject Mapper"
IMapper mapper = MapperFactory.CreateMapper("MyObject Mapper");

// get a DataSet from a MyObject object.
MyObject myObject = new MyObject();
myObject.Id = Guid.NewGuid();
myObject.Name = "Fred";
DataSet dataSet = mapper.FromDomainObject(myObject);

// now get a DataSet from a collection of MyObject objects.
Collection<MyObject> myObjectCollection = new Collection<MyObject>();
MyObject myObject1 = new MyObject();
MyObject myObject2 = new MyObject();
myObjectCollection.Add(myObject1);
myObjectCollection.Add(myObject2);
DataSet dataSet = mapper.FromDomainObjectCollection(myObjectCollection);

One of the primary goals of the MAB was to complement the QAB to provide an Enterprise Library ORM tool. So the MAB and the QAB work seamlessly together. The following example shows how an object can be mapped from a DataSet derived from the QAB:

[C#]
// get a domain object from a QAB query
IDictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("Id", 27);
MyObject myObject = MapperFactory.CreateMapper("MyObject Mapper").ToDomainObject<MyObject>(QueryFactory.CreateQuery("Read MyObject By Id").ExecuteForRead(parameters));

The following example shows how an object can be saved back to data storage via the QAB.

[C#]
// save a domain object back to data storage via a QAB query
MyObject myObject = new MyObject();
myObject.Id = Guid.NewGuid();
myObject.Name = "Fred";
QueryFactory.CreateQuery("Create MyObject").ExecuteForWrite(MapperFactory.CreateMapper("MyObject Mapper").FromDomainObject(myObject));

Basic Mappers

Basic mappers map one data storage entity to one domain object. The basic mapper holds a single domain object type name and an optional table name. Reflection is used to create instances of the domain object using its parameterless constructor and then loading data into its public fields and properties from the DTO. Because we use a DataSet as the DTO it is possible to have multiple tables in the DataSet so the mapper can take a table name to define which table we wish to map objects from or to.

Super Mappers

Super Mappers map one data storage entity to multiple super types based on an inherited base class and a column in the data store dedicated to describing the super type for the record. A Super Mapper can hold a collection of SuperType objects that map a super type code to a super type name. Say, we had a base class called Animal and three super types called Cat, Dog and Horse and our column dedicated to holding a representative code holds the values C, D and H accordingly then we would configure three SuperType objects in our collection to match each of these codes to the actual super type names. When creating domain objects from the DTO the super mapper looks for the code value in the designated column and matches the code with its collection of SuperType objects extracting the super type name from the matched entry and using reflection to create the appropriate domain object super type instance. The data store has to hold a column for the super set of all public fields and properties from all of the super types. When the mapping process is being performed the public fields and properties are matched with columns from the DTO and any extra columns are ignored.

The SuperType collection is optional and if left blank the assumption is that instead of containing a code value the designated type column contains the actual super type name. Of course type names are rather long so, unless a rules engine model is being implemented, codes are going to be much more efficient on storage space.

The following is an example of getting a collection of super type objects based on the Animal abstract base class. Note how we have to use the base class even though the actual instances will be super types:

[C#]
// get a collection domain object super types from a QAB query
Collection<Animal> myAnimals = MapperFactory.CreateMapper("Animal Mapper").ToDomainObjectCollection<Animal>(QueryFactory.CreateQuery("Read Animals").ExecuteForRead());

The myAnimals collection will now contain instances of Cat, Dog and Horse.

Custom Mappers

It is quite likely that a situation could arise where a mapping convention is required that is not supported. An example might include the mapping of a single domain object from multiple tables etc. For this you can create a custom mapper by inheriting from MapperBase which in turn implements the IMapper interface. Through the Attributes property you will need a mechanism for linking maps. For more details refer to the Extending the Mapping Application Block page.

Last edited Jun 15, 2010 at 9:13 PM by ewdev, version 10

Comments

No comments yet.