Reputation: 3311
So I have a generic base controller implementing CRUD, which derives from APIController
.
public class GenericController<TEntity> : ApiController where TEntity : class {
private readonly GenericModel<TEntity> _model;
public IModel Model {
get { return _model; }
}
public GenericController(IGenericModel<TEntity> model) {
_model = (GenericModel<TEntity>)model;
}
[HttpPost, Route]
public IHttpActionResult Post(TEntity entity) {
TEntity newEntity = _model.Create(entity);
String urlLink = $"{Request.RequestUri}{RequestContext.VirtualPathRoot}{((IGenericEntity)newEntity).ID}";
return Created(urlLink, newEntity);
}
[HttpGet, Route]
public IHttpActionResult GetList() {
return Ok(_model.ReadList());
}
[HttpGet, Route("{ID:int}")]
public IHttpActionResult Get(Int64 ID) {
return Ok(_model.Read(ID));
}
[HttpPut, Route]
public IHttpActionResult Put(TEntity entity) {
_model.Update(entity);
return Ok();
}
[HttpDelete, Route("{ID:int}")]
public IHttpActionResult Delete(Int64 ID) {
_model.Delete(ID);
return Ok();
}
}
And have allowed controller inheritance via a CustomDirectRouteProvider
as such:
config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());
Where my CustomDirectRouteProvider is:
public class CustomDirectRouteProvider : DefaultDirectRouteProvider {
protected override IReadOnlyList<IDirectRouteFactory> GetActionRouteFactories(HttpActionDescriptor actionDescriptor) {
return actionDescriptor.GetCustomAttributes<IDirectRouteFactory> (inherit: true);
}
}
And this all works beautifully, until I want to override methods like POST. As an example:
[RoutePrefix(@"api/Test")]
public class DerivedController : GenericController<ConcreteEntity> {
public DerivedController (IGenericModel<ConcreteEntity> model) : base(model) {
}
[HttpPost, Route]
public new IHttpActionResult Post(ConcreteEntity entity) {
//New Post Functionality Here
}
}
At which point I get the error:
"Multiple actions were found that match the request"
So my question is, how can I override the route itself? I basically want my new POST to take precedence over the generic one. I thought by virtue of the generic route/method being hidden that this would just work, but alas I was incorrect in my thinking.
Upvotes: 1
Views: 2390
Reputation: 247018
Was able to recreate your issue, and through a process of trial and error was able to get it to work by making GenericController
an abstract class
public abstract class GenericController<TEntity> : ApiController where TEntity : class {
[HttpPost, Route]
public virtual IHttpActionResult Post(TEntity entity) {
TEntity newEntity = _model.Create(entity);
String urlLink = $"{Request.RequestUri}{RequestContext.VirtualPathRoot}{((IGenericEntity)newEntity).ID}";
return Created(urlLink, newEntity);
}
}
and removing the attributes on the inherited action of the DerivedController
as they matched the inherited class
[RoutePrefix(@"api/Test")]
public class DerivedController : GenericController<ConcreteEntity> {
public DerivedController (IGenericModel<ConcreteEntity> model) : base(model) {
}
public override IHttpActionResult Post(ConcreteEntity entity) {
//New Post Functionality Here
}
}
Upvotes: 4