ASP.NET Core api controller not being instantiated - c#

I've created an API controller in my .NET Core 3.1 application (with an Angular frontend, C#).
For some strange reason its not being instantiated, if I try to call any methods on the controller from my Typescript service, nothing happens, it just skips past the call, no error message is generated but the method in the controller isn't accessed.
I've traced it to the fact that the controller isn't being instantiated but I can't see why. Has anyone else experienced this issue?
I inject a service into the constructor, but the service is being added to ioc at startup so it cant be that (along with the other services used), can anyone help?
This is part of the controller code, I've added a breakpoint to the constructor, but its not being hit. I had exactly the same issue with a previous controller I had added, I spent ages trying to figure out why it wasn't being instantiated, then suddenly, it was, despite the fact that I had made no code changes, so I'm baffled by this.
public class RepController : BaseApiController
{
private readonly IRepService _repService;
private readonly ILookupService _lookupService;
private readonly IUserContext _userContext;
public RepController(IRepService repService,
ILookupService lookupService,
IUserContext userContext)
{
Assert.NullCheck(repService);
Assert.NullCheck(lookupService);
Assert.NullCheck(userContext);
_repService = repService;
_lookupService = lookupService;
_userContext = userContext;
}
}

I think the problem is inheriting form BaseApiController. You should try setting that to Controller.
You should also make your that you specify routing on your controllers. A link to the docs about endpoint routing.
Maybe your endpoints dont have a correct return type, this should be of Type ActionResult

I found the problem, I had this decorator
[Route("api/[controller]"]
instead of this
[Route("api/[controller]/[action]"]
its working now

Related

Is possible to alter Scoped service from controller?

I am pretty much new in Asp.Net Core world and I am playing around with services now. I had an idea to create instance of class I created (named CourseEditor) that will be accessible through whole controller (in every action on that very controller). So I added the class as a Scoped service to Startup.cs and method ConfigureServices:
services.AddScoped<CourseEditor>();
Now I have my controller CourseEditorController.cs.
[Authorize(Roles = "Admin")]
[ViewLayout("_CourseEditorLayout")]
public class CourseEditorController : Controller
{
private CourseEditor _courseEditor;
private readonly SignInManager<IdentityUser> _signInManager;
public CourseEditorController(SignInManager<IdentityUser> signInManager, CourseEditor courseEditor)
{
_courseEditor = courseEditor;
_signInManager = signInManager;
}
[HttpGet]
public async Task<IActionResult> OpenCourse(int courseId)
{
Database db = new Database();
_courseEditor = await db.LoadCourseForEditByIDAsync(courseId);
return RedirectToAction("Index");
}
public IActionResult Index()
{
return View(_courseEditor);
}
public IActionResult EditHead()
{
return View(_courseEditor);
}
}
And I am quite stuck. Because every time I load into this controller, the _courseEditor is rewritten to default values. So now I am trying to figure out, how to alter the parameters of the service CourseEditor itself so it won't "reset" the _courseEditor every time I jump between actions.
So basically I am trying to alter the service CourseEditor.Title in controller CourseEditorController.cs because it's by default null and it's rewriting the _courseEditor.Title from actual text to null. Can I do that?
//Edit:
I forgot to explain how the Controller works. So basically when user moves to this "editor" controller, first it goes through action "OpenCourse" that will load all the data as _courseEditor.Title and stuff from MySQL Database. But as you can see, after that there is a RedirectToAction("Index"). So the _courseEditor is run through the constructor and there is everything set back to null since it's the value that was set when the program was initializing the service. Or at least I think this is happening.
So the solution is to add the service not as a Scoped service, but as a Singleton.
services.AddSingleton<CourseEditor>();
Unfortunately for me this does not solve anything since Singleton represents one instance for whole application which means every user will see data from this very instance of editor. They cannot create their "own" instance of editor.
One way how to reach that possibility of creating more instances is via ConcurrentDictionary (thank you #Legacy Code for the explanation). More details about this static dictionary is here in Asp.net core docs:
https://learn.microsoft.com/cs-cz/dotnet/api/system.collections.concurrent.concurrentdictionary-2?view=netcore-3.1
Second way is probably to just recover the data from database on every Action call.
Another way might be to use some kind of transporter as Cookies but this approach is not very secure since User can manipulate with cookies and it might be really hard to store a complex object there.

Is changing contextLifetime to Singleton a correct solution to fix the following error?

I am using Asp.net Core 2. Consider the following classes:
public class BlogDbContext: DbContext
{
.....
}
public interface IBlogData { ... }
public class BlogData : IBlogData
{
private BlogDbContext _context;
public BlogData(BlogDbContext context) { ... }
.......
}
When I used the default value contextLifetime: ServiceLifetime.Scoped as follows,
public void ConfigureServices(IServiceCollection services)
{
.....
services.AddDbContext<BlogDbContext>(...);
.....
services.AddSingleton<IBlogData, BlogData>();
}
Compilation, first migration and first database update were performed without any error. But I got the following error when visiting the page for the first time.
InvalidOperationException: Cannot consume scoped service 'MyProject.Data.BlogDbContext' from singleton 'MyProject.Services.IBlogData'.
Question
Is it correct if I fix the error by changing contextLifetime as follows ?
public void ConfigureServices(IServiceCollection services)
{
.....
services.AddDbContext<BlogDbContext>(...,contextLifetime: ServiceLifetime.Singleton);
.....
services.AddSingleton<IBlogData, BlogData>();
}
Note: this problem is specific to Asp.Net Core 2.0.
It's because you are trying to use a scoped service from a singleton service.
This is new to asp.net core 2.0. Only singleton services can be consumed by a
signleton service.
You need to add BlogData as Scoped.
No, you should generally always used scoped for DbContext in asp.net core that way it gets created once per request and is automatically disposed for you at the end of the request.
You are not really showing the code where the error is happening, but my guess is it is happening because you are running some code in startup to run the migrations. If you confirm that or show the code where the error is actually happening I could offer more help
Old question without a great answer.
The reason you get this error is because a scoped service has to be recreated every time a new page request is made (Atleast within ASP.net). How it does that is everytime a service is "requested" by way of injection (For example within a constructor), it caches the first time it's requested, then for subsequent requests, it simply returns that same instance. At the end of the page load, it trashes this instance.
Now a singleton is instead cached that first time it's requested, but it's never disposed. Every request ever made for the service will return the exact same instance.
The problem is if you have a parent service that is singleton that then references a child service that is scoped. That first request, the parent and child are created. The second request, well the parent is a singleton so it isn't recreated. Now we have a problem, because the child service is scoped, how can it be created for each request if the thing that is requesting it (And thus kicking off the DI), is a singleton? It can't. So an exception is thrown.
Interestingly, it is more about saving yourself from hanging yourself more than anything. For example, if you replace the scoped instance with a transient one, you will still have the same "problem", but it won't throw an exception.
More info here if you need further examples : https://dotnetcoretutorials.com/2018/03/20/cannot-consume-scoped-service-from-singleton-a-lesson-in-asp-net-core-di-scopes/

ASP.NET5 500 Error when using the repository pattern

Edit 3: I don't know what I did to fix this, but my problem went away. I think the most important things I found, and to take away if you have this issue is that in ASP.NET 5 MVC 6 we have to add middleware for almost everything, including the developer error pages using app.UseDeveloperExceptionPage();... after that I realized that the stack trace might be referring to other classes referenced from within the controller such as the repository and the context object, in my case. I messed with them a bit but I don't remember changing anything drastic. I think I may have had a typo in the constructor of my repository class.
I'm getting a 500 Internal Server Error with the controller, but it displays the static json data without the constructor just fine. I suspect there is something going wrong with the built-in dependency injection but I can't figure it out as I'm not getting any errors or warnings during build.
Edit: I see absolutely nothing in browser when navigationg to http://localhost:5000/api/blogpost/. Just a blank page. No Error. No nothing. Using Postman to send the HTTP request, I get error code 500. Again, by commenting out the constructor I see {"name":"Cameron"}, and get code 200 OK in both browser and Postman. There are no exceptions within VS, and there are no errors in the Output console either.
Edit 2: I found the middleware app.UseDeveloperExceptionPage(); and it produces MissingMethodException: No parameterless constructor defined for this object. - If I make a parameterless constructor, it produces: InvalidOperationException: Multiple constructors accepting all given argument types have been found in type 'MyApp.Controllers.BlogPostController'. There should only be one applicable constructor. - It seems like something whacky is going on with ASP.NET5's dep injection?
Here's my controller:
using MyApp.Data.Repository;
using MyApp.Models;
using Microsoft.AspNet.Mvc;
using System;
namespace MyApp.Controllers
{
[Route("api/[controller]")]
public class BlogPostController : Controller
{
// If I comment out this var and the constructor this works.
private IBlogPostRepository _repository;
// If I leave them here, I get a 500 Internal Server Error.
// If I set a breakpoint here, it never fires.
// If I create a constructor that takes zero args, it never fires.
// I do not get any errors.
public BlogPostController(IBlogPostRepository repository)
{
_repository = repository;
}
[HttpGet]
public JsonResult Get()
{
return Json(new { name = "Cameron" });
}
}
}
Here is Startup.cs ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
// MVC 6
services.AddMvc();
// EntityFramework 7
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<AppDbContext>(options =>
{
// Will use the last Data:DefaultConnection:ConnectionString
// that was loaded from the config files in the constructor.
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]);
});
// Injections
services.AddTransient<AppDbContextSeedData>();
services.AddScoped<IBlogPostRepository, BlogPostRepository>();
}
I had this same problem. My constructor was did not have a scope. The class was public, so I added "public" before the constructor and it worked!
So, NOT WORKING:
public class VisualizationsController : Controller
{
VisualizationsController() {...}
}
And WORKING:
public class VisualizationsController : Controller
{
public VisualizationsController() {...}
}
I came across a situation where I was adding another controller and forgot to update the Startup.cs page.
In Startup.cs, under the ConfigureServices, try adding:
services.AddSingleton<ISomeRepository, SomeRepository>();
services.AddSingleton<ISomeOtherRepository, SomeOtherRepository>();
Assuming you're passing in ISomeOtherRepository in your new controller constructor

SimpleMembershipProvider intermittently returning wrong user

I am administrator of a small practice project web application, AngularJS front-end pulling its back-end data from a C#/.NET WebAPI, and I'm handling security using the SimpleMembershipProvider.
I suspect that the way I implemented said security is not the best (I'm told ASP.NET Identity is now the way to go?) but that's another question altogether.
The issue that I'm very bewilderingly running into is that I get occasional reports that on a given page load to display a particular user's data, it returns somebody else's. Reloading the page fixes the issue (evidently) and I haven't been able to duplicate the scenario myself, or figure out anything particularly consistent in the users to which this happens.
None of the information being displayed is at all sensitive in nature (the app's just a friendly front end for an already public third-party API) so I'm not in panic mode about this, but I am both concerned and confused and want it fixed.
Here is what one of my API controller endpoints looks like:
[Authorize]
public class UserController : ApiController
{
private static int _userId;
private readonly IUserProfileRepository _userProfileRepository;
public UserController()
{
_userProfileRepository = new UserProfileRepository(new DatabaseContext());
_userId = WebSecurity.GetUserId(User.Identity.Name);
}
public UserProfileDto Get()
{
return _userProfileRepository.GetUserProfileById(_userId).ToDto();
}
}
Any feedback on where I might be going wrong here or what might be causing the intermittant inconsistency would be very much appreciated. (Laughter also acceptable if the way I handled this is just really bad. :P )
Static class fields are shared by all instances/threads of the same AppDomain (in your case - process). Different http requests are processed by threads running in parallel. Any two threads running [almost] at the same time may (will) change the value of _userId. You are assigning _userId in the constructor of your controller, and a new instance of this controller is created for each http request that is to be responded to by UserController. Therefore, this assignment will happen multiple times.
You will have hard time replicating this problem, since you are a single user testing the code, hence there are no overlapping request threads.
Remove static specifier from the _userId field declaration of the controller class.
Note: make sure that DatabaseContext is disposed of. One place that can be used for this is the overriden Controller.Dispose.
Change the Get to retrieve the user id rather than from a static variable:
public UserProfileDto Get()
{
return _userProfileRepository.GetUserProfileById(WebSecurity.GetUserId(User.Identity.Name)).ToDto();
}

Ninject Creating an Extra Instance when using NinjectHttpModule

For some reason Ninject is creating an additional instance of my object when I use NinjectHttpModule in my MVC 4 app.
If I use NinjectHttpModule (the Ninject.MVC3 default) but do not actually have any IHttpModule classes that require constructor injection, it works fine. But as soon as I create a class that implements IHttpModule and that requires constructor injection, Ninject for some reason creates two instances of my object.
I added some tracking code to the class that is being duplicated to verify that it was being duplicated. Every time an instance is created, the static count variable is incremented:
namespace Trigger.Events
{
public class TriggerEventRegistry : ITriggerRegistry
{
private static int count;
public TriggerEventRegistry()
{
TriggerEventRegistry.count++;
}
}
}
Here is my IHttpModule:
namespace TriggerDevelopment.ApplicationTriggers
{
public class RegisterTriggerComponentsHttpModule : IHttpModule
{
ITriggerEventRegistry eventRegistry;
public RegisterTriggerComponentsHttpModule(ITriggerEventRegistry eventRegistry)
{
this.eventRegistry = eventRegistry;
}
}
....
}
By the time a TriggerEventRegistry is injected into my controller (on the same request), the TriggerEventRegistry.count equals 2. If I comment out the constructor on RegisterTriggerComponentsHttpModule, then the value of TriggerEventRegistry.count equals 1 (which is should since there should only be one instance/request).
Here is the binding code:
Bind<ITriggerEventRegistry>().To<TriggerEventRegistry>().InRequestScope();
Any help on this would be greatly appreciated!
Note
I even made a request to my app using curl to avoid multiple HTTP requests being made by the browser looking for assets, a favicon or something like that. Still no joy.
Update
Upon further investigation, I'm also seeing that the ctor and the Init method of RegisterTriggerComponentsHttpModule is being called twice.
It's going to call your HttpModule as many times as there are requests. For instance, most web browsers submit at least two requests, the page request and a favicon request. Try adding something like an image to the page, and see if you get three requests...

Categories