Ich versuche resolve instances by key mit SimpleInjector.
In meinem Fall sind die Schlüssel Zeichenfolgen, die aus einer Konfigurationsdatei stammen, und ich brauche die Factory, um den richtigen Typ basierend auf der Zeichenfolge zurückzugeben.Auflösen von Instanzen durch Schlüssel und automatische Registrierung mit SimpleInjector
Ich habe eine ähnliche Lösung wie die im obigen Link beschrieben, aber leicht geändert, so dass die Instanzen ihre eigenen Schlüssel bereitstellen können.
(es wird viele Klassen, die IFoo
implementieren, so würde Ich mag ihnen mit ihre Schlüssel Auto-Register)
Dies ist die komplette Arbeitsbeispiel (.NET Core-Konsole app):
(ich hielt es für die Lesbarkeit kurz, so gibt es nur eine Klasse, die IFoo
implementiert, und ich weggelassen, um die auto-register code)
using SimpleInjector;
using System;
using System.Collections.Generic;
namespace SimpleInjectorTest1
{
public interface IFoo
{
string Name { get; }
}
public class SpecificFoo : IFoo
{
public string Name { get { return "foooo"; } }
}
public interface IFooFactory
{
void Add(IFoo foo);
IFoo Create(string fooName);
}
public class FooFactory : Dictionary<string, IFoo>, IFooFactory
{
public void Add(IFoo foo)
{
// use the instance's Name property as dictionary key, so I don't
// need to hard-code it in the code which does the registration
this.Add(foo.Name, foo);
}
public IFoo Create(string fooName)
{
return this[fooName];
}
}
public class Program
{
public static void Main(string[] args)
{
var container = new Container();
// TODO: loop everything that implements IFoo, create
// an instance and add it to the factory
var factory = new FooFactory();
factory.Add(new SpecificFoo());
container.RegisterSingleton<IFooFactory>(factory);
container.Verify();
// usage
var factory2 = container.GetInstance<IFooFactory>();
IFoo foo = factory2.Create("foooo");
Console.WriteLine("Success!");
}
}
}
Diese sehr gut in der begi gearbeitet nning, bis ich erkannte, dass SpecificFoo
(und die anderen IFoo
s auch) benötigt eine Abhängigkeit über SimpleInjector.
Also, wenn ich SpecificFoo
in die Fabrik hinzufügen, muss ich die Instanz über SimpleInjector statt new SpecificFoo()
erstellen.
Also änderte ich meinen Code wie unten dargestellt:
using SimpleInjector;
using System.Collections.Generic;
namespace SimpleInjectorTest2
{
// dummy dependency
public interface IBar { }
public class Bar : IBar { }
// marker interface
public interface IFoo
{
string Name { get; }
}
public interface ISpecificFoo : IFoo
{
// empty by purpose
}
public class SpecificFoo : ISpecificFoo, IFoo
{
private readonly IBar bar;
public SpecificFoo(IBar bar) { this.bar = bar; }
public string Name { get { return "foooo"; } }
}
public interface IFooFactory
{
void Add(IFoo foo);
IFoo Create(string fooName);
}
public class FooFactory : Dictionary<string, IFoo>, IFooFactory
{
public void Add(IFoo foo)
{
// use the instance's Name property as dictionary key, so I don't
// need to hard-code it in the code which does the registration
this.Add(foo.Name, foo);
}
public IFoo Create(string fooName)
{
return this[fooName];
}
}
public class Program
{
public static void Main(string[] args)
{
var container = new Container();
container.Register<IBar, Bar>();
var factory = new FooFactory();
// TODO: loop everything that implements IFoo, create
// an instance and add it to the factory
container.Register<ISpecificFoo, SpecificFoo>();
factory.Add(container.GetInstance<ISpecificFoo>());
// The next line throws an exception because of this:
// https://simpleinjector.readthedocs.io/en/latest/decisions.html#the-container-is-locked-after-the-first-call-to-resolve
container.RegisterSingleton<IFooFactory>(factory);
}
}
}
Wie bereits oben gesagt , schlägt die Registrierung der Fabrik, weil the container is locked after the GetInstance
call.
Ich weiß, dass ich das Werk von Dictionary<string, Func<IFoo>>
statt zu erben ändern könnte (wie in Resolve instances by key in der Dokumentation gezeigt), aber dann muß ich den String-Schlüssel für die Registrierung zur Verfügung zu stellen, wie in der Dokumentation im gezeigten Beispiel:
Wie kann ich eine Fabrik verwenden, um Typen nach Schlüssel aufzulösen, aber die Typen ihre Schlüssel selbst zur Verfügung stellen?
Ich möchte nicht eine Zeile wie oben im Registrierungscode hinzufügen müssen, jedes Mal, wenn ich eine neue Klasse hinzufügen, die IFoo
implementiert.
Ich lese bereits Registration of open generic types (und Antworten wie this one als auch), aber ich denke, es trifft nicht auf meine Situation zu, weil ich nach String-Schlüssel auflösen muss.
Große Antwort! Eine Frage, würden Sie Tests für die 'FooFactory' schreiben? Ich hatte Tests bis jetzt * (vergewissere dich, dass die Registrierung von neuem "IFoo" funktioniert/vergewissere dich, dass die Fabrik abwirft, wenn ein nicht existierender Schlüssel angefordert wird) *, aber mit deiner zweiten Lösung benötigt der Fabrik-Konstruktor jetzt einen "Container" es ist schwieriger zu testen. –
@ChristianSpechr Normalerweise würde ich keine Uni-Tests schreiben, da ich weiß, dass ein Integrationstest diesen Code abdeckt. Aber im Allgemeinen habe ich diese Art von Fabriken nie in meinen Anwendungen; Ich kenne Ihren speziellen Anwendungsfall nicht, aber ich stelle fest, dass sie meistens aus den falschen Gründen verwendet werden. – Steven
@ChristianSpecht Ich habe den zweiten Teil Ihrer Frage vergessen, also die automatische Registrierung. Sehen Sie sich die Methode 'Container.GetTypesToRegister' an. Es ermöglicht das Abrufen von Typen in Assemblys, die von Ihrer IFoo-Schnittstelle abgeleitet sind. – Steven