0

I want to create a class to call stored procedures in my SQL Server. I'm using C# with .NET Core 3.1. 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.

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.

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

    // do something else

    return rtn;
}

I saw other two posts (Convert base class to derived class and this one) and I understood I can't cast in the way I wanted. Then I was my extension from that

/// <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;
}

My questions are:

  • is there a more efficient way to cast the base class with a derive class in .NET Core?
  • is this the best practice for casting?
  • any other guide lines?
  • this procedure is using Reflection. In performance point of view, is it the right and cheapest way to implement this cast?
Enrico
  • 3,592
  • 6
  • 45
  • 102
  • I'm confused at the premise. If the `BaseCall` does not know about derived types (which it doesn't appear to be), then it cannot even pass along information from the stored procedure that would allow to construct a more derived type later. And if it does know about them, then it can construct them right away. And, if the additional information is not required to construct a derived type, then you can simply have a constructor on all the derived types that accepts a base type instance and copies from it. – GSerg Jun 07 '20 at 08:42
  • It also may be [the case](https://meta.stackexchange.com/q/66377/147640) that you don't want to come up with your own classes for database responses and use an ORM framework such as Entity Framework. – GSerg Jun 07 '20 at 08:45
  • I don't get your comment. The `BaseCall` is a class that calls the stored procedures and receives the result from them. Who is calling the `BaseClass` has its own return type based on the `BaseResponse` plus some other fields. I don't want to use `AutoMapper` (for example) to map each field from the `BaseResponse` to the derive class. – Enrico Jun 07 '20 at 08:49
  • I'm using `Entity Framework` to call the stored procedures and get the result. – Enrico Jun 07 '20 at 08:51
  • 1
    `Who is calling the BaseClass has its own return type based on the BaseResponse plus some other fields` - how is that caller going to get these other fields if `BaseResponse` does not return them from the database? – GSerg Jun 07 '20 at 08:52
  • I gave some context to explain my specific problem. The real question is there is a simple way to cast a `BaseResponse` with a derive class? – Enrico Jun 07 '20 at 08:52
  • Can you explain why you don't change `BaseCall` to return `InvoiceResponse` instead? – mjwills Jun 07 '20 at 08:56
  • Here a post for future reference https://www.puresourcecode.com/dotnet/csharp/map-base-class-to-derive-class/ – Enrico Jun 10 '20 at 06:46

1 Answers1

0

You 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.

What are you looking for (and trying to do) is called mapping and there are a lot of libraries for that, including but not limited to Automapper, Mapster or ExpressMapper for example.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • I have an error when I cast `BaseResponse` with a derive class. I'm update the post. – Enrico Jun 07 '20 at 08:57
  • @Enrico You can never cast a base class to a derived class. What you are doing is not even called casting, it's creating an unrelated instance and filling it with data. That is, you have developed your own automapper - which [you said](https://stackoverflow.com/questions/62242853/cast-base-class-to-derive-class-in-net-core#comment110081587_62242853) you don't want to use, even though you are using it. – GSerg Jun 07 '20 at 09:01