I have base controlller I try property injection but not work...
public class BaseController : Controller
{
public ILoggingService loggingService { get; set; }
public BaseController()
{
}
}
This is my Autofac config...
var builder = new ContainerBuilder();
// builder.Register(c => new BaseController { loggingService = c.Resolve<ILoggingService>() });
builder.RegisterControllers(Assembly.GetExecutingAssembly())
.PropertiesAutowired();
builder.Register(c => new BaseController()).OnActivated(e =>
{
e.Instance.loggingService = e.Context.Resolve<ILoggingService>();
});
builder.RegisterAssemblyTypes(Assembly.Load("Aizen.Services"))
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces()
.InstancePerRequest();
You haven't registered a concrete type for your ILoggingService interface. Without that, Autofac doesn't know what it actually needs to set your property to.
Add something like this (replacing with the real object of course):
builder.RegisterType<YourLoggingServiceGoesHere>().As<ILoggingService>();
You can try this solution:
builder.Register(c => new BaseController { loggingService = c.Resolve<ILoggingService>() });
Related
I'm trying to utilize dependency injection in GraphQL.net to resolve fields on a graph type, but I keep getting the following error:
System.Exception: Failed to call Activator.CreateInstance. Type: graphql_di.Models.MyObjectGraphType
---> System.MissingMethodException: Cannot dynamically create an instance of type 'graphql_di.Models.MyObjectGraphType'. Reason: No parameterless constructor defined.
I've set up a clean .NET 6 web API for testing it, like so:
MyObject.cs
public class MyObject
{
public MyObject(string someProperty)
{
SomeProperty = someProperty;
}
public string SomeProperty { get; }
}
MyObjectGraphType
public class MyObjectGraphType : ObjectGraphType<MyObject>
{
public MyObjectGraphType(IMyService myService)
{
Field<StringGraphType>(name: "someProperty", resolve: x => x.Source?.SomeProperty ?? string.Empty);
Field<StringGraphType>(name: "name", resolve: x => myService.GetMyName());
}
}
RootQuery.cs
public class RootQuery : ObjectGraphType
{
public RootQuery()
{
Field<MyObjectGraphType>(
name: "myquery",
resolve: _ =>
{
MyObject myObject = new("some pre-populated property");
return myObject;
}
);
}
}
MySchema.cs
public class MySchema : Schema
{
public MySchema(IServiceProvider serviceProvider) : base(serviceProvider)
{
Query = serviceProvider.GetRequiredService<RootQuery>();
}
}
Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddMvc()
.AddMvcOptions(x => x.EnableEndpointRouting = false);
builder.Services.AddSingleton<IMyService, MyService>();
builder.Services.AddGraphQL()
.AddSchema<MySchema>()
.AddDataLoader()
.AddGraphTypes();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
app.UseMvc();
app.UseSwagger();
app.UseSwaggerUI();
app.Run();
Dependency injection is working in the sense, that I can inject IMyService into RootQuerys constrcutor, but it's not working on MyObjectGraphType
Does anyone know what I am missing here?
Any help/hint is greatly appreciated :-)
I organize my project into class libraries and a main caller (now is a console application, then will Apis).
DAL library
BL library
Models (entity) library
Main (console application)
I added Automapper and configured it to work between DAL and BL (Models rapresents all the entity that exposes the BL layer as point in common with other projects).
That's good, but i decided to inject a IMapper via an IoC Container so i can pass the interface to constructors.
Keeping in mind my architecture how can i configure Ninject for this purpose?
I'm using Automapper with "Api Instance" like this:
var config = new MapperConfiguration(cfg => {
cfg.AddProfile<AppProfile>();
cfg.CreateMap<Source, Dest>();
});
var mapper = config.CreateMapper();
Thanks
SOLUTION:
In the Business Logic layer i added a Ninject module:
public class AutomapperModule : NinjectModule
{
public StandardKernel Nut { get; set; }
public override void Load()
{
Nut = new StandardKernel();
var mapperConfiguration = new MapperConfiguration(cfg => { CreateConfiguration(); });
Nut.Bind<IMapper>().ToConstructor(c => new AutoMapper.Mapper(mapperConfiguration)).InSingletonScope();
}
public IMapper GetMapper()
{
return Nut.Get<IMapper>();
}
private MapperConfiguration CreateConfiguration()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfiles(Assembly.GetExecutingAssembly());
cfg.AddProfiles(Assembly.Load("DataAccess"));
});
return config;
}
}
It's a mix between the examples on AutoMapper site and the answer of Jan Muncinsky.
I also added a Get method for returing the context mapper, just for helper.
The client just have to call something like this:
var ioc = new AutomapperModule();
ioc.Load();
var mapper = ioc.GetMapper();
in then passing mapper to constructors...
If you have a better solution feel free to post.
In the simplest form it's easy as:
var kernel = new StandardKernel();
var mapperConfiguration = new MapperConfiguration(cfg => { cfg.AddProfile<AppProfile>(); });
kernel.Bind<IMapper>().ToConstructor(c => new Mapper(mapperConfiguration)).InSingletonScope();
var mapper = kernel.Get<IMapper>();
With usage of Ninject modules:
public class AutoMapperModule : NinjectModule
{
public override void Load()
{
var mapperConfiguration = new MapperConfiguration(cfg => { cfg.AddProfile<AppProfile>(); });
this.Bind<IMapper>().ToConstructor(c => new Mapper(mapperConfiguration)).InSingletonScope();
this.Bind<Root>().ToSelf().InSingletonScope();
}
}
public class Root
{
public Root(IMapper mapper)
{
}
}
...
var kernel = new StandardKernel(new AutoMapperModule());
var root = kernel.Get<Root>();
I want to register Repository with Autofac so that it is the same as in parameterless constructor? Which I then intend to remove.
Example code:
public class GroupController : ApiController
{
protected IRepository Repository;
protected ISettings Settings;
protected GroupController()
{
Settings = new Settings();
Repository = new Repository(User.Identity.IsAuthenticated ? Settings.ConfigAuth : Settings.Config)
}
public GroupController(ISettings settings, IRepository repository)
{
Repository = repository;
Settings = settings;
}
}
This is an api controller in .Net framework.
Settings are written in web.config.
This is how autofac config looked like before I had only one config.
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
var settings = new Settings();
Builder.RegisterType<Repository>()
.As<IRepository>()
.WithParameter(new TypedParameter(typeof(RepoConfig), settings.Config))
.InstancePerRequest();
There is currently only one implementation of IRepository.
public class Repository : IRepository
{
private readonly RepoConfig _config;
public(RepoConfig config)
{
_config = config;
}
}
You can use Lambda Registrations. From Autofac documentation
You can register components using lambda expressions and make a
runtime choice right in the registration for how to handle things.
Note this may have an effect on performance depending on the expense
of the runtime check and how often it gets executed, but it’s an
option.
http://docs.autofac.org/en/latest/register/registration.html (Check lambda expression components)
Based on your example using Lambda Registrations, you can do autofac reg as below (I have not tested)
var builder = new ContainerBuilder();
builder.Register(c => new Settings()).As<ISettings>();
builder.Register<Repository>((c) =>
{
ISettings s = c.Resolve<ISettings>();
Settings repoSettings = User.Identity.IsAuthenticated ? s.ConfigAuth : s.Config;
return new Repository(repoSettings);
}
)
.As<IRepository>()
.InstancePerRequest();
Container = builder.Build();
Then remove your parameter-less constructor and add
public GroupController(IRepository repository)
{
Repository = repository;
}
hope it helps.
I implemented an workaround for this implementation. Not the best way but it works.
This way initialization of Repository doesn't need parameter and I set it with a propertie in constructor of Controller.
public class GroupController : ApiController
{
protected IRepository Repository;
protected ISettings Settings;
public GroupController(ISettings settings, IRepository repository)
{
Settings = settings;
Repository = repository;
Repository.RepoConfig = User.Identity.IsAuthenticated ? Settings.ConfigAuth : Settings.Config
}
}
public class Repository : IRepository
{
private readonly RepoConfig _config;
public RepoConfig RepoConfig
{
get => _config;
set
{
_config = value;
}
}
...
}
And for autofac there is standard DI refister:
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
Builder.RegisterType<Repository>()
.As<IRepository>()
.InstancePerRequest();
I have tried various permutations of this but my current configuration (as it relates to AutoMapper) is like this:
builder.RegisterAssemblyTypes().AssignableTo(typeof(Profile)).As<Profile>();
builder.Register(c => new MapperConfiguration(cfg =>
{
foreach (var profile in c.Resolve<IEnumerable<Profile>>())
{
cfg.AddProfile(profile);
}
})).AsSelf().SingleInstance();
builder.Register(c => c.Resolve<MapperConfiguration>().CreateMapper(c.Resolve)).As<IMapper>().InstancePerLifetimeScope();
builder.RegisterType<MappingEngine>().As<IMappingEngine>();
I have a constructor using IMapper mapper, however I continue to get the YSOD:
None of the constructors found with'Autofac.Core.Activators.Reflection.DefaultConstructorFinder'
on type '' can be invoked with the available services and parameters:
Cannot resolve parameter 'AutoMapper.IMapper mapper' of constructor
'Void .ctor(...,...,..., AutoMapper.IMapper)'.
This class works perfectly without the automapper reference so I'm certain that the trouble lies with my automapper configuration.
I'm not sure what I'm missing here as I'm very new to both AutoFac and AutoMapper.
Edit:
I've also tried:
builder.Register(c => new MapperConfiguration(cfg =>
{
cfg.CreateMap<IdentityUser, AspNetUser>().ReverseMap();
})).AsSelf().SingleInstance();
builder.Register(ctx => ctx.Resolve<MapperConfiguration>().CreateMapper()).As<IMapper>();
//I've tried both of these lines separately, neither work
builder.Register(c => c.Resolve<MapperConfiguration>().CreateMapper(c.Resolve)).As<IMapper>().InstancePerLifetimeScope();
I've also tried manually adding the profiles per the suggestion in the comments
As I mentioned in a comment, your AutoFac code appears to be correct (except for the assembly scanning portion).
I created the following test app, and it does in fact run without any exceptions and puts a 3 into the Output window (as intended):
using System.Diagnostics;
using Autofac;
using AutoMapper;
namespace Sandbox
{
public partial class App
{
public App()
{
var builder = new ContainerBuilder();
builder.Register(
c => new MapperConfiguration(cfg =>
{
cfg.AddProfile(new TestProfile());
}))
.AsSelf()
.SingleInstance();
builder.Register(
c => c.Resolve<MapperConfiguration>().CreateMapper(c.Resolve))
.As<IMapper>()
.InstancePerLifetimeScope();
builder.RegisterType<MappingEngine>()
.As<IMappingEngine>();
builder.RegisterType<Test>().AsSelf();
var container = builder.Build();
container.Resolve<Test>();
}
}
public class TestProfile : Profile
{
protected override void Configure()
{
CreateMap<Source, Destination>();
}
}
public class Test
{
public Test(IMapper mapper)
{
var source = new Source { Id = 3 };
var destination = mapper.Map<Destination>(source);
Debug.Print(destination.Id.ToString());
}
}
public class Source
{
public int Id { get; set; }
}
public class Destination
{
public int Id { get; set; }
}
}
I would suggest creating a new branch of your app in version control and stripping things out until it works.
This is worked for me...
builder = new ContainerBuilder();
builder.Register(
c => new MapperConfiguration(cfg =>
{
cfg.AddProfile(new TestProfile());
}))
.AsSelf()
.SingleInstance();
builder.Register(
c => c.Resolve<MapperConfiguration>().CreateMapper(c.Resolve))
.As<IMapper>()
.InstancePerLifetimeScope();
builder.RegisterType<MappingEngine>()
.As<IMappingEngine>();
builder.RegisterType<Test>().AsSelf();
var container = builder.Build();
container.Resolve<Test>();
Lets say I have these four types (with constructors).
public class MyDbContext : IDataContextAsync
{
public class MyDataContext() { }
}
public class UnitOfWork : IUnitOfWorkAsync
{
public UnitOfWork(IDataContextAsync dataContext)
{
_dataContext = dataContext;
}
}
public class Repository<TEntity> : IRepositoryAsync<TEntity>
{
public Repository(IDataContextAsync context, IUnitOfWorkAsync unitOfWork)
{
_context = context;
_unitOfWork = unitOfWork;
}
}
public class AccountService : IAccountService
{
private readonly IUnitOfWorkAsync _uow;
private readonly IRepositoryAsync<Account> _repository;
public AccountService(IUnitOfWorkAsync uow, IRepositoryAsync<Account> repository)
{
_uow = uow;
_repository = repository;
}
}
I try to register them like so in my container (I'm using WCF by the way).
var builder = new ContainerBuilder();
builder.RegisterType<MyDbContext>().As<IDataContextAsync>();
builder.RegisterType<UnitOfWork>().As<IUnitOfWorkAsync>();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepositoryAsync<>));
builder.RegisterAssemblyTypes(typeof(AccountService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.As(t => t.GetInterfaces().FirstOrDefault(
i => i.Name == "I" + t.Name));
Container = builder.Build();
It seems the container generated instance of the unitOfWork that is injected to the AccountService is different than the unitOfWork instance created when the repository is instantiated by the container. How do I ensure the repository gets instantiated with the same instance? For example, here is how the objects would normally be instantiated without a container.
var context = new MyDbContext();
_uow = new UnitOfWork(context);
_repository = new Repository<Account>(context, _uow);
AccountService service = new AccountService(_uow, _repository);
As you can see, the _uow, and the _uow parameter the repository is created with are the same instance. That is not the case when Autofac instantiates the objects and I can't seem to figure out how to "tie them together".
By default Autofac scope is per dependency. It means that each time Autofac need to resolve a dependency, it will return a new instance.
If you want to have a common instance per your lifetime or request, you have to register your type using the appropriate scope.
var builder = new ContainerBuilder();
builder.RegisterType<MyDbContext>()
.As<IDataContextAsync>()
.InstancePerLifetimeScope();
builder.RegisterType<UnitOfWork>()
.As<IUnitOfWorkAsync>()
.InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(Repository<>))
.As(typeof(IRepositoryAsync<>))
.InstancePerLifetimeScope();
builder.RegisterAssemblyTypes(typeof(AccountService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.As(t => t.GetInterfaces().FirstOrDefault(i => i.Name == "I" + t.Name))
.InstancePerLifetimeScope();
Container = builder.Build();
See Instance Scope in Autofac documentation for more information.
By the way, Autofac integration with WCF seems to not support per-request lifetimescope
Due to WCF internals, there is no explicit support in WCF for per-request lifetime dependencies.
http://docs.autofac.org/en/latest/integration/wcf.html
This limitation is related to the fact that WCF has it own instance scope. It is recommended to use InstanceContextMode.PerCall to be sure that WCF ask a new instance of the service for each WCF call. You can use the ServiceBehavior attribute
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class MyService : IMyService
{ }
I read the source code of AutofacInstanceContext.cs and it seems that a new LifetimeScope is created for each WCF call. So you can register your dependency as InstancePerLifetimeScope and it should work.