Resolving unnamed Ninject multi-binding - ninject

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.

Related

Strongly typed configuration is not registered but still retrieved from ServiceProvider

I wrote a test that checks that every ...Configuration class in my project (I have several of them) is resolved when I call serviceProvider.GetRequiredService. The test works, but I don't understand why it works also when I remove the registration for a class: I just need to register a single configuration. For example:
[Fact]
public void Test()
{
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("./Configurations/appsettings1.json");
var services = new ServiceCollection();
//services.Configure<ActorSystemConfiguration>(configuration.GetSection("ActorSystem"));
var serviceProvider = services.BuildServiceProvider();
var configurationSection = serviceProvider.GetRequiredService<IOptionsMonitor<ElasticSearchConfiguration>>();
Assert.Equal("elasticsearchserverurl", configurationSection.CurrentValue.ServerUrl);
}
if I run the test in this way, without any call to services.Configure, then I get the expected behaviour with a "No service for type 'Microsoft.Extensions.Options.IOptionsMonitor`1[TNW.Server.Configurations.ElasticSearchConfiguration]' has been registered." exception.
If I remove the comment on the line about services.Configure (note: about another configuration section!) then I always get an empty but not null configuration object.
From the documentation GetRequiredService should throw the exception and GetService should return a null object.
I want to understand what is going on in order to be sure that I am not doing anything wrong and eventually I don't need to care about missing registrations here.

Symfony Dependency injection with services

Trying to create a service that logs information to a database. The service has to call the Doctrine\ORM\EntityManager in the constuctor, but I keep getting this error:
Catchable Fatal Error: Argument 1 passed to AppBundle\Service\EmailLoggerManager::__construct() must be an instance of Doctrine\ORM\EntityManager, none given, called in /Users/augustwhitlock/Desktop/symfony/SymfonyRepositories/forms/src/AppBundle/Controller/DefaultController.php on line 45 and defined
Here is what I have in my service file
namespace AppBundle\Service;
use Doctrine\ORM\EntityManager;
use AppBundle\Entity\Logger;
class EmailLoggerManager
{
private $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function logMessageToDatabase($type, $message, $date)
{
$logger = new Logger();
$logger->setMessageType = $type;
$logger->setMessageText = $message;
$logger->setMessageDate = $date;
$this->em->persist($logger);
$this->em->flush();
}
This I how I'm handling the injection of the EntityManager.
app.email_logger_manager:
class: AppBundle\Services\EmailLoggerManager
arguments: ['#doctrine.orm.entity_manager']
At this point I'm just learning about service and trying different things out. But this doesn't want to work.
Here is the edit of the DefaultController. I'm adding lines 45 and 46. There is nothing about it except the class definition.
$emailLoggerManager = new EmailLoggerManager();
$emailLoggerManager->logMessageToDatabase('Info', 'Hiya', new \DateTime());
return new Response('Message Logged');
The whole concept behind the class is to just use doctrine in the service to log things to the database, clearing my controllers from having to be clogged of all that code.
You should call the service from the controller as follows:
$this->get('app.email_logger_manager')
->logMessageToDatabase('Info', 'Hiya', new \DateTime());
instead of instantiating the class directly in the controller.
Furthermore it is advisable to pass the "#doctrine" service instead of #doctrine.orm.entity_manager due to the possibility of the EntityManager being closed.
The constructor would than have to receive Doctrine\Bundle\DoctrineBundle\Registry instead of Doctrine\ORM\EntityManager

Why resolving a collection also create 1 element?

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.

AutoFac Exception after upgrading NServiceBus

I just upgraded NServiceBus from 4.6 to 5.0
I did the steps suggested in the "4 to 5" document and am able to compile. Now I receive the following Error:
None of the constructors found with
'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type
'Nop.Web.Controllers.ShoppingCartController' can be invoked with the
available services and parameters: Cannot resolve parameter
'NServiceBus.IBus bus' of constructor 'Void .ctor(NServiceBus.IBus, ...
What has to be done?
(Update: My Configuration)
public static class ServiceBus
{
public static void Init(ILifetimeScope scope)
{
var configuration = new BusConfiguration();
configuration.EndpointName(ConfigurationManager.AppSettings["ServiceBusEndpointName"]);
configuration.UseTransport<MsmqTransport>();
configuration.UseSerialization<JsonSerializer>();
configuration.UsePersistence<RavenDBPersistence>();
configuration.DisableFeature<Sagas>();
configuration.Transactions().Enable();
configuration.AssembliesToScan(AllAssemblies
.Matching("Nop.Services.dll")
.And("TengoMessages.dll")
.And("Partner.Pricing.Messages.dll")
.And("Partner.Pricing.Infrastructure.dll"));
configuration.UseContainer<AutofacBuilder>();
configuration.PurgeOnStartup(false);
var bus = Bus.Create(configuration);
bus.Start();
var newBuilder = new ContainerBuilder();
newBuilder.RegisterInstance(bus);
newBuilder.Update(Singleton<IContainer>.Instance);
}
I don't use AutoFac, so I'm not familiar with the ContainerBuilder concept, but it looks like you want to use an existing container with NServiceBus?
Create the instance of your container first, and then change your configuration code to use:
configuration.UseContainer<AutofacBuilder>(customizations =>
customizations.ExistingContainer(container));
It looks like the second to last line of code is registering the bus--this should not be necessary, as the above code will ensure all NSB-related classes get properly registered.

Resolving HttpRequestScoped Instances outside of a HttpRequest in Autofac

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...
}