Castle Windsor cannot find System.Messaging - dependency-injection

I realise this question is very similar to this one, but unfortunately it doesn't quite solve my problem.
I have a console app with the following in my castle config section:
<component id="LegacyMessageFormatter"
service="System.Messaging.IMessageFormatter, System.Messaging"
type="MsmqLogProcessor.Core.Services.LegacyMessageFormatter, MsmqLogProcessor.Core"/>
I initially got an exception "The type name System.Messaging.IMessageFormatter, System.Messaging could not be located." when newing up my WindsorContainer like so:
var container = new WindsorContainer(new XmlInterpreter());
I've set Copy Local to true on System.Messaging. That works when I hit F5.
However, when I publish my console app and run it on a production box I get the same issue. I cannot see the System.Messaging dll in the ApplicationFiles folder after install, presumably because it thinks it's in the GAC.
I've tried copying the dll into this folder, but still no joy.
Any ideas what I'm missing? Is it even a good idea to specify a System assembly as a service?

Try using the fully qualified type name:
<component id='LegacyMessageFormatter'
service='System.Messaging.IMessageFormatter, System.Messaging, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
type='...'/>

Windsor does not lookup types in System assemblies, as you can see here
https://github.com/castleproject/Castle.Windsor/blob/master/src/Castle.Windsor/MicroKernel/SubSystems/Conversion/TypeNameConverter.cs#L170
it explicitly skips types defined in assemblies with a name starting with System.
Don't know why but you can workaround this by using your own ConversionManager
public class MyConversionManager : DefaultConversionManager
{
protected override void InitDefaultConverters()
{
Add(new SystemMessaging_TypeConverter(new TypeNameParser()));
base.InitDefaultConverters();
}
}
public class SystemMessaging_TypeConverter : TypeNameConverter
{
public SystemMessaging_TypeConverter(ITypeNameParser parser) : base(parser)
{
}
protected override bool ShouldSkipAssembly(System.Reflection.Assembly assembly)
{
return !assembly.FullName.StartsWith("System.Messaging") && base.ShouldSkipAssembly(assembly);
}
}
and install it in your container as follows:
var container = new WindsorContainer();
container.Kernel.AddSubSystem(SubSystemConstants.ConversionManagerKey, new MyConversionManager());
container.Install(Configuration.FromAppConfig());
And then register the service without the assembly name:
<component id="LegacyMessageFormatter"
service="System.Messaging.IMessageFormatter"
type="MsmqLogProcessor.Core.Services.LegacyMessageFormatter, MsmqLogProcessor.Core">

Related

Autofac Dependency Resolver ocasionally crash on controller resolving mvc 5 (DependencyResolutionException)

i have following problem, I have web application in mvc5 with autofac dependency injection and generic data repositories. Usually all is working fine, the web is runing with no error, but after some time online following error appear:
The dependency resolver is folowing:
public class IoCConfig {
public static IContainer BuildContainer < T > () {
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterApiControllers(typeof(T).Assembly);
builder.RegisterControllers(typeof(T).Assembly);
try {
foreach(var type in AssemblyLoader.Load < IDependency > ()) {
var registration = builder.RegisterType(type);
foreach(var interfaceType in type.GetInterfaces().Where(itf => typeof(IDependency).IsAssignableFrom(itf))) {
registration = registration.As(interfaceType);
if (typeof(IUnitOfWorkDependency).IsAssignableFrom(interfaceType)) {
registration = registration.InstancePerRequest();
}
}
}
} catch (Exception ex) {
throw ex;
}
return builder.Build();
}
}
and is executed on Application_Start.
The example constructor of the controller is:
[Authorize]
public class ClassesManagmentController: Controller {
IDepartmentsRepository _departmentRepo;
IClassesRepository _classesRepo;
IClassesCategoriesRepository _classesCategoriesRepo;
public ClassesManagmentController(IDepartmentsRepository departmentsRepo,
IClassesRepository classesRepo,
IClassesCategoriesRepository classesCatiegoriesRepo) {
_departmentRepo = departmentsRepo;
_classesRepo = classesRepo;
_classesCategoriesRepo = classesCatiegoriesRepo;
}
// the rest of the Controller code
}
And the example repo interface and class are:
public class ClassesCategoriesRepository: Bases.RepositoryBase < ClassesCategoryDto > , IClassesCategoriesRepository {
public ClassesCategoriesRepository(IContextFactory factory): base(factory) {
}
}
public interface IClassesCategoriesRepository: IGenericRepository < ClassesCategoryDto > , IDependency {}
The repositories are in dedicated project, the dependency resolver and identification interfaces (IDependency) are in dedicated project, and web is in one more project.
I checked already twice if all projects of my solution are referencing the same autofac and mvc libraries. i was trying different resolving types but i dont found solution yet.
Any help will be appreciated.
If things are working for a while and then stop working chances are IIS is recycling the app pool and your assembly scanning mechanism isn't picking up all the assemblies correctly. Per the documentation:
When hosting applications in IIS all assemblies are loaded into the AppDomain when the application first starts, but when the AppDomain is recycled by IIS the assemblies are then only loaded on demand.
To avoid this issue use the GetReferencedAssemblies() method on System.Web.Compilation.BuildManager to get a list of the referenced assemblies instead:
var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>();
That will force the referenced assemblies to be loaded into the AppDomain immediately making them available for module scanning.
If this controller never works but everything else does, do a test in one of the working controllers - try to manually resolve the IDepartmentsRepository and see if you can. If you can, then... strange things are afoot. But if you can't, then it means that particular repo isn't being picked up by the scanning and isn't registered, so the exception is telling you exactly what's wrong.

StructureMap, Web Api 2, and IUserStore error

I have just started experimenting with Web Api 2 and StructureMap, having installed StructureMap.MVC4 Nuget package. Everything seems to work fine until I tried to register a user. I got this error when this implementation of IHttpControllerActivator tried to instantiate a controller:
public class ServiceActivator : IHttpControllerActivator
{
public ServiceActivator(HttpConfiguration configuration) { }
public IHttpController Create(HttpRequestMessage request
, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
var controller = ObjectFactory.GetInstance(controllerType) as IHttpController;
return controller;
}
}
The error I got was:
StructureMap Exception Code: 202
No Default Instance defined for PluginFamily Microsoft.AspNet.Identity.IUserStore`1[[Microsoft.AspNet.Identity.EntityFramework.IdentityUser, Microsoft.AspNet.Identity.EntityFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]], Microsoft.AspNet.Identity.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
I understand what the error is, but not entirely sure how to solve it. Is it correct to assume the default scanner in StructureMap could not find a default implementation of IUserStore? Here's the initialisation code I used:
ObjectFactory.Initialize(x => x.Scan(scan =>
{
scan.AssembliesFromApplicationBaseDirectory();
scan.WithDefaultConventions();
}));
Any ideas please? Thanks.
EDIT:
I think I may have solved the initial issue using this:
x.For<Microsoft.AspNet.Identity.IUserStore<IdentityUser>>()
.Use<UserStore<IdentityUser>>();
But now there's another default instance StructureMap couldn't work out - the dbcontext. Here's the next error message I'm getting:
ExceptionMessage=StructureMap Exception Code: 202
No Default Instance defined for PluginFamily System.Data.Entity.DbContext, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Now I'm really lost...
The WithDefaultConventions() call won't pick up your DbContext and AspNet Identity implementations. You'll want to look at some of the other methods like SingleImplementationsOfInterface() and ConnectImplementationsToTypesClosing.
By default when I setup my StructureMap container, I will do the following configuration in order to ensure that StructureMap will always resolve the interfaces and base classes of my preferred class to my actual preferred class:
ioc.For<MyDbContext>().HybridHttpOrThreadLocalScoped().Use<MyDbContext>();
ioc.For<DbContext>().HybridHttpOrThreadLocalScoped().Use<MyDbContext>();
For the new AspNet Identity classes, just subclass the generic classes they give you out of the box:
public class MyUserManager : UserManager<MyUser> { }
public class MyUserStore : UserStore<MyUser> { }
And then again, make sure StructureMap knows about these:
ioc.For<IUserStore<MyUser>>().Use<MyUserStore>();
ioc.For<UserStore<MyUser>>().Use<MyUserStore>();
ioc.For<UserManager<MyUser>>().Use<MyUserManager>();
Generally, you don't have to explicitly register every class with StructureMap, but with my DbContext and Identity classes, I prefer to have those explicity registered for maintenance purposes.
ericb: I can see the purpose of what you've posted but I can't quite get it to work. The MyUserManager class declaration "public class MyUserManager : UserManager { }" is complaining that the UserManager interface does not contain a constructor that takes 0 arguments?
Any help would be greatly appreciated!
Ps. This is by no means an answer but I'm not qualified enough to simply comment on your answer unfortunately!
Update: Found a solution here: Dependency Injection Structuremap ASP.NET Identity MVC 5
For clarity we replaced any of the above with the following in the IoC file:
x.For<Microsoft.AspNet.Identity.IUserStore<ApplicationUser>>()
.Use<Microsoft.AspNet.Identity.EntityFramework.UserStore<ApplicationUser>>();
x.For<System.Data.Entity.DbContext>().Use(() => new ApplicationDbContext());
I'm sure we're missing out on some extra benefits that ericb gets with his solution but we're not utilising anything that would take advantage of them.
There is a quick and easy workaround to this problem as well, and in many cases may be sufficient. Go to AccountController.cs and above the default constructor (the one with no params or code in it) add [DefaultConstructor] and resolve using structuremap.
[DefaultConstructor]
public AccountController()
{
}
Though the proper IoC solution is this...
For<IUserStore<ApplicationUser>>().Use<UserStore<ApplicationUser>>();
For<DbContext>().Use<ApplicationDbContext>(new ApplicationDbContext());
For<IAuthenticationManager>().Use(() => HttpContext.Current.GetOwinContext().Authentication);
Or you can try constructor injection method:
x.For<IUserStore<ApplicationUser>>().Use<UserStore<ApplicationUser>>()
.SelectConstructor(() => new UserStore<ApplicationUser>(new MyContext()));

How can we support modular and testable patterns with ASP.NET MVC 4 and MEF 2?

We're trying to use MEF 2 with ASP.NET MVC 4 to support an extensible application. There are really 2 parts to this question (hope that's okay SO gods):
How do we use Microsoft.Composition and the MVC container code (MEF/MVC demo source) to replace Ninject as our DI for ICoreService, ICoreRepository, IUnitOfWork, and IDbContext?
It looks like we can't use both Ninject and the MVC container at the same time (I'm sure many are saying "duh"), so we'd like to go with MEF, if possible. I tried removing Ninject and setting [Export] attributes on each of the relevant implementations, spanning two assemblies in addition to the web project, but Save() failed to persist with no errors. I interpreted that as a singleton issue, but could not figure out how to sort it out (incl. [Shared]).
How do we load multiple assemblies dynamically at runtime?
I understand how to use CompositionContainer.AddAssemblies() to load specific DLLs, but for our application to be properly extensible, we require something more akin to how I (vaguely) understand catalogs in "full" MEF, which have been stripped out from the Microsoft.Composition package (I think?); to allow us to load all IPluggable (or whatever) assemblies, which will include their own UI, service, and repository layers and tie in to the Core service/repo too.
EDIT 1
A little more reading solved the first problem which was, indeed, a singleton issue. Attaching [Shared(Boundaries.HttpRequest)] to the CoreDbContext solved the persistence problem. When I tried simply [Shared], it expanded the 'singletonization' to the Application level (cross-request) and threw an exception saying that the edited object was already in the EF cache.
EDIT 2
I used the iterative assembly loading "meat" from Nick Blumhardt's answer below to update my Global.asax.cs code. The standard MEF 2 container from his code did not work in mine, probably because I'm using the MEF 2(?) MVC container. Summary: the code listed below now works as desired.
CoreDbContext.cs (Data.csproj)
[Export(typeof(IDbContext))]
[Shared(Boundaries.HttpRequest)]
public class CoreDbContext : IDbContext { ... }
CoreRepository.cs (Data.csproj)
[Export(typeof(IUnitOfWork))]
[Export(typeof(ICoreRepository))]
public class CoreRepository : ICoreRepository, IUnitOfWork
{
[ImportingConstructor]
public CoreRepository(IInsightDbContext context)
{
_context = context;
}
...
}
CoreService.cs (Services.csproj)
[Export(typeof(ICoreService))]
public class CoreService : ICoreService
{
[ImportingConstructor]
public CoreService(ICoreRepository repository, IUnitOfWork unitOfWork)
{
_repository = repository;
_unitOfWork = unitOfWork;
}
...
}
UserController.cs (Web.csproj)
public class UsersController : Controller
{
[ImportingConstructor]
public UsersController(ICoreService service)
{
_service = service;
}
...
}
Global.asax.cs (Web.csproj)
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
CompositionProvider.AddAssemblies(
typeof(ICoreRepository).Assembly,
typeof(ICoreService).Assembly,
);
// EDIT 2 --
// updated code to answer my 2nd question based on Nick Blumhardt's answer
foreach (var file in System.IO.Directory.GetFiles(Server.MapPath("Plugins"), "*.dll"))
{
try
{
var name = System.Reflection.AssemblyName.GetAssemblyName(file);
var assembly = System.Reflection.Assembly.Load(name);
CompositionProvider.AddAssembly(assembly);
}
catch
{
// You'll need to craft exception handling to
// your specific scenario.
}
}
}
}
If I understand you correctly, you're looking for code that will load all assemblies from a directory and load them into the container; here's a skeleton for doing that:
var config = new ContainerConfiguration();
foreach (var file in Directory.GetFiles(#".\Plugins", "*.dll"))
{
try
{
var name = AssemblyName.GetAssemblyName(file);
var assembly = Assembly.Load(name);
config.WithAssembly(assembly);
}
catch
{
// You'll need to craft exception handling to
// your specific scenario.
}
}
var container = config.CreateContainer();
// ...
Hammett discusses this scenario and shows a more complete version in F# here: http://hammett.castleproject.org/index.php/2011/12/a-decent-directorycatalog-implementation/
Note, this won't detect assemblies added to the directory after the application launches - Microsoft.Composition isn't intended for that kind of use, so if the set of plug-ins changes your best bet is to detect that with a directory watcher and prompt the user to restart the app. HTH!
MEF is not intended to be used as DI framework. Which means that you should separate your "plugins" (whatever they are) composition from your infrastructure dependencies, and implement the former via MEF and the latter via whatever DI framework you prefer.
I think there are a little misunderstandings on what MEF can and can't do.
Originally MEF was conceived as purely an extensibility architecture, but as the framework evolved up to its first release, it can be fully supported as a DI container also. MEF will handle dependency injection for you, and does so through it's ExportProvider architecture. It is also entirely possible to use other DI frameworks with MEF. So in reality there are a number of ways things could be achieved:
Build a NinjectExportProvider that you can plug into MEF, so when MEF is searching for available exports, it will be able to interrogate your Ninject container.
Use an implementation of the Common Services Locator pattern to bridge between MEF and Ninject or vice versa.
Because you are using MEF for the extensibility, you'll probably want to use the former, as this exposes your Ninject components to MEF, which in turn exposes them to your plugins.
The other thing to consider, which is a bit disappointing, is in reality there isn't a lot of room for automagically plugging in of features ala Wordpress on ASP.NET. ASP.NET is a compiled and managed environment, and because of that you either resort to late-binding by loading assemblies manually at runtime, or you restart the application to pick up the new plugins, which sort of defeats the object of being able to plug new extensions in through the application.
My advice, is plan your architecture to pick up any extensibility points as startup and assume that any core changes will require a deployment and application restart.
In terms of the direct questions asked:
The CompositionProvider accepts in instance of ContainerConfiguration which is used internally to create the CompositionContainer used by the provider. So you could use this as the point by which you customise how you want your container to be instantiated. The ContainerConfiguration supports a WithProvider method:
var configuration = new ContainerConfiguration().WithProvider(new NinjectExportDescriptorProvider(kernel));
CompositionProvider.SetConfiguration(configuration);
Where NinjectExportDescriptorProvider might be:
public class NinjectExportDescriptorProvider: ExportDescriptorProvider
{
private readonly IKernel _kernel;
public NinjectExportDescriptorProvider(IKernel kernel)
{
if (kernel == null) throw new ArgumentNullException("kernel");
_kernel = kernel;
}
public override IEnumerable<ExportDescriptorPromise> GetExportDescriptors(
CompositionContract contract, DependencyAccessor dependencyAccessor)
{
var type = contract.ContractType;
if (!_kernel.GetBindings(type).Any())
return NoExportDescriptors;
return new[] {
new ExportDescriptorPromise(
contract,
"Ninject Kernel",
true, // Hmmm... need to consider this, setting it to true will create it as a shared part, false as new instance each time,
NoDependencies,
_ => ExportDescriptor.Create((c, o) => _kernel.Get(type), NoMetadata)) };
}
}
}
Note: I have not tested this, this is all theory, and is based on the example AppSettingsExportDescriptorProvider at: http://mef.codeplex.com/wikipage?title=ProgrammingModelExtensions
It's different from using the standard ExportProvider, because using the CompostionProvider is built around lightweight composition. But essentially you're wrapping up access to your Ninject kernel and making it available to your CompositionContainer.
As with adding a specific new provider (see above), you can use the ContainerConfiguration to read the available assemblies, probably something like:
var configuration = new ContainerConfiguration().WithAssemblies(AppDomain.GetAssemblies())
Again, I haven't tested all of this, but I hope it at least points you in the right direction.

Asp.NET MVC 3 will not resolve routes for an MVC area loaded outside of bin directory

I have MVC areas in external libraries which have their own area registration code just as a normal MVC area would. This area registration gets called for each dll (module) and I have verified the RouteTable contains all the routes from the loaded modules once loading has been completed.
When I reference these external areas in the main site they get pulled into the bin directory and load up fine. That is, when a request is made for a route that exists in an external library, the correct type is passed to my custom controller factory (Ninject) and the controller can be instantiated.
Once I move these dll's outside of the bin directory however (say to a Modules folder), there appears to be an issue with routing. I have checked that the RouteTable has all the required routes but by the time a request makes its way into the ninject controller factory the requested type is null. From reading here an SO link here this behaviour seems to occur when ASP.NET MVC cannot find the controller matching the requested route or does not know how to make sense of the route.
When loading the modules externally I have ensured that the modules that I want loaded are loaded into the app domain via a call to Assemby.LoadFrom(modulePath);
I did some research and it appears that when attempting to load a library outside of bin you need to specify private probing in app.config as pointed out here;. I have mine set to 'bin\Modules' which is where the mvc area modules get moved too.
Does anyone have any ideas why simply moving an mvc area project outside of the bin folder would cause the requested type passed into the controller factory to be null resulting in the controller to be instantiated?
Edit:
All routes registered in external areas have the namespace of the controller specified in the route
Below is a fragment of code that creates a new Ninject kernel, reads a list of module names from a file to enable, and then goes searching for the enabled modules in the bin/Modules directory. The module is loaded via the assembly loader, has its area(s) registered and then loaded into the ninject kernel.
// comma separated list of modules to enable
string moduleCsv = ConfigurationManager.AppSettings["Modules.Enabled"];
if (!string.IsNullOrEmpty(moduleCsv)) {
string[] enabledModuleList = moduleCsv.Replace(" ", "").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
_Modules = enabledModuleList ?? new string[0];
// load enabled modules from bin/Modules.
var moduleList = Directory.GetFiles(Server.MapPath("~" + Path.DirectorySeparatorChar + "bin" + Path.DirectorySeparatorChar + "Modules"), "*.dll");
foreach (string enabledModule in enabledModuleList) {
string modulePath = moduleList.Single(m => m.Contains(enabledModule));
// using code adapted from from AssemblyLoader
var asm = AssemblyLoader.LoadAssembly(modulePath);
// register routes for module
AreaRegistrationUtil.RegisterAreasForAssemblies(asm);
// load into Ninject kernel
kernel.Load(asm);
}
}
This is the crux of the Ninject controller factory that receives the aforementioned Ninject kernel and handles requests to make controllers. For controllers that exist within an assembly in bin/Modules the GetControllerType(...) returns null for the requested controller name.
public class NinjectControllerFactory : DefaultControllerFactory
{
#region Instance Variables
private IKernel _Kernel;
#endregion
#region Constructors
public NinjectControllerFactory(IKernel kernel)
{
_Kernel = kernel;
}
protected override Type GetControllerType(System.Web.Routing.RequestContext requestContext, string controllerName)
{
// Is null for controller names requested outside of bin directory.
var type = base.GetControllerType(requestContext, controllerName);
return type;
}
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
IController controller = null;
if (controllerType != null)
controller = _Kernel.Get(controllerType) as IController;
return controller;
}
}
Update on Ninject Nuget Install
I couldn't get it to install Ninject.MVC3 via NuGet for some reason. Visual Studio was giving some schemaVersion error when clicking the install button (I have installed other Nuget packages like ELMAH btw).
I did find out something else that was interesting though, and that is that if I pass in the extra module assembilies to the NinjectControllerFactory I have and search those when the type cannot be resolved it finds the correct type and is able to build the controller. This leads to another strange problem.
The first route to be requested from an external module is the /Account/LogOn in the auth and registration module. The virtual path provider throws an error here after it has located the view and attempts to render it out complaining of a missing namespace. This causes an error route to fire off which is handled by an ErrorHandling module. Strangely enough, this loads and render fine!
So I am still stuck with two issues;
1) Having to do a bit of a dodgy hack and pass in the extra module assemblies to the NinjectControllerFactory in order to be able to resolve types for Controllers in external modules
2) An error with one particular module where it complains about a namespace not being found
These two issues are obviously connected because the assembly loading just isn't loading up and making everything available that needs to be. If all these mvc areas are loaded from the bin directory everything works fine. So it is clearly a namespacing/assembly load issue.
LoadFrom load the assembly into the loading context. These types are not available to the other classes in the default Load context. Probably this is the reason why the controller is not found.
If you know which assemblies have to be loaded then you should always use Assembly.Load(). If you don't know which assemblies are depolyed in the directory then either guess from the filesnames the assembly names or use Assembly.ReflectionOnlyLoadFrom() (preferably using a temporary AppDomain) to get the assembly names. Then load the assemblies using Assembly.Load() with the assembly name.
If your assemblies contain NinjectModules you can also use kernel.Load() which does what I described above. But it only loads assemblies containing at least one module.
Read up http://msdn.microsoft.com/en-us/library/dd153782.aspx about the different assembly contexts.
Here is a small extract from the Ninject codebase. I removed the unnecessary stuff but did not try to compile or run so probably there are minor issues with this.
public class AssemblyLoader
{
public void LoadAssemblies(IEnumerable<string> filenames)
{
GetAssemblyNames(filenames).Select(name => Assembly.Load(name));
}
private static IEnumerable<AssemblyName> GetAssemblyNames(IEnumerable<string> filenames)
{
var temporaryDomain = CreateTemporaryAppDomain();
try
{
var assemblyNameRetriever = (AssemblyNameRetriever)temporaryDomain.CreateInstanceAndUnwrap(typeof(AssemblyNameRetriever).Assembly.FullName, typeof(AssemblyNameRetriever).FullName);
return assemblyNameRetriever.GetAssemblyNames(filenames.ToArray());
}
finally
{
AppDomain.Unload(temporaryDomain);
}
}
private static AppDomain CreateTemporaryAppDomain()
{
return AppDomain.CreateDomain(
"AssemblyNameEvaluation",
AppDomain.CurrentDomain.Evidence,
AppDomain.CurrentDomain.SetupInformation);
}
private class AssemblyNameRetriever : MarshalByRefObject
{
public IEnumerable<AssemblyName> GetAssemblyNames(IEnumerable<string> filenames)
{
var result = new List<AssemblyName>();
foreach(var filename in filenames)
{
Assembly assembly;
try
{
assembly = Assembly.LoadFrom(filename);
}
catch (BadImageFormatException)
{
// Ignore native assemblies
continue;
}
result.Add(assembly.GetName(false));
}
return result;
}
}
}

Resolving a type without registering first - prism 4 and Untiy

First of all I would like to remark I am new with the concept of prism, DI and containers. I am looking on one of the code samples provided with the Prism Library:
The code simply injects a view with the "Hello World" string (in a TextBlock element) to a region in the shell.
When the application starts-up, it creates a new BootStrapper instance, which creates and initializes the shell:
public class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.Resolve<Shell>();
}
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.RootVisual = (UIElement)this.Shell;
}
protected override void ConfigureModuleCatalog()
{
base.ConfigureModuleCatalog();
ModuleCatalog moduleCatalog = (ModuleCatalog)this.ModuleCatalog;
moduleCatalog.AddModule(typeof(HelloWorldModule.HelloWorldModule));
}
}
My question refers to the method CreateShell(). I couldnt find nowhere in the supplied code (including not in a configuration file or any xaml file...) where do they register the type Shell, and even if it was registered - the supplies Shell class doesnt implement any interface... what is the meaning of resolving a specific type?
the Shell implementation:
public partial class Shell : UserControl
{
public Shell()
{
InitializeComponent();
}
}
This looks like a magic to me, so I tried to create my own type (MyType) and resolve it the same way:
Container.Resolve<MyType>();
By setting a breakepoint inside MyType constructor, I saw that it DID resolved MyType. Can somebody please explain to me how does it work?
These couple of threads should answer your question:
http://compositewpf.codeplex.com/Thread/View.aspx?ThreadId=230051
Does unity just make clasess with out needing anything registered?
Additionally, if you are eager to get more detail into how Unity can do this, simple download Unity 2.0 and open the source code that is provided with the installer.
I hope this helps.
Thanks,
Damian
You do not need to register a type you want to resolve. You need to register the dependencies of a type, that you want to resolve. In this case, the Shell doesn't need any dependencies, so you can resolve it simply. But for an example (not really), if your shell getting an interface IService as a parameter, then you must register IService, before you resolve Shell.
Otherwise you will get Dependency Resolution Failed Exception. In Prism 4.1 it will be swallowed silently due to TryResolve.

Resources