Correctly override the generic BaseController - c#

I have the generic BaseController like this:
public class BaseController<T> : Controller where T : BaseEntity
{
protected readonly IRepository _repository;
public BaseController(IRepository repository)
{
_repository = repository;
}
// POST: TController/Create
[HttpPost]
[ValidateAntiForgeryToken]
public virtual async Task<IActionResult> Create(T item)
{
try
{
if (ModelState.IsValid)
{
await _repository.AddAsync(item);
}
return RedirectToAction(nameof(Index));
}
catch
{
return PartialView();
}
}
Do I correctly override this action in the derived controller class
public class PaysController : BaseController<Pays>
{
public PaysController(IRepository repository): base(repository) { }
// POST: Pays/Create
[HttpPost]
[ValidateAntiForgeryToken]
public override async Task<IActionResult> Create([Bind("IDPays,Code,Nom")] Pays pays)
{
return await base.Create(pays);
}
Especially, should I reuse the method attributes(like ValidateAntiForgeryToken), and will the binding Bind work in that case?

Method attributes do not need to be reused on the overriden method:
var attributes = typeof(PaysController).GetMethod("Create").GetCustomAttributes(false);
Debug.Assert(attributes.Any(x => x.GetType() == typeof(HttpPostAttribute)));
Debug.Assert(attributes.Any(x => x.GetType() == typeof(ValidateAntiForgeryTokenAttribute)));
The binding Bind will work in the overrided method. You will need to mark the base controller as abstract, otherwise ASP.NET Core does not know, which controller and endpoint to choose and throws an exception:
Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The
request matched multiple endpoints

Related

Moving MediatR from a MVC project to Razor Pages. Cannot get basic syntax to work

The working MVC version is
public class StatCategoriesController : BaseController
{
[HttpGet]
public async Task<ActionResult<IEnumerable<StatCategoryPreviewDto>>> GetStatCategoryPreview([FromQuery] GetStatCategoryPreviewQuery query)
{
return Ok(await Mediator.Send(query));
}
}
The RAZOR version is
public class CategoriesModel : PageModel
{
private IMediator _mediator;
protected IMediator Mediator =>
_mediator ?? (_mediator = HttpContext.RequestServices.GetService<IMediator>());
public async Task<IEnumerable<StatCategoryPreviewDto>> OnGet([FromQuery] GetStatCategoryPreviewQuery query)
{
return await Mediator.Send(query);
}
}
And the RAZOR verion doesn't return the JSON.. instead it returns..
nvalidOperationException: Unsupported handler method return type 'System.Threading.Tasks.Task1[System.Collections.Generic.IEnumerable1[Srx.Application.StatCategories.Models.StatCategoryPreviewDto]]'.
Microsoft.AspNetCore.Mvc.RazorPages.Internal.ExecutorFactory.CreateHandlerMethod(HandlerMethodDescriptor handlerDescriptor)
Any idea ?
A razor page method should return type that implements IActionResult in order to properly execute action result. If you need to return json you can use JsonResult and changing action return type to IActionResult will be enough
public async Task<IActionResult> OnGet([FromQuery] GetStatCategoryPreviewQuery query)
{
var result = await Mediator.Send(query);
return new JsonResult(result);
}

Could I Create One Controller Like Laravel Resource Controller in ASP.NET Core?

So, I have for example this Laravel Resource Controller code like this:
class BaseAPIController extends Controller
{
public function index()
{
return self::$model->all();
}
}
So, I was trying to do like that in ASP.NET C#:
[ApiController]
public class BaseAPIController<T> : ControllerBase
{
[HttpGet]
public ActionResult<IEnumerable<T>> Get()
{
using (ExamRTContext db = new ExamRTContext())
{
return db.${typeof(T).Name}.Select(x => x);
}
}
}
But I don't have any idea how to do like that.
So, Let say I just wanted to do simple CRUD in 3 tables. All operation is same, for example Get() is used to get all data from that model.
Instead of writing it 3 times, I wanted to just write it once and extend it to each model controller.
Any idea how to do that?
C# does not allow you to compose expressions at runtime like that.
However, EF has an API to do this.
You're looking for .Set<T>().
If you want to perform simple CRUD operations with entity framework you could create a generic repository.
Repository:
public class GenericRepository<TEntity, TContext>
where TContext : DbContext
where TEntity : class
{
protected readonly TContext context;
public GenericRepository(TContext context)
{
this.context = context;
}
public virtual async Task Add(TEntity model)
{
await context.Set<TEntity>().AddAsync(model);
await context.SaveChangesAsync();
}
public virtual async Task<TEntity> Get(int id)
{
return await context.Set<TEntity>().FindAsync(id);
}
public virtual async Task<IEnumerable<TEntity>> GetAll()
{
return await context.Set<TEntity>().ToListAsync();
}
public virtual async Task<TEntity> FindFirstBy(Func<TEntity,bool> predicate)
{
return await Task.Run(()=> context.Set<TEntity>().FirstOrDefault(predicate));
}
public virtual async Task<IEnumerable<TEntity>> FilterBy(Func<TEntity,bool> predicate)
{
return await Task.Run(()=> context.Set<TEntity>().Where(predicate).ToList());
}
public virtual async Task Update()
{
await context.SaveChangesAsync();
}
public virtual async Task Remove(TEntity model)
{
context.Set<TEntity>().Remove(model);
await context.SaveChangesAsync();
}
}
To be able to use it you just have to inject it in the controller specifying the Entity Type and the Context. In your example it would be like:
Controller Base:
[ApiController]
public class BaseAPIController<T> : ControllerBase
{
protected readonly GenericReposoitory<T,ExamRTContext> repository;
public BaseAPIController(GenericRepository<T,ExamRTContext> repository) {
this.repository = repository;
}
[HttpGet]
public ActionResult<IEnumerable<T>> Get()
{
var entities = repository.GetAll();
if (entities!= null) {
return Ok(entities);
}
return NotFound();
}
}
In Startup:
services.AddTransient(typeof(GenericRepository<,>), typeof(GenericRepository<,>));

Inherit http verb attributes from abstract REST controller

I am trying to implement a base REST controller in aspnetcore 1.0.1 (kind of inspired from NancyFx) and it feels like this should be something that can be achieved with such a composable framework, however, I just cant get it right. The google foo is clearly weak with me today!
I have the following base controller (obviously not fully implemented yet)...
[Route("api/[controller]")]
public abstract class RestApiController<T> : Controller
{
protected abstract Func<int, Task<T>> Get { get; }
protected abstract Func<Task<IEnumerable<T>>> List { get; }
[HttpGet()]
protected virtual async Task<IEnumerable<T>> OnList()
{
if (this.List == null)
{
this.NotFound();
}
return await this.List.Invoke();
}
[HttpGet("{id:int}")]
protected virtual async Task<T> OnGet(int id)
{
if (this.Get == null)
{
this.NotFound();
}
return await this.Get.Invoke(id);
}
}
Which is inherited by the actual controller doing the work...
public class ArticleSummariesController : RestApiController<ArticleExtension>
{
private readonly ArticleManager articleManager;
protected override Func<int, Task<ArticleExtension>> Get => null;
protected override Func<Task<IEnumerable<ArticleExtension>>> List => this.ListAll;
public ArticleSummariesController(ArticleManager articleManager)
{
this.articleManager = articleManager;
}
private async Task<IEnumerable<ArticleExtension>> ListAll()
{
return await this.articleManager.GetAllAsync();
}
}
The idea is that the base controller will be responsible for handling the actual requests but delegate responsibility to it's children to provide and manipulate the data. This is so that we can ensure REST conformance in the requests but loosely couple domain logic from the controllers into "managers" that act as a facade and take repositories and apply business logic.
The problem with the code so far is that the HttpGet() attributes on the base class do not produce routes for the child class. The controller route attribute on the base class is inherited though (as stated in the docs).
I could be wrong, but I immediately assume that you need a RouteAttribute on either the base class or the subclass. For example:
[Route("api/[controller]")]
public abstract class RestApiController<T> : Controller
{
protected abstract Func<int, Task<T>> Get { get; }
protected abstract Func<Task<IEnumerable<T>>> List { get; }
[HttpGet, Route("list")]
protected virtual async Task<IEnumerable<T>> OnList()
{
if (this.List == null)
{
this.NotFound();
}
return await this.List.Invoke();
}
[HttpGet, Route("get/{id:int}")]
protected virtual async Task<T> OnGet(int id)
{
if (this.Get == null)
{
this.NotFound();
}
return await this.Get.Invoke(id);
}
}
Yet again, the answer was staring me in the face! I had the parent methods declared as protected not public so they were not considered eligible actions!
After reading more about the ApplicationModel here it all became clear...
ActionModel – represents an action of a controller. An instance of
this class is created for each eligible action on a controller. There
are multiple requirements for a method to become an action, such as
being public, non-abstract and not inherited from object.
So the modified code below works...
[Route("api/[controller]")]
public abstract class RestApiController<T> : Controller
{
protected abstract Func<int, Task<T>> Get { get; }
protected abstract Func<Task<IEnumerable<T>>> List { get; }
[HttpGet()]
public virtual async Task<IEnumerable<T>> OnList()
{
if (this.List == null)
{
this.NotFound();
}
return await this.List.Invoke();
}
[HttpGet("{id:int}")]
public virtual async Task<T> OnGet(int id)
{
if (this.Get == null)
{
this.NotFound();
}
return await this.Get.Invoke(id);
}
}
public class ArticleSummariesController : RestApiController<ArticleExtension>
{
private readonly ArticleManager articleManager;
protected override Func<int, Task<ArticleExtension>> Get => null;
protected override Func<Task<IEnumerable<ArticleExtension>>> List => this.ListAll;
public ArticleSummariesController(ArticleManager articleManager)
{
this.articleManager = articleManager;
}
private async Task<IEnumerable<ArticleExtension>> ListAll()
{
return await this.articleManager.GetAllAsync();
}
}

Can't route to methods in BaseController when Derived is using ODataRoute

I'm having problems routing to my BaseController for controllers where I define extra [ODataRoute]s.
GET ~/odata/Foos WORKS
GET ~/odata/Foos(1) WORKS
GET ~/odata/Foos(1)/Bars WORKS
GET ~/odata/Bars 404 Not Found
BaseController
public abstract class BaseController<T> : ODataController where T : class
{
protected IGenericRepository<T> Repository;
public BaseController(IGenericRepository<T> repository)
{
Repository = repository;
}
[EnableQuery]
public IHttpActionResult Get() // <---------- THIS IS THE PROBLEM
{
return Ok(Repository.AsQueryable());
}
[EnableQuery]
public IHttpActionResult Get(int key) // WORKS
{
var entity = Repository.GetByKey(key);
return Ok(entity);
}
}
Controllers
public class FoosController : BaseController<Foo>
{
public FoosController(IGenericRepository<Foo> repository)
: base(repository)
{
}
// Can route to base without problems
// Both base.Get() and base.Get(1) works
}
public class BarsController : BaseController<Bar>
{
public FoosController(IGenericRepository<Bar> repository)
: base(repository)
{
}
// Can't route to base.Get()
// But base.Get(1) works
// GET /Foos(1)/Bars
[EnableQuery]
[ODataRoute("Foos({key})/Bars")]
public IHttpActionResult GetBars(int key) // WORKS
{
var result = Repository.AsQueryable().Where(x => x.FooId == key);
return Ok(result);
}
}
Additionally I tried going the convention way. But that doesn't work either.
public class FoosController : BaseController<Foo>
{
public FoosController(IGenericRepository<Foo> repository)
: base(repository)
{
}
// GET /Foos(1)/Bars
[EnableQuery]
public IHttpActionResult GetBars(int key) // WORKS
{
var result = Repository.AsQueryable().Bars;
return Ok(result);
}
}

Web api access controller method from another controller method

I have two different controller --> Controller A,Controller B
And I have different methods each controller and their return values IHttpActionResult (Method A controller A ,Method B and Controller B)
How can I access another controller method and take its content from another controller
Controller B ,and Inside Method B
IHttpActionResult result = ControllerA.MethodA()
and I want to read result.content inside controller B
When request comes, only controller which should process request is instantiated automatically. You can instantiate second controller manually, but I would recommend to move MethodA functionality either to base controller class
public class BaseController : ApiController
{
// ...
public IHttpActionResult MethodA(int id)
{
var foo = repository.Get(id);
if (foo == null)
return NotFound();
return Ok(foo);
}
}
public class ControllerA : BaseController
{
//...
}
public class ControllerB : BaseController
{
public IHttpActionResult MethodB(int id)
{
var result = MethodA();
//..
}
}
or move common logic to separate class (e.g. service), so you would be able to call it from both controllers.
public class ControllerA : ApiController
{
private IFooService fooService;
public ControllerA(IFooService fooService)
{
this.fooService = fooService;
}
public IHttpActionResult MethodA(int id)
{
// use fooService.Method()
}
}
public class ControllerB : ApiController
{
private IFooService fooService;
public ControllerB(IFooService fooService)
{
this.fooService = fooService;
}
public IHttpActionResult MethodB(int id)
{
// use fooService.Method()
}
}
I would consider using a common base class for the two controllers (if there is a method you want to use on both)
for example
public abstract class MyBaseController
{
public void CommonMethod()
{
// Do something here
}
}
then use them like
public class ControllerA : MyBaseController
{
public void MethodA()
{
base.CommonMethod();
// Do something else
}
}
public class ControllerB : MyBaseController
{
public void MethodB()
{
base.CommonMethod();
// Do Something else
}
}
1) You can use static class and static method inside to share it for another controllers
public static class CommonMethods
{
public static string SomeMethod(string s)
{
string RetString;
...
return (RetString);
}
}
Now you can use it in any controllers
string SomeMethodResult = CommonMethods.SomeMethod("Say Hello");
2) And another method is to create an instance of a controller class and call instances methods:
public class V1Controller : ApiController
{
public void Put(int id, [FromBody]string value)
{
HomeController hc = new HomeController();
hc.SomeMethod();
}
}
AController aController = new AController();
var getResponse = aController.YourMethod(values);
If your method returns Json then you can easily solve it with
JavaScriptSerializer().Deserialize

Categories

Resources