Map base class to derive class

csharp wallpaper

I’m facing a problem when I want to map a base class to a derive class. A derive class is a class that derives from a base class. In this post I explain my solution to this problem.

Scenario

I want to create a class to call stored procedures in my SQL Server. I’m using C# with .NET Core 3.1. With Entity Framework Core I want to call stored procedures in an existing database for what I created the model. All stored procedures return the same results but in some cases I have to do more activities and then every function has its own return type base on a base class, in the code below called BaseResponse. Sometimes, I want to add more details to a function response such as calculated fields or other details. For example, InvoiceResponse is a derive class of BaseResponse but with IsPaid method.

public class BaseResponse
{
    public int ErrorCode { get; set; }
    public string Message { get; set; }
}

public class InvoiceResponse : BaseResponse
{
    public bool IsPaid { get; set; }
}

Then, I have my BaseCall that it is responsible to call a stored procedure and return the BaseResponse.

public async Task<BaseResponse> BaseCall(string procedureName, string[] params)
{
    BaseResponse rtn = new BaseResponse();

    // call SQL Server stored procedure

    return rtn;
}

In another class I want to cast the BaseResponse with the derive class. For that, I thought I can cast the BaseResponse with the derive class but I was wrong. I’m doing that because the call to a stored procedure is always the same.

public async Task<InvoiceResponse> GetInvoice(int id)
{
    InvoiceResponse rtn = new InvoiceResponse();
    BaseResponse response = BaseCall("myprocedure", null);
    rtn = (InvoiceResponse)response;

    // do something else

    return rtn;
}

Unfortunately, I can’t cast (without getting error) expression returning/containing base class instance to any inheritor type if this instance not actually inherits it (and to check this there are type-testing operators in C#). Casting as the docs state is an attempt by compiler to perform an explicit conversion in runtime. Also, as you mentioned you can’t implement custom explicit conversion from or to base class.

My solution

What I’m you looking for (and trying to do) is called mapping and there are a lot of libraries for that, including but not limited to AutomapperMapster or ExpressMapper for example. I want to avoid these tools because they required a lot of configuration. My goal is simply to map a base class to a derive class.

So, I came up with my own implementation of a mapper to map a BaseClass with a derive class. This is the code.

/// <summary>
/// Class BaseClassConvert.
/// </summary>
public static class BaseClassConvert
{
    /// <summary>
    /// Maps to new object.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="sourceobject">The sourceobject.</param>
    /// <returns>T.</returns>
    /// <remarks>
    /// The target object is created on the fly and the target type
    /// must have a parameterless constructor (either compiler-generated or explicit)
    /// </remarks>
    public static T MapToNewObject<T>(this object sourceobject) where T : new()
    {
        // create an instance of the target class
        T targetobject = (T)Activator.CreateInstance(typeof(T));

        // map the source properties to the target object
        MapToExistingObject(sourceobject, targetobject);

        return targetobject;
    }

    /// <summary>
    /// Maps to existing object.
    /// </summary>
    /// <param name="sourceobject">The sourceobject.</param>
    /// <param name="targetobject">The targetobject.</param>
    /// <remarks>The target object is created beforehand and passed in</remarks>
    public static void MapToExistingObject(this object sourceobject, object targetobject)
    {
        // get the list of properties available in source class
        var sourceproperties = sourceobject.GetType().GetProperties().ToList();

        // loop through source object properties
        sourceproperties.ForEach(sourceproperty =>
        {
            var targetProp = targetobject.GetType().GetProperty(sourceproperty.Name);

            // check whether that property is present in target class and is writeable
            if (targetProp != null && targetProp.CanWrite)
            {
                // if present get the value and map it
                var value = sourceobject.GetType().GetProperty(sourceproperty.Name)
                                        .GetValue(sourceobject, null);
                targetobject.GetType().GetProperty(sourceproperty.Name).SetValue(targetobject, value, null);
            }
        });
    }
}

This code is working and I can use it like:

public async Task<InvoiceResponse> GetInvoice(int id)
{
    InvoiceResponse rtn = new InvoiceResponse();
    BaseResponse response = BaseCall("myprocedure", null);
    response.MapToExistingObject(rtn);

    // do something else

    return rtn;
}

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.