Skip to content
JulianR edited this page Mar 18, 2012 · 36 revisions

Overview

News

  • (18-3-2012) Released 1.3.0. Added many features, fixed some bugs :)

Download

ThisMember can be installed as a package from NuGet:

http://nuget.org/List/Packages/ThisMember

Pages

ThisMember

ThisMember is a library to make mapping of members on types that have something in common, easy and fast. Its aims are:

  • Convention over configuration. If you don't specify something, ThisMember will make a best effort attempt.
  • But if you do want or need to configure something, ThisMember should give you the possibility to.
  • It should be fast. It's allowed some warm-up time, but after that every mapping call should ideally be not significantly different from creating your mappings manually.
  • Easy API. Every operation should have a generic, strongly typed variant, as well as a reflection based one.

Essentially, it should try to let you have your cake and let you eat most of it too.

Simplest example

var mapper = new MemberMapper();

var result = mapper.Map<SourceType, DestinationType>(source);

Example with a custom mapping

var mapper = new MemberMapper();

mapper.CreateMap<Customer, CustomerDto>(source => new CustomerDto
{
  FullName = source.FirstName + " " + source.LastName,
  TotalOrderAmount = source.Orders.Sum(o => o.Amount)
});

Use cases

Productivity

There are a lot of scenarios in which mapping of one type, to another similar type is needed. You may receive a view model or a data-transfer-object (DTO) of for example a user, that you wish to update and store in the database. But your data layer of course uses a data model and you would need to make the translation, or mapping, of the view model to this data model yourself, through tedious lines of assigning properties even though most properties are exactly the same on both sides. Or perhaps it happens the other way around: you have a data model that you retrieved from a database and wish to turn this into a view model.

ThisMember allows you to easily do that, it's capable of generating mappings between two types on its own, while allowing you to specify your own customizations to them as well.

Verifiability

By default ThisMember is very permissive and won't complain if there's a member on the destination type which does not have a corresponding mapping from the source type. And in a lot of cases, you may not care about that or want to be bothered with meaningless exceptions of non-matching members. However, it often to makes sense to verify that you didn't forget to set a property, which you can't do when you make your mappings manually, other than by writing a lot of code to verify this, which in turn also needs to be maintained (which you can also forget).

With ThisMember, you can opt in to be warned if a destination member is not being mapped properly. Maybe you added a column to your database and a property to your data model recently, but forget to add it to the view model. Perhaps you did add it to the view model, but forgot to actually set the property somewhere. Or maybe you actually don't want the property to be there on your view model, ThisMember will force you to be explicit about your intentions.

In all those cases, if you turned on the option, ThisMember will throw an exception.

You won't have to wait until your application is running in a production environment though, you can just write a unit test that verifies the mappings that you care about to be correct.

Utility

ThisMember allows you to easily make a deep clone of any object.

Change History

1.3.0

  • Fixed a bug where automatic conversion to and from decimal didn't work.
  • Fixed a bug where automatic conversion of nullable value types didn't work.
  • Fixed a bug where a custom mapping was not respected when mapping a collection of the type that you have a custom mapping for.
  • Added support for Conversions.
  • Added an option to display Debug Information of your maps.
  • Added the ability to automatically Flatten Object Hierarchies.
  • Added the ability of dictionary-to-list (using the values of the dictionary) and dictionary-to-dictionary mappings. Temporary limitation is that the 'contents' of the dictionaries must be the same type.
  • Changed behavior of ThisMember with regards to reference equality. Previously, it was inconsistent, with ThisMember sometimes making a clone when it could simply assign members to each other without any additional mapping. The current behavior is that ThisMember never makes a clone/mapping if it doesn't have to. It goes for the simplest, fastest and most correct mapping without guaranteeing anything about reference equality.

1.2.0

  • Breaking change on the signature when specifying mapping options. Added a mapping depth parameter.

  • Added the ability to remap properties inside the mapping options. This allows you to define your own conventions. An example of this would be:

      mapper.CreateMap<Source, Destination>(
      options: (source, dest, options, depth) =>
      {
        if (source != null && source.Name.EndsWith("ID"))
        {
          var sourceType = source.DeclaringType;
          var destType = dest.DeclaringType;
    
          options.MapProperty(sourceType.GetProperty(source.Name.Replace("ID")), dest);
        }
      });
    

Instead of using the properties ending in "ID" this looks for a similarly named property without the "ID" to map from.

  • Fixed bug with .HasValue check on nullables with projections and the Entity Framework.
  • Added ToDictionary to projectable interfaces. This was previous erroneously missing.
  • Added option to projections on whether or not to consider collection types, with regards to problems when such a mapping was to be translated to SQL.
  • Added option to specify a maximum mapping depth in the type hierarchy.
  • Moved the MaxCloneDepth option to mapper.Options.Cloning.MaxCloneDepth.

1.1.0

  • ThisMember no longer tries to access private property setters, but will indicate if so desired that the setter cannot be used because it is private.
  • Explicit and implicit conversions are supported. Either through overloading the explicit and implicit operators on user defined types, or the system defined conversions between the primitive types such as int to long and double to float.
  • If the destination type has or is a collection type which is not null then ThisMember will preserve the existing contents of that collection. The items from the corresponding source are only added, but the existing items will not be overwritten. Can be configured through Options.Conventions.PreserveDestinationListContents = true/false. This is not supported for array types on the destination type.

1.0.2

  • Fixed a bug with nullable value types used in a projection.

1.0.1

  • Fixed a bug that caused an exception when using projections in some circumstances.
  • Expanded IProjectable<T> API, adding Project and ToDictionary.

1.0.0

Bumped the version number to 1.0.0 to indicate that I consider it a stable library, meaning that I will try to limit breaking changes to it.

  • Added the ability to pass in parameters of any type to be used in a custom mapping, as part of the mapping process.
  • Improved thread safety.
  • Improved cloning: prevented maps of reasonably complex types with many navigation properties to become ridiculously large (10k+ LoC) by adding a configurable maximum recursion depth to deep cloning (mapper.Options.Conventions.MaxCloneDepth).
  • Fixed a bug with recursion where a property being skipped on a type because of a recursive relationship, would cause it to be skipped in a different situation as well where there was no danger of infinite recursion.

0.8.5

  • Fixed bug where a constructor was expected on value types like decimal when mapping from a source object to a destination object of the same type.

0.8.3

  • Added Page method to ICollectionProjectable, for paged results.

0.8.2

  • Fixed a bug where AsCollectionProjectable returned IOptionalProjectable<T>.

0.8.1

  • Added CreateProjection methods to IMemberMapper.
  • Fixed bug where an exception occurred during map-creation because two incompatible types were being assigned to each other.

0.8.0

  • Added first version of Project API to IMemberMapper. This returns a mapping as an expression, for use as the projections in for example LINQ-to-Entity queries (so for example query.Select(mapper.Project<Source,Destination>())). Supported right now are simple member-to-member mappings (including collections) and most custom mappings.
  • Added several varying IProjectable interfaces, that allow you to specify a projection on the result of a sequence (can be anything from an array to a database query) without allowing you to alter the sequence. Useful for bridging some awkwardness between a data access layer and a service layer.

0.7.6

  • Fixed a bug where making a deep clone of an object did in fact not.
  • Fixed a NullReferenceException that happened when you explicitly passed in a null destination object.
  • Fixed a bug where members of type ICollection<T> would cause an exception during the mapping procress.

Next Version

  • Automatic conversions of primitive types ** Implemented in 1.2.0 **
  • Parameters in your maps that can be used in your custom mappings. Implemented in 1.0.0

Future Versions

  • More advanced conventions, like automatically flattening object hierarchies during mapping (so: mapping source.Address.Street to destination.AddressStreet. ** Implemented in 1.3.0 **
  • More advanced custom mappings, allowing custom mappings of arbitrary depth rather than just the flat custom mappings.
  • Safe mapping of recursive relationships