I am using Autofac 3.5.2 on Mono and when I try to register a generic collection and then resolve it I get the right instance where 1 element of the right type has already been added. To explain it in code:
class Fake {}
var builder = new ContainerBuilder();
builder.RegisterType<Fake>();
bilder.RegisterGeneric(typeof(List<>));
var scope = builder.Build();
var list = scope.Resolve<List<Fake>>();
Console.WriteLine(list.Count); // => prints 1!
Is this to be expected? Why? How can I avoid that?
Autofac has built-in support for collection and will by default try to use the constructor with the most available arguments when it resolves a service.
Autofac automatically uses the constructor for your class with the most parameters that are able to be obtained from the container
> http://autofac.readthedocs.org/en/latest/register/registration.html#register-by-type
List<T> contains a constructor which take a IEnumerable<T>.
When Autofac resolve List<Fake> it will choose the constructor with IEnumerable<T>, then resolve IEnumerable<T> which will resolve all available instance of T.
If you have more than one Fake registered, Autofac will resolve all of them when you resolve. For example :
var builder = new ContainerBuilder();
builder.RegisterType<Fake1>().As<IFake>();
builder.RegisterType<Fake2>().As<IFake>();
builder.RegisterGeneric(typeof(List<>));
var scope = builder.Build();
var list = scope.Resolve<List<IFake>>();
Console.WriteLine(list.Count); // => prints 2!
You can specify which constructor to use when you register List<T>
var builder = new ContainerBuilder();
builder.RegisterType<Fake1>().As<IFake>();
builder.RegisterType<Fake2>().As<IFake>();
builder.RegisterGeneric(typeof(List<>)).UsingConstructor(Type.EmptyTypes);
var scope = builder.Build();
var list = scope.Resolve<List<IFake>>();
Console.WriteLine(list.Count); // => prints 0!
Or you can ignore default behavior by using the ContainerBuildOptions.ExcludeDefaultModules parameter in the Build method
var builder = new ContainerBuilder();
builder.RegisterType<Fake1>().As<IFake>();
builder.RegisterGeneric(typeof(List<>));
var scope = builder.Build(ContainerBuildOptions.ExcludeDefaultModules);
var list = scope.Resolve<List<IFake>>();
Console.WriteLine(list.Count); // => prints 0!
I won't recommend removing default behaviors unless you really know what you do.
Related
In an AzureFunction, I register some services as scoped and some other as Singleton.
It turned out that a dependency of a Singleton was a Scoped service (a bad thing) and at runtime I get this kind of exception.
I would like to be able to write an unit test to check the same thing as the runtime but I did not manage to succeed. Anyone can help me ?
What I tried:
// Arrange
var functionStartup = new MyFunctionStartup();
var serviceCollection = new ServiceCollection();
var functionHostBuilder = A.Fake<IFunctionsHostBuilder>();
A.CallTo(() => functionHostBuilder.Services).Returns(serviceCollection);
// Act
// doing in fact under the hood:
// services.AddSingleton<ISingleton, Singleton>();
// services.AddScoped<IScopedDependencyOfSingleton, ScopedDependencyOfSingleton>();
functionStartup.Configure(functionHostBuilder);
// Assert
var provider = serviceCollection.BuildServiceProvider();
var singleton = provider.GetService<ISingleton>();
You can get an underlying DryIoc IContainer from the service provider and Validate for the captive dependency in a singleton:
var container = provider.GetRequiredService<IContainer>();
var errors = container.Validate(ServiceInfo.Of<ISingleton>());
if (errors.Count > 0)
{
// handle errors
}
Here is the documentation for Validate.
I am new to Ninject and am struggling to get this test to pass. (This test passed with Autofac but the behaviour seems different in Ninject).
[Test]
public void RegisterInstance_unnamed_should_return_unnamed_when_multiple_registrations()
{
var sut = new StandardKernel();
var instance1 = new Dependency3();
var instance2 = new Dependency3();
sut.Bind<Dependency3>().ToConstant(instance1).Named("instance1");
sut.Bind<Dependency3>().ToConstant(instance2);
sut.Get<Dependency3>("instance1").ShouldBeSameAs(instance1);
sut.Get<Dependency3>().ShouldBeSameAs(instance2);
}
When I call the last line I get this exception message:
Ninject.ActivationException : Error activating Dependency3
No matching bindings are available, and the type is not self-bindable.
Activation path: 1) Request for Dependency3
How do I resolve a binding that is not named when there are multiple bindings?
Thanks
If you want to treat the un-named binding as a "default", you're required to add .BindingConfiguration.IsImplicit = true to the named bindings. Like so:
Bind<Dependency3>().ToConstant(instance1)
.Named("instance1")
.BindingConfiguration.IsImplicit = true;
Otherwise the named binding will satisfy a request without a name as well.
How can I inject a custom method into a .net assembly using mono.cecil, and then call it in the entrypoint?
I like to do this to implement security methods after the binary is built.
To inject method you need to get the type you want to add it the method and then add a MethoDefinition.
var mainModule = ModuleDefinition.ReadModule(assemblyPath);
var type = module.Types.Single(t => t.Name == "TypeYouWant");
var newMethodDef= new MethodDefinition("Name", MethodAttributes.Public, mainModule.TypeSystem.Void);
type.Methods.Add(newMethodDef);
To call this method form the entry point, you need to get the entry point MethodDefinition and the new injected MethodReference and add instruction in the entry point method to call the new injected method.
var newMethodRef = type.Methods.Single(m => m.Name == "Name").Resolve();
var entryPoint= type.Methods.Single(m => m.Name == "YourEntryPoint");
var firstInstruction = entryPoint.Body.Instructions.First();
var il = entryPoint.Body.GetILProcessor();
il.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Callvirt, newMethodRef));
mainModule.Write(assemblyPath);
Note: Yes I know its C# and not VB but I'm sure once you got the idea you can easily convert it to VB.
You can make use of the Module.Import() function.
Example Class can be seen in the video:
https://www.youtube.com/watch?v=heTCisgYjhs
Credits to TheUnknownProgrammer's importer class.
I'm getting an ActivationException saying there was an error activating IEventBroker. MyDataSource takes an IEventBroker has a parameter. If I don't use the child kernel, there is no issue. What is going on?
var kernel = new StandardKernel();
var childKernel = new ChildKernel(kernel);
var eventBroker = new EventBroker();
childKernel.Bind<IEventBroker>().ToConstant(eventBroker);
var myDS = childKernel.Get<MyDataSource>();
From the ChildKernel readme:
The default behavior of Ninject that classes are bound to themself if
not explicit still exists. But in this case this will be done by the
top most parent. This means that this class can not have any
dependency defined on a child kernel. I strongly suggest to have a
binding for all objects that are resolved by ninject and not to use
this default behavior.
So you need to explicitly bind MyDataSource to self to make it work:
var kernel = new StandardKernel();
var childKernel = new ChildKernel(kernel);
var eventBroker = new EventBroker();
childKernel.Bind<IEventBroker>().ToConstant(eventBroker);
childKernel.Bind<MyDataSource>().ToSelf();
var myDS = childKernel.Get<MyDataSource>();
Suppose I have a dependency that is registered as HttpRequestScoped so there is only one instance per request. How could I resolve a dependency of the same type outside of an HttpRequest?
For example:
// Global.asax.cs Registration
builder.Register(c => new MyDataContext(connString)).As<IDatabase>().HttpRequestScoped();
_containerProvider = new ContainerProvider(builder.Build());
// This event handler gets fired outside of a request
// when a cached item is removed from the cache.
public void CacheItemRemoved(string k, object v, CacheItemRemovedReason r)
{
// I'm trying to resolve like so, but this doesn't work...
var dataContext = _containerProvider.ApplicationContainer.Resolve<IDatabase>();
// Do stuff with data context.
}
The above code throws a DependencyResolutionException when it executes the CacheItemRemoved handler:
No scope matching the expression 'value(Autofac.Builder.RegistrationBuilder`3+<>c__DisplayClass0[MyApp.Core.Data.MyDataContext,Autofac.Builder.SimpleActivatorData,Autofac.Builder.SingleRegistrationStyle]).lifetimeScopeTag.Equals(scope.Tag)' is visible from the scope in which the instance was requested.
InstancePerLifetimeScope(), rather than HttpRequestScoped(), will give the result you need.
There is a caveat though - if IDatabase requires disposal, or depends on something that requires disposal, this won't happen if you resolve it from the ApplicationContainer. Better to do:
using (var cacheRemovalScope =
_containerProvider.ApplicationContainer.BeginLifetimeScope())
{
var dataContext = cacheRemovalScope.Resolve<IDatabase>();
// Do what y' gotta do...
}