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

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

Related

Correctly override the generic BaseController

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

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<,>));

how to add GET returns to WebApi tutorials for new classes

I am trying to find the proper way to add more "GET" return statements to my first web api project using EF and repositories. I've followed numerous examples and tutorials and they all show how to setup a web api, but now I'm trying to add to it. Example being, if I have a second class, how do I GET all values from this new class?
So far I've followed the standard examples online and everything works fine.
What I have done so far is make a second controller, then add the method names to my IRepository, and the actual code to my Repository.cs.
I just want to make sure I am doing this the proper way. Basically, should I just keep adding controllers for every class I want to return individually, then just keep making my repository longer and longer? Is that the right way to do this?
FIRST CONTROLLER (BASED ON TUTORIALS)
public class ClientController : ApiController
{
private IRepository _repo;
public ClientController(IRepository repo)
{
_repo = repo;
}
public IQueryable<Client> Get()
{
return _repo.GetAllClients();
}
public IQueryable<Client> Get(bool includeDetails)
{
IQueryable<Client> query;
if (includeDetails)
{
query = _repo.GetAllClientsWithDetails();
}
else
{
query = _repo.GetAllClients();
}
return query;
}
public Client Get(int id)
{
return _repo.GetClient(id);
}
}
IREPOSITORY
public interface IRepository
{
IQueryable<Client> GetAllClients();
IQueryable<Client> GetAllClientsWithDetails();
Client GetClient(int id);
IQueryable<Trade> GetAllTrades();
Trade GetTrade(int id);
}
REPOSITORY
public class Repository : IRepository
{
private xxxxV002Context db;
public Repository(xxxxV002Context db)
{
this.db = db;
}
//------------------------------------------------------------------------
public IQueryable<Client> GetAllClients()
{
return db.Clients;
}
public IQueryable<Client> GetAllClientsWithDetails()
{
return db.Clients.Include("Mapping_ClientAccount");
}
public Client GetClient(int id)
{
return db.Clients.Include("Mapping_ClientAccount.Account").FirstOrDefault(o => o.ClientID == id);
}
//-----------------------------------------------------------------------
public IQueryable<Trade> GetAllTrades()
{
return db.Trades;
}
public Trade GetTrade(int id)
{
return db.Trades.FirstOrDefault(x => x.TradeID == id);
}
}
SECOND CONTROLLER (NEW)
public class TradeController : ApiController
{
private IRepository _repo;
public TradeController(IRepository repo)
{
_repo = repo;
}
// GET api/<controller>
public IQueryable<Trade> Get()
{
return _repo.GetAllTrades();
}
// GET api/<controller>/5
public Trade Get(int id)
{
return _repo.GetTrade(id);
}
// POST api/<controller>
public void Post([FromBody]string value)
{
}
// PUT api/<controller>/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/<controller>/5
public void Delete(int id)
{
}
}
Just as a test I added this to ClientController, and using Routes I returned data from a different class successfully. So do I even need to have multiple controllers or can I just keep putting more methods in the one controller? What is best practice?
public class ClientController : ApiController
{
private IRepository _repo;
public ClientController(IRepository repo)
{
_repo = repo;
}
public IQueryable<Client> Get()
{
return _repo.GetAllClients();
}
public IQueryable<Client> Get(bool includeDetails)
{
IQueryable<Client> query;
if (includeDetails)
{
query = _repo.GetAllClientsWithDetails();
}
else
{
query = _repo.GetAllClients();
}
return query;
}
public Client Get(int id)
{
return _repo.GetClient(id);
}
[Route("api/client/{customerId}/orders")]
[HttpGet]
public Trade GetOrdersByCustomer(int customerId)
{
xxxxContext db = new xxxxContext();
var x1 = db.Trades.FirstOrDefault(x => x.TradeID == customerId) as xxxx.Models.Trade;
return x1;
}
}
I would echo what #LoekD said, and I would add that it is a good idea to put http verb attributes on your methods, rather than just relying on convention. It makes things clearer if your controllers get larger and it makes things more consistent.
For instance, on this method:
public IQueryable<Client> Get(bool includeDetails)
Make it:
[HttpGet]
public IQueryable<Client> Get(bool includeDetails)
It's not necessary but it will save you time in the future.
For your repository, it should really return only a specific type. Based on your code above, you would want a repository for Client objects and another repository for Trade objects.
Other than that, I think you are on the right path.
You can use the Routing feature to specify which GET operations your api supports, and which parameters it takes.
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
For example, if you wanted to get a User by Name or by ID it could look like this:
[Route("users/{id:int}"]
public User GetUserById(int id) { ... }
[Route("users/{name}"]
public User GetUserByName(string name) { ... }
(The Route attributes are placed on your Actions)

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

How can I access a property from an ActionFilterAttribute in my ApiController?

I have a custom ActionFilterAttribute. For the sake of this question let's assume it's as follows:
public class CustomActionFilterAttribute : ActionFilterAttribute {
public bool success { get; private set };
public override void OnActionExecuting(HttpActionContext actionContext) {
//Do something and set success
success = DoSomething(actionContext);
}
}
My controller is then decorated with CustomActionFilter. What I am looking for is a way (in my controller method) to do something like:
[CustomActionFilter]
public class MyController : ApiController {
public ActionResult MyAction() {
//How do I get the 'success' from my attribute?
}
}
If there is a more accepted way of doing this please let me know.
I discovered I could do the following to satisfy my problem:
[CustomActionFilter]
public class MyController : ApiController {
public ActionResult MyAction() {
var myAttribute = ControllerContext
.ControllerDescriptor
.GetCustomAttributes<CustomActionFilter>()
.Single();
var success = myAttribute.success;
}
}

Categories

Resources