10
votes

Extension d'usine d'objet personnalisée pour l'unité

J'utilise le conteneur IOC UNITY, et je dois intercepter les appels pour résoudre pour une certaine interface de base et exécuter mon propre code personnalisé pour construire ces types.

En d'autres termes, dans le code d'échantillon ci-dessous, lorsque j'appelle conteneur.Resolve () , s'il n'a pas eu d'instance de type de mise en œuvre concrete, il appelle MyFactoryFunction Pour en construire un, sinon je veux qu'il retourne la copie en cache.

Le conteneur d'unité standard n'est pas en mesure de construire ces objets ( Mettre à jour: car ils sont des objets de distance .NET, les classes de béton n'existent pas dans n'importe quel assemblage sur l'ordinateur local. ), et je ne veux pas les créer à l'avant et les stocker avec registerInstance. xxx

Je suppose que je peux créer une extension de l'unité pour le faire, mais je Je me demandais s'il y a déjà une solution là-bas, je peux emprunter.


0 commentaires

4 Réponses :


-1
votes

Le conteneur Unity agit déjà comme une usine qui sait construire (et effectuer une injection de dépendance pour) des types arbitraires, de sorte que votre usine accepte un type T semble redondante. Pouvez-vous élaborer plus sur pourquoi cela n'est pas possible?

Si cela n'est vraiment pas possible (plus probablement trop de travail), vous pouvez peut-être enregistrer votre usine avec le conteneur à la place?


1 commentaires

La raison est que nous utilisons .NET Remoting pour créer la classe. Nous ne pouvons donc pas simplement appeler son constructeur - le type de béton n'existe dans aucun assemblage sur le client. Nous devons nous connecter à un serveur et demander son objet qui remplit l'interface en question.



5
votes

Mise à jour forte> Cette réponse était pour l'unité 1.2. Pour une solution qui fonctionne avec Unity 2, voir mon autre réponse.


OK, j'ai mis en œuvre l'extension moi-même. Dans le constructeur, je cache l'objet car je veux que ce soit un singleton w.r.t mon conteneur. La raison de basecontext code> est que je veux que cela soit mis en cache dans le conteneur de niveau supérieur plutôt que dans des conteneurs enfants à partir desquels il a été demandé. P>

MyFactory factory = new MyFactory();
container = new UnityContainer();
container.AddExtension(new FactoryMethodUnityExtension<IBase>(factory.Create));


2 commentaires

Pourriez-vous expliquer votre stratégie de cache? Pourquoi avoir besoin d'ajouter du type créé au conteneur après la création?


@Dmitriy - parce que je veux seulement créer chaque interface de télécommande .NET une fois



8
votes

En complétude, je devrais ajouter une autre réponse qui fonctionne sous l'unité 2, puisque mon autre réponse ne fonctionne plus. Il est légèrement plus impliqué puisque vous devez faire une politique de constructeur personnalisée. Grâce à CTavares du projet Unity qui a fourni beaucoup d'aide sur ce fil dans la mise en œuvre de cette optique:

public class FactoryUnityExtension : UnityContainerExtension
{
    private ICustomFactory factory;
    private CustomFactoryBuildStrategy strategy;

    public FactoryUnityExtension(ICustomFactory factory)
    {
        this.factory = factory;
    }

    protected override void Initialize()
    {
        this.strategy = new CustomFactoryBuildStrategy(factory, Context);
        Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);
        Context.Policies.Set<ParentMarkerPolicy>(new ParentMarkerPolicy(Context.Lifetime), new NamedTypeBuildKey<ParentMarkerPolicy>());
    }
}

public class ParentMarkerPolicy : IBuilderPolicy
{
    private ILifetimeContainer lifetime;

    public ParentMarkerPolicy(ILifetimeContainer lifetime)
    {
        this.lifetime = lifetime;
    }

    public void AddToLifetime(object o)
    {
        lifetime.Add(o);
    }
}

public interface ICustomFactory
{
    object Create(Type t);
    bool CanCreate(Type t);
}

public class CustomFactoryBuildStrategy : BuilderStrategy
{
    private ExtensionContext baseContext;
    private ICustomFactory factory;


    public CustomFactoryBuildStrategy(ICustomFactory factory, ExtensionContext baseContext)
    {
        this.factory = factory;
        this.baseContext = baseContext;
    }

    public override void PreBuildUp(IBuilderContext context)
    {
        var key = (NamedTypeBuildKey)context.OriginalBuildKey;

        if (factory.CanCreate(key.Type) && context.Existing == null)
        {
            context.Existing = factory.Create(key.Type);
            var ltm = new ContainerControlledLifetimeManager();
            ltm.SetValue(context.Existing);

            // Find the container to add this to
            IPolicyList parentPolicies;
            var parentMarker = context.Policies.Get<ParentMarkerPolicy>(new NamedTypeBuildKey<ParentMarkerPolicy>(), out parentPolicies);

            // TODO: add error check - if policy is missing, extension is misconfigured

            // Add lifetime manager to container
            parentPolicies.Set<ILifetimePolicy>(ltm, new NamedTypeBuildKey(key.Type));
            // And add to LifetimeContainer so it gets disposed
            parentMarker.AddToLifetime(ltm);

            // Short circuit the rest of the chain, object's already created
            context.BuildComplete = true;
        }
    }
}


0 commentaires

5
votes

Unity (v2) vous permet de spécifier une usine. Il permet de faire quelques fonctions différentes, notamment de prendre le type à accumuler / nom, etc. Exemple simple:

cont.RegisterType<TestClass>(new InjectionFactory(c => CreateTest()));

var svc = cont.Resolve<TestClass>();


1 commentaires

On dirait que vous devez utiliser la forme générique de registertype