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:
- A method that applies the values of the properties on a
BLL.Product
to aDAL.Product
.- Alternatively... and preferrably, an extension method for
DAL.Product
that applies the values from a givenBLL.Product
.
- Alternatively... and preferrably, an extension method for
- The same, but applying values from a
DAL.Product
to aBLL.Product
. - A method that instantiates a new
DAL.Product
and then applies the values of a givenBLL.Product
(using the method above).- Alternatively... and preferrable, an extension method for
BLL.Product
that returns a newDAL.Product
.
- Alternatively... and preferrable, an extension method for
- The same, but instantiating a new
BLL.Product
as a copy of aDAL.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?