Windsor Dependencies injection in mvc 4 rc into parametereized apicontroller - asp.net-mvc-4

So, i used Mark Seemann's example to do dependency injection with Windsor in MVC 4 RC Web Api, but i get an exception saying that it can't resolve the dependencies to my ApiController
public class StatisticsController : ApiController
{
private readonly ILogger _logger;
private readonly IClickMessageProducer _producer;
public StatisticsController(ILogger logger,
IClickMessageProducer clickMsgProducer)
{
_logger = logger;
_producer = clickMsgProducer;
}
public string Get(string msg, string con) {...}
}
My Global.asax looks like this:
protected void Application_Start()
{
// different configs removed for brevity
BootstrapContainer();
}
private static IWindsorContainer _container;
private static void BootstrapContainer()
{
_container = new WindsorContainer()
.Install(FromAssembly.This(), new ProducerInstaller())
.Install(FromAssembly.This(), new WebWindsorInstaller());
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator),
new WindsorHttpControllerActivator(_container));
}
The Installers gives Windsor the references needed to IClickMessageProducer. I have it working with IController in a genuine MVC 4 project so i'm confident that part is working.
To specify, this is the error message i get, when trying to access a method in StatisticsController with a GET call to the API:
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Can't create component 'APIMVC.Controllers.StatisticsController'
as it has dependencies to be satisfied.
'APIMVC.Controllers.StatisticsController' is waiting for the following
dependencies: - Service 'Castle.Core.Logging.ILogger' which was not registered.
</ExceptionMessage>
<ExceptionType>Castle.MicroKernel.Handlers.HandlerException</ExceptionType>
<StackTrace>...</StackTrace>
</Error>
The call being something like this:
"http://localhost:60000/api/statistics?msg=apitest&con=apimvc"
If anyone has a working example or just a comment to the problem about my Windsor implementation i'll be happy to see it.

Your ILogger implementation isn't registered with Windsor. Remove the ILogger parameter from StatisticsController and try again. If it works, you're going to need to register an ILogger implementation.

_container = new WindsorContainer().Install(FromAssembly.This(), new ProducerInstaller()).Install(FromAssembly.This(), new WebWindsorInstaller());
this was the part at fault. As you can see I call Install(FromAssembly.This()) twice witch caused the LoggerInstaller to try to add a LoggingFacilitytwice causing an error.
The new implementation would look like this:
_container = new WindsorContainer().Install(FromAssembly.This(), new ProducerInstaller(), new WebWindsorInstaller());

Related

ASP.NET Core WebApplicationFactory Server.Host is not accessible

I am using Microsoft.AspNetCore.Testing.WebApplicationFactory<Startup> in my integration tests. I'm trying to access _fixture.Server.Host property, but it's throwing an exception
"The TestServer constructor was not called with a IWebHostBuilder so IWebHost is not available".
Could anyone tell, why is it so?
Here is my code:
public class OneTest:IClassFixture<WebApplicationFactory<Startup>>{
private readonly WebApplicationFactory<Startup> _fixture;
public OneTest(WebApplicationFactory<Startup> fixture){
_fixture=fixture;
}
[Fact]
public async Task TestCanBeAuthenticated(){
var host=_fixture.Server.Host; // Exception is being thrown
// ... code ...
}
}
If you check the TestServer class,you could found serval different constructors:
The error just indicates the server created by WebApplicationFactory doesn't contain the instance of IWebHostBuilder in it's constructor
So,what's the purpose of accessing the host of TestServer in your test?

ASP.NET Core 3.1 get connection string in class library

I am crazy confused. I refactored my solution into two projects; a web and a DAL. I am trying to follow the principles of DI, but I don't know how to get my connection to the DAL.
Here is what I have so far:
In web project:
public void ConfigureServices(IServiceCollection services)
{
// connection string
var connectionString = new ConnectionString(Configuration.GetConnectionString("connectionstringfromappsettings"));
services.AddSingleton(connectionString);
...
}
Class in web project to get value
public sealed class ConnectionString
{
public ConnectionString(string value) => Value = value;
public string Value { get; }
}
Class in DAL repo that I'm not sure how to get configuration:
public class ContactRepo : IContactRepository
{
private readonly ConnectionString _connectionString;
public ContactRepo(ConnectionString connectionString)
{
_connectionString = connectionString;
}
...
}
Thanks for the help in advance.
Your are mapping your configuration to ConnectionString object located on your main Web app(which you called WWW project). You have a dependency from Web project to DAL class library as expected. you can not call the classes in Main app from DAL project. You may locate your ConnectionString at DAL project or some another layer which both projects have dependency towards. Also, you can directly call Configuration.GetSection(..)... when Configuration is the injected instance of IConfiguration.
I had the class ConnectionString in the WWW project.
My fault:
This post helped me walk it though a bit.
Here
I think you should research Inversion Of Control(IOC) on google. For more deep info you should look Castle Windsor and Ninject packages.

ASP.NET CORE 2.1 Angular Template with Unity Dependency Injection

I am attempting to make a new project that is dependent on some old libraries. Those libraries are using Unity for dependency injection. They are also using the Property Injection with the DependencyAttribute. My new project is a ASP.NET Core 2.1 application using the provided Angular Template. I have configured it for unity doing the following:
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
var container = /* calls internal method to get container */;
return WebHost.CreateDefaultBuilder(args)
.UseUnityServiceProvider(container)
.UseStartup<Startup>();
}
The container is created in an internal library and works fine everywhere else.
When I use Constructor Injections the services are injected correctly, however using the Property Injection does not work. All properties injected in this way end up NULL. Any ideas how to get Property Injection to work?
Constructor Injection (works):
private readonly IServiceA ServiceA;
public ControllerA(IServiceA serviceA)
{
this.ServiceA = serviceA;
}
Property Injection (doesn't work):
[Dependency]
public IServiceA ServiceA { get; set; }

Log hangfire events using existing serilog

Im new to hangfire, and im trying to setup a way to log the events using my existing serilog logger in an asp.net web api. Here is my logger class:
public static class LoggerInitializer
{
private static ILogger CreateLog()
{
var settings = Settings.Instance;
Log.Logger = new LoggerConfiguration().
MinimumLevel.Debug().
WriteTo.RollingFile(settings.LoggerDebugDirectory +"Debug-{Date}.txt", restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Debug,
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}").
WriteTo.RollingFile(settings.LoggerVerboseDirectory + "Debug-{Date}.txt").
CreateLogger();
return Log.Logger;
}
static ILogger _logger;
public static ILogger GetLogger()
{
if (_logger != null)
return _logger;
return _logger = CreateLog();
}
}
and in my startup file I add the code from the hangfire documentation:
GlobalConfiguration.Configuration
.UseSqlServerStorage(Settings.Instance.NLSContextConnectionString);
app.UseHangfireDashboard();
app.UseHangfireServer();
My hangfire works perfectly, but how do i enable make hangfire use my serilog?
It's possible that Hangfire is initializing and caching its own internal logger before CreateLog() is being called by the application.
To test this theory, try moving the code that initializes Log.Logger to the very beginning of the app's startup code, e.g. in Global.Application_Start() or similar.
In Hangfire 1.6.19 (and maybe before that, I did not check) adding the NuGet Package to your project gives you an extension method on IGlobalConfiguration :
configuration.UseSerilogLogProvider();

ASP.NET Core Identity - Extending Password Hasher

I'm working towards moving an application from Web Forms to MVC and opted to go with MVC 6 using ASP.NET Core.
In my current application I have a custom password hasher used with Identity. The implementation is very simple in my custom UserManager class:
public ApplicationUserManager()
: base(new UserStore<IdentityUser>(new AuthContext()))
{
this.PasswordHasher = new SqlPasswordHasher();
}
I'm trying to do the same with .NET Core but the PasswordHasher property doesn't exist in UserManager. I see that the constructor will take an IPasswordHasher parameter so I tried this:
public ApplicationUserManager(IUserStore<ApplicationUser> store, IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<ApplicationUser> passwordHasher, IEnumerable<IUserValidator<ApplicationUser>> userValidators,
IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators, ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors, IServiceProvider serviceProvider, ILogger<UserManager<ApplicationUser>> logger)
: base(store, optionsAccessor, new SqlPasswordHasher(), userValidators, passwordValidators, keyNormalizer, errors,
serviceProvider, logger)
{
}
In SqlPasswordHasher I'm simply overriding the VerifyHashedPassword method which looks like this:
public override PasswordVerificationResult VerifyHashedPassword(ApplicationUser user, string hashedPassword, string providedPassword)
{
// My custom logic is here
...
}
However, the above doesn't work. I have a breakpoint set in the VerifyHashedPassword method of SqlPasswordHasher and it doesn't get triggered.
I thought I was going about this the wrong way and I should be utilizing DI to accomplish this. I updated the constructor of my user manager so that it doesn't instantiate a new SqlPasswordHasher, but uses the default interface parameter instead:
public ApplicationUserManager(IUserStore<ApplicationUser> store, IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<ApplicationUser> passwordHasher, IEnumerable<IUserValidator<ApplicationUser>> userValidators,
IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators, ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors, IServiceProvider serviceProvider, ILogger<UserManager<ApplicationUser>> logger)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors,
serviceProvider, logger)
{
}
Then in Startup.cs I added a scoped service:
services.AddScoped<IPasswordHasher<ApplicationUser>, SqlPasswordHasher>();
But again, this doesn't work and the breakpoint in SqlPasswordHasher is never triggered.
I have a similar line for my custom Sign In Manager:
services.AddScoped<SignInManager<ApplicationUser>, ApplicationSignInManager>();
That works great. The ApplicationSignInManager takes a UserManager parameter and I can see that the UserManager takes an IPasswordHasher parameter.
I'm assuming SignInManager uses the UserManager which uses the PasswordHasher. So my question is, how do I get the UserManager to use my custom Password Hasher? Or if thats not the case, how do I get the SignInManager to use my Password hasher?
EDIT: I've been able to confirm that when my ApplicationUserManager is instantiated, my SqlPasswordHasher is being used in the constructor so the DI is working properly. I just can't figure out why my override of VerifyHashedPassword is not being triggered.
It turns out the problem was not related to code at all. Adding my SqlPasswordHasher to the services via
services.AddScoped<IPasswordHasher<ApplicationUser>, SqlPasswordHasher>();
worked perfectly.
The problem was with how I migrated the data. Since I was using an existing database that was being used with an older version of Identity, I had to add the following fields to my existing AspNetUsers table:
NormalizedUserName
ConcurrencyStamp
LockoutEnd
NormalizedEmail
However I didn't populate the NormalizedUserName or NormalizedEmail fields. So that's why it was never triggering my override of VerifyHashedPassword; because it never found my user since it was looking up based on NormalizedUserName.
Once I populated those fields it started triggering my VerifyHashedPassword method.