0

I'm looking for an elegant way (a design pattern if such exists, not a library) to map two classes that share the same base class, without duplicating the code that maps the properties of the base class.

Let's say I have

public abstract class RequestBase
{
    public DateTime BornAt { get; set; }
}

public class RequestV1 : RequestBase
{
    public string Name { get; set; }
}

public class RequestV2 : RequestBase
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

and I want to

var record = this.MapFrom(someV2Request);

where

public PersonRecord MapFrom(RequestV1 request) => new PersonRecord
{
    Name = request.Name,
    BornAt = request.BornAt,
};

public PersonRecord MapFrom(RequestV2 request) => new PersonRecord
{
    Name = $"{request.FirstName} {request.LastName}",
    BornAt = request.BornAt,
};

but I don't want both methods to implement the mapping of the base class property BornAt independently.

There must be some elegant solution that I can't come up with.

4
  • the most elegant solution is the one where you dont need to do a mapping at all
    – Ewan
    Nov 4, 2022 at 13:33
  • Consider using AutoMapper or one of its similar friends; they let you write the code zero times. Nov 4, 2022 at 13:52
  • @Ewan not having the problem doesn't really sound like a solution to me ;) If I need to communicate with some external thing that I do not control, mapping is going to happen one way or another Nov 4, 2022 at 14:23
  • @PhilipKendall I'm sure it does, but it doesn't teach me how to solve problems in an elegant way. I do have to confess that I'm not much of an AutoMapper enthusiast Nov 4, 2022 at 14:25

1 Answer 1

5

I assume you don't want introduce a dependency from PersonRecord to the request classes or vice versa, and all the code is somewhere in your mapper class. Then the straightforward solution is

private InitCommonAttributes(PersonRecord person, RequestBase request)
{
    person.BornAt = request.BornAt;
}

public PersonRecord MapFrom(RequestV1 request)
{
    var person = new PersonRecord
    {
        Name = request.Name,
    };
    InitCommonAttributes(person,request);
    return person;
}

public PersonRecord MapFrom(RequestV2 request)
{
    var person = new PersonRecord
    {
        Name = $"{request.FirstName} {request.LastName}",
    };
    InitCommonAttributes(person,request);
    return person;
};

Extracting common code into reusable functions is not a "pattern", AFAIK.

If this is really just about one attribute and no more logic, I would consider to stay with the original solution. The extra clutter in the code above may not be worth to make this code DRY as long as the common logic here consist only in the assignment of one variable. If there is more common logic involved, however, it may be sensible.

4
  • Thank you so much. I was moving in the same direction, but for some reason it never really sat right with me. This is the variant that I'm going with for now: dotnetfiddle.net/17vtCt Nov 4, 2022 at 14:22
  • Found out that in my case, this solution wouldn't play nice with non-nullable reference types. In my new approach, the specific MapFrom methods lets the generic MapFrom method map from the baseclass, then invoke a Func that contains the specific mapping logic: dotnetfiddle.net/q82l4G Nov 4, 2022 at 15:32
  • (oh, these weren't questions, just concrete examples of what my final implementation looks like, for future reference. prolly by myself ;)) Nov 4, 2022 at 22:07
  • 1
    @JoepGeevers: alright, these code examples might be a good fit for Codereview.SE, I guess.
    – Doc Brown
    Nov 4, 2022 at 23:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.