Autofac asp.net web api constructor injection works but property injection does not - asp.net-web-api

I am beginner in autofac and I have to use it in new legacy project asp.net web api.
I am registering of interface and injection works fine with constructor injection.
However, the constructor is being called in numerous places directly new(), and I don't want to replace it everywhere.
So I thought about property injection, but cannot get it to work, the dependency is always null.
The app is split into multiple projects and multiple autofac modules. Autofac configuration as per docs: https://docs.autofac.org/en/latest/integration/webapi.html
I tried to make small demo app, and I was able to get property injection working using all methods from docs: https://autofac.readthedocs.io/en/latest/register/prop-method-injection.html
using Autofac;
public class Program
{
public static void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<MyDependency>().As<IMyDependency>().SingleInstance();
builder.RegisterType<MyService>().OnActivated(e => e.Instance.MyDependency1 = e.Context.Resolve<IMyDependency>());
//builder.Register(c => new MyService { MyDependency1 = c.Resolve<IMyDependency>() });
//builder.RegisterType<MyService>().WithProperty("MyDependency1", new MyDependency()).SingleInstance();
var container = builder.Build();
container.Resolve<MyService>();
}
}
public class MyService
{
public IMyDependency MyDependency1 { get; set; }
}
public class MyDependency : IMyDependency
{
public void Hello()
{
Console.WriteLine("Hello from MyDependency1");
}
public MyDependency()
{
Hello();
}
}
public interface IMyDependency
{
public void Hello();
}
Unfortunately none of these works for my full project, the object is always null. I know it would be difficult to get help, but maybe someone can advice what to look for?

I just tried reproducing this using the WithProperty registration you have there and the test passes - I can't reproduce it, property injection is working.
If it's not working in your full project, something else is going on. Below is the totally working test I used to verify.
public class ExampleTests
{
[Fact]
public void PropertyInjection()
{
var builder = new ContainerBuilder();
builder.RegisterType<MyDependency>().As<IMyDependency>().SingleInstance();
builder.RegisterType<MyService>().WithProperty("MyDependency1", new MyDependency()).SingleInstance();
var container = builder.Build();
var svc = container.Resolve<MyService>();
Assert.NotNull(svc.MyDependency1);
}
}
public class MyService
{
public IMyDependency MyDependency1 { get; set; }
}
public class MyDependency : IMyDependency
{
public void Hello()
{
Console.WriteLine("Hello from MyDependency1");
}
public MyDependency()
{
Hello();
}
}
public interface IMyDependency
{
public void Hello();
}

Related

Unable to resolve service for type with our own class

We are getting this exception when calling a web api controller:
InvalidOperationException: Unable to resolve service for type 'SDS.Lambda.Interfaces.ISecretManager' while attempting to activate 'SDS.Lambda.Controllers.SapController'.\r\n <p class="location">Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, bool isDefaultParameterRequired)
StartUp.cs contains the following:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ISecretManager, SecretManager>();
}
The controller has this constructor:
public class SapController : Controller
{
public SapController(ISecretManager secretManager)
{
_secretManager = secretManager;
}
}
We have the same issue with other types being injected into the constructor, but the IConfiguration instance can be injected, for example that parameter does not cause an exception:
public SapController(IConfiguration configuration, ISecretManager secretManager)
The ISecretManager interface looks like this (yes, it really does):
namespace SDS.Lambda.Interfaces
{
public interface ISecretManager
{
}
}
And the class (yes, really - I reduced it down to avoid complexity):
namespace SDS.Lambda.Interfaces
{
public class SecretManager : ISecretManager
{
}
}
Are we providing the interface/concrete type incorrectly?
Is there a way to retrieve the concrete type to test whether it has been provided properly?
When execution reaches the bottom of ConfigureServices, if we look at the services instance result enumeration, in the debugger view, the types we are injecting are listed, so we can't see why they are failing to be instantiated.
UPDATE
To elaborate and explain the issue with another class/dependency in the same solution:
Controller:
namespace SDS.Lambda.Controllers
{
[Route("api/[controller]")]
public class SapController : Controller
{
readonly IHelper helper;
public SapController(IHelper helpme)
{
helper = helpme;
}
...
}
Interface:
namespace SDS.Lambda.Interfaces
{
public interface IHelper
{
}
}
Class:
namespace SDS.Lambda.Helpers
{
public class Helper : IHelper
{
public Helper()
{
}
}
}
StartUp:
namespace SDS.Lambda
{
public class Startup
{
public static IConfiguration Configuration { get; private set; }
private readonly AppSettings _appSettings;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
_appSettings = configuration.GetSection("AppSettings").Get<AppSettings>();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(logger => logger.AddLambdaLogger());
services.AddSingleton<IHelper, Helper>();
services.AddControllers();
}
...
}

How do I get different Configs into Custom Plugin Implementations of the same Interface?

Simplified example:
I have a custom plugin ICustomPlugin
public interface ICustomPlugin : IPlugin
{
}
and two implementations which require information from different configs. (Of course this only makes sense if the interface would define some common API but let's keep it simple)
public class CustomPluginWithConfigA : ICustomPlugin
{
}
public class CustomPluginWithConfigB: ICustomPlugin
{
}
Since I want to use both implementations my ModuleController should start both of them
[ServerModule(ModuleName)]
public class ModuleController : ServerModuleBase<ModuleConfig>
{
...
protected override void OnStart()
{
foreach (var converter in Container.ResolveAll<ICustomPlugin>())
converter.Start();
}
...
}
Now, what is the clean way to get only the config information that each implementation needs into the respective classes CustomPluginWithConfigA and CustomPluginWithConfigB?
Referencing the classes in the controller or providing the same config to all implementations feels equaly wrong to me.
You have several options and it depends whether your implementations CustomPluginA and CustomPluginB are located within the module, e.g. they are fixed components rather than flexible plugins, or loaded from additional MORYX packages.
In the first scenario you can simply add your components configuration values to the ModuleConfig and inject the config into your plugin, because a modules config is registered in its local container by default.
// In the module config
[DataMember]
public int ValueForA { get; set; }
[DataMember]
public string ValueForB { get; set; }
// In CustomPluginA: Injected
public ModuleConfig Config { get; set; }
public void SomeMethod()
{
var a = Config.ValueForA;
}
If on the other hand your plugins are fully located outside your module or can be extended with external implementations, you should use IConfiguredPlugin<TConfig> for your plugins and define a plugin base config. You will then instantiate your plugins with a factory passing their dedicated configs until we implement MORYX-Platform#10.
public class MyPluginConfig : IPluginConfig
{
[DataMember, PluginNameSelector(typeof(ICustomPlugin))]
public virtual string PluginName { get; set; }
[DataMember]
public int ValueForA { get; set; }
}
public interface ICustomPlugin : IConfiguredPlugin<MyPluginConfig>
{
}
[PluginFactory(typeof(IConfigBasedComponentSelector))]
public interface ICustomPluginFactory
{
ICustomPlugin Create(MyPluginConfig config);
}
// In your module config
[DataMember, PluginConfigs(typeof(ICustomPlugin))]
public List<MyPluginConfig> ConfiguredPlugins { get; set; }
// In your plugin
public class CustomConfigA : MyPluginConfig
{
public override PluginName { get { return nameof(CustomPluginA); } set { } }
}
[ExpectedConfig(typeof(CustomConfigA)]
[Plugin(LifeCycle.Transient, typeof(ICustomPlugin), Name = nameof(CustomPluginA))]
public class CustomPluginA : ICustomPlugin
{
public void Initialize(MyPluginConfig config)
{
var typed = (CustomPluginConfigA)config; // MORYX takes care of correct type
}
}
// In your controller Initialize
Container.LoadComponents<ICustomPlugin>(); // Load from all DLLs and packages
// In Start
var factory = Container.Resolve<ICustomPluginFactory>();
foreach (var config = Config.ConfiguredPlugins)
{
var plugin = factory.Create(config); // Calls Initialize with the config
}
Hope that answers your question.

Property injection in to Web Api controller using Autofac

I'm trying to set a property on an System.Web.Http.ApiController to a value of a resolved IServerPackageRepository. The controller runs in a HttpSelfHostServer and the DependencyResolver has been set to AutofacWebApiDependencyResolver. Here is the code from the Autofac.Module.Load method
...
builder.RegisterType<ServerPackageRepository>()
.As<IServerPackageRepository>()
.SingleInstance()
.WithParameter("path", this.StoragePath);
builder.RegisterApiControllers(Assembly.GetExecutingAssembly())
.PropertiesAutowired();
The ApiController controller itself has a property of type
public IServerPackageRepository Repository { get; set; }
but is never resolved.
I am trying to do it this way because ApiController won't take nothing but default constructors. Any suggestions on how to do this the correct way using Autofac?
If the ApiController is only using the default constructor is sounds like the dependency resolver is not being called and may not be registered with Web API correctly. Here is a working example of self-hosting with constructor injection.
The dependency (in this case a simple logger):
public interface ILogger
{
void Log(string text);
}
public class Logger : ILogger
{
public void Log(string text)
{
Debug.WriteLine(text);
}
}
A simple controller with a dependency on the logger:
public class ValuesController : ApiController
{
readonly ILogger _logger;
public ValuesController(ILogger logger)
{
_logger = logger;
}
// GET api/values
public IEnumerable<string> Get()
{
_logger.Log("GET api/values");
return new string[] { "value1", "value2" };
}
}
The console application:
class Program
{
static void Main(string[] args)
{
var configuration = new HttpSelfHostConfiguration("http://localhost:8080");
configuration.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var builder = new ContainerBuilder();
// Register API controllers using assembly scanning.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// Register API controller dependencies.
builder.Register<ILogger>(c => new Logger()).SingleInstance();
var container = builder.Build();
// Set the dependency resolver implementation.
var resolver = new AutofacWebApiDependencyResolver(container);
configuration.DependencyResolver = resolver;
// Open the HTTP server and listen for requests.
using (var server = new HttpSelfHostServer(configuration))
{
server.OpenAsync().Wait();
Console.WriteLine("Hosting at http://localhost:8080/{controller}");
Console.ReadLine();
}
}
}
Hit the controller action using:
http://localhost:8080/api/values
Please test this out and let me know if you have any problems.
Not sure if this is what you want but you can create your own base controller and inject the IServerPackageRepository into it.
public class MyApiController : ApiController {
public IServerPackageRepository ServerPackageRepository { get; set; }
public MyApiController(IServerPackageRepository serverPackageRepository) {
ServerPackageRepository = serverPackageRepository;
}
}
Then, use this as your base controller:
public class ProductsController : MyApiController {
public ProductsController(IServerPackageRepository serverPackageRepository)
: base(serverPackageRepository) {
}
public IEnumerable<Product> Get() {
ServerPackageRepository.DoWork();
//...
}
}
An alternative would be to directly wire your dependency to the property like so:
var repo = new ServerPackageRepository(path: this.StoragePath);
builder.RegisterInstance(repo)
.SingleInstance();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly())
.WithProperty("Repository", repo)
.PropertiesAutowired();

Confusion over MVC3 Code First / Repositories

Please can someone help me because I am getting confused.
I have an Entity like this:
public class Code
{
public int ID { get; set; }
public int UserID { get; set; }
public string CodeText { get; set; }
}
and an Interface like this:
public interface ICodeRepository
{
IQueryable<Code> Codes { get; }
void AddCode(Code code);
void RemoveCode(Code code);
Code GetCodeById(int id);
}
and a Repository like this:
public class SQLCodeRepository : ICodeRepository
{
private EFSQLContext context;
public SQLCodeRepository()
{
context = new EFSQLContext();
}
public IQueryable<Code> Codes
{
get { return context.Codes; }
}
public void AddCode(Code code)
{
context.Codes.Add(code);
context.SaveChanges();
}
public void RemoveCode(Code code)
{
context.Codes.Remove(code);
context.SaveChanges();
}
public Code GetCodeById(int id)
{
return context.Codes.Where(x => x.ID == id).FirstOrDefault();
}
}
and a Context like this:
public class EFSQLContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Code> Codes { get; set; }
public DbSet<PortfolioUser> PortfolioUsers { get; set; }
}
If I declare my controller like this:
public class SearchController : Controller
{
private ICodeRepository cRepo;
public SearchController(ICodeRepository codeRepository)
{
cRepo = codeRepository;
}
}
and then try to do cRepo.GetCodeById(1) nothing happens. But if I declare private ICodeRepository rep = new SQLCodeRepository and then call rep.GetCodeById(1) I can see the method in the Repository being called.
What am I doing wrong?
It looks like from the constructor signature, you are going to be doing some dependency injection. The step you are missing is to set up a DI container using a tool like Castle Windsor. You then configure the MVC resolver to use the DI container to give you the correct implementation of ICodeRepository.
See this
You'll need to create a resolver that implements IDependencyResolver and IDependencyScope and a controller factory that inheritsDefaultControllerFactory
Once you have those you can do something like the following:
MyContainer container; // this needs to be a class level member of the asax
var configuration = GlobalConfiguration.Configuration;
container = new MyContainer() // may need additional stuff here depending on DI tool used
configuration.DependencyResolver = new MyDependancyResolver(container);
var mvcControllerFactory = new MyFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(mvcControllerFactory);
You would call the above code from the asax Application_Start()
See this answer for more specifics on using Ninject and MVC3

FluentValidation + Ninject: how to set up AbstractValidator for dependency injection

I am using FluentValidation and Ninject. I am trying to inject a service into AbstractValidator
[Validator(typeof(CompetitionFormModelValidator))]
public class CompetitionFormModel
{
public string FirstName { get; set; }
}
and for my validation:
public class CompetitionFormModelValidator : AbstractValidator<CompetitionFormModel>
{
IUserService UserService;
public CompetitionFormModelValidator(IUserService UserService)
{
this.UserService= UserService;
RuleFor(c => c.FirstName).NotEmpty().WithMessage(" ").Length(1, 100);
Custom(c =>
{
//.. try uusing UserService here
return null;
});
}
}
In my global.asax.cs
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
var ninjectValidatorFactory = new NinjectValidatorFactory(new StandardKernel());
ModelValidatorProviders.Providers.Add(new FluentValidationModelValidatorProvider(ninjectValidatorFactory));
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
FluentValidationModelValidatorProvider.Configure(x => x.ValidatorFactory = ninjectValidatorFactory);
}
In NinjectWebCommon.cs
private static void RegisterServices(IKernel kernel)
{
AssemblyScanner.FindValidatorsInAssembly(Assembly.GetExecutingAssembly())
.ForEach(match => kernel.Bind(match.InterfaceType)
.To(match.ValidatorType));
kernel.Bind<IUserService>().To<UserService>();
}
The project compiles just fine. When I wasn't trying to use DI, validation was working just fine too. Now that I am trying inject IUserService, validation isn't called.
Have I set up the configuration of ninject.web.mvc.fluentvalidation properly ? Any help would be greatly appreciated.
In your Application_Start, you new up the NinjectValidatorFactory with a new StandardKernel instead of using your existing kernel - so the validators you registered on your existing kernel won't be found by the NinjectValidatorFactory.
Moving this block of code to a place where you have access to the existing kernel and passing that in should fix the problem.

Resources