I was using CastleWindsor in my ASP.NETCore2.2 WebAPI project and was working fine. I'm migrating to ASP.NETCore3.1 now and it doesn't look like CastleWindor has offical support for that so I decided to move to Autofac with minimal changes but having some issues resolving the dependencies.
In my project, I've maintained very loose coupling between different layers in the application namely, business layer, data layer, and translation layer. All of those layers are in their own assemblies. And then in my main project, I've a folder say "dependencies" which will hold all the DLLs of differnet layers. Additionally, I've a separate project that lists all the interfaces that are implemented by the different layers and which needs to be resolved by the IoC container.
The project having all the interfaces looks like this:
namespace Shared.Interfaces
{
public interface IBusinessLayer<T>
{
....
}
public interface IDataLayer<T>
{
....
}
public interface ITranslationLayer<T>
{
....
}
}
The implementing projects looks like this:
namespace POC.Person.BusinessLayer
{
public class BusinessLayer<T> : IBusinessLayer<T> where T : Models.Person
{
...
}
}
namespace POC.Person.DataLayer
{
public class DataLayer<T> : IDataLayer<T> where T : Models.Person
{
...
}
}
namespace POC.Person.TranslationLayer
{
public class TranslationLayer<T> : ITranslationLayer<T> where T : Models.Person
{
...
}
}
Using Autofac in my migrated .netcore3.1 project, Startup.cs looks like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
//and other codes
}
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new DependencyResolver());
}
DependencyResolver is a class that inherits from Autofac.Module, which is again in a separate assembly in different project which looks like this:
namespace IOC.Autofac
{
public class DependencyResolver: Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
// get our path to dependencies folder in the main project
var path = Directory.GetCurrentDirectory() + "\\dependencies\\";
//get all the assemblies inside that folder
List<Assembly> assemblies = new List<Assembly>();
foreach (string assemblyPath in Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories))
{
var assembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
assemblies.Add(assembly);
}
// Register and resolve the types with the container
builder
.RegisterAssemblyTypes(assemblies.ToArray())
.AsClosedTypesOf(typeof(IBusinessLayer<>))
.AsClosedTypesOf(typeof(IDataLayer<>))
.AsClosedTypesOf(typeof(ITranslationLayer<>))
.AsImplementedInterfaces()
.InstancePerRequest();
}
}
}
I'm getting this error and I've not been able to fix it:
":"Unable to resolve service for type 'Shared.Interfaces.IBusinessLayer`1[Models.Person]' while attempting to activate 'POC.Person.Controllers.PersonController'.","
Inside my controller I've injection which looks like this:
namespace POC.Person.Controllers
{
public class PersonController : ControllerBase
{
private readonly IBusinessLayer<Models.Person> _bl;
public PersonController(IBusinessLayer<Models.Person> bl)
{
_bl = bl;
}
//other codes
}
}
Program.cs looks like this:
namespace POC.Person
{
public class Program
{
public static void Main(string[] args)
{
var host = BuildWebHost(args);
host.Build().Run();
}
public static IHostBuilder BuildWebHost(string[] args)
{
return Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel()
.UseStartup<Startup>()
.UseIIS()
.UseIISIntegration();
;
}).ConfigureAppConfiguration((context, config) =>
{
var builtConfig = config.Build();
});
}
}
}
It looks like with autofac involving generics, registering and resolving the type is not that straight forward?
Autofac does not currently support registering open generics whilst assembly scanning. It's a long-running known issue. You can do assembly scanning, you can register open generics, you can't do both at the same time. There are some ideas in that linked issue on ways some folks have solved it.
Out of the box, the scanning logic would, thus, be reduced to:
builder
.RegisterAssemblyTypes(assemblies.ToArray())
.AsImplementedInterfaces()
.InstancePerRequest();
You need to register generics separately, like:
builder
.RegisterGeneric(typeof(TranslationLayer<>))
.As(typeof(ITranslationLayer<>));
Related
I'm building a small Nancy web project.
In a method of one of my classes (not a nancy module), I would like to basically do:
var myThing = TinyIoC.TinyIoCContainer.Current.Resolve<IMyThing>();
However, there is only one registration in .Current (non public members, _RegisteredTypes) which is:
TinyIoC.TinyIoCContainer.TypeRegistration
Naturally, in my above code, I'm getting:
Unable to resolve type: My.Namespace.IMyThing
So, I guess I'm not getting the same container registered in my bootstrapper?
Is there a way to get at it?
EDIT
To flesh out a bit more of what I'm trying to do:
Basically, my url structure looks something like:
/{myType}/{myMethod}
So, the idea being, going to: /customer/ShowAllWithTheNameAlex would load the Customer service, and execute the showAllWithTheNameAlex method
How I do this is:
public interface IService
{
void DoSomething();
IEnumerable<string> GetSomeThings();
}
I then have an abstract base class, with a method GetService that returns the service.
It's here that i'm trying to use the TinyIoC.TinyIoCContainer.Current.Resolve();
In this case, it would be TinyIoC.TinyIoCContainer.Current.Resolve("typeName");
public abstract class Service : IService
{
abstract void DoSomething();
abstract IEnumerable<string> GetSomeThings();
public static IService GetService(string type)
{
//currently, i'm doing this with reflection....
}
}
Here's my implementation of the service.
public class CustomerService : Service
{
public void DoSomething()
{
//do stuff
}
public IEnumerable<string> GetSomeThings()
{
//return stuff
}
public IEnumerable<Customer> ShowAllWithTheNameAlex()
{
//return
}
}
Finally, I have my Nancy Module, that looks like:
public class MyModule : NancyModule
{
public MyModule()
{
Get["/{typeName}/{methodName}"] = p => ExecuteMethod(p.typeName, p.methodName);
}
private dynamic ExecuteMethod(string typeName, string methodName)
{
var service = Service.GetService(typeName);
var result = service.GetType().GetMethod(methodName).Invoke(service, null);
//do stuff
return result; //or whatever
}
}
#alexjamesbrown - The short answer is, you don't. Nancy was specifically designed so that you did not deal with the container directly. You mention that the class, that you want to take a dependency on IMyThing, is not a NancyModule. Well this is not an issue, as long as one of your modules has a reference to it, then those dependencies can also have their own dependencies that will be satisfied at runtime.
public interface IGreetingMessageService
{
string GetMessage();
}
public class GreetingMessageService: IGreetingMessageService
{
public string GetMessage()
{
return "Hi!";
}
}
public interface IGreeter
{
string Greet();
}
public class Greeter
{
private readonly IGreetingMessageService service;
public Greeter(IGreetingMessageService service)
{
this.service = service;
}
public string Greet()
{
return this.service.GetMessage();
}
}
public class GreetingsModule : NancyModule
{
public GreetingModule(IGreeter greeter)
{
Get["/"] = x => greeter.Greet();
}
}
The above will work just fine and Greeter will have it's dependency on IGreetingMessageService satisfied at runtime
I have had a very similar issue, needing to "share" the container. The reason this is an issue is that my program runs as a service using Nancy self hosting to provide a REST API. My modules have dependencies which are injected by Nancy itself, but the other parts of the app which are not referenced from modules also need dependencies injected.
Multiple containers are not a sensible option here (or anywhere really), I need to share the container between Nancy and the rest of the app.
I simply did the following
(I'm using Autofac but I suspect that TinyIoC in similar)
public class Bootstrapper : AutofacNancyBootstrapper
{
private static readonly Lazy<ILifetimeScope> container = new Lazy<ILifetimeScope>(RegisterTypes);
public static ILifetimeScope Container => container.Value;
protected override ILifetimeScope GetApplicationContainer()
{
return container.Value;
}
// Create container and register my types
private static ILifetimeScope RegisterTypes()
{
var builder = new ContainerBuilder();
// Register all my own types.....
return builder.Build();
}
}
Then, in my main code, I can use the container myself
public class Program
{
public static void Main(string[] args)
{
// Resolve main service with all its dependencies
var service = Bootstrapper.Container.Resolve<Service>();
service.Run();
}
}
As my NancyHost is within the Service, the container is constructed (once) upon its first use in main, this static is then used when Nancy gets round to creating the Bootstrapper itself.
In an ideal world, I wouldn't really want a globally accessible container, normally it would be local to the main function.
In this particular case "not dealing with the container directly" is highly problematic:
public interface IFoo {}
public class Foo : IFoo { public Foo(string bar) {} }
Assume IFoo already is a constructor dependency of a Nancy module.
Note the Foo constructor's string dependency. I need to communicate to the container to use that constructor for an IFoo singleton, when encountered as a Nancy module dependency. I need to register that on the TinyIoC instance NancyFx uses, and pass in the actual value of bar.
I am trying to configure an application such that types from assemblyA can be used by my console to allow for logging in an AOP style. The JournalInterceptor will just write out method calls, input and maybe output arguments to a log file or datastore of some kind.
I can register one type at a time but I would like to register all types in one go. Once I get going I may add some filtering to the registered types but I am missing something.
I am trying to use Classes.FromAssemblyContaining but am not sure how to get at an IRegistration instance for the call to WindsorContainer::Register
Any clues?
// otherAssembly.cs
namespace assemblyA
{
public class Foo1 { public virtual void What(){} }
public class Foo2 { public virtual void Where(){} }
}
// program.cs
namespace console
{
using assemblyA;
public class JournalInterceptor : IInterceptor {}
public class Program
{
public static void Main()
{
var container = new Castle.Windsor.WindsorContainer()
.Register(
Component.For<JournalInterceptor>().LifeStyle.Transient,
// works but can't be the best way
Component.For<Foo1>().LifeStyle.Transient
.Interceptors<JournalInterceptor>(),
Component.For<Foo2>().LifeStyle.Transient,
.Interceptors<JournalInterceptor>(),
// how do I do it this way
Classes.FromAssemblyContaining<Foo1>()
.Pick()
.LifestyleTransient()
.Interceptors<JournalInterceptor>()
);
Foo1 foo = container.Resolve<Foo1>();
}
}
}
Implement a Pointcut. In Castle Windsor this is done by implementing the IModelInterceptorsSelector interface.
It would go something like this:
public class JournalPointcut : IModelInterceptorsSelector
{
public bool HasInterceptors(ComponentModel model)
{
return true; // intercept everything - probably not a good idea, though
}
public InterceptorReference[] SelectInterceptors(
ComponentModel model, InterceptorReference[] interceptors)
{
return new[]
{
InterceptorReference.ForType<JournalInterceptor>()
}.Concat(interceptors).ToArray();
}
}
Then register the Interceptor and the Pointcut with the container:
this.container.Register(Component.For<JounalInterceptor>());
this.container.Kernel.ProxyFactory.AddInterceptorSelector(new JournalPointcut());
For in-depth explanation, you may want to see this recording.
I foolishly decided to try something new on a Friday job!
So I have used NuGet to add Ninject.Web.Mvc 2.2.x.x to my .Net MVC2 project.
I've altered my Global.asax.cs
using System.Web.Mvc;
using System.Web.Routing;
using IntegraRecipients;
using Mailer;
using Ninject;
using Ninject.Web.Mvc;
using Ninject.Modules;
namespace WebMailer
{
public class MvcApplication : NinjectHttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("favicon.ico");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Mail", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
}
protected override IKernel CreateKernel()
{
return new StandardKernel(new INinjectModule[] { new MailModule()});
}
internal class MailModule : NinjectModule
{
public override void Load()
{
Bind<IMailing>().To<Mailing>();
Bind<IMailingContext>().To<MailingContext>();
Bind<IRecipientContext>().To<RecipientContext>();
}
}
}
}
and I've created a controller like so...
using System.Linq;
using System.Web.Mvc;
using WebMailer.Models;
namespace WebMailer.Controllers
{
[ValidateInput(false)]
public class MailController : Controller
{
private readonly IMailingContext _mailContext;
private readonly IRecipientContext _integraContext;
public MailController(IMailingContext mail,IRecipientContext integra)
{
_mailContext = mail;
_integraContext = integra;
}
public ActionResult Index()
{
return View(_mailContext.GetAllMailings().Select(mailing => new MailingViewModel(mailing)).ToList());
}
}
}
But the controller is still insisting that
The type or namespace name 'IRecipientContext' could not be found (are you missing a using directive or an assembly reference?)
and
The type or namespace name 'IMailingContext' could not be found (are you missing a using directive or an assembly reference?)
My google-fu has failed me and I really hope this is just a silly typo/missing line thing
Thanks in advance
P
Ninject does not change the way assemblies are compiled! It deos not magically add references to other assemblies or add using directives. If you are using interfaces from other assemblies you have to add a using directive and a reference to this assembly.
All Ninject is about is to wire up your application at runtime.
I am have what appears to be a similar problem.
I have a simple WPF Window project with the compiled Ninject.dll linked in. However, the following is giving me errors...
using Ninject;
namespace CatalogueManager
{
public class ServiceLocator
{
public IMainWindowViewModel GetMainWindowViewModel()
{
return Kernel.Get<IMainWindowViewModel>();
}
static IKernel Kernel;
static ServiceLocator()
{
Kernel = new StandardKernel(new NinjectConfiguration());
}
}
}
In particular, "Ninject" namespace and IKernel are prompting the compile time message "type or name space 'X' not found..."
i have an interface that defines some methods and i have N classes that implement it. How can i register all the classes found in all the loaded assemblies with autofac?
You will have to "know about" the assemblies containing the classes, you could perhaps load them yourself with Assembly.Load(..).
From there, it is easy to register the classes:
var assemblies = new[]{....};
builder.RegisterAssemblyTypes(assemblies)
.Where(t => typeof(IMyInterface).IsAssignableFrom(t))
.As<IMyInterface>();
Update: to get to the registered instances, you can use Autofac builtin support for collections:
public class MyService
{
private readonly IEnumerable<IMyInterface> _services;
public MyService(IEnumerable<IMyInterface> services)
{
_services = services;
}
public void DoStuffWithServices()
{
foreach(var svc in _services)
{
...
}
}
}
Using MVC and trying to use dependency injection for controllers, but when I try to call a method on a controller that takes a dependency, I get the "no parameterless constructor" error. Here's my setup:
ProductRepository : IProductRepository
ProductService : IProductService {
public ProductService(IProductRepository repository) {} }
ProductController {
public ProductController(IProductService service) {} }
In Global.asax:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
InitializeServiceLocator();
RegisterRoutes(RouteTable.Routes);
}
protected virtual void InitializeServiceLocator()
{
IWindsorContainer container = new WindsorContainer();
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(container));
container.RegisterControllers(typeof(HomeController).Assembly);
ComponentRegistrar.AddComponentsTo(container);
foreach (var handler in container.Kernel.GetAssignableHandlers(typeof(object)))
{
System.Diagnostics.Debug.WriteLine(String.Format("{0} {1}",
handler.ComponentModel.Service,
handler.ComponentModel.Implementation));
}
ServiceLocator.SetLocatorProvider(() => new WindsorServiceLocator(container));
}
ComponentRegistrar:
public static void AddComponentsTo(IWindsorContainer container)
{
AddCustomRepositoriesTo(container);
AddApplicationServicesTo(container);
}
When InitializeServiceLocator completes, I can see that all Controllers, Services and Repositories are registered.
Any help greatly appreciated.
I'd still like to know the problem, but I've worked around it by creating my own WindsorControllerFactory per this post (with modification to avoid any web.config action): http://mvcsharp.wordpress.com/2010/01/09/setting-up-ioc-in-asp-net-mvc-using-castle-windsor/
The WindsorControllerFactory in the previous code was the MvcContrib.Castle.WindsorControllerFactory. Anyone using MvcContrib version successfully?
Thanks.