Autofac can't resolve dependency - c#

I can't resolve registered type IQMService.
Error:No accessible constructors were found for the type '__ASP.FastObjectFactory_app_web_gmbeg5il'.
Below is the code invoke Resolve:
public IQMService qMService;
public QMModule QMModule;
public PageBase() {
qMService = AutofacDependency.Resolve<IQMService>();
QMModule = new QMModule(qMService);
}
Definition of model:
public interface IQMService{
somefunction()
}
public class QMService : IQMService{
public QMService()
{
someprocess();
}
}
Previously working code of AutofacDependency() defined:
static AutofacDependency()
{
Assembly[] assemblys = AppDomain.CurrentDomain.GetAssemblies ();
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes (assemblys);
builder.RegisterModule (new ConfigurationSettingsReader ("autofac"));
container = builder.Build();
}
After I upgrade .net from 4.0 to 4.8, and update autofac from 3.5.2 to 6.4.0, ConfigurationSettingsReader no longer exist, so I have to update the config file and change the code to below, not work,
static AutofacDependency()
{
Assembly[] assemblys = AppDomain.CurrentDomain.GetAssemblies ();
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes (assemblys);
//builder.RegisterModule (new ConfigurationSettingsReader ("autofac"));
ConfigurationBuilder cfgbuilder = new ConfigurationBuilder();
cfgbuilder.AddXmlFile("autofac.config");
var module = new ConfigurationModule(cfgbuilder.Build());
builder.RegisterModule(module);
container = builder.Build();
}
I also test this way
static AutofacDependency()
{
Assembly[] assemblys = AppDomain.CurrentDomain.GetAssemblies ();
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes (assemblys);
builder.RegisterType<Strix.QM.Service.QMService>().As<Strix.QM.Service.IQMService>().InstancePerLifetimeScope();
container = builder.Build();
}
But I always get the error No accessible constructors were found even though I can it's defined. Thank you if any help, and if any necessary information missing please comment I will append it.

The short version here is that this...
Assembly[] assemblys = AppDomain.CurrentDomain.GetAssemblies ();
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes (assemblys);
...is really not something you should ever do. This literally registers every type in the entire running system, including everything in every .NET base library, into dependency injection. Be way, way more selective than that when registering. I'd bet if you remove that and only register the stuff you need, then DI won't try to resolve the odd .NET stuff that doesn't have accessible constructors.

Related

How to run non-static code that uses dependency injection in .net core

I created a new console app in VS2017 and I am trying to run this code to demonstrate I can encrypt and decrypt strings in .NET Core. I have tried calling RunSample from the Program.cs Main but it wants it to be a static method. If I make RunSample static then I'm getting a null reference exception when trying to set the var protectedpayload.
using System;
using Microsoft.AspNetCore.DataProtection;
public class MyClass
{
private readonly IDataProtector _protector;
public MyClass(IDataProtectionProvider provider)
{
_protector = provider.CreateProtector("Contoso.MyClass.v1");
}
public void RunSample()
{
Console.WriteLine("Enter input:");
string input = Console.ReadLine();
var protectedPayLoad = _protector.Protect(input);
Console.WriteLine($"Protect returned: {protectedPayLoad}");
var unprotectedPayLoad = _protector.Unprotect(protectedPayLoad);
Console.WriteLine($"Unprotect returned: {unprotectedPayLoad}");
}
}
How can I run it?
UPDATE:
Trying to run it from Program.cs, I have the following but .MyClass has a "cannot resolve" syntax error / red underline on it.
using System;
namespace encrypttest
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
new Program().MyClass.RunSample();
}
}
}
UPDATE 2:
I created my app by going to new project, .NET Core and then Console App (.NET Core):
UPDATE 3:
I've changed my code as suggested but have the following error:
UPDATE 4:
I've removed the using as suggested, but now I get:
you need DI your IDataProtector in Program.cs
class Program
{
static void Main(string[] args)
{
ServiceCollection services = new ServiceCollection();
services.AddScoped<IDataProtector>();
services.AddScoped<MyClass>();
using (var serviceProvider = services.BuildServiceProvider())
{
var service = serviceProvider.GetService<MyClass>();
service.RunSample();
}
}
}
update - 1:
Install from NuGet
Install-Package Microsoft.Extensions.DependencyInjection
update - 2:
remove using like this
var serviceProvider = services.BuildServiceProvider()
var service = serviceProvider.GetService<MyClass>();
service.RunSample();
update - 3:
My code is DI example...
you need change like this
ServiceCollection services = new ServiceCollection();
services.AddDataProtection();
services.AddScoped<MyClass>();
var serviceProvider = services.BuildServiceProvider();
var service = serviceProvider.GetService<MyClass>();
service.RunSample();
Install from NuGet
Install-Package Microsoft.AspNetCore.DataProtection
The complaint about making RunSamples static is because you're not newing up the class that defines it. If it's not an instance, then the method must be static to access it. However, since the class (and method) has a dependency that needs to be satisfied, you cannot make it static. Simply, you need to use dependency injection to create an instance of your class with its dependency satisfied in order to call the RunSamples method.
To use dependency injection in a console app, it's as simple as adding the Microsoft.Extensions.DependencyInjection NuGet package, and then:
var services = new ServiceCollection()
.BuildServiceProvider();
However, that's not very useful as you haven't registered anything, so just do all that before calling BuildServiceProvider:
var serviceProvider = new ServiceCollection()
.AddDataProtection()
.AddScoped<MyClass>()
.BuildServiceProvider();
Since you want to utilize data protection, you'll obviously need the Microsoft.AspNetCore.DataProtection NuGet as well.
Then, when you want an instance of MyClass:
using (var scope = serviceProvider.CreateScope())
{
var myClass = scope.GetRequiredService<MyClass>();
myClass.RunSamples();
}
I guess this is what you are missing (from this link: https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/using-data-protection?view=aspnetcore-2.1)
public static void Main(string[] args)
{
// add data protection services
var serviceCollection = new ServiceCollection();
serviceCollection.AddDataProtection();
var services = serviceCollection.BuildServiceProvider();
// create an instance of MyClass using the service provider
var instance = ActivatorUtilities.CreateInstance<MyClass>(services);
instance.RunSample();
}
You have to initiate an instance of IDataProtectionProvider by calling ActivatorUtilities.CreateInstance

How to setup composite class with microsoft dependency injection?

this is dotnet.core question
let's say i have class NotifierResolver:
public class NotifierResolver : INotifierResolver
{
private readonly List<INotifier> _notifiers;
public NotifierResolver(List<INotifier> notifiers)
{
_notifiers = notifiers;
}
public INotifier Resolve(INotification notification)
{
var notifier = _notifiers.Single(h => h.CanNotify(notification));
if (notifier == null)
{
throw new Exception($"could not resolve notification: {notification.GetType()}");
}
return notifier;
}
}
How do i setup it's dependencies under service collection ( Microsoft.Extensions.DependencyInjection ) ?
var serviceProvider = new ServiceCollection()
.????
.AddSingleton<INotifierResolver, NotifierResolver>()
.BuildServiceProvider();
You may need to build up the composite at the composition root like below.
var serviceProvider = new ServiceCollection()
.AddTransient<INotifier, SomeNotifier>()
.AddTransient<INotifier, SomeOtherNotifier>()
.AddTransient<INotifier, YetAnotherNotifier>()
.AddSingleton<INotifierResolver, NotifierResolver>(_
=> new NotifierResolver(_.GetServices<INotifier>().ToList())//<-- Note GetServices
)
.BuildServiceProvider();
So assuming you have multiple INotifier registered with the service collection, when resolving the INotifierResolver, the provider with resolve all the dependencies via IServiceProvider.GetServices extension method and inject them into the dependent class.

Providing Autofac with pre-resolved dependencies

Please forgive me if I make any mistakes in asking this question as it is the first question I have asked on this site.
I am currently trying to add Autofac to an already existing project. I have been using Autofac for about 6 hours and C# for about 4 weeks, I am originally a Python developer so please forgive any C# ignorance. I currently have the following test method:`
public static void BasicTest()
{
using (var scope= Program.container.BeginLifetimeScope())
{
ITradeManager tm = scope.Resolve<ITradeManager>();
List<IGameData> gamesList = new List<IGameData>();
List<IUserData> usersList = new List<IUserData>();
IScraperManager sc = scope.Resolve<IScraperManager>(new NamedParameter("sc",(scope.Resolve<UniScraper>(new NamedParameter("games", gamesList), new NamedParameter("users", usersList)))));
IDatabaseIC db = scope.Resolve<IDatabaseIC>();
IAppCore app = scope.Resolve<IAppCore>(
new NamedParameter("tm", tm),
new NamedParameter("sc", sc),
new NamedParameter("db", db));
IUserData tradeUser = scope.Resolve<IUserData>();
app.Initialise(tradeUser, uniUser);
app.Run();
}
And have the following container setup:
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<AppCore>().As<IAppCore>();
builder.RegisterType<DatabaseInteractionControler>().As<IDatabaseIC>();
builder.RegisterType<ScraperManager>().As<IScraperManager>();
builder.RegisterType<TradeManager>().As<ITradeManager>();
builder.RegisterType<GameData>().As<IGameData>();
builder.RegisterType<UserData>().As<IUserData>();
builder.RegisterType<Scrapers.Scrapers.Uni.UniScraper>().AsSelf().As<IBaseScraper>();
builder.RegisterType<Scrapers.Scrapers.Hill.HillScraper>().AsSelf().As<IBaseScraper>();
builder.RegisterType<Scrapers.Marathon.MarathonScraper>().AsSelf().As<IBaseScraper>();
container = builder.Build();
Tests.AppTest.BasicTest();
}
My issue is that when I try to pass already resolved objects as parameters to Autofac for resolving IAppcore, it ignores the already resolved objects I'm passing as arguments, and instead of tries to resolve as if I did not pass any parameters to it at all!
Have I miss understood something or is there an obvious solution that I am too new to C# and Autofac to easily see?
If you are interested the project I originally wrote (and now converting to use Autofac) was a simple set of web scrapers that pulled in sports news from various different sites and collected them together in a handy little desktop app.
Based on this test, one guess is that the names of your named parameters ("tm", "sc", "db") don't match the actual names of those parameters in the classes' constructors. If they don't match, Autofac doesn't throw an exception. It just resolves an instance of the registered type and ignores your NamedParameter.
To clarify, if your constructor looks like this:
public SomeConstructor(SomeRequiredDependency xyz)
Then the name specified in the NamedParameter must be "xyz".
public class RequiresDependency
{
public RequiresDependency(Dependency dependency)
{
Dependency = dependency;
}
public Dependency Dependency { get; }
}
public class Dependency
{}
[TestClass]
public class AutofacTest
{
[TestMethod]
public void ResolvesWithSpecifiedParameters()
{
var builder = new ContainerBuilder();
builder.RegisterType<RequiresDependency>();
builder.RegisterType<Dependency>();
var myDependency = new Dependency();
using (var container = builder.Build())
{
var resolved =
container.Resolve<RequiresDependency>(new NamedParameter("dependency", myDependency));
Assert.AreSame(myDependency, resolved.Dependency);
}
}
[TestMethod]
public void ResolvesWithRegisteredTypeIfParameterNameDoesntMatch()
{
var builder = new ContainerBuilder();
builder.RegisterType<RequiresDependency>();
builder.RegisterType<Dependency>();
var myDependency = new Dependency();
using (var container = builder.Build())
{
var resolved =
container.Resolve<RequiresDependency>(new NamedParameter("x", myDependency));
Assert.AreNotSame(myDependency, resolved.Dependency);
}
}
}

When using Autofac's autoregister functionality, how can I specify that one type should be InstancePerRequest

We have a Web API project and using the Autofac Web API Integration as the IoC container. The code that we use to register all of our types is as follows:
public class CompositionRootConfigurator
{
public static AutofacWebApiDependencyResolver Configure(Assembly servicesAssembly)
{
var container = BuildContainer(servicesAssembly);
var resolver = new AutofacWebApiDependencyResolver(container);
return resolver;
}
public static IContainer BuildContainer(Assembly servicesAssembly)
{
/*TO DELETE ONCE THE REFERENCES ISSUE IS RESOLVED!*/
var dummy = new EmployeesBL(new ContextFactory(new DBContextFactory(new RoleBasedSecurity(), new Identity())));
var builder = new ContainerBuilder();
if (servicesAssembly != null) // this is a temporary workaround, we need a more solid approach here
{
builder.RegisterApiControllers(servicesAssembly);
}
/* Registers all interfaces and their implementations from the following assemblies in the IoC container
* 1. CB.CRISP.BL
* 2. CB.CRISP.BL.CONTRACTS
* 3. CB.CRISP.DAL
* 4. CB.CRISP.DAL.CONTRACTS
* The current assembly is excluded because the controllers were registered with the builder.RegisterApiControllers expression above.
*/
var appAssemblies = AppDomain.CurrentDomain
.GetAssemblies()
.Where(a => a.ToString().StartsWith("CB.CRISP"))
.ToArray();
builder.RegisterAssemblyTypes(appAssemblies).AsImplementedInterfaces();
if (servicesAssembly != null)
{
builder.RegisterAssemblyTypes(servicesAssembly).AsImplementedInterfaces();
}
return builder.Build();
}
}
Now suppose we have a MyType which implements IMyType and this is the only one that must be a single instance per request and it will be injected in several objects along the hierarchy.
I am at a loss in how to specify this within this existing code. If I just go ahead and just do
builder.RegisterType<MyType>()
.As<IMyType>()
.InstancePerRequest();
since it will also be registered with all the others Will one registration overwrite the other one, will they be duplicated, are there potential problems?
Thank you for your insight.
Autofac will override the first registration and will accept the last one. Here is more detail.
So you should register MyType after registering all type.
I haven't seen any potential problem of this.
But you can register all types like this to be sure.
builder.RegisterAssemblyTypes(servicesAssembly).Except<MyType>().AsImplementedInterfaces();
Here is more detail about scanning.

Unable to resolve a controller that was loaded from external dll

I am building a Web API using MVC4 Web API with an IoC container (Simple Injector in this case, but I don't think this problem is related to that container) that should expose a variety of CRUD and query operations. The reason for using IOC in my case is that we are a dev shop and I need to be able to let customers build their own web API controllers to expose the data they need to expose need from our system. Consequently, I was hoping to design my solution in a way that allowed me to dogfood my own product by making all the controllers, both ours and our customers', external and loadable through IOC.
The website does not have any reference to the library but the library contains controllers that I want to use in the website. The types are registered in the container and the DependencyResolver is set to the custom dependency resolver. I have the code finding the dll plugin and loading the controller type but when I try to navigate to the route that it would represent it says it can't find it.
i.e. if I try to navigate to /api/Test1Api I should see the text "hello world"
My problem here is that although I have loaded my controller type, I am unable to translate that into a route that the website says is there.
I get the error
No HTTP resource was found that matches the request URI
No type was found that matches the controller named 'Test1Api'.
Here is how I register the container
public static class SimpleInjectorInitializer
{
/// <summary>Initialize the container and register it as MVC3 Dependency Resolver.</summary>
public static void Initialize()
{
//// Did you know the container can diagnose your configuration? Go to: http://bit.ly/YE8OJj.
// Create the IOC container.
var container = new Container();
InitializeContainer(container);
container.RegisterMvcAttributeFilterProvider();
// Verify the container configuration
container.Verify();
// Register the dependency resolver.
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
}
private static void InitializeContainer(Container container)
{
var appPath = AppDomain.CurrentDomain.BaseDirectory;
string[] files = Directory.GetFiles(appPath + "\\bin\\Plugins", "*.dll",
SearchOption.AllDirectories);
var assemblies = files.Select(Assembly.LoadFile);
// register Web API controllers
var apiControllerTypes =
from assembly in assemblies
where !assembly.IsDynamic
from type in assembly.GetExportedTypes()
where typeof(IHttpController).IsAssignableFrom(type)
where !type.IsAbstract
where !type.IsGenericTypeDefinition
where type.Name.EndsWith("Controller", StringComparison.Ordinal)
select type;
// register MVC controllers
var mvcControllerTypes =
from assembly in assemblies
where !assembly.IsDynamic
from type in assembly.GetExportedTypes()
where typeof(IController).IsAssignableFrom(type)
where !type.IsAbstract
where !type.IsGenericTypeDefinition
where type.Name.EndsWith("Controller", StringComparison.Ordinal)
select type;
foreach (var controllerType in apiControllerTypes)
{
container.Register(controllerType);
}
foreach (var controllerType in mvcControllerTypes)
{
container.Register(controllerType);
}
}
}
Any help is appreciated.
One big warning about your solution. It is quite crucial to register your controller into your container as well (this advice holds for all DI frameworks, although some frameworks by default force you to register concrete types as well). Otherwise you will certainly get bitten by the same problem as this developer had.
Since the IHttpControllerTypeResolver makes use of the IAssembliesResolver, the simplest (and safest) solution is to simply ask the IHttpControllerTypeResolver for all controls to register. Your SimpleInjectorInitializer in that case will look like this:
public static class SimpleInjectorInitializer
{
public static void Initialize()
{
// Create the IOC container.
var container = new Container();
InitializeContainer(container);
container.RegisterMvcAttributeFilterProvider();
// Verify the container configuration
container.Verify();
// Register the dependency resolver.
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
}
private static void InitializeContainer(Container container)
{
GlobalConfiguration.Configuration.Services
.Replace(typeof(IAssembliesResolver),
new CustomAssembliesResolver());
var services = GlobalConfiguration.Configuration.Services;
var controllerTypes = services.GetHttpControllerTypeResolver()
.GetControllerTypes(services.GetAssembliesResolver());
// register Web API controllers (important!)
foreach (var controllerType in controllerTypes)
{
container.Register(controllerType);
}
}
}
Also note that your CustomAssembliesResolver can be made considerably easier:
public class CustomAssembliesResolver
: DefaultAssembliesResolver
{
private Assembly[] plugins = (
from file in Directory.GetFiles(
appPath + "\\bin\\Plugins", "*.dll",
SearchOption.AllDirectories)
let assembly = Assembly.LoadFile(file)
select assembly)
.ToArray();
public override ICollection<Assembly> GetAssemblies()
{
var appPath =
AppDomain.CurrentDomain.BaseDirectory;
return base.GetAssemblies()
.Union(this.plugins).ToArray();
}
}
I figured it out!
So I created a new class in my Web Api call CustomAssembliesResolver that inherits from DefaultAssembliesResolver. Essentially I add my assembly to the list of assemblies that are parsed when looking for controllers. I still have the code that uses Simple Injector for the DI portion of the solution.
public class CustomAssembliesResolver : DefaultAssembliesResolver
{
public override ICollection<Assembly> GetAssemblies()
{
var appPath = AppDomain.CurrentDomain.BaseDirectory;
var baseAssemblies = base.GetAssemblies();
var assemblies = new List<Assembly>(baseAssemblies);
var files = Directory.GetFiles(appPath + "\\bin\\Plugins", "*.dll",
SearchOption.AllDirectories);
var customAssemblies = files.Select(Assembly.LoadFile);
// register Web API controllers
var apiControllerAssemblies =
from assembly in customAssemblies
where !assembly.IsDynamic
from type in assembly.GetExportedTypes()
where typeof(IHttpController).IsAssignableFrom(type)
where !type.IsAbstract
where !type.IsGenericTypeDefinition
where type.Name.EndsWith("Controller", StringComparison.Ordinal)
select assembly;
foreach (var assembly in apiControllerAssemblies)
{
baseAssemblies.Add(assembly);
}
return assemblies;
}
}
I also added the following line to the beginning of the App_Start in the Global.asax.cs
GlobalConfiguration.Configuration.Services.Replace(typeof(IAssembliesResolver), new CustomAssembliesResolver());
Hope this helps someone!
A huge, huge thank you to Steven for his help and insight! I really appreciate it!!

Categories