2

The goal:

In one library (DAL), I have a bunch of classes... one of these is called "Product". In a second library (BLL), I have a corresponding bunch of classes... including its own "Product" class. The class definitions are not the same. Some of the properties have different names (ProductID vs. Id, for example). Classes in one library may have additional properties that don't exist in the other library. There are many such pairs of classes. I want to learn how to translate between the two classes in the best way.

Yes, I know that AutoMapper exists. I'm trying to learn how to use generics and extension methods.


I know that for each pair of classes, there are four methods that need to be implemented:

  1. A method that applies the values of the properties on a BLL.Product to a DAL.Product.
    • Alternatively... and preferrably, an extension method for DAL.Product that applies the values from a given BLL.Product.
  2. The same, but applying values from a DAL.Product to a BLL.Product.
  3. A method that instantiates a new DAL.Product and then applies the values of a given BLL.Product (using the method above).
    • Alternatively... and preferrable, an extension method for BLL.Product that returns a new DAL.Product.
  4. The same, but instantiating a new BLL.Product as a copy of a DAL.Product.

Without extension methods, I have this working code:

public abstract class Adapter<DomainType, PortalType> {
   protected abstract DomainType ToDomainType (PortalType EntityToConvert);
   protected abstract PortalType ToPortalType (DomainType EntityToConvert);
   protected abstract void ApplyValuesTo (PortalType _That, ref DomainType _This);
   protected abstract void ApplyValuesTo (DomainType _That, ref PortalType _This);
}

public class ProductAdapter : Adapter<BLL.Product, DAL.Product> {
   protected override BLL.Product ToDomainType (DAL.Product EntityToConvert) {
      BLL.Product ConvertedEntity = new BLL.Product ();
      ApplyValuesTo (EntityToConvert, ref ConvertedEntity);
      return ConvertedEntity;
   }

   protected override DAL.Product ToPortalType (BLL.Product EntityToConvert) {
      DAL.Product ConvertedEntity = new DAL.Product ();
      ApplyValuesTo (EntityToConvert, ref ConvertedEntity);
      return ConvertedEntity;
   }

   protected override void ApplyValuesTo (DAL.Product _That, ref BLL.Product _This) {
      _This.Id = _That.ProductId;
      _This.Label = _That.Label;
      // yada yada yada
   }

   protected override void ApplyValuesTo (BLL.Product _That, ref DAL.Product _This) {
      _This.ProductId = _That.Id;
      _This.Label = _That.Label;
      // yada yada yada
   }
}

I'm resigned to the fact that my ApplyValuesTo methods will be specific for each pair of classes, but my ToDomainType and ToPortalType methods are annoyingly similar.

protected override OneType ToOneType (AnotherType EntityToConvert) {
   OneType ConvertedEntity = new OneType (); // The only line that changes; different pairs of classes will be instantiating different types of objects.
   ApplyValuesTo (EntityToConvert, ref ConvertedEntity);
   return ConvertedEntity;
}

Surely there's a way to move this to a base class, but how? (Note: all of my classes would have similar constructors, though I haven't yet decided if they will be parameter-less constructors. And depending on the library, my classes may not have a common base... blame EntityFramework for that.)

For my ApplyValuesTo methods, I'm extremely unhappy with passing ref arguments.I would much prefer the syntax of an extension method. Something like:

public static class ProductAdapter {
   public static void GetValuesFrom (this DAL.Product _This, BLL.Product _That) {
      _This.ProductId = _That.Id;
      _This.Label = _That.Label;
      // yada yada yada
   }
}

// Usage
DALProductInstance.GetValuesFrom (BLLProductInstance);

That's fine and dandy, but it seems like this should be something defined in an abstract base class using generics.


So here's what I want. This is what I naively think should work.

public abstract static class Adapter<DomainType, PortalType> {
   public abstract static void GetValuesFrom (this DomainType _This, PortalType _That);
   public abstract static void GetValuesFrom (this PortalType _This, DomainType _That);

   public static PortalType Adapt (this DomainType _This) {
      PortalType ConvertedEntity = new PortalType ();
      ConvertedEntity.GetValuesFrom (_This);
      return ConvertedEntity;
   }

   public static DomainType Adapt (this PortalType _This) {
      DomainType ConvertedEntity = new DomainType ();
      ConvertedEntity.GetValuesFrom (_This);
      return ConvertedEntity;
   }
}

public static ProductAdapter : Adapter<BLL.Product, DAL.Product> {
   public override static void GetValuesFrom (this BLL.Product _This, DAL.Product _That) {
      _This.ProductId = _That.Id;
      // yada yada yada
   }

   public override static void GetValuesFrom (this DAL.Product _This, BLL.Product _That) {
      _This.Id = _That.ProductId;
      // yada yada yada
   }
}

There are some severe compilation problems with this. First, you can't have a static class with generics. So instead...

public static abstract class Adapter {
   public static PortalType Adapt<PortalType, DomainType> (this DomainType _This) {}
}

But... you can't define multiple generic types in a method without specifying all of those types through the method parameters. (This)

After some research, finding things like this and this, I get the gut feeling that I can get a lot closer using a fluent-like API. (Method chaining?) But I haven't figured out how to make that work, either.

What's the proper syntax for getting my naive solution to work?

Community
  • 1
  • 1
Jason
  • 349
  • 3
  • 9
  • 2
    TL;DR mate, if you want help, try shortening it.. do keep in mind that people do this without getting paid. – Eyal Perry Jun 02 '16 at 19:57
  • Just do a mapper class that either only maps properties that have the same name o add some other mechanism to pass in the mapping routes. The mapping can be done using reflection and add generics to avoid casting. – Cat_Clan Jun 02 '16 at 20:03
  • Your solution is not naive at all. Generically mapping between arbitrary types is not a trivial exercise. You _could_ move `ToPortalType` and `ToDomainType` to the generic base class if you constrain them to have a parameterless constructor. – D Stanley Jun 02 '16 at 20:09
  • Also keep in mind that extension methods and fluent interfaces are just syntactic sugar - they make method calls _look_ cleaner but add no functionality in and of themselves. – D Stanley Jun 02 '16 at 20:10
  • @DStanley Actually, they have even less functionality because they are more ambiguous. In other words, there are cases where the compiler would give an error for the extension method call syntax where it wouldn't for the static method call syntax. – Tom Blodget Jun 04 '16 at 23:46

0 Answers0