Autofac and Automapper - Profiles registred but not resolved - asp.net-core

I have a ASP.NET Core app that uses Autofac to inject Automapper.
Firstly, I'm trying to register Automapper profiles from Assembly:
builder.RegisterAssemblyTypes(assembly)
.AssignableTo<Profile>()
.OnActivated(e => System.Diagnostics.Debug.WriteLine(e.Instance.GetType()))
.AutoActivate();
I have introduced some debug logs to check whether my profiles are registred. And it works, I see my profiles in debug window.
Than I register Automapper and try to resolve previously registered profiles:
builder.Register(ctx => new MapperConfiguration(cfg =>
{
var resolvedProfiles = ctx.Resolve<IEnumerable<Profile>>(); // Length is 0
foreach(var resolvedProfile in resolvedProfiles)
{
cfg.AddProfile(resolvedProfile);
}
}).CreateMapper())
.SingleInstance();
Doesn't work unfortunantely. Autofac doesn't resolve any of the previously registered profiles. Why and how can I fix it?

Just add the type to your registration (via .As<Profile>()):
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.AssignableTo<Profile>()
.As<Profile>()
.OnActivated(e => System.Diagnostics.Debug.WriteLine(e.Instance.GetType()))
.AutoActivate();

Related

What is the MassTransit pattern for retrieving RabbitMQ password from a dependency?

Using MassTransit.RabbitMq 8.0.9, in a .Net Core 3.1 project using AspNetCore 3.1.10 and IServiceContainer. The password for RabbitMq is stored in a secrets vault, accessible from a dependency-injected interface. All of the examples I've been able to find just get the password from configuration.
I'd like to do something like
var secrets = serviceProvider.GetRequiredService<ISecretRetrieval>();
var rabbitPassword = secrets.GetRabbitMqPassword();
and then hand that password to IRabbitHostConfigurator, but inside UseMassTransit...UseRabbitMq, there isn't an IServiceProvider instance that I've seen.
Alternatively, I could create a configuration object with a constructor-injected dependency on ISecreteRetrieval. I see examples for IConfiguration<MassTransitHostOptions> that show how to create and register my own class with its own constructor dependencies. Can I do that with IConfiguration<RabbitMqHostSettings> even though RabbitMqHostSettings is an interface, not a class like MassTransitHostOptions?
In the UsingRabbitMq block, the first parameter is a service provider.
x.UsingRabbitMq((context, cfg) =>
{
var secrets = context.GetRequiredService<ISecretRetrieval>();
cfg.Host("hostname", h =>
{
h.Password(secrets.GetRabbitMqPassword());
});
});

Migration to Minimal API - Test Settings Json not overriding Program

Thanks to this answer: Integration test and hosting ASP.NET Core 6.0 without Startup class
I have been able to perform integration tests with API.
WebApplicationFactory<Program>? app = new WebApplicationFactory<Program>()
.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
});
});
HttpClient? client = app.CreateClient();
This has worked using the appsettings.json from the API project. Am now trying to use integrationtestsettings.json instead using:
IConfiguration configuration = new ConfigurationBuilder()
.SetBasePath(ProjectDirectoryLocator.GetProjectDirectory())
.AddJsonFile("integrationtestsettings.json")
.Build();
WebApplicationFactory<Program>? app = new WebApplicationFactory<Program>()
.WithWebHostBuilder(builder =>
{
builder.ConfigureAppConfiguration(cfg => cfg.AddConfiguration(configuration));
builder.ConfigureServices(services =>
{
});
});
_httpClient = app.CreateClient();
I have inspected the configuration variable and can see the properties loaded from my integrartiontestsettings.json file. However, the host is still running using the appsettings.json from the server project.
Previously, in .Net5, I was using WebHostBuilder and the settings were overridden by test settings.
WebHostBuilder webHostBuilder = new();
webHostBuilder.UseStartup<Startup>();
webHostBuilder.ConfigureAppConfiguration(cfg => cfg.AddConfiguration(_configuration));
But cannot get the test settings to apply using the WebApplicationFactory.
It seems the method has changed.
Changing:
builder.ConfigureAppConfiguration(cfg => cfg.AddConfiguration(configuration));
To:
builder.UseConfiguraton(configuration);
has done the trick.
builder.ConfigureAppConfiguration, now it's configuring the app (after your WebApplicationBuilder.Build() is called) and your WebApplication is created.
You need to "inject" your configurations before the .Build() is done. This is why you need to call UseConfiguraton instead of ConfigureAppConfiguration.

Prevent ASP.NET Core discovering Controller in separate assembly

I've got an ASP.NET Core API that references a nuget package that contains a Controller.
By default, this controller is registered and can respond to requests. However, I only want to add this in certain circumstances - e.g. if it's in the DEV environment.
My Startup looks like this:
services.AddControllers()
.AddMvcOptions(cfg => {
cfg.Filters.Add(new CustomExceptionFilterAttribute())
});
I expected I'd need to call AddApplicationPart(typeof(ClassInPackage).Assembly) after calling AddCointrollers to register this controller?
Can someone advise a way I can enable / disable the registration of this controller?
Ok, I've found a solution - remove the ApplicationPart that contains the Controller. Any other dependencies in the assembly can still be used.
In Startup.cs / wherever you do your IoC:
if(hideControllerFromOtherAssembly)
{
var appPartManager = (ApplicationPartManager)services.FirstOrDefault(a => a.ServiceType == typeof(ApplicationPartManager)).ImplementationInstance;
var mockingPart = appPartManager.ApplicationParts.FirstOrDefault(a => a.Name == "MyMockLibrary.Namespace");
if(mockingPart != null)
{
appPartManager.ApplicationParts.Remove(mockingPart);
}
}
You can also manipulate ApplicationParts via the extension method:
AddMvc().ConfigureApplicationPartManager()
This wasn't suitable for me as I'd written an extension method in my nuget package
https://learn.microsoft.com/en-us/aspnet/core/mvc/advanced/app-parts?view=aspnetcore-5.0

Autofac v3, Web Api and the Authorize attribute

I have a project with both Mvc Controllers and Web Api controllers.
Everything is wired up using autofac 3 :
var builder = new ContainerBuilder();
builder.Register(x => NHibernateConfigurator.BuildSessionFactory()).SingleInstance();
builder.Register(x => x.Resolve<ISessionFactory>().OpenSession()).InstancePerHttpRequest();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
builder.Register(x => new WebApiTransactionAttribute()).PropertiesAutowired();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<ExtensibleActionInvoker>().As<IActionInvoker>();
builder.RegisterWebApiFilterProvider(config);
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
The problem is that when I add the [Authorize] attribute (the one from System.Web.Http) I get :
Cannot choose between multiple constructors with equal length 1 on type 'System.String'.
Select the constructor explicitly, with the UsingConstructor() configuration method, when the component is registered.
Can someone point me in the right direction ?
Thanks.
Apologies, this is somewhat of a guess but the AnyConcreteTypeNotAlreadyRegisteredSource here looks like it may be too broad a registration:
Provides registrations on-the-fly for any concrete type not already registered with the container.
The Autofac Filter Provider could then be registering all kinds of framework concrete types and getting in a muddle.
It may be if you take this out and put in more targeted registrations it will help, perhaps register by convention instead.

Autofac returns different instance in asp.net mvc web api

I'm using autofac in an asp.net mvc and webapi project.
In the configuration I'm doing this :
var builder = new ContainerBuilder();
builder.Register(x => NHibernateConfigurator.BuildSessionFactory()).SingleInstance();
builder.Register(x => x.Resolve<ISessionFactory>().OpenSession()).InstancePerHttpRequest();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
Now the problem is that in an api controller if I inject ISession via the constructer and also call
DependencyResolver.Current.GetService<ISession>()
it will return 2 different instances.
I'm guessing the problem is because of these 2 lines :
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
But how can I make it return the same instance ?
Edit:
Just to be more clear - I'm expecting the same instance of ISession per HttpRequest. Right now I'm getting different instances on the same request.
Thanks
Ok, I think I found the answer - it can't be done .. at least not with DependencyResolver.
ASP .Net 4 Web Api RC + Autofac manual resolving
I did what it said in the comments, added IComponentContext to the constructor and used that to resolve what I needed.
It seems to be working. Thanks.