c# - Resolving a class with a custom parameter in Simple Injector -


i'm creating wpf mvvm application using simple injector di container. i'm having issues when i'm trying resolve view simple injector, because i'm in need of passing parameter constructor @ construction time (not when registering view container, not applicable: simple injector pass values constructor).

what i'm after this:

var item = container.getinstance<mytype>(myparameter); 

i've read several places not possible in simple injector because should not done (including here: https://simpleinjector.codeplex.com/discussions/397080).

is true, , if so, how instead?


background information

i have collection of multiple view models , models looked specific key, , parameter want pass view key view model use. i've found necessary because view models , models used in multiple locations of application, , need stay in sync / same instances if have same key. don't think i'm able use lifetime scope solve this, , there no way know keys when i'm registering container. i've understood viewmodellocator approach might servicelocator (anti-?)pattern, however, best i've got.

my constructor looks this, , want iviewmodellocator resolved, while pass in key:

public fillpropertiesview(iviewmodellocator vml, object key) {     // .. removed code      // assign view model     var viewmodel = vml.getviewmodel<fillpropertiesviewmodel>(key);     datacontext = viewmodel; } 

the iviewmodellocator looks following (and similar interface exists models).

public interface iviewmodellocator {     // gets view model associated key, or default     // view model if no key supplied     t getviewmodel<t>(object key = null) t : class, iviewmodel; } 

now have following questions:

  • what best way able resolve view view model key?
  • do have refactoring enable this?
  • am missing out on of power of di container since have created own dictionary based viewmodellocator?

extended information

i have shown viewmodellocator above, , reason i'm using keep blendability (basically provides me design time data when opened in blend). runtime view model have been same every instance of view (not dependent on key), if did not have have different windows opened @ same time (see next paragraph). issue described above, however, same when viewmodel fetching model, , modellocator needs key fetch existing model if exists.

this part of vsto application targeting powerpoint (which affects parts of design). when object on screen selected, task panel opened (this fillpropertiesview explained above). object selected has key supplied view, in order view extract correct view model viewmodellocator. view model reference model using imodellocator (similar iviewmodellocator) , same key. @ same time, controller fetch model modellocator using same key. controller listens change events model , updates objects on screen. same process replicated whenever new object selected, , @ same time multiple windows open interacts same or different objects simultaneously (that is, multiple task panes unique view models).

up until have resolved view default, parameterless constructor, , injected view model method call afterwards:

// sets view model on view public void setviewmodel(iviewmodellocator vml, object key) {     // assign view model     _viewmodel = vml.getviewmodel<fillpropertiesviewmodel>(key);     datacontext = _viewmodel; } 

i never had register view container, resolved concrete type this:

string key = "testkey" // read selected object var view = container.getinstance<fillpropertiesview>(); var vml = container.getinstance<iviewmodellocator>(); view.setviewmodel(vml, key); 

this issue surfaced when tried refactor did not have call setviewmodel() method every , manually resolve view models etc. got messy when had manual initiation within view model initiate model in same way.


viewmodellocator

the viewmodellocator working wrapper around di container, i.e. view models registered in simple injector.

the registrations following (in class called compositionhost):

container.registersingle<iviewmodellocator, viewmodellocator>(); container.registersingle<imodellocator, modellocator>(); 

the implementation looks this:

// base implementation used viewmodellocator , modellocator public class servicelocator<tservice> tservice : class {     private readonly dictionary<combinedtypekey, tservice> _instances =         new dictionary<combinedtypekey, tservice>();      // gets service instance based on type , key.     // key makes possible have multiple versions of same service.     public t getinstance<t>(object key = null) t : class, tservice     {         var combinedkey = new combinedtypekey(typeof(t), key);          // check if have instance         if (_instances.containskey(combinedkey))         {             return _instances[combinedkey] t;         }          // create new instance         // compositionhost static reference di container (and          // should perhaps injected, however, not main issue here)         var instance = compositionhost.getinstance<t>();         _instances.add(combinedkey, instance);         return instance;     }      // combined key ease dictionary operations     private struct combinedtypekey     {         private readonly object _key;         private readonly type _type;          public combinedtypekey(type type, object key)         {             _type = type;             _key = key;         }          // equals , gethashcode() overridden     } }  public class viewmodellocator : iviewmodellocator {      private readonly servicelocator<iviewmodel> _viewmodellocator;       public viewmodellocator(servicelocator<iviewmodel> locator)     {         _viewmodellocator = locator;          // dummy code registers design time data removed     }      // iviewmodel empty interface implemented view models     public t getviewmodel<t>(object key = null) t : class, iviewmodel     {         return _viewmodellocator.getinstance<t>(key);     }  } 

injecting service locator classes (almost) never way go, because disallows compile time checking of dependencies , runtime dependency analysis. reason can advice register root types (such views) since otherwise simple injector left in dark , not able advice possible misconfigurations might have.

since have view + viewmodel pairs cached together, might depend on model instance reused multiple view + viewmodel pairs, suggest following design.

define abstraction views , view models:

public interface iview<tmodel> {     iviewmodel<tmodel> viewmodel { get; } }  public interface iviewmodel<tmodel> {     tmodel model { get; set; } } 

define abstraction retrieving/caching view key.

public interface iviewprovider<tview, tmodel> tview : iview<tmodel> {     tview getviewbykey(object key); } 

with these abstractions view can follows:

public class fillpropertiesview : iview<fillpropertiesmodel> {     public fillpropertiesview(fillpropertiesviewmodel viewmodel)     {         this.viewmodel = viewmodel;     }      public iviewmodel<fillpropertiesmodel> viewmodel { get; private set; } } 

and controllers can depend upon iviewprovider<tview, tmodel> abstraction can reload view when new key coming in:

public class fillpropertiescontroller : controller {     iviewprovider<fillpropertiesview, fillpropertiesmodel> viewprovider;     fillpropertiesview view;      public fillpropertiescontroller(         iviewprovider<fillpropertiesview, fillpropertiesmodel> provider) {         this.viewprovider = provider;     }      public void reinitialize(object key) {         this.view = this.viewprovider.getviewbykey(key);     } } 

the implementation iviewprovider<tview, tmodel> this:

public class viewprovider<tview, tmodel> : iviewprovider<tview, tmodel>      tview : class, iview<tmodel> {     dictionary<object, tview> views = new dictionary<object, tview>();     container container;     imodelprovider<tmodel> modelprovider;      public viewprovider(container container,         imodelprovider<tmodel> modelprovider) {         this.container = container;         this.modelprovider = modelprovider;     }      public tview getviewbykey(object key) {         tview view;          if (!this.views.trygetvalue(key, out view)) {             this.views[key] = view = this.createview(key);         }          return view;     }      private tview createview(object key) {         tview view = this.container.getinstance<tview>();         view.viewmodel.model = this.modelprovider.getmodelbykey(key);         return view;     } } 

this implementation depends on (previously undefined) imodelprovider<tmodel> abstraction. old modellocator, using generic type can make implementation easier, because can have 1 instance of type per tmodel (the same holds viewprovider), saves having things storing elements { type + key } combination.

you can register follows:

assembly asm = assembly.getexecutingassembly();  container.registermanyforopengeneric(typeof(iview<>), asm); container.registermanyforopengeneric(typeof(iviewmodel<>), asm); container.registeropengeneric(typeof(iviewprovider<,>),      typeof(viewprovider<,>), lifestyle.singleton); container.registeropengeneric(typeof(imodelprovider<>),      typeof(modelprovider<>), lifestyle.singleton);  var controllers =     type in asm.gettypes()     type.issubclassof(typeof(controller))     !type.isabstract     select type;  controllers.tolist().foreach(t => container.register(t));  container.verify(); 

with registermanyforopengeneric let simple injector search supplied assemblies implementations of given open generic abstraction , simple injector batch-register them you. registeropengeneric specify open-generic abstraction , tell simple injector implementation use when close-generic version of abstraction requested. last line searches through application controller types , registers them in system.


Comments

Popular posts from this blog

javascript - how to protect a flash video from refresh? -

android - Associate same looper with different threads -

visual studio 2010 - Connect to informix database windows form application -