We are developing an internal NuGet package which contains a ServiceRes
and ServiceRes<T>
classes which can be used to wrap a response from the service layer into a common structure. This is the code that we have currently:
public class ServiceRes
{
protected internal ServiceRes() { }
public bool IsSuccess { get; set; }
public string? Message { get; set; }
public static ServiceRes Success()
{
return Success(message: null);
}
public static ServiceRes Success(string? message)
{
return new ServiceRes
{
IsSuccess = true,
Message = message
};
}
public static ServiceRes MapFrom<TPrev>(ServiceRes<TPrev> othr)
where TPrev : class
{
return new ServiceRes
{
IsSuccess = othr.IsSuccess,
Message = othr.Message
};
}
}
public class ServiceRes<T> : ServiceRes where T : class
{
protected internal ServiceRes() { }
public T? ResponseObj { get; set; }
public static ServiceRes<T> Success(T responseObj)
{
return new ServiceRes<T>
{
IsSuccess = true,
Message = null,
ResponseObj = responseObj
};
}
public static new ServiceRes<T> MapFrom<TPrev>(ServiceRes<TPrev> othr)
where TPrev : class
{
return MapFrom(othr, null);
}
public static ServiceRes<T> MapFrom<TPrev>(ServiceRes<TPrev> othr, Func<TPrev?, T?>? responseObjMapper)
where TPrev : class
{
return new ServiceRes<T>
{
IsSuccess = othr.IsSuccess,
Message = othr.Message,
ResponseObj = responseObjMapper?.Invoke(othr.ResponseObj)
};
}
}
We have a .NET Core web application project which references our private NuGet package. We can use it like this:
public class ServiceResTest
{
private class Obj1
{
public string? Name { get; set; }
}
private class Obj2
{
public string? Value { get; set; }
}
public void Test()
{
// base usage
var x1 = ServiceRes.Success();
var x2 = ServiceRes.Success(message: "User created successfully");
var x3 = ServiceRes<Obj1>.Success(new Obj1());
// mapping
var x4 = ServiceRes.MapFrom(x3); // ServiceResult<Obj1> -> ServiceResult
var x5 = ServiceRes<Obj2>.MapFrom(x3); // ServiceResult<Obj1> -> ServiceResult<Obj2> (ResponseObj = null)
var x6 = ServiceRes<Obj2>.MapFrom(x3, x => new Obj2() // ServiceResult<Obj1> -> ServiceResult<Obj2>
{
Value = $"{x?.Name}_value"
});
}
}
However, we have some advanced use-cases in our web application which require extending BaseService<T>
class. We would like to be able to map ServiceResult<T>
to ServiceResultExt<T>
, preferrably in the similar fashion than existing MapFrom
methods. Comments below indicate where the process fails.
public class ServiceResExt<T> : ServiceRes<T> where T : class
{
public int FailedLoginsCount { get; set; }
public static ServiceResExt<T> SuccessExt(T responseObj, int failedLoginsCount)
{
var baseRes = ServiceRes<T>.Success(responseObj);
// map ServiceRes<T> to ServiceResExt<T>
var x1 = ServiceResExt<T>.MapFrom(baseRes); // x1 is of type ServiceRes<T>
// we would like to assing parameter failedLoginsCount to property FailedLoginsCount
x1.FailedLoginsCount = failedLoginsCount;
return x1;
}
}
What would be the best approach to handle cases where we have a class which extends ServiceRes<T>
(ex. ServiceResExt<T>
) and we would like to utilize existing methods (ex. Success
), but map the response to ServiceResExt<T>
? Can the mapping logic for derived classes be placed inside ServiceRes<T>
class so it is common for all classes which inherit from it (we don’t want anyone manually instantiating ServiceRes
class, hence the protected internal
constructor)? Can ServiceRes<T>
return an instance of the derived class?