Is this doable in ASP.NET Core? I want to move existing HomeContoroller logic to IHomeService, but there're action logic, where they call some base controller methods that are difficult to do without controller, so I thought moving action logic to this service and use DI to get the current controller instance in there. Or maybe there's a better approach?
public class HomeController : ControllerBase
{
public MyController(IHomeService service)
{
}
}
public class HomeService : IHomeService
{
public HomeService(ControllerBase controller, IOtherService otherService)
{
}
}
There are two problems with your approach:
HomeService accepts a ControllerBase instance. Since all controller classes inherit from ControllerBase. While you could map a specific type to ControllerBase in your DI system, this would not be very useful.
You are introducing a chicken or the egg problem with the circular dependency on ControllerBase:
HomeController depends on HomeService and
HomeService depends on having a controller (most probably HomeController)
To break the cycle you could remove controller from the constructor of HomeService and pass it as parameter to those methods of IHomeService that need access controller.
Another approach would be to change controller to a property and wire up instances in the constructor:
public class HomeController
{
public HomeController(IHomeService homeService)
{
// Assign controller to service here:
homeService.Controller = this;
}
}
I would recommend to use the method parameter approach, so the compiler forces you to pass controller where it is needed.
Related
I'm currently switching from .net framework to .net core 3.1.
Defining Api Controllers inside the namespace is all fine and works.
Now I have the case, that I need to declare the Api Controllers within another class, like this:
namespace Api.Controllers
{
public class MainClass : BaseClass
{
public MainClass()
{
}
[ApiController]
[Route("Test")]
public class TestController : ControllerBase
{
[HttpGet]
public int GetResult()
{
return 0;
}
}
}
}
The result is, that the Api Controller can't be found after calling the "AddControllers" method inside the "ConfigureServices" method of the startup class.
The MainClass is instantiated before the Startup class will be called.
I've tried to change the global route and defining an area like "{area=Test}/{controller=Test}", or set the ApiController attribute above the MainClass, but none of them worked.
Is there a way to get this working?
Looks like the default ControllerFeatureProvider does not treat nested controller types as controller. You can add (don't need to replace) your custom provider to change that behavior, like this:
public class NestedControllerFeatureProvider : ControllerFeatureProvider
{
protected override bool IsController(TypeInfo typeInfo)
{
if(!typeInfo.IsClass) return false;
if(typeInfo.IsAbstract) return false;
var isNestedType = typeInfo.DeclaringType != null;
var isPublic = true;
var parentType = typeInfo.DeclaringType;
while(parentType != null){
isPublic = parentType.IsPublic;
parentType = parentType.DeclaringType;
}
return isNestedType && isPublic;
}
}
Then add that provider to the ApplicationPartManager in Startup.ConfigureServices like this:
services
.AddMvc()
.ConfigureApplicationPartManager(appPart => {
appPart.FeatureProviders.Add(new NestedControllerFeatureProvider());
});
If you want to replace the default ControllerFeatureProvider, just find it in the FeatureProviders and remove it. Of course then you need to ensure that your custom one should handle everything just like what done by the default logic, something like this:
//for IsController
return base.IsController(typeInfo) || <...your-custom-logic...>;
NOTE: You can refer to the default implementation of ControllerFeatureProvider to learn some standard logic to implement your own logic correctly. The code above is just a basic example. To me, as long as the classes inherits from ControllerBase and not abstract, they can work fine as a controller to serve requests. There would be no serious troubles except some weird conventions (e.g: class name not ending with Controller is still a controller or some standard attributes applied on the controller class are not working ...).
We should not use nested controller classes. Each controller class should be put in a separate file (as a good practice). However the point of this answer (the most interesting part that I'm pretty sure not many know about, is the use of ControllerFeatureProvider which can help you customize the features set in other scenarios). And really if you really have to stick with your design somehow, you of course have to use this solution, no other way.
I have configured my .net-core 2.1 service with a database-context in the start-up method.
services.AddDbContext<DatabaseContext>(options => options.UseSqlServer(Configuration.GetConnectionString(nameof(DatabaseContext))));
Now i could do the following to get my database-context in my controller:
var context = serviceProvider.GetService<DatabaseContext>();
This works very well. But how could i access the Database-Context in a normal class something like this sould be done:
public class MyAccessClass{
public MyAccessClass(){
//Access the ServiceProvider or get the Database-Context class anywhere else
}
}
I don't want to pass the database-context object through the constructor or to initialize the DatbaseContext Class again.
Thanks for your help.
You should take your dependencies through the constructor, preferrably an interface, e.g. IDatabaseContext, but code sample below based on your code. If you add MyAccessClass as a service, e.g. services.AddTransient<MyAccessClass>(), and then use dependency injection in your controller, the database context would be automatically injected in the constructor by the default IoC container in ASP.NET Core.
You shouldn't have it rely on IServiceProvider, the reasoning is that your class wants to make no assumption of implementations, it just needs the database context. Having it rely on IServiceProvider would assume this context, and any possible future dependencies, comes from the IoC in ASP.NET Core which may not be the case (what if you just want to release this as a class library later on?). It would make the MyAccessClass class hard to test and use outside of a controller.
You say in your comment:
"...or get the Database-Context class anywhere else"
That anywhere else is completely flexible by simply accepting the context into the constructor since your class doesn't know where anywhere else is but whatever is creating your class does know!
Example of DI in ASP.NET Core
Take context as a dependency through constructor
public class MyAccessClass{
private readonly DatabaseContext databaseContext;
public MyAccessClass(DatabaseContext databaseContext) {
this.databaseContext = databaseContext;
}
}
Add as service
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<MyAccessClass>();
}
Inject into a controller
public class MyController : Controller
{
private readonly MyAccessClass myAccessClass;
//Happily injected through DI
public MyController(MyAccessClass myAccessClass)
{
this.myAccessClass = myAccessClass;
}
}
Or inject straight into an action
public class MyController : Controller
{
public MyController()
{
}
public IActionResult MyAction([FromServices] MyAccessClass myAccessClass)
{
}
}
In a Sitecore project I've integrated Simple Injector using this article
It uses sitecore pipelines and then uses a method in App_start
namespace BBC.App_Start
{
public class SimpleInjector : IPackage
{
public void RegisterServices(Container container)
{
GetContainer.RegisterServices(container);
container.Register(() => new SitecoreContext(), Lifestyle.Scoped);
container.Register(() => new Container(), Lifestyle.Singleton);
}
}
}
Simply I can inject container into controller constructor but can't have container in View files.
I tried to declare a static property in App-start and save container to it. but still I'm getting no registration type in Views
What is the best way to have container object in views?
As Stephen suggests in his comment, the literal answer to your question is "you shouldn't do that - because it's not really the way MVC and DI are supposed to work". The more detailed answer goes something like this:
The job of your view is to present data that it has been passed via the Model. Views should not really contain logic. Very simple stuff like "if flag is false, hide this block of mark-up" is ok, but the more complex code to work out what the value of the flag is shouldn't be in the view.
MVC tries to make our website code better by encouraging you to separate presentation (the View) from data (the Model) and logic (the Controller). This should make our code easier to work with - So if you have processing that needs doing, then it should really be happening when your controller method runs.
If your view requires some special data, best practice suggests it should work it out in the controller method and pass it to the view in the model. The code might look more like this:
public class MyModel
{
public string SpecialData { get; set; }
}
public class MyController : Controller
{
public ActionResult DoSomething()
{
// do whatever processing is needed
var somethingCalculate = resultFromYourOtherObject();
// do other stuff
var model = new MyModel() { SpecialData = somethingCalculated };
return View(model);
}
}
And then the View just needs to accept the MyModel class as its model, and render the SpecialData property - no logic required.
I think also it's considered a bad idea to have calls to fetch objects from your DI container spread about your codebase. For MVC apps, generally your DI container gets wired in to the process of creating a controller for a request when the app starts up. Rather than passing about a DI Container into your controllers, the DI framework extends the Controller-creation process, and the container isn't exposed outside of this. When the MVC runtime needs to create a controller, the controller-creation logic uses the DI framework to fetch objects for all the controller's dependencies.
Without more detail about what you actually want to achieve, it's difficult to say what the "right" approach to creating your object(s) here is, but the two most common patterns are probably:
1) Constructor injection: Your controller has a parameter which accepts the object required. The DI container creates this object for you at the point where it creates the controller, so your controller gets all its dependencies when it is created. Good for: scenarios where you know how to create the object at the beginning of the request.
public interface IMySpecialObject
{
string DoSomething();
}
public class MyController : Controller
{
private IMySpecialObject _specialObject;
public MyController(IMySpecialObject specialObject)
{
_specialObject = specialObject;
}
public ActionResult RenderAView()
{
// do some stuff
var data = _specialObject.DoSomething();
return View(data);
}
}
As long as IMySpecialObject and a concrete implementation for it are registered with your DI container when your app starts up, all is well.
2) Factory classes: Sometimes, however, the object in question might be optional, or it might require data that's not available at controller-creation time to create it. In that case, your DI framework could pass in a Factory object to your controller, and this is used to do the construction of the special object later.
public interface ISpecialFactory
{
ISpecialObject CreateSpecialObject(object data);
}
public class MyController : Controller
{
private IMySpecialFactory _specialFactory;
public MyController(IMySpecialFactory specialFactory)
{
_specialFactory = specialFactory;
}
public ActionResult RenderAView()
{
// do some stuff
if( requireSpecialObject )
{
var data = getSomeData();
var specialObject = _specialFactory.CreateSpecialObject(data);
var data = _specialObject.DoSomething();
return View(data);
}
return View("someOtherView");
}
}
But a good book on using DI may suggest other approaches that fit your specific problem better.
On an Asp.Net Core project I am injecting Entity Framework DbContext:
public MessageRepository(MyDbContext context) {
}
And the configuration is:
services
.AddEntityFramework()
.AddSqlServer()
.AddDbContext<Context>(x => x.UseSqlServer(connectionString);
Should I create an interface, IMyDbContext, and injecting it instead?
public class MyDbContext : DbContext, IMyDbContext { }
public MessageRepository(IMyDbContext context) {
}
In all ASP.NET Core examples I see the concrete type, MyDbContext, is being injected and not an interface ...
What option should I choose?
Currently working on a project myself, where I decided to go with 2 interfaces like this
public interface IDbContext : IDisposable
{
DbContext Instance { get; }
}
and
public interface IApplicationDbContext : IDbContext
{
DbSet<MyEntity> MyEntities { get; set; }
...
}
My concrete DbContext would then just implement the application context interface
public class ApplicationDbContext : DbContext, IApplicationDbContext
{
public DbContext Instance => this
public DbSet<MyEntity> MyEntities { get; set; }
}
This allows my implementation of the Application context to be injected as the application context interface, while also providing me access to the DbContext methods through the Instance property getter without having to add methods needed from the DbContext class to the interface.
Until now, this works great.
We're always injecting an interface, since it's easier to mock in unit and integration tests.
Are you willing to change the signature of the MessageRepository constructor? It relies on the concrete type.
Do you write tests for your code? Using and interface would make it easier to mock the database context.
If you've answered "no" to one or more of the above, inject the concrete type; otherwise, inject the interface.
[EDIT]
use the following.
context services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());
In my opinion, you should always rely on an interface as #Uli said, but when it comes to the DbContext you need to take in mind that you are exposing all methods of the DbContext of EntityFramework Core
public class MyDbContext : DbContext, IMyDbContext { }
in that case, you don't need to implement any method that you are exposing because DbContext handles that for you.
BUT if EF code change the DbContext and you make an update to your project then you will be on a painful situation of updating your IMyDbContext every time, also all your unit testing. In my opinion, that will give you a lot of headaches.
This answers/questions can help you to understand why https://stackoverflow.com/a/6768712/819153
Unit testing EF - how to extract EF code out from BL?
i am new in MVC and learning. here i am putting some code. so see first
public class HomeController : BaseController
{
private IProductRepository productRepository;
private string strRouteValue;
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
strRouteValue = this.ControllerContext.RouteData.Values["method"].ToString();
this.productRepository = Factory.Create(strRouteValue);
}
[HttpGet]
public ActionResult Index(int id)
{
productRepository.Get(id);
return View();
}
[HttpPost]
public ActionResult Index(Product model)
{
productRepository.Add(model);
return View();
}
}
what Initialize function does ?
every one must say this is where people would init many object, if so then we can do it in constructor of controller too. so what is special about controller Initialize function ?
what is difference between controller Initialize function and controller constructor ?
Check the documentation for that method: MSDN: Controller.Initialize():
Initializes data that might not be available when the constructor is called.
This method cannot be called directly. Override this method in order to provide additional processing tasks before any ActionResult methods are called, such as setting the thread culture or assigning a custom provider for TempData objects. If you override this method, call the base control's Initialize method.
And as I suggested on your previous twenty or so questions about MVC, Dependency Injection and controller instantiation: stop piecing together advice from poor blogposts and irrelevant answers on SO. Buy a decent MVC book and read it from cover to cover. Then do the same with a book about Unit Testing. You will never get a thorough understanding of things if you continue this way.
There is a difference between instantiating a controller and initializing it. Instantiating is moreover a .NET concept not MVC, so every class is automatically instantiated using default constructor. So, constructor is basically a concept of class whereas Initializing is concept of action method. We override Initialize() method in order to provide additional processing tasks before any ActionResult methods are called, such as setting the thread culture or assigning TempData objects etc....