Structuremap defines a 'BuildUp' method that takes an already-constructed object and performs setter injection to push in configured dependencies into that object.
Does Autofac have an equivalent method ?
The question was referring to already-constructed objects (ones not registered in the container) so the correct answer is either InjectProperties or InjectUnsetProperties.
The following test demonstrates the behaviour
public class TestPropertyInjection
{
public object ShouldBeInjectedByAutofac { get; set; }
}
[Fact]
public void Autofac_can_inject_properties()
{
var builder = new ContainerBuilder();
builder.RegisterType(typeof(object));
var container = builder.Build();
var existingObjectNotRegisteredInContainer = new TestPropertyInjection();
container.InjectProperties(existingObjectNotRegisteredInContainer);
// can also use InjectUnsetProperties to only set unset properties
//container.InjectUnsetProperties(existingObjectNotRegisteredInContainer);
Assert.NotNull(existingObjectNotRegisteredInContainer.ShouldBeInjectedByAutofac);
}
Try InjectUnsetProperties.
Sample:
public class YourModule : Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
builder.RegisterType<YourClass>().OnActivated(e => e.Context.InjectUnsetProperties(e.Instance));
}
}
In more recent versions of Autofac you can simply use:
builder.RegisterType<YourClass>().PropertiesAutowired();
Related
I've read a lot of articles regarding database migration on startup and no matter what approach I use my efforts aren't going anywhere. My main problem that i'm getting is no parameterless constructor defined for type startup
I have my DataContext class
public class DataContext : DbContext
{
public DataContext(DbContextOptions options) : base(options)
{
}
public DataContext()
{
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
if (options.IsConfigured)
{
//means that context has been added during dependency injection and no further action required.
}
else
{
//means context is being accessed during Add-Migration therefore we need to set the options. The whole DI/Configuration process won't have run yet, so need some other way to get connection string.
//probably below is a bit too fancy, just hardcoding would be fine. But anyway it seems to work and transfers to different developers machines
//you must have {Values: { SqlConnectionString : xyz}} in local.settings.json in Unite.FunctionApp project dir
var localSettingsJson =
Path.Combine(local.settings.json");
var config = new ConfigurationBuilder()
.AddJsonFile(localSettingsJson, false)
.Build();
options.UseSqlServer(config["Values:SqlConnectionString"]);
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{... }
My Startup Class
// register assembly
[assembly: FunctionsStartup(typeof(Startup))]
{
// inherit FunctionsStartup
public class Startup : FunctionsStartup
{
private DataContext _context;
public Startup(DataContext context)
{
_context = context;
}
public override void Configure(IFunctionsHostBuilder builder)
{
var executionContextOptions = builder.Services.BuildServiceProvider()
.GetService<IOptions<ExecutionContextOptions>>().Value;
var config = new ConfigurationBuilder()
.SetBasePath(executionContextOptions.AppDirectory)
.AddJsonFile("local.settings.json", true)
.AddUserSecrets(Assembly.GetExecutingAssembly(), false)
.AddEnvironmentVariables()
.Build();
builder.Services.AddSingleton<IConfiguration>(config);
var sqlConnection = config["SqlConnectionString"] ??
throw new Exception("SQL Connection String Not Defined");
builder.Services.AddDbContext<DataContext>(options => options.UseSqlServer(sqlConnection));
_context.Database.MigrateAsync();
}
}
}
If I have my paramaterless DataContext method in my class why am i still getting this issue that it isn't defined?
Add your parameterless constructor before the other constructor in your DataContext class.
In my application, I have a service that requires a constructor parameter not resolved by Autofac, that I instantiate using a delegate factory:
public class Service
{
public Service(string parameter /*, ... other dependencies */)
{
}
public delegate Service Factory(string parameter);
}
This works great! I really love this feature.
I also like the Controlled Lifetime relationship, so I can let my component depend on a Func<Owned<ISomething>> like this:
public class Component
{
private Func<Owned<ISomething>> _somethingFactory;
/* constructor omitted for brevity */
public void DoSomethingUseful()
{
using (var ownedSomething = _somethingFactory())
{
/* Lots of useful code here */
}
}
}
My problem is that now I want to combine the two. I can't have an instance of Func<Owned<Service>> injected, because it needs that parameter, so my current solution is to abstract the factory away into another service, say IServiceFactory:
public interface IServiceFactory
{
Service Create(string parameter);
}
...implemented as such:
public class ServiceFactory : IServiceFactory
{
private Service.Factory _internalFactory;
public ServiceFactory(Service.Factory internalFactory)
{
_internalFactory = internalFactory;
}
public Service Create(string parameter)
{
return _internalFactory(parameter);
}
}
My component then becomes this:
public class Component
{
Func<Owned<IServiceFactory>> _serviceFactoryFactory;
/* ... */
}
The need for such a field name leaves a bad taste in my mouth to the point that I suspect there must be a cleaner way to handle this case.
Is there another way?
You could change your injected factory to include the string parameter:
private Func<string, Owned<ISomething>> _somethingFactory;
Then you can pass the string to the factory when you want to create a new instance:
public void DoSomethingUseful()
{
using (var ownedSomething = _somethingFactory("my parameter"))
{
/* Lots of useful code here */
}
}
I've created a .NET Fiddle with a small working sample.
I'm building a small Nancy web project.
In a method of one of my classes (not a nancy module), I would like to basically do:
var myThing = TinyIoC.TinyIoCContainer.Current.Resolve<IMyThing>();
However, there is only one registration in .Current (non public members, _RegisteredTypes) which is:
TinyIoC.TinyIoCContainer.TypeRegistration
Naturally, in my above code, I'm getting:
Unable to resolve type: My.Namespace.IMyThing
So, I guess I'm not getting the same container registered in my bootstrapper?
Is there a way to get at it?
EDIT
To flesh out a bit more of what I'm trying to do:
Basically, my url structure looks something like:
/{myType}/{myMethod}
So, the idea being, going to: /customer/ShowAllWithTheNameAlex would load the Customer service, and execute the showAllWithTheNameAlex method
How I do this is:
public interface IService
{
void DoSomething();
IEnumerable<string> GetSomeThings();
}
I then have an abstract base class, with a method GetService that returns the service.
It's here that i'm trying to use the TinyIoC.TinyIoCContainer.Current.Resolve();
In this case, it would be TinyIoC.TinyIoCContainer.Current.Resolve("typeName");
public abstract class Service : IService
{
abstract void DoSomething();
abstract IEnumerable<string> GetSomeThings();
public static IService GetService(string type)
{
//currently, i'm doing this with reflection....
}
}
Here's my implementation of the service.
public class CustomerService : Service
{
public void DoSomething()
{
//do stuff
}
public IEnumerable<string> GetSomeThings()
{
//return stuff
}
public IEnumerable<Customer> ShowAllWithTheNameAlex()
{
//return
}
}
Finally, I have my Nancy Module, that looks like:
public class MyModule : NancyModule
{
public MyModule()
{
Get["/{typeName}/{methodName}"] = p => ExecuteMethod(p.typeName, p.methodName);
}
private dynamic ExecuteMethod(string typeName, string methodName)
{
var service = Service.GetService(typeName);
var result = service.GetType().GetMethod(methodName).Invoke(service, null);
//do stuff
return result; //or whatever
}
}
#alexjamesbrown - The short answer is, you don't. Nancy was specifically designed so that you did not deal with the container directly. You mention that the class, that you want to take a dependency on IMyThing, is not a NancyModule. Well this is not an issue, as long as one of your modules has a reference to it, then those dependencies can also have their own dependencies that will be satisfied at runtime.
public interface IGreetingMessageService
{
string GetMessage();
}
public class GreetingMessageService: IGreetingMessageService
{
public string GetMessage()
{
return "Hi!";
}
}
public interface IGreeter
{
string Greet();
}
public class Greeter
{
private readonly IGreetingMessageService service;
public Greeter(IGreetingMessageService service)
{
this.service = service;
}
public string Greet()
{
return this.service.GetMessage();
}
}
public class GreetingsModule : NancyModule
{
public GreetingModule(IGreeter greeter)
{
Get["/"] = x => greeter.Greet();
}
}
The above will work just fine and Greeter will have it's dependency on IGreetingMessageService satisfied at runtime
I have had a very similar issue, needing to "share" the container. The reason this is an issue is that my program runs as a service using Nancy self hosting to provide a REST API. My modules have dependencies which are injected by Nancy itself, but the other parts of the app which are not referenced from modules also need dependencies injected.
Multiple containers are not a sensible option here (or anywhere really), I need to share the container between Nancy and the rest of the app.
I simply did the following
(I'm using Autofac but I suspect that TinyIoC in similar)
public class Bootstrapper : AutofacNancyBootstrapper
{
private static readonly Lazy<ILifetimeScope> container = new Lazy<ILifetimeScope>(RegisterTypes);
public static ILifetimeScope Container => container.Value;
protected override ILifetimeScope GetApplicationContainer()
{
return container.Value;
}
// Create container and register my types
private static ILifetimeScope RegisterTypes()
{
var builder = new ContainerBuilder();
// Register all my own types.....
return builder.Build();
}
}
Then, in my main code, I can use the container myself
public class Program
{
public static void Main(string[] args)
{
// Resolve main service with all its dependencies
var service = Bootstrapper.Container.Resolve<Service>();
service.Run();
}
}
As my NancyHost is within the Service, the container is constructed (once) upon its first use in main, this static is then used when Nancy gets round to creating the Bootstrapper itself.
In an ideal world, I wouldn't really want a globally accessible container, normally it would be local to the main function.
In this particular case "not dealing with the container directly" is highly problematic:
public interface IFoo {}
public class Foo : IFoo { public Foo(string bar) {} }
Assume IFoo already is a constructor dependency of a Nancy module.
Note the Foo constructor's string dependency. I need to communicate to the container to use that constructor for an IFoo singleton, when encountered as a Nancy module dependency. I need to register that on the TinyIoC instance NancyFx uses, and pass in the actual value of bar.
This is what I've got:
public interface INamed
{
string Name { get; }
}
public interface IService
{
}
public class Service : IService
{
}
public class ServiceUser
{
public ServiceUser(IEnumerable<Lazy<IService, INamed>> services)
{
var cnt = services.Count(); // Always 0.
}
}
var builder = new ContainerBuilder();
builder.Register(c => new Service())
.As<IService>()
.WithMetadata<INamed>(m => m.For(n => n.Name, "Test"));
builder.RegisterType<ServiceUser>();
var container = builder.Build();
var su = container.Resolve<ServiceUser>();
The collection of Lazy<IService, INamed> services in the ServiceUser ctor is always empty. Can you explain what I'm doing wrong? One thing I immediately don't understand is where the concrete implementation of INamed is coming from. I guess Autofac generates that internally? Please help clear things up for me.
Upgrade to the latest Autofac.
I have edited the original question since the same error is occurring the difference being the implementation, I have now added Ninject to the mix.
I have created a class for the validation rules
public class AlbumValidator : AbstractValidator<Album> {
public AlbumValidator() {
RuleFor(a => a.Title).NotEmpty();
}
}
I have created a ValidatorModule for Ninject
internal class FluentValidatorModule : NinjectModule {
public override void Load() {
AssemblyScanner.FindValidatorsInAssemblyContaining<AlbumValidator>()
.ForEach(result => Bind(result.InterfaceType).To(result.ValidatorType).InSingletonScope());
}
}
Here is my ValidatorFactory
public class NinjectValidatorFactory : ValidatorFactoryBase {
public override IValidator CreateInstance(Type validatorType) {
if (validatorType.GetGenericArguments()[0].Namespace.Contains("DynamicProxies")) {
validatorType = Type.GetType(string.Format("{0}.{1}[[{2}]], {3}",
validatorType.Namespace,
validatorType.Name,
validatorType.GetGenericArguments()[0].BaseType.AssemblyQualifiedName,
validatorType.Assembly.FullName));
}
return Container.Get(validatorType) as IValidator;
}
IKernel Container { get; set; }
public NinjectValidatorFactory(IKernel container) {
Container = container;
}
}
and the relevant parts from my Global
protected override void OnApplicationStarted() {
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
var factory = new NinjectValidatorFactory(Container);
ModelValidatorProviders.Providers.Add(
new FluentValidationModelValidatorProvider(factory));
DataAnnotationsModelValidatorProvider
.AddImplicitRequiredAttributeForValueTypes = false;
}
protected override IKernel CreateKernel() {
return Container;
}
IKernel Container {
get { return new StandardKernel(new FluentValidatorModule()); }
}
I load the sample site click on the create new album link and then click the create button leaving the title empty I am then greeted with the error protected override void OnApplicationStarted() {
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
var factory = new NinjectValidatorFactory(Container);
ModelValidatorProviders.Providers.Add(
new FluentValidationModelValidatorProvider(factory));
DataAnnotationsModelValidatorProvider
.AddImplicitRequiredAttributeForValueTypes = false;
}
protected override IKernel CreateKernel() {
return Container;
}
IKernel Container {
get { return new StandardKernel(
new Bootstrapper(),
new FluentValidatorModule()); }
}
I load up the create form and click create leaving the title empty low and behold an error
This property cannot be set to a null value.
The line it references is within the Entity Framework auto generated class, I traced the
Namespace.Contains("DynamicProxies")
and it was returning false, is this because I told EF to use a custom namespace SampleMusicStore.Web?
Or am I missing something else?
Cheers.
The problem is that Entity Framework is generating dynamic proxies on your classes, and then your system is trying to validate against the proxy classes instead of the classes you defined.
The way to resolve this is the same as this answer.