How does asp.net mvc 6 knows what resources to add when a controller does not inherith from Controller class? - clr

I startted studing the internals from asp.net mvc (6/vnext) and I have a lot of questions but I would like to keep one here. I am using Visual Studio 2015 preview and I saw a new feature in asp.net mvc 6 (or vNext) that controller are not required to inherith from Controller base class. I saw in the asp.net mvc 4/5 the Controller class (or its abstractions) has the properties for resources like TempData, ViewData, ViewBag and method like View(), Json(), File() etc.
Now, in the new version, we are not required to inherit from the Controller base class. So, my question is, how does asp.net mvc knows what resources to add when a controller does not inherit from Controller class? I saw we can add a property and it will be resolved at runtime but how is it done in the asp.net core? Is there a IoC working inside the asp.net core to resolve it?

MVC 6 locates controllers based on a type being public and ending with the suffix Controller. The reality is a bit more complicated than that, and it's also customizable to support other conventions, but that's the essence of it.
Beyond that, MVC 6 has a system to "hydrate" a controller (and views, and other specific types) with required data. This is mostly done with the [Activate] attribute that tells MVC to call into the dependency injection system to populate dependencies on an instance of a class. You can even see in the built-in Controller base class how this is done:
https://github.com/aspnet/Mvc/blob/2353bd911ac137e7858de5bef88af8f7eccaa49f/src/Microsoft.AspNet.Mvc.Core/Controller.cs#L92L99
namespace Microsoft.AspNet.Mvc
{
public class Controller : ...
{
...
[Activate]
public ActionContext ActionContext { get; set; }
[Activate]
public IUrlHelper Url { get; set; }
[Activate]
public IActionBindingContextProvider BindingContextProvider { get; set; }
...
}
}
This is triggered by DefaultControllerFactory here:
https://github.com/aspnet/Mvc/blob/2eef4dd3cf2964136dea9a144b1ee8c21df25c23/src/Microsoft.AspNet.Mvc.Core/DefaultControllerFactory.cs#L41
_controllerActivator.Activate(controller, actionContext);
And that uses an IControllerActivator, and in particular, the DefaultControllerActivator that scans for the [Activate] attribute and assigns values to the properties:
https://github.com/aspnet/Mvc/blob/c16214a53db7f16b06fd9b5a59fa1541e8ecd87a/src/Microsoft.AspNet.Mvc.Core/DefaultControllerActivator.cs

Related

Why the class name of a Controller in ASP.NET Core MVC don't have to be suffix with Controller?

I have been writing ASP.NET MVC for more then 10 years. I always know there is a convention for the Controller class must have it's name suffix with Controller. As I know, it's a hard limit on ASP.NET MVC 5 and before.
One day I saw one of my colleague write a controller in ASP.NET Core 6 like this:
using Microsoft.AspNetCore.Mvc;
namespace WebApplication2.Controllers;
public class Home : Controller
{
private readonly ILogger<Home> _logger;
public Home(ILogger<Home> logger)
{
_logger = logger;
}
public IActionResult Index()
{
return View();
}
}
I shocked. I can't believe there is someone would write Controller code like this. Then I see this:
A controller is an instantiable class, usually public, in which at least one of the following conditions is true:
The class name is suffixed with Controller.
The class inherits from a class whose name is suffixed with Controller.
The [Controller] attribute is applied to the class.
Then I know the Controller suffix is not a MUST. It's just a recommended naming rule. Without Controller-suffix, it might come with come drawback like this. I also found this post. I always believe naming controller with -Controller suffix is a good practices. I just want to know that if anyone know why ASP.NET Core teams decided not have to be naming controllers with Controller suffix?
why the hard limit had been removed
I think the reason may as below:
From this answer we see
By not having the controller naming convention, we'd have two types
with the same name hence would always have to provide namespaces to
distinguish between the two. But because we do have this convention
this is not necessary and no type clashes occur.
By having Controller suffix on controller types, this is not necessary.
In ASP.NET Core the namespace of the controller class is unimportant, although the tradition is maintained by tooling that continues to place controller classes always under a folder named Controllers. In fact, you can now place your controller classes in any folders and any namespaces that you wish. As long as at least one of the following conditions is true.
Besides,controllers need no Controller type name appending because after all they're just like any other class.It is a form of optimization that reduces overhead and the memory footprint.

How to rewrite MVC Actions & properties to ASP Core MVVM Razor Pages

In the new release of ASP CORE 2.0 last month, they introduced Razor Pages, this has me in a loop since the old controller & the model frpm MVC are missing from ASP CORE 2 Razor pages.
My understanding from this page is that we get default binding of the properties using a [BindProperty] attribute outside the Action/Method!!!!, this is because its moved to an MVVM framework as opposed to MVC framework.
Question: While trying to rewrite the traditional actions, since there are no controllers, how to move to the code to the new RazorPages MVVM framework, i.e. and where and how to bind the properties, and actions/handlers?
Since the properties are not in the signature, how does the action/handler know which properties were passed to it from the View/Razor Page?
What is the PageModel?
public class CreateModel : PageModel // what is this pagemodel, is it new or the same old model?
{
private readonly AppDbContext _db;
public CreateModel(AppDbContext db)
{
_db = db;
}
[BindProperty]
public Customer Customer { get; set; } // why is the property outside?
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_db.Customers.Add(Customer);
await _db.SaveChangesAsync();
return RedirectToPage("/Index");
}
}
Razor pages is from my understanding pretty much a replacement for the old asp.net forms where you simply have a page with logic. A bit like how php does things.
If you create a page, let's say Pages/Index2.cshtml you should also create (or it may be created for you in Visual Studio) a "code-behind" file called Pages/Index2.cshtml.cs for example.
// The page file
#page
#using RazorPages
#model IndexModel2
<h2>Separate page model</h2>
<p>
#Model.Message
</p>
// The code-behind file
using Microsoft.AspNetCore.Mvc.RazorPages;
using System;
namespace RazorPages
{
public class IndexModel2 : PageModel
{
public string Message { get; private set; } = "PageModel in C#";
public void OnGet()
{
Message += $" Server time is { DateTime.Now }";
}
}
}
You can still have models and initialize them in the code-behind file. But if you want controllers, I would suggest you do not use razor pages and just use the classical mvc. You can create a new project with it, just don't choose razor pages from the template. You certainly do not need to create a razor pages project. It is just an option. I personally do not really use it since I think is prone for one to repeat code since every code-behind file is just valid for one page afaik.
What is the PageModel?
A pagemodel is simply the code-behind file that does server side logic for your specific page.
I am not sure exactly what you are asking for, you bind models like any other razor page and properties in the code-behind class. The model is the code-behind file in my example.
how does the action/handler know which properties were passed to it from the View/Razor Page?
The action handler knows it by you specifying it in the razor page:
<input asp-for="Customer.Name" />
Please read more about Razor Pages here:
https://learn.microsoft.com/en-us/aspnet/core/mvc/razor-pages/?tabs=visual-studio

Initizlizing a DbContext in a BaseController for MVC6 and EF7?

Following the ASP.Net 5 Getting Started document I see that because of dependency injection at the top of any controller that will access the database I need to put something like
private DbContext _Context;
public HomeController(DbContext Context)
{
_Context = Context;
}
to inject the DbContext into the controller for use. In my ASP.Net 5 MVC 6 Web App every single page will be interacting with the database so I thought I would create a BaseController that the reset of my controllers would inherit from and put the injection code there. I have done this but every controller that inherits from the BaseController give me the error
There is no argument given that corresponds to the required formal parameter 'Context' of 'BaseController.BaseController(DbContext)
I am new to DI and not sure if I am doing things right or even if it can be done the way I want to. Is it possible to do it this way or do I have to put that code on every controller I make, and if I do have to do that how can I write an action in the BaseController that interacts with the DB?
If your base controller has a constructor that takes DbContext then any controller that inherits it must also use the same constructor like this so it can pass the dependency to the base class:
public HomeController(DbContext Context):base(Context)
{
}

ASP.NET MVC Dependency Injection Unity with WCF services - working sample solution

I'm looking for a working sample of an ASP.NET MVC web application that uses Unity and calls an WCF service. I've looked at a lot of explanations on how to add dependency injection to WCF services but frankly I'm a little over my head here. It doesn't help that I'm new to WCF services as well.
I'm currently using Unity with Contructor injection for our ASP.NET MVC applications but so far we aren't using any WCF Web Services. The plan is to start using web services and I'm very confused on how to incorporate Unity with them.
I would love a nice working sample that I could walk through to better understand how to go about it.
I will try to provide you with some guidance.
Let's suppose that you have an existing products WCF service that is defined like this (we don't care about the implementation, that's not important for the moment, you could implement it as you wish, varying from hardcoded values, passing through a SQL database and an ORM, to consuming another service on the cloud):
[DataContract]
public class Product
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
}
[ServiceContract]
public interface IProductsService
{
[OperationContract]
Product Get(int id);
}
Now in your ASP.NET MVC application the first step is to add a service reference to it by pointing to the WSDL. This will generate proxy client classes.
Next you could add the Unity.Mvc3 NuGet package to your MVC application
Then in your Application_Start you could configure the container (obviously this configuration could be externalized into a separate method to avoid cluttering your Global.asax with it):
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
var container = new UnityContainer();
container
.RegisterType<IProductsService, ProductsServiceClient>()
.Configure<InjectedMembers>()
.ConfigureInjectionFor<ProductsServiceClient>(new InjectionConstructor("*"));
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
IProductsService and ProducsServiceClient used in this configuration are the proxy classes generated when you imported the web service definition.
From now on things become trivial:
public class HomeController : Controller
{
private readonly IProductsService _productsService;
public HomeController(IProductsService productsService)
{
_productsService = productsService;
}
public ActionResult Index()
{
var product = _productsService.Get(1);
return View(product);
}
}
and some corresponding Index view:
#model Product
<div>
#Html.DisplayFor(x => x.Name)
</div>
As you can see from this example thanks to the IProductsService abstraction, HomeController is totally decoupled from any concrete implementations of the service. In Today in your Global.asax you decided to use WCF (ProductsServiceClient), but tomorrow you could decide to use some completely different implementation. With a single changes in your DI container configuration you could switch the implementation. Thanks to this weak coupling, your controllers are fully unit testable in isolation.
What is important to realize here is that your business is the Product class and the IProductsService interface. This is what reflects your domain. This is the M in MVC. Implementations could change, but this should stay the same, otherwise you have wrongly identified your business requirements which could be catastrophic in long term.
Remark: one thing that I haven't covered in this example, and which is very important, is the usage of view models. In a properly architected ASP.NET MVC application you should never pass domain models to your views (in this example the Product class). You should use view models. View models are classes that are specifically designed for the requirements of a given view. So in a real world ASP.NET MVC application you would have a ProductViewModel class which to which the Product domain model will be mapped in the controller action and it is this ProductViewModel that will be passed to the view. Those view models should be defined in the MVC project as, contrary to your domain models, they are not reusable and reflect only the specific requirements of a single view. To ease the mapping between your domain models and view models you may take a look at AutoMapper.
It sounds like you're already injecting your MVC Controllers using Unity and all you want to do is start injecting the WCF services you host as well. To inject WCF services, you need to use an IInstanceProvider.
Complete working solution is here:
http://orand.blogspot.com/2006/10/wcf-service-dependency-injection.html
You need 4 very very simple classes:
MyServiceHostFactory
MyServiceHost
DependencyInjectionServiceBehavior
DependencyInjectionInstanceProvider
define those, specify your new ServiceHostFactory:
<%# ServiceHost
Service="NamespaceC.ServiceLayer, AssemblyC"
Factory="NamespaceD.MyServiceHostFactory, AssemblyD"
%>
and you're done.
I know it's a bit late in the game, but I've written a Nuget package to simplify the process of using WCF in your MVC/WebApi app, and it leverages Unity.
Check out Unity.Mvc.Wcf on Codeplex or GitHub for details.

Constructor Dependency Injection in a ASP.NET MVC Controller

Consider:
public class HomeController : Controller
{
private IDependency dependency;
public HomeController(IDependency dependency)
{
this.dependency = dependency;
}
}
And the fact that Controllers in ASP.NET MVC must have one empty default constructor is there any way other than defining an empty (and useless in my opinion) constructor for DI?
If you want to have parameterless constructors you have to define a custom controller factory. Phil Haack has a great blog post about the subject.
If you don't want to roll your own controller factory you can get them pre-made in the ASP.NET MVC Contrib project at codeplex/github.
You don't have to have the empty constructor if you setup a custom ControllerFactory to use a dependency injection framework like Ninject, AutoFac, Castle Windsor, and etc. Most of these have code for a CustomControllerFactory to use their container that you can reuse.
The problem is, the default controller factory doesn't know how to pass the dependency in. If you don't want to use a framework mentioned above, you can do what is called poor man's dependency injection:
public class HomeController : Controller
{
private IDependency iDependency;
public HomeController() : this(new Dependency())
{
}
public HomeController(IDependency iDependency)
{
this.iDependency = iDependency;
}
}
Take a look at MVCContrib http://mvccontrib.github.com/MvcContrib/. They have controller factories for a number of DI containers. Windsor, Structure map etc.
You can inject your dependency by Property for example see: Injection by Property
Using Ninject looks like this:
[Inject]
public IDependency YourDependency { get; set; }

Resources