Click here to Skip to main content
15,894,343 members
Articles / Programming Languages / C#
Tip/Trick

PresentationMapper 2.0

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
3 Jun 2015CPOL5 min read 6K   13   3  
This article describes the new features appearing in PresentationMapper 2.0

Introduction

This article describes new features that comes with version 2.0 of PresentationMapper as listed below:

- Mapping with Fluent API.
- Support of open generic classes.
- Support of polymorphism.

Full code can be downloaded at the top of the article.

Background

See my previous article (http://www.codeproject.com/Tips/986671/Creating-Presentation-Objects-with-PresentationMap), where I've described how to use PresentationMapper.

This article only describes the new features.

Mapping with Fluent API

Mapping with a fluent API offers 2 benefits:

- Your business classes don't directly refer the presentation classes. In terms of design, this approach is better, but is less straightforward than attributes mapping (this is comparable with Entity Framework Code First when the developper has to choose between attributes mapping and Fluent API).

- With API mapping, it is possible to map types and their members when they are not known at design time (by using Type for the class, and String for members name. 

To use API mapping, we first need to call :

MapContext.InitializeAPIMappings(initializer =>
{
   // Do something
});

This method takes an Action<IMapperAPIInitializer> as a parameter, and must be called only once during the lifetime of the application (otherwise an Exception will be thrown).

The action will let us specify the types to map by using the Map() method of the initializer.

For example, if we need to map PresentationClass to BusinessClass:

initializer.Map<PresentationClass2, BusinessClass2>()

The Map<,>() method returns an IMapperAPI object that lets us specify how members can be mapped (if the names aren't equal), by using the WithMember() method. This method also takes an IMapperAPIMember as a second parameter that lets us link the properties with the LinkTo() method.

For example, if we want to map the member "SomeBool" to "SomeBoolean", we would write:

initializer.Map<PresentationClass2, BusinessClass2>()
    .WithMember(x=>x.SomeBoolean, api=>api.LinkTo(x=>x.SomeBool));

The LinkTo() method is the only one possible for the moment, but other methods will be implemented later.

 The MapContext class also offers the possibility to map types by using the Type keyword, instead of generic arguments. In this case, the members are specified by giving their name as String.

For example, the code above is equivalent to the code below:

initializer.Map(typeof(PresentationClass2), typeof(BusinessClass2))
        .WithMember("SomeBoolean", api => api.LinkTo("SomeBool"));

Remarks:

It is possible to use API mapping along with attribute mapping. If there is an ambiguity between 2 members, the attribute mapping will take precedence.

Open generic classes

Support of generic classes was a major and required improvement to PresentationMapper. While it was previously possible to map closed generic types together, it forced the developper to do it for each specific argument type, which is not convenient. Now, it is possible to map open generic classes (for which one or argument types are not specified).

Firstly, here is how to map open generic classes with attribute mapping

Code for presentation class:

    public class PresentationClass3<T>
    {
        public int Id;
        public T AnotherGenericField;
    }

Code for business class:

[Presentation("PresentationClass3`1")]
 public class BusinessClass3<T>
 {
     public int Id;
     [LinkTo("AnotherGenericField")]
     public T GenericField;
 }

As we can see, it's very similar to normal class mapping. The only difference resides in the string representation of the type of PresentationClass3<>, which contains a `1 suffix. Actually, this is the standard representation of a generic type in .Net. The number contained in the suffix represents the number of generic arguments (1 in our case).

If we want to use API mapping for open generic type, we have no choice but to use the non generic form of the Map() method of the MapContext class.

For example:

initializer.Map(typeof(PresentationClass4<,,>), typeof(BusinessClass4<,>))
        .WithMember("propAbis", api => api.LinkTo("propA"));

Note that the number of generic arguments of both types don't have to match. We can also match a generic presentation class to a non generic business class.

Now, to create instances of presentation objects, it's pretty straightforward:

var businessClass3 = new BusinessClass3<int>(Id = 3, GenericField = 99);
var presentationClass3 = mapContext.Create<PresentationClass3<int>>(businessClass3);

The presentationClass3 object will have Id=3, and AnotherGenericField=99

Now, what about using a mapped type as a generic argument? 

Well, the generic argument will be automatically replaced by the mapped type as intended.

For example:

var businessclass3 = new BusinessClass3<BusinessClass2>(Id=1, GenericField=new BusinessClass2());
var presentationClass3 = mapContext.Create<PresentationClass3<PresentationClass2>>(businessClass3); 

The presentationClass3 object will have its AnotherGenericField member set to an instance of type PresentationClass2.

Polymorphism

Polymorphism is also a big improvement in PresentationMapper. The need is to be able to specify a base class as the generic parameter of the Create<> method of MapContext, and the returning result type must be of the most concrete declared mapped class. 

The addition of polymorphism support requires no additional setting from the developper. All he needs to do is to declare the classes that are part of the hierarchy like for usual classes.

For example, let's say we have this presentation base class, and one of its derived type

   public class PolymorphicPresentationBaseClass
    {
        public int Key;
        public string Name;
    }
    
    public class PolymorphicPresentationSubClass : PolymorphicPresentationBaseClass
    {
        public bool Abool;
    }

And the associated business classes:

    [Presentation(typeof(PolymorphicPresentationBaseClass))]
    public class PolymorphicBusinessBaseClass
    {
        public int Key;
        public string Name;
    }

    [Presentation(typeof(PolymorphicPresentationSubClass))]
    public class PolymorphicBusinessSubClass : PolymorphicBusinessBaseClass
    {
        public bool Abool;
    }

Here is how we create instances:

var subBusiness = new PolymorphicBusinessSubClass() { Key = 5, Name = "uuu", Abool = true };
var subPresentation = mapContext.Create<PolymorphicPresentationBaseClass>(subBusiness);

In this example, we have specified PolymorphicPresentationBaseClass as the returned type, but what is actually returned is an instance of PolymorphicPresentationSubClass. So, we can cast the returned result to the latter type, and access the Abool property.

The engine will always return the most concrete presentation type that has been declared, but it is not required to declare them all.

For example, if we had another subclass of PolymorphicBusinessSubClass, but this type is not mapped to a presentation type, any instance of this new subclass will return an instance of PolymorphicPresentationSubClass.

Polymorphism also works on collections. Let's say we have a collection of PolymorphicPresentationBaseClass.

The Create<> method will return the desired collection populated with instances matching the most concrete declared type. 

Polymorphic generic types

Now, this is fun. What happens if the generic argument of a generic type is a mapped polymorphic type, like PresentationClass3<PolymorphicPresentationBaseClass>.

Well, this won't be a problem. Every time the generic argument appears in the class, it will contain the most concrete type associated to the business value.

For example, if have this class

public class PolymorphicPresentationContainerClass
{
    public List<PolymorphicPresentationBaseClass> List;
    public PolymorphicPresentationBaseClass AnObject;
    public PresentationClass3<PolymorphicPresentationBaseClass> ContainerField;
}

GenericField will always be of type PresentationClass3<PolymorphicPresentationBaseClass> (in C#, a class<A> doesn't inherit from class<B> even if A inherits from B), but ContainerField.AnotherGenericField will be of type PolymorphicPresentationSubClass.

History

  • 3rd June, 2015: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
France France
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --