Get data in controller using context or create method? - c#

Which of the following is the correct, or at least the best one:
Create a method to retrieve data in controller:
public ActionResult Index()
{
var list = _context.MyClass.Take(10);
return View(list);
}
or use context directly:
public ActionResult Index()
{
var list = MyClass.MethodWrapperToGet(10);
return View(list);
}
My concern with the first is that the database is too exposed; making it too easy for developers to misuse.

It really depends on the size of your project. For something small or a quick prototype I'd go with the option where controllers access the DbContext directly.
public ActionResult Index()
{
var list = _context.MyClass.Take(10);
return View(list);
}
I personally prefer separating concerts. In other words, I would create a service class that hands the controller exactly the data that it needs. Remember that the Controller should not know how to execute tasks but instead what needs to be executed after what.
This of course, does not mean you have to implement the repository pattern. Your service class could access the DbContext directly if you would like to.

Ideally you use a variation of exposing _context where you could pass that context through Dependency Injection so you can Unit Test your controller.
Static calls are very hard to test, at least in .Net

Related

Make Sure Application Layer does not Return IQueryable

We have a Data Layer, Repository, and then Application layer. Is there way to prevent an Application layer project from returning any IQueryable?
Currently we code review all return methods, review proper architecture, and ensure they tag ToList() at end when needed .
Is there an option in Visual studio Builds, Xunit option, or Post execute Design method to ensure all calls are not IQueryable, but converted to IEnumerable, IList, etc?
Asnwers are based on questions without Minimal, Reproducible Example and may not be accurate.
We have a Data Layer, Repository, and then Application layer. Is there
way to prevent an Application layer project from returning any
IQueryable?
Assuming application layer refers to ASP.NET Core controllers and the problem is that queryable source is disposed before actual query is executed.
Option 1:
Do not return IQueryable<T> from repository. IMHO, repository suppose to return materialized query, IEnumerable<T>.
In case you are dealing with large project and repository might become huge mess, you may consider different approach.
Option 2:
Inherit ControllerBase and/or Controller and override every single method accepting model and make sure passed model is not IQueryable, otherwise execute query and pass result to base method. 20 methods in ControllerBase and 4 methods in Controller class.
API example:
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
public IActionResult Get() {
using (var context = new MaterializeQueryContext()) {
return Ok(context
.MaterializeQuery
.Where(x=> x.Id > 1));
}
}
public override OkObjectResult Ok(object model) {
if (model is IQueryable queryable) {
var toList = typeof(Enumerable)
.GetMethod(nameof(Enumerable.ToList))
.MakeGenericMethod(new[] { queryable.ElementType });
var result = toList.Invoke(null, new[] { queryable });
return base.Ok(result);
}
return base.Ok(model);
}
}
MVC example:
public class HomeController : Controller {
public IActionResult Index() {
using (var context = new MaterializeQueryContext()) {
return View(context
.MaterializeQuery
.Where(x => x.Id > 1));
}
}
public override ViewResult View(object model) {
if (model is IQueryable queryable) {
var toList = typeof(Enumerable)
.GetMethod(nameof(Enumerable.ToList))
.MakeGenericMethod(new[] { queryable.ElementType });
var result = toList.Invoke(null, new[] { queryable });
return base.View(result);
}
return base.View(model);
}
}
Downsides:
Reflection will add some overhead
It may be buggy in more complex scenarios
ToList method invocation will be blocking thread
Adding async support is (probably) impossible or it will be too complex
Non covered scenarios (example below)
For example returning result directly.
public IActionResult Get() {
using (var context = new MaterializeQueryContext()) {
return new OkObjectResult(context
.MaterializeQuery
.Where(x=> x.Id > 1));
}
}
Currently we code review all return methods, review proper
architecture, and ensure they tag ToList() at end when needed.
I would suggest instead of reviewing code to sit down and write custom Roslyn Analyzers to avoid repeated tasks that can be solved once. Also you can write code fixes for those scenarios to make it even easier to fix.
You can find already made analyzers and code fixes on GitHub.
.NET Analyzers
awesome-analyzers
Is there an option in Visual studio Builds, Xunit option, or Post
execute Design method to ensure all calls are not IQueryable, but
converted to IEnumerable, IList, etc?
Yes, it is possible to use Roslyn Syntax Transformation.
Hope it helps!
If you want to make sure your application layer does not return any IQueryable you have 2 options.
1- Create an interface to force all implementations to return an IEnumerable if you want.
2- Create an abstract class to force all subclasses to implement your abstract methods.
In this case your application layer class will implement the interface or the abstract class.
You can have as many layers as you want on your architecture, the cons here is that it will introduce another abstraction concept. I am seeing this pattern repeated on different projects where you have a DataAccessLayer that works with your Repositories and return IQueryable. So you can execute filters on the database side easy, but when you are done with your data and want to pass it to your ApplicationLayer(BusinessLayer) you return an IEnumerable. So you have the control if you want to execute queries with filter on the database side or you can bring your Entities into memory executing the ToList( ) and filter them on memory. But it is up to you where you want to stop bubbling your IQueryable to a higher layer.

Questions about good practices with MVC and Entity Framework

I'm trying to build a simple MVC application with VS and Entity Framework, i have few questions.
I want to add some default values to my model, i can do that by
including default values to constructor like this:
public Worker()
{
WorkerActive = true;
}
But default controller code is like this, and it's not returning anything
public ActionResult Create()
{
return View();
}
If i change that to this, it works but i'm not sure if i'm doing something wrong:
public ActionResult Create()
{
return View(new Worker());
}
Are there any problems here?
I have a combobox with all workers in it, i want to show some
records based on the selection. For example, i want to show all
records where WorkerId=ComboboxValue. I can do this easily
by using this:
workers = m.Workers.Where(w => w.BranchId == curUser.BranchId).ToList<Worker>();
But it's in a view. Is using
statements like where in view a bad practice? I can add a new method
to controller like getUsersByBranch(BranchId) and use that.
What's the right way to do it?
Thanks
1) I'd argue that your models should be as stupid as possible, just properties and metadata. Your controller can certainly have logic in it to manipulate the model, though. That's what it's for - to return the model to the view, however you see fit:
public ActionResult Create()
{
var model = new Worker { WorkerActive = true };
return View(model);
}
Plus, you won't have to worry about needing different default values in a different controller.
2) The view is 'supposed' to be pretty dumb too. But as with all best practices, it really comes down to the overhead of refactoring and the potential gain. No, that code probably shouldn't be in your view. Ideally, if it's required by the view, it'd be a property of some sort on your model, that you controller has set up.
The logic with the 'best practice' of simple views is that it can get overly convoluted very quickly if you keep doing this, leading to spaghetti code. But like I said, you should try things and see if you like them sometimes, instead of simply going along blindly with best practices.
by 'property on your model' I mean:
public class CreateModel
{
public List<User> UsersInBranch { get; set; }
}
then your controller can fill it in, like above. Keeps your view cleaner, and lets the controller perform it's intended functionality. With MVC and Razor, logic in your view is not really in your view (in my mind), because it's still running server side. It feels like mixing server and client side operations to me.
public ActionResult Create()
{
return View(new Worker());
}
No problems in here, but Worker is a Entity? Maybe you should separate infrastructure (ef mapping) code from presentation (mvc).
workers = m.Workers.Where(w => w.BranchId == curUser.BranchId).ToList<Worker>();
Don't do this in your View. It would be better if you move this code to repository. Take a look at the good paper from Martin Fowler about this pattern:
http://martinfowler.com/eaaCatalog/repository.html

Convention for actions returning multiple View Types (html, json, partials)

I am building a website in ASP.NET MVC5. In my controllers, I have some actions that return the same data but in different formats mostly Views, partial views and Json.
For example, I would have a controller which display a list of items :
// some action returning a view
public ActionResult List()
{
var model= _repository.Items.ToViewModel();
return View(model)
}
// some action returning a partial view
[ChildActionOnly]
public ActionResult ListPartial()
{
var model= _repository.Items.ToViewModel();
return PartialView("_ListPartial", model)
}
// some action returning JSON
public ActionResult GetList()
{
var model= _repository.Items.ToViewModel();
return Json(model, JsonRequestBehavior.AllowGet);
}
How can I make a clear distinction between my actions ?
What kind of conventions should be followed to keep it clean?
Should the actions co-exist in the same controller ?
I'm not sure what you mean by "make a clear distinction between my actions". From a compiler perspective, the distinction between action methods will always depend on one of two things: the name of the action and the parameters that action takes. So, you could have two actions with the same name, but different parameters, or two actions with totally different names and the same or different parameters. From a developer perspective, you can add to the previous list: return value. Personally, I can look at any of the actions you have here and clearly see what they're doing based on that, i.e. the one that returns the model encoded as JSON, is obviously a JSON result.
As far as keeping things clean goes, again, that's a somewhat loaded question. What defines "clean"? Your methods look perfectly "clean" to me as-is. You've got a bit of code duplication, but I'd consider that acceptable in this scenario. All you're doing is retrieving a model in each. One could argue for leaving such duplicate code, as potentially each method could diverge over time, retrieving more or less parts of that model or including other related entities, etc.
However, if you want to remove the code duplication, you could factor that line out into an internal method on the controller, and then have each action call that internal method. That way, if you need to modify the line, you only need to do it in one place, but of course, if you need to make a modification just for one of these actions, you're back to where you were before.
Now, to should the actions co-exist in the same controller, this is a bit subjective as well. Since, they're so closely related in functionality, with just different return values, there's an easy argument for keeping them in the same controller. However, it's equally valid to suggest moving them into different controllers so, for example, you have all your JSON returning actions together. A lot of it boils down to personal preference.
Now, all that said, in this one particular scenario, since literally the only difference is the return value depending on the methodology being used to get the action, you can actually combine all of these into one action:
public ActionResult List()
{
var model= _repository.Items.ToViewModel();
if (Request.IsAjaxRequest())
{
return Json(model, JsonRequestBehavior.AllowGet);
}
if (ControllerContext.IsChildAction)
{
return PartialView("_ListPartial", model);
}
return View(model);
}
This is possible because your action's return value of ActionResult, is a base class that all of ViewResult, PartialViewResult and JsonResult inherit from.
ActionResult is an abstract class and all other result classes are derived from that.
Instead of user ActionReault for all method use derived class name. Like JsonResult, ViewResult , PartialViewResult

Is passing a service reference to another service layer bad practice?

I've got a C# MVC application that I break up in the following way:
View -> Controller -> Service -> Repository
I use the thin controller practice with each view having a unique view model that's returned from the relevant service.
Quick Example:
View: /NewAppointment/Step1
Its controler would look like this:
public ActionResult Step1()
{
return View(_appointmentService.Step1GetModel() );
}
And the appointment service layer would look like this:
public Step1Model Step1GetModel()
{
return new Step1Model();
}
Thus I have several different service layers in use through out my application, each implementing a distinct interface.
My question comes in when I need to have one service layer interact with another service layer. In this case, is it better practice to pass an interface reference to the service call, or should I let the controller handle collecting all the data and then pass the relevant results back to the service?
Example:
Say I want to populate my view model with the customer's information by default. The two ways I see of doing this are:
Pass an customer interface reference to the appointment service, and then let the appointment service call the appropriate GetCustomer method in the customer service...
In Code:
private ICustomerService _customerService;
private IAppointmentService _appointmentService;
public ActionResult Step1()
{
var viewModel = _appointmentService.Step1GetModel( _customerService );
return View(viewModel);
}
OR
Let the controller handle the logic of getting the customer, then pass that result to the appointment service.
In Code:
private ICustomerService _customerService;
private IAppointmentService _appointmentService;
public ActionResult Step1()
{
var customer = _customerService.GetCustomer();
var viewModel = _appointmentService.Step1GetModel( customer );
return View(viewModel);
}
I'm torn as to which would be better practice. The first keeps the controller nice and thin, but creates an inter-service dependency between the appointment service and the customer service. The second puts more logic into the controller, but keeps the services totally independent.
Anyone have thoughts as to which would be better practice?
Thanks~
Thinking about his purely conceptually I don't think it makes sense for your services to know anything about your view models. One of the main reasons to have a controller in the first place is to separate out your view logic from your business logic, but if your services are returning view specific data then they're inherently tied to your business logic.
Ideally I'd expect the method to look like this:
public ActionResult Step1()
{
var customer = _customerService.GetCustomer();
var appointment = _appointmentService.GetAppointmentFor(customer);
var viewModel = new Step1ViewModel(customer, appointment);
return View(viewModel);
}
To answer your question more directly though, I think it's fine for your services to know about each other, they're part of the same conceptually layer.
Also, one more thing...
It sounds like you have a lot of parallel class hierarchies what with having services, repositories and controllers. It might make more sense to use something like the unit of work pattern and a powerful ORM to do something like:
public MyController(IUnitOfWork unitOfWork)...
public ActionResult Step1()
{
var customer = unitOfWork.Find<Customer>();
var viewModel = new Step1ViewModel(customer.Appointment);
return View(viewModel);
}
After all, the value of your application is in the model, not in the services.

Is it possible to make data from an Action Method available in an Action Filter?

The Background:
We are supplied with html files - 'wrappers' - from our client, into which we need to inject the content that we produce. They have different wrappers for different pages and we have to inject the corresponding content into a special tag that they supply in the wrapper.
The wrapper file name corresponds to the name of the action method. So for the example below, a custom action filter is executed which will determine the name of the wrapper and then call a method in the BaseController (which every controller implements) which will load the wrapper and inject our content into it.
[WrapperAction]
public ActionResult Home()
{
return View();
}
The reason I put this in an ActionFilter is because I didn't want to be calling the BaseController's method to populate the wrapper in every action method that needed a wrapper. I thought it was much tidier to decorate the method with the ActionFilterAttribute
WrapperAction is defined as follows:
public class WrapperAction : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext aec)
{
var baseController = (BaseController)filterContext.Controller;
string wrapper = aec.RequestContext.RouteData.Values["action"].ToString();
baseController.PopulateWrapper(wrapper);
}
}
and PopulateWrapper is defined as
public void PopulateWrapper(string wrapperName)
{
// get wrapper file from disk
Wrapper wrapper = _wrapperService.GetWrapper(Site.Id, wrapperName);
// populate the file with our CSS (it already has its own pre-populated CSS)
// our CSS file is determined by the id of a Site object.
AppHelper.PopulateWrapperCss(wrapper, this.Site.Id);
// make wrapper sections available to the Master page,
// split so that we can place our content around them
ViewData["WrapperTop"] = wrapper.WrapperTop;
ViewData["WrapperMiddle"] = wrapper.WrapperMiddle;
ViewData["WrapperBottom"] = wrapper.WrapperBottom;
}
The Dilema:
Now however, a new requirement has come in. There are some new tags in the wrapper that I need to populate, and I have to populate them with different data depending on the action method that calls the wrapper. That data is determined in the action methods, but the data needs to be used in the PopulateWrapper method that gets called by the WrapperAction.
I now need to have a method similar to
AppHelper.PopulateWrapperTags(wrapper, this.TagData);
and I need to have some way for BaseController's TagData property to be populated with data. I can asign the property in the action method like follows
[WrapperAction]
public ActionResult Home()
{
base.TagData = GetTagData();
return View();
}
but that kind of defeats the point of me having the WrapperAction in the first place because I don't want to have to be referring to the BaseController like that.
The Question:
Is there a way for me to supply WrapperAction with the data it needs to populate the wrapper with? Do I need to take the hit and start calling
var tagData = GetTagData();
string wrapperName = RouteData.Values["action"].ToString();
base.PopulateWrapper(wrapperName, tagData);
in every controller? Is there a better way for me to do this?
What you have already written is really very good and needs only slight adjustment to meet your new requirement.
The key is that your action filter is a type of OnActionExecuted which runs after your action code has completed, but before the view is executed. Therefore the filter method can build on whatever has taken place within the action method.
To achieve your requirement, create the tagData variable within your base controller, so that the controllers that inherit from it can populate it if necessary.
Then, within your action filter you simply need something like
if (tagData == null)
baseController.PopulateWrapper(wrapper)
else
baseController.PopulateWrapper(wrapper, tagData)
I'm sure you get the general idea.
imho the following was just right, so I don't see how the action filter is tidier:
public ActionResult Home()
{
PopulateWrapper();
return View();
}
and in the controller base:
public void PopulateWrapper(string wrapperName)
{
string wrapperName = RouteData.Values["action"].ToString();
//... rest of populate wrapper
then you go:
public ActionResult Home()
{
PopulateWrapper(GetTagData()); // after defining an overload in base
return View();
}
Assuming you still go the action filter way, I don't see why you are forcing the dependency with controller base with it i.e. why the PopulateWrapper is in controller base.
Note that you can also pass the data in ViewData, but that feels worst in your scenario - specially given the dependency in controller base.

Categories