2010-08-10 12 views
1

J'ai une interface à résoudre et l'une des dépendances de l'objet mappé a une propriété que je voudrais définir avec une valeur que je n'ai disponible que lorsque je résous l'objet de niveau supérieur.Comment puis-je injecter une propriété uniquement si la valeur est non nulle à l'exécution avec Unity?

Il n'y a pas de valeur par défaut valide pour la propriété. Si elle n'est pas définie, elle doit être null et ne doit être définie que si la valeur que j'ai disponible au moment de la résolution n'est pas nulle.

Cette injection de propriété conditionnelle est-elle possible?

J'ai essayé ...

container.RegisterType<ProductInstanceValidatorBase, CartItemPurchaseTypeValidator>("CartItemPurchaseTypeValidator", new InjectionProperty("AccountEntity", null); 

... mais il a dit que je ne pouvais pas utiliser une valeur nulle!

J'ai aussi essayé cela sur la volonté ...

container.Resolve<ProductInstanceValidatorBase>(new PropertyOverride("AccountEntity", value)); 

... mais cela jette une exception lorsque la valeur est nulle. Il est dit:

L'inférence de type de paramètre ne fonctionne pas pour les valeurs nulles. Indiquez explicitement le type de paramètre à l'aide d'une instance correctement configurée des classes InjectionParameter ou InjectionParameter. Nom du paramètre: parameterValue

Fondamentalement, je suis à la recherche d'enregistrer une propriété qui est uniquement définie avec une dérogation et seulement si la valeur de remplacement est non nul. Des idées? Sûrement d'un point de vue sémantique, l'injection de propriété devrait être être facultative.

Cheers, Ian.

Répondre

5

Une solution possible à ce problème est d'implémenter un Null Object Pattern et de passer le Account.Empty lorsque vous n'avez pas de compte valide. Voici ce qu'une classe pourrait ressembler à:

public class Account { 
    public static readonly Account Empty = new Account(); 
} 

//at resolution time 
Account account = null; 
if (HasAccount) 
    account = GetAccount(); 
else 
    account = Account.Empty; 

container.Resolve<ProductInstanceValidatorBase>(new PropertyOverride("AccountEntity", account)); 

De cette façon, le compte est jamais null et Entity Framework ne se plaindrait pas.

Cependant, je sens que vous pourriez avoir un plus gros problème avec la conception du système. Conteneur IoC permet un système plus faiblement couplé, où le câblage des composants est défini avant le programme est exécuté, pas tandis que il est exécuté. A en juger par le nom, AccountEnyity est une classe d'entité, pas une classe de service, et vous normalement ne pas inscrire des entités avec le conteneur IoC, seulement des services. Compte tenu de ce qui précède, je suggère que AccountEntity ne devrait pas être une propriété injectable, mais plutôt une propriété normale. Vous externalisez alors la création ProductInstanceValidatorBase dans une usine et laissez-le prendre soin de mettre en AccountEntity propriété

public interface IProductInstanceValidatorFactory{ 
    ProductInstanceValidatorBase Create(Account account); 
} 
public class ProductInstanceValidatorFactory : IProductInstanceValidatorFactory{ 
    public ProductInstanceValidatorBase Create(Account account){ 
    var validator = new ProductInstanceValidator(); 
    validator.AccountEnity = account; 
    return validator; 
    } 
} 

Vous ne devez même pas enregistrer ProductInstanceValidatorBase avec Unity, mais inscrivez-vous dès l'usine.Voici comment utiliser l'usine ressemblerait:

Account account = null; 
container.Resolve<IProductInstanceValidatorFactory>().Create(account); 
+0

Salut Igor, merci pour la réponse. J'ai évité le problème d'inférence de type en enveloppant la valeur de compte prioritaire avec un InjectionParameter typé et j'ai utilisé un objet nul qui pourrait être vérifié dans le setter. Je pense que vous avez raison d'avoir un problème avec la conception. L'objet de niveau supérieur prend réellement un tableau de ProductInstanceValidatorBase et seulement un type concret de ceux-ci a un paramètre de compte ainsi je suis venu à la conclusion qu'il s'agissait d'une abstraction qui fuit. Je peux essayer de mettre la référence de compte sur un sous-type de l'instance de produit en cours de validation. –

+0

Si le constructeur d'une fabrique personnalisée (n'utilisant pas IoC) qui crée un tableau de ces validateurs nécessite une longue liste de dépendances pour satisfaire les dépendances de tous les différents validateurs dans le tableau résultant, comment arrêter le paramètre constructeur de l'usine liste d'être vraiment long? –

-1

Je serais d'accord avec Igor. Cependant, si vous devez absolument résoudre le problème Entity, pourquoi ne pas simplement prendre votre conteneur IoC en tant que dépendance de constructeur, puis essayer de Resolve dans le constructeur?

+0

Pourquoi le vote à la baisse? – arootbeer

+0

Ce n'était pas moi! Mais peut-être parce que faire votre suggestion polluerait son code avec l'anti-modèle de Localisateur de Service? –

+0

C'est en gros ce que j'ai pensé - j'ai essayé d'adoucir cela en le gardant dans le contexte du design actuel (que nous sommes apparemment tous d'accord pour dire que c'était un peu imparfait). Je suppose que la création de quelque chose d'imparfait vaut probablement le downvote :) – arootbeer

4

Vous pouvez utiliser le InjectionParameter avec des valeurs nulles, mais vous devez spécifier le type. Pour vos deux scénarios Je suppose que votre paramètre AccountEntity est de type ou dérivé de IAccount:

container.RegisterType<ProductInstanceValidatorBase, CartItemPurchaseTypeValidator>("CartItemPurchaseTypeValidator", new InjectionProperty<IAccount>(null); 

Notez que j'ai laissé tomber le nom tel qu'il est pas spécifiquement nécessaire dans le scénario ci-dessus, mais il est nécessaire pour la scénario ci-dessous.

container.Resolve<ProductInstanceValidatorBase>(new PropertyOverride("AccountEntity", new InjectionProperty<IAccount>(null))); 
+2

Cela ne fonctionne plus comme InjectionProperty n'est pas tapé. Je crois que vous devez utiliser InjectionParameter –