I'm trying to make dynamic menu (stored in DB), that is showing on all web app pages.
Using Google I found that it is better to make menu view as a part of Master View (_Layout.cshtml). And because of that, every action method of the controller must contain data with the menu model. To avoid code duplication I found the solution to create a base controller and provide data using its constructor:
https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/views/passing-data-to-view-master-pages-cs
Also, I'm trying to use async/await possibilities and my PageService (menu) is using ToListAsync() to get data from DB. So now I have a problem, that BaseController constructor has an async method:
public class BaseController : AsyncController, IBaseController
{
private readonly IPageService _pageService;
public BaseController(IPageService pageService)
{
_pageService = pageService;
SetBaseViewModelAsync();
}
private async Task SetBaseViewModelAsync()
{
ViewData["Pages"] = await _pageService.GetAllAsync();
}
}
I know that this is BAD CODE, but I don't know how to design this situation properly. Maybe there is another better way to create the dynamic menu or another way to get data asynchronously?
Also, I found this article, but I don't know if I can apply its solutions because I don't know if I can handle controller instance creation:
http://blog.stephencleary.com/2013/01/async-oop-2-constructors.html
Instead of deriving everything from a base controller (which can be a lot of extra work and testing) you can just create a controller called MenuController, create a method called Default and then call it from your Layout:
[ChildActionOnly]
public Default()
{
var viewModel = _pageService.GetAllAsync();
return Partial(viewModel);
}
in your layout:
#{Html.RenderAction("Default", "Menu");}
This is really the easiest and cleanest solution. The biggest PRO is that you can control the cache for the menu separate from the method calling. There are no good solution for asp.net-mvc (1-5) to run Async code in this fashion. (ActionFilters can't be async and (Render)Partials can't be async. You can still call an async method, it will just run Sync.
Render vs Non-Render Performance.
I changed functionality to call Html.RenderAction in my View, as Erik Philips suggested:
#{
Html.RenderAction("Index", "Pages");
}
and controller:
public class PagesController : AsyncController, IPagesController
{
private readonly IPagesService _pagesService;
public PagesController(IPagesService pagesService)
{
_pagesService = pagesService;
}
[HttpGet]
[Route("")]
public async Task<ActionResult> IndexAsync()
{
var viewModel = await _pagesService.GetAllAsync();
return PartialView("MenuPartial", viewModel);
}
}
But RenderAction doesn't work with async controller Actions:
Async PartialView causes "HttpServerUtility.Execute blocked..." exception
So seems like sync call is the only one possible here.
Related
C# ASP.NET Core Web API: general newbie question. Trying to implement basic Asynchronous Request-Reply Pattern. Basic idea is that:
Client calls POST {url}/DataProcessor/DataX with some payload
Client checks on the status of the transaction using GET {url}/DataProcessor/DataXStatus, until HTTP Status Code of 200 is returned
Client gets xActionId from the response above and then calls GET {url}/DataProcessor/GetResults/{xActionId} to retrieve results of data processing.
This is what my controller looks like: When I call DataXStatus method (after properly calling the DataX method), the _processData isn't in the right state, like it has gone out of scope after DataX method is done. What am I doing wrong? Is DataProcessorController object gone after any one method is complete? What is the right way to do this?
Thanks in advance
[Route("[controller]")]
[ApiController]
public class DataProcessorController : ControllerBase
{
private ProcessData _processData = new ProcessData() ;
[HttpPost]
[Route("DataX")]
public IActionResult DataX([FromBody] xData value)
{
_processData.CalculateResult(value);
return Accepted();
}
[HttpGet]
[Route("DataXStatus")]
public IActionResult DataXStatus()
{
if(_processData.IsRunning())
{
return Accepted();
}
return Ok(new xDataStatus(){id = _processData.GetxActionId()});
}
[HttpGet]
[Route("GetResults/{xActionId}")]
public IActionResult GetResults(string xActionId)
{
return Ok(new xDataResults(){resX = _processData.GetResults()});
}
}
Answering this on my mobile so please forgive any typos.
Basically, yes the class is re-instaintaited on every request. The api does not keep context of previous requests unless the object you are keeping track of is static, which being me to my first solution for this:
Quick and dirty option:
I would recommend you use dependency injection for this instead. That gives you the option to run your 'proccessData' class as a singleton. Which basically means it's static.
Better option:
The other more correct way is to use a known library for this. There are are tons of libraries that are built for handling async requests in web apis. I think you should use hangfire for this, it takes a few minutes to set up and also has tons of configurion options.
https://docs.hangfire.io/en/latest/getting-started/aspnet-core-applications.html
Let me know if you need any further help!
I create the web app include Home Page and Admin Page. I create a _sliderRepository.AllSliders to return of list of sliders in a database. How I reuse the repository code for 2 controllers
public class HomeController : Controller
{
private readonly ISliderRepository _sliderRepository;
public HomeController( ISliderRepository sliderRepository)
{
_sliderRepository = sliderRepository;
}
public IActionResult Index()
{
return View(_sliderRepository.AllSliders);
}
}
public class AdminController : Controller
{
private readonly ISliderRepository _sliderRepository;
public AdminController( ISliderRepository sliderRepository)
{
_sliderRepository = sliderRepository;
}
public IActionResult Slider()
{
return View( _sliderRepository.AllSliders);
}
}
Several options:
Use inheritance
Create a base controller class that inherits the Controller class and add there methods like CreateViewFrom. The main disadvantage here is if you use some service that needs to be injected in the base controller, then all inheriting controller would have to provide the service.
Use extension methods
Just create static class and define there extensions methods that again will create the responses you need. The main disadvantage here is you don't have access to functionality that exists in the Controller class.
Do nothing
You can do nothing, the cost is zero, just copy and paste code across controllers. The disadvantage is over time the maintenance cost increases. You need to find the best time when to decide to attack code duplication. In my experience do it as soon as possible.
General advice
Keep your controllers very light, they should not be services. In your particular example I can predict at some point you would want to save a slider in the database and the slider images in storage. Don't do this orchestration in the Controller classes. Extract an ISildersService abstraction and do the orchestration there. Keep 'em light.
if we look at ViewResult object returned by action method, ViewResult implements ActionResult(implements IActionResult) as
public class ViewResult : ActionResult
{
...
public override Task ExecuteResultAsync(ActionContext context);
}
ExecuteResultAsync seems to be the guy that generates responses.
But I always get told that it is Razor converts CSHTML files into C# classes, compiles them, and then creates new instances each time a view is required to generate a result. Below is the C# class that Razor creates for the Index.cshtml view
public class ASPV_Views_Home_Index_cshtml : RazorPage<string[]> {
...
public override async Task ExecuteAsync() {
WriteLiteral(#"<!DOCTYPE html><html><head> ...
}
}
so ExecuteAsync also seems to generate responses?
When ViewResult gets executed by the framework (i.e. calling ViewResult.ExecuteResultAsync
), it is responsible for 1. creating the IView that represents the correct cshtml files (view and layout) and 2. feeding this information to the IViewEngine. The view engine is the one responsible for compiling and writing the page contents to the output stream (i.e. calling IRazorPage.ExecuteAsync Method).
So, the ViewResult just loads everything it needs, and in the end, IRazorPage.ExecuteAsync is the one who truly writes to the output stream.
I'm having trouble in how to write my ASP.NET MVC application, mainly in how to use my business logic. I will provide just some example. I don't know if this is right:
public class UserController {
public ActionResult Create(User user){
UserService service = new UserService();
if(!service.UserExists(user.Email)){
if(service.InsertUser(user)){
service.SendConfirmation(user);
}
}
}
}
Or this is right
public class UserController {
public ActionResult Create(User user){
UserService service = new UserService();
service.CreateUser(user);
}
}
In this second example the method CreateUser of UserService will check if user exists, then will insert, then will send the email.
The main difference is that in the second example the controller calls only one method while in the second it calls many methods and receives answers, in both cases the logic is inside UserService.
What's correct?
The second one is the one to choose. It utilizes proper encapsulation. The controller should not do logic but communicate with the services and feed the views with the data and manage program flow.
however you should receive some response from your service.
In your example it could mean some enum value or a boolean value to determine if the creation of the user was successfull or anything...
on this you can then let the controller manage what view comes next and what data it gets...
The recommended way to make an edit page for ASP.NET MVC is to have two methods on a controller called Edit: one GET action and one POST action, both sharing the same name but overloaded differently. Validation errors are shown on the POST action if the edit fails. Then the user can share or bookmark the URL even if it's off of a POST: the URL goes to the GET version on the return.
So far, so good. But then there's the ASP.NET async pattern on controllers. You have EditAsync and EditCompleted. On the two different EditCompleted methods, how do you tell the GET apart from the POST? If you rename the POST action, you lose the nice behavior discussed earlier.
Is there a nice way to get these two patterns to work together?
Generally the XyzAsync() method provides the XyzCompleted() method some state object that tells it what unit of work is being performed, so the XyzCompleted() method can inspect this object and do the right thing. However, if you want to have a different Completed method for each verb, this is possible via the following:
[ActionName("Edit"), HttpGet]
public void EditGetAsync() { }
public ActionResult EditGetCompleted() { }
[ActionName("Edit"), HttpPost]
public void EditPostAsync() { }
public ActionResult EditPostCompleted() { }