I have the following autofac-config:
public static void RegisterDI()
{
var builder = GetBuilder();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
public static ContainerBuilder GetBuilder()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetCallingAssembly());
builder.RegisterFilterProvider();
var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>().Where(x => x.FullName.Contains("Soundyladder")).ToArray();
builder.RegisterAssemblyTypes(assemblies)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces()
.InstancePerRequest();
builder.RegisterAssemblyModules(assemblies);
return builder;
}
My application consist of three layers: UI, Service, and DataAcces. Here is my UserRepository from the DataAccess-layer:
public class UserRepository : IUserRepository
{
}
Here is my service from the the service layer:
public UserService(IUserRepository userRepository)
{
this._userRepository = userRepository;
}
And here is my controller:
public UserController(IUserService userService)
{
this._userService = userService;
}
Everytime I start the application, I get the following error:
None of the constructors found with
'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type
'Soundyladder.Service.Services.UserService' can be invoked with the
available services and parameters: Cannot resolve parameter
'Soundyladder.DataAccess.Repositories.IUserRepository userRepository'
of constructor 'Void
.ctor(Soundyladder.DataAccess.Repositories.IUserRepository)'.
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: Autofac.Core.DependencyResolutionException: None of
the constructors found with
'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type
'Soundyladder.Service.Services.UserService' can be invoked with the
available services and parameters: Cannot resolve parameter
'Soundyladder.DataAccess.Repositories.IUserRepository userRepository'
of constructor 'Void
.ctor(Soundyladder.DataAccess.Repositories.IUserRepository)'.
I have no idea why this happens. I have the same setup In this project that I have In my other projects. When I compare my other projects with this, I can't see any difference.
(Posted solution on behalf of the question author).
I changed the autofac config. Now it's working:
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterFilterProvider();
var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>().Where(x => x.FullName.Contains("Soundyladder")).ToArray();
builder.RegisterAssemblyTypes(assemblies)
.AsImplementedInterfaces()
.InstancePerRequest();
return builder;
It looks like you only registering "Service" suffix types, and forgot about "Repository" suffix types.
.Where(t => t.Name.EndsWith("Service"))
Related
I'm trying to inject a SignalR IHubContext into a Web API 2.x controller in an ASP.NET MVC 5 app Framework 4.72 (not .NET Core). It's throwing this exception when calling the Web API controller MyController:
An error occurred when trying to create a controller of type 'MyController'. Make sure that the controller has a parameterless public constructor
The inner exception says:
None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'MyController' can be invoked with the available services and parameters: Cannot resolve parameter 'Microsoft.AspNet.SignalR.IHubContext[MyHub] context' of constructor 'Void .ctor(Microsoft.AspNet.SignalR.IHubContext [MyHub])'.
I don't mind doing this using property injection but haven't had any luck getting that to work. So I'm doing injection into the c'tor of the controller.
I've followed these answers for help:
https://stackoverflow.com/a/37913821/177416 --> c'tor injection
https://stackoverflow.com/a/29793864/177416 --> c'tor injection
https://stackoverflow.com/a/26810399/177416 --> property injection
https://stackoverflow.com/a/15600493/177416 --> property injection
Here's the Web API controller:
public class MyController : WebApiController
{
public IHubContext<MyHub> Context { get; set; }
public MyController(IHubContext<MyHub> context)
{
Context = context;
}
}
And here's the pertinent part of the Startup.cs:
public void Configuration(IAppBuilder app)
{
// Other code...
var builder = new ContainerBuilder();
var config = new HttpConfiguration();
builder.RegisterHubs(Assembly.GetExecutingAssembly());
builder.RegisterControllers(typeof(MvcApplication).Assembly)
.InstancePerRequest();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly())
.InstancePerRequest();
builder.RegisterType<AutofacDependencyResolver>()
.As<IDependencyResolver>()
.SingleInstance();
builder
.Register(c => c.Resolve<IConnectionManager>().GetHubContext<MyHub>())
.Named<IHubContext>("MyHub");
builder.RegisterType<MyController>()
.WithParameter(
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(IHubContext),
(pi, ctx) => ctx.ResolveNamed<IHubContext>("MyHub")
)
);
var container = builder.Build();
app.UseAutofacMiddleware(container);
DependencyResolver.SetResolver(new Autofac.Integration.Mvc.AutofacDependencyResolver(container));
config.DependencyResolver = new AutofacWebApiDependencyResolver((IContainer)container);
app.Map("/signalr", map =>
{
var hubConfiguration = new HubConfiguration
{
Resolver = new AutofacDependencyResolver(container),
};
map.RunSignalR(hubConfiguration);
});
}
What am I missing? Thanks.
Your first problem is that typeof(IHubContext) is not the same as typeof(IHubContext<MyHub>). You can get around that by using:
pi.ParameterType == typeof(IHubContext).MakeGenericType(typeof(MyHub))
However, old versions of SignalR don't support the generic interfaces very well, so it would probably work better if you left the comparison as is, and inject an IHubContext rather than an IHubContext<MyHub> in MyController.
This is the way we register the classes using Autofac
builder.Register(c => new FileLogger(ILogger)).As<ILogger>().SingleInstance();
var sizeinkb= Convert.ToInt32(configuration.GetValue<string>("Settings:SizeInKb"));
builder.RegisterType<Broker>().As<IBroker>().WithParameter("size", sizeinkb).SingleInstance();
builder.RegisterType<MainClass>().As<IMainClass>().SingleInstance();
var container = builder.Build();
ContainerFactory.SetContainer(container);
Here is our class
public class MainClass:IMainClass
{
public MainClass(IBroker broker,ILogger logger)
{
_broker = broker,
_logger = logger
}
}
Here is the broker class
public class Broker:IBroker
{
public Broker(ILogger logger,
int size)
{
}
}
Autofac.Core.DependencyResolutionException
HResult=0x80131500
Message=An error occurred during the activation of a particular registration. See the inner exception for details. Registration: Activator = MainClass (ReflectionActivator), Services = [MyProject.IMainClass], Lifetime = Autofac.Core.Lifetime.RootScopeLifetime, Sharing = Shared, Ownership = OwnedByLifetimeScope ---> None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'MyProject.MainClass' can be invoked with the available services and parameters:
Cannot resolve parameter 'MyProject.IBroker broker' of constructor 'Void .ctor(MyProject.IBroker, MyProject.ILogger)'.
Here is another approach where the dependencies can be resolved during the lambda setup
//...removed for brevity
var sizeinkb = Convert.ToInt32(configuration.GetValue<string>("Settings:SizeInKb"));
builder.Register(c => new Broker(c.Resolve<ILogger>(), sizeinkb))
.As<IBroker>().SingleInstance();
builder.RegisterType<MainClass>().As<IMainClass>().SingleInstance();
var container = builder.Build();
ContainerFactory.SetContainer(container);
I have a problem with Autofac and Automapper.
In my WebApi I have this code to register my classes:
public static void Initialize(HttpConfiguration config, IContainer container)
{
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
_container = container;
}
private static IContainer RegisterServices(ContainerBuilder builder)
{
//Register your Web API controllers.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterModule<MapperInstaller>();
builder.RegisterType<VurpEntities>();
builder.RegisterType<EntityFrameworkUnitOfWork>().As<IUnitOfWork>();
builder.RegisterType<AppService>().As<IAppService>();
builder.RegisterType<AppRepository>().As<IAppRepository>();
builder.RegisterType<BusinessService>().As<IBusinessService>();
builder.RegisterType<BusinessRepository>().As<IBusinessRepository>();
//...
//all my types are registered here
//...
//Set the dependency resolver to be Autofac.
_container = builder.Build();
return _container;
}
I created a Resolver to map a special field of my object:
public class ProductTypeResolver : IMemberValueResolver<ProductDto, Product, string, Enumeration.ProductType>
{
public ProductType Resolve(ProductDto source, Product destination, string sourceMember, ProductType destMember, ResolutionContext context)
{
Enum.TryParse(sourceMember, out destMember);
return destMember;
}
}
I have a Profile that map DTO object in Entity object:
CreateMap<ProductDto, Product>()
.ForMember(dest => dest.Category, opt => opt.Ignore())
.ForMember(dest => dest.Feature, opt => opt.Ignore())
opts.ResolveUsing<ProductTypeResolver, string>(src => src.type));
When I try to map the object Product p = _mapper.Map<Product>(productDto); I receive the error:
An exception of type 'System.ObjectDisposedException' occurred in
Autofac.dll but was not handled in user code
Additional information: This resolve operation has already ended. When
registering components using lambdas, the IComponentContext 'c'
parameter to the lambda cannot be stored. Instead, either resolve
IComponentContext again from 'c', or resolve a Func<> based factory to
create subsequent components from.
Anyone can help me?
I solved with this:
builder.Register(c =>
{
//This resolves a new context that can be used later.
var context = c.Resolve<IComponentContext>();
var config = context.Resolve<MapperConfiguration>();
return config.CreateMapper(context.Resolve);
})
.As<IMapper>()
.InstancePerLifetimeScope();
Has anyone tried to use the new View Injection from ASP.NET Core?
I'm trying to use straight forward as described on the documentation (https://docs.asp.net/en/latest/mvc/views/dependency-injection.html) but no success at all.
The unique diference from my implementation and the documentation is that I'm using AutoFac for DI.
When I try to use the injection on my view I get an exception that my Service has not been registered.
#inject Domain.Service.LevelService LevelService
Error Message:
ComponentNotRegisteredException: The requested service 'Domain.Service.LevelService' has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.
Btw, the service is correctly registered and can be accessed from the controller for example.
Edit to include Startup:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddMemoryCache();
services.AddSession();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
return new AutofacServiceProvider(DependencyInjection.RegisterServices(services));
}
Code of the method RegisterServices:
public static IContainer RegisterServices(IServiceCollection services)
{
// Create the container builder.
var builder = new ContainerBuilder();
var assembly = Assembly.GetExecutingAssembly();
assembly.GetTypes()
.Where(x => x.IsSubclassOf(typeof(ServiceInjectionModule)))
.ToList()
.ForEach(x =>
{
var t = (ServiceInjectionModule)Activator.CreateInstance(x, new object[] { true });
t.AddtoContainer(builder);
});
// Add automapper configurations
var mapperConfiguration = AutoMapperConfig.Configure();
var mapper = mapperConfiguration.CreateMapper();
builder.RegisterInstance(mapper).As<IMapper>();
// Populate default services
builder.Populate(services);
return builder.Build();
}
The problem is in the assembly scanning section you've written. It's much easier to use the built in functionality of AutoFac. Not sure your code is .Net Core just based on the fact you're not using GetTypeInfo. GetTypeInfo is backwards compatible so will work with .Net 4.x
public static IContainer RegisterServices(IServiceCollection services)
{
// Create the container builder.
var builder = new ContainerBuilder();
var assembly = Assembly.GetExecutingAssembly();
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.GetTypeInfo().IsSubclassOf(typeof(ServiceInjectionModule)))
.AsSelf();
// Add automapper configurations
var mapperConfiguration = AutoMapperConfig.Configure();
var mapper = mapperConfiguration.CreateMapper();
builder.RegisterInstance(mapper).As<IMapper>();
// Populate default services
builder.Populate(services);
return builder.Build();
}
OK, I solved the problem.
Well, I didn't paid attention and seems that no one too :p.
The problem is that I'm trying to inject an instance and not an interface. Just changed the implementation and everything started working.
Final code:
#inject Domain.Service.Interfaces.ILevelService LevelService
I use the following code in order to register log4net for all the classes that need it.
public class LogInjectionModule : Module
{
private readonly string _configPath;
public LogInjectionModule(string configPath)
{
_configPath = configPath;
}
protected override void AttachToComponentRegistration(IComponentRegistry registry,
IComponentRegistration registration)
{
XmlConfigurator.Configure(new FileInfo(_configPath));
registration.Preparing += OnComponentPreparing;
}
private static void OnComponentPreparing(object sender, PreparingEventArgs e)
{
var t = e.Component.Activator.LimitType;
e.Parameters = e.Parameters.Union(new[]
{
new ResolvedParameter((p, i) => p.ParameterType == typeof (ILog),
(p, i) => LogManager.GetLogger(t))
});
}
}
All the classes are registered using autofac's types scanning:
builder.RegisterAssemblyTypes(typeof (IResourceFinder).Assembly)
.AsImplementedInterfaces();
And it works fine!
One class needs to be registered explicitly tries to resolve ILog and fails
builder.Register(x => new ClassThatNeedsILog(x.Resolve<ILog>())).AsImplementedInterfaces();
Here is that class
public class ClassThatNeedsILog
{
public ClassThatNeedsILog(ILog log)
{
}
}
I am getting the following exception:
Autofac.Core.Registration.ComponentNotRegisteredException : The
requested service 'log4net.ILog' has not been registered. To avoid
this exception, either register a component to provide the service,
check for service registration using IsRegistered(), or use the
ResolveOptional() method to resolve an optional dependency.
Your LogInjectionModule never registers any ILog into the container only supplies paramters for the resolved instances on the preparing face, and it only works for instances created by Autofac.
So when you write builder.Register(x => new ClassThatNeedsILog(x.Resolve<ILog>())) you are creating the ClassThatNeedsILog manually. with new ClassThatNeedsILog(...)
Hence Autofac does not know about your instance creation (so your OnComponentPreparing won't run) and because you haven't really registered any ILog implementation you get the ComponentNotRegisteredException.
You have two options:
register an ILog in the container directly
let Autofac create your ClassThatNeedsILog type.
So you can just register an ILog in the container with:
builder.RegisterInstance(LogManager.GetLogger("Logger")).As<ILog>();
Then your code will work fine.
Or if you anyway creating the ClassThatNeedsILog by hand can just supply directly the ILog there:
builder.Register(x => new
ClassThatNeedsILog(LogManager.GetLogger(typeof(ClassThatNeedsILog))))
.AsImplementedInterfaces();
The other options is to let Autofac create the instances for you, so change your registration to:
builder.RegisterType<ClassThatNeedsILog>()
.AsImplementedInterfaces();
In this case Autofac will handle the instance creation for you and it will call the OnComponentPreparing method of your module.
If you want to supply additional constructor parameters you can use WithParameter and WithParameters methods:
builder.RegisterType<ClassThatNeedsILog>()
.AsImplementedInterfaces()
.WithParameters(new Parameter[] {
ResolvedParameter.ForNamed<IAnotherInterface>("NAME"),
ResolvedParameter.ForNamed<IYetAnotherInterface>("ANOTHERNAME")});
The builder needs to Build a container before it can Resolve.
Try something like this (untested)
builder
.RegisterAssemblyTypes(typeof (IResourceFinder).Assembly)
.AsImplementedInterfaces();
/* process LogInjectionModule */
IContainer container = builder.Build();
var updateBuilder = new ContainerBuilder();
updateBuilder
.Register(x => new ClassThatNeedsILog(x.Resolve<ILog>()))
.AsImplementedInterfaces();
updateBuilder.Update(container);
nemesv's answer resolved the issue for me.
Though, I had to spend some more time getting it to work after using the RegisterType approach, as I was missing the below:
config.Filters.AddRange(config.DependencyResolver.GetServices(typeof(IExceptionFilter)).Select(o => o as IExceptionFilter));
Just adding mentioning it here, if other's have forgotten to configure the filter in the first place.