Click here to Skip to main content
15,116,653 members
Articles / Programming Languages / C#
Tip/Trick
Posted 29 Apr 2015

Tagged as

Stats

12.1K views
8 bookmarked

Creating Presentation Objects with PresentationMapper

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
29 Apr 2015CPOL7 min read
How to use PresentationMapper to generate presentation objects

Introduction

In most n-tiers applications, business entities and service or presentation entities are separated, and the developers must implement methods or constructors in order to create the presentation objects from the business data. This tip will explain how to work with PresentationMapper Nuget package to automate this task, and thus avoid potential mistakes.

Background

There certainly are other tools out there that already do objects mapping, but I believe the tool I propose here offers many benefits as listed below:

  • Attribute mapping. Types are mapped directly in the business entity code through attributes.
  • API mapping. Types are mapping by using a fluent API (V2.0). 
  • Speed. Reflections are often used in other tools to do the job, and we all know that this brings a negative impact on performance. PresentationMapper only uses reflection on static initialization to create dynamic func delegates. Once the initialization is done (once for the whole application lifetime), the delegates are used to build the objects. This is as fast as what a developer could do if he had to do it manually.
  • Use of a context to build the objects. Similarly to a unit of work, the context caches the objects that are created and reuse them instead of rebuilding them.
  • Possibility to specify the objects to include, just like what the "Include" does when using Entity Framework. This is useful when the objects are big and we don't want to pass all data to the presentation object.
  • A business object can map multiple presentation objects.
  • Collections can be passed to the presentation objects independently on the type of the collection used. For example, we can map a List<int> to an int[] or ISet<int>. However, the tools only support arrays and generic collections. Hashtable, or IList are not supported for example.
  • Support for open generic types (V2.0)
  • Support for polymorphism (V2.0)

Installation

To install the package, use Nuget package manager. You can find the package by typing "PresentationMapper" in the search textbox, or by typing "Install-Package PresentationMapper" in the console manager.

Class Samples

At first, we need two classes. One for the business side, and another one for the service (or presentation) side.

Let's make them simple:

C#
public class BusinessClass
{
    public int Key { get; set; }
    public string Description { get; set; }
}

public class PresentationClass
{
    public int Id { get; set; }
    public string Description { get; set; }
}

So, we want to map BusinessClass with PresentationClass. This can be done either by using attribute mapping, or API mapping.

Attribute mapping

This can be done by using the "Presentation" attribute.

Also, by default, the engine will try to map anything that has the same name, so the "Description" property will be mapped by default, but not "Key". To do so, we will use the "LinkTo" attribute.

So, now the BusinessClass will look like this:

C#
[Presentation(typeof(PresentationClass))]
public class BusinessClass
{
    [LinkTo("Id")]
    public int Key { get; set; }
    public string Description { get; set; }
}

That's it. The two classes are now properly mapped. Please note that if a member is not mapped to a business member or is not assignable, its value will be the default one.

Note that we're using auto-properties in our example, but the library recently supports fields as well.

Moreover, the use of "typeof()" in the Presentation attribute requires that the class libraries containing the presentation types must be referenced in the libraries containing the business types. Technically, it should not be a problem, but it's not pure in terms of design. To work around this, the Presentation attribute also includes another constructor that takes a string as a parameter. This string represents the name of the presentation object, which can be one of the forms:

  • <namespace>.<typename>, <assembly name>
    C#
    [Presentation("MyNamespace.PresentationClass, MyLibrary"))]
    public class MyClas
  • <namespace>.<typename>
    In this case, we omit the assembly. Therefore, the tool will search the type in all assemblies.
    C#
    [Presentation("MyNamespace.MyClass"))]
    public class MyClass
  • <typename>
    In this case, we omit the assembly and the namespace. Therefore, the tool will search the type in all assemblies, but will throw an exception if more than one type matches the name. However, it is common that business types and presentation types have the same name (and differ only by their namespace). The tool ignores the class where the Presentation attribute is put, so such declaration works very well:
    C#
    [Presentation("MyClass"))]
    public class MyClass

Initialization

During startup, the engine needs to discover all the attributes mapping that is written by the developers. To do so, it will look into the working directory for all types of files (DLL, EXE). This will work, but can be time consuming in a big solutions containing hundreds of files or assemblies. This is specially too bad if those files do not contain our business entities. The tool offers initialization methods that are optional but can help to start up a little bit faster (helpful during development phase).

The first one lets us specify a search pattern (used by the Directory.GetFiles() method). For example, if we know that all our business entities are stored into *.business.dll, we will use:

C#
MapContext.Initialize("*.business.dll");

Multiple patterns can be specified bu using the | character:

C#
MapContext.Initialize("*.dll|*.exe");

The second one lets us specify a list of assemblies directly.

C#
MapContext.Initialize(new [] {"myBusinessAssembly1.dll", "myBusinessAssembly2.dll"});

API mapping

Mapping with a fluent API is possible since verison 2.0 of PresentationMapper. Please refer to the version 2 article to learn about this feature.

Creating the Objects

Now, we can use the MapContext class to build our presentation object by passing an instance of a business class:

C#
var bClass = new BusinessClass() { Key = 55, Description = "desc" };
using (var ctx = new MapContext())
{
    var pClass = ctx.Create<PresentationClass>(bClass);
}

Let's now have another business class and its associated presentation class:

C#
[Presentation(typeof(Presentation2Class))]
 public class Business2Class
 {
     public string Name { get; set; }
 }

 public class Presentation2Class
 {
     public string Name { get; set; }
 }

Now our first business class and our first presentation class both should contain a reference to these new classes:

C#
[Presentation(typeof(PresentationClass))]
 public class BusinessClass
 {
     [LinkTo("Id")]
     public int Key { get; set; }
     public string Description { get; set; }
     public Business2Class AnObject {get; set; }
 }
C#
public class PresentationClass
{
    public int Id { get; set; }
    public string Description { get; set; }
    public Presentation2Class AnObject { get; set; }
}

If we call the Create<> method of the MapContext, we will also get the value of AnObject. And Presentation2Class also contains a reference to its parent (PresentationClass), this will be retrieved as well. Please be aware though that bidirectional references can cause some serialization issue, if we need to pass these objects as standard SOAP objects (WCF can solve this problem).

Collections

In the case of collections, the tool supports arrays, IList<T>, List<T>, ICollection<T>, Collection<T>, ISet<T>, and Set<T>. Any of these types can be on the business side or presentation side. The tool will convert the collection type if necessary. For example, an int[] in the business type can be converted to a List<int> in the presentation type.

Specifying Inclusions

Automatically retrieving all sub-objects is a nice feature, but in the case of big objects, this is not what we always want. There is a way to tell the tool to only get objects that we specify under Linq queries form.

If we don't any of the sub-objects, we can pass NULL as the second argument of the Create function.

C#
var pClass = ctx.Create<PresentationClass>(bClass, null);

If we want to include the "AnObject" sub-object, we will specify it this way:

C#
var pClass = ctx.Create<PresentationClass>(bClass, x=>x.AnObject);

The second parameter of the Create function is a params, so we can add other inclusions if we want.

If we need to include a property of collection, we can use the "Select" method of Linq:

C#
x=>x.MyCollection.Select(y=>y.Property)

In this case, it is not necessary to include MyCollection too, it will be done automatically.

Dealing with Multiple Presentation Objects

We can map as many presentation objects as we want to a single business entity. To do so, simply add a Presentation attribute for each of them.

If a single property of the business type must match different presentation property (coming from different presentation objects), we can prefix the property name with the name of the presentation type in the LinkTo attribute.

For example, if we have another presentation object:

C#
public class AnotherPresentationClass
{
    public int Id { get; set; }
    public string Description { get; set; }
    public Presentation2Class AnotherObject { get; set; }
}

We can bind it this way:

C#
[Presentation(typeof(PresentationClass))]
[Presentation(typeof(AnotherPresentationClass))]
public class BusinessClass
{
    [LinkTo("Id")]
    public int Key { get; set; }
    public string Description { get; set; }
    [LinkTo("AnotherPresentationClass.AnotherObject")]
    public Business2Class AnObject { get; set; }
}

That's it for the tutorial. I hope that this tool will be helpful for you.

If you have any suggestions, or bugs to fix, you can email me at minhl.huong@gmail.com.

 

Polymorphism and generic classes support

Since version 2.0, polymorphis and generic classes are supported.  Please refer to the version 2 article to learn about these features.

Related Tools

History

  • 29th April, 2015: Initial version
  • 19th May, 2015: Added new features (Fields, Presentation constructor)

License

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

Share

About the Author

HUONG Minh-Luong
Architect
France France
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 Pin
Zakariel0029-Apr-15 23:07
MemberZakariel0029-Apr-15 23:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.