In a ASP.NET Web API 2.0-project I want to access with the HTTP verbs GET
and POST
similar objects from the data model, all implementing the interface ITableRow
. So I have a base class offering default implementations of access to these model classes:
class ControllerBase
{
protected string whereAmI = "base controller";
public IHttpActionResult Get(int id)
{
Console.WriteLine(whereAmI + ": GET");
[... here is the default action to get the data object(s) ...]
}
public IHttpActionResult Post([FromBody] ITableRecord value)
{
Console.WriteLine(whereAmI + ": POST " + value.GetType().ToString());
[... here is the default action to create a new data object ...]
}
}
and a derived class that wants to use the ControllerBase
default implementation of Post()
:
public class ElementsController : ControllerBase
{
protected string whereAmI = "element controller";
}
Using the GET route works easy, but there is a problem calling the POST route:
The automatic call through the Web API 2.0 framework of the Post now calls the ElementsController.Post()
method, but can't create an element object since it only knows it has to build an ITableRecord
object as value - and since this is only an interface, the variable value
stays null
.
Now I can write in each of my derived classes a specific definition of Post()
, calling the ControllerBase.Post()
from there:
public override IHttpActionResult Post([FromBody] Element value) // this won't work since the argument types doesn't fit
{
return base.PostHelper(value); // so I have to call a helper function in base
}
My question is: Is there a better (say: more DRY) way to tell the derived class which specific type the argument of the Post()
method has to be, without ?
This looks like a job for generics. First, make your base class generic, and change the Post
method to use the generic type. For example:
// Note the type constraint too, you may not want this depending on your use case
class ControllerBase<T> where T : ITableRecord
{
public IHttpActionResult Post([FromBody] T value)
{
//...
}
}
And now your derived controller would look like this:
public class ElementsController : ControllerBase<Element>
{
//...
}