Windsor castle Injecting properties of constructed object - dependency-injection

Some dependency injection containers enable you to inject configured services into an already constructed object.
Can this be achieved using Windsor, whilst taking account of any service dependencies there may be on the target object?

This is an old question but Google led me here recently so thought I would share my solution lest it help someone looking for something like StructureMap's BuildUp method for Windsor.
I found that I could add this functionality myself relatively easily. Here is an example which just injects dependencies into an object where it finds a null Interface-typed property. You could extend the concept further of course to look for a particular attribute etc:
public static void InjectDependencies(this object obj, IWindsorContainer container)
{
var type = obj.GetType();
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
if (property.PropertyType.IsInterface)
{
var propertyValue = property.GetValue(obj, null);
if (propertyValue == null)
{
var resolvedDependency = container.Resolve(property.PropertyType);
property.SetValue(obj, resolvedDependency, null);
}
}
}
}
Here is a simple unit test for this method:
[TestFixture]
public class WindsorContainerExtensionsTests
{
[Test]
public void InjectDependencies_ShouldPopulateInterfacePropertyOnObject_GivenTheInterfaceIsRegisteredWithTheContainer()
{
var container = new WindsorContainer();
container.Register(Component.For<IService>().ImplementedBy<ServiceImpl>());
var objectWithDependencies = new SimpleClass();
objectWithDependencies.InjectDependencies(container);
Assert.That(objectWithDependencies.Dependency, Is.InstanceOf<ServiceImpl>());
}
public class SimpleClass
{
public IService Dependency { get; protected set; }
}
public interface IService
{
}
public class ServiceImpl : IService
{
}
}

No, it can't.

As Krzysztof said, there is no official solution for this. You might want to try this workaround though.
Personally, I consider having to do this a code smell. If it's your code, why isn't it registered in the container? If it isn't your code, write a factory/adapter/etc for it.

Related

Unit Testing a Controller - How Do I Handle the Connection String?

I can make it work, but I want to know what the best practice is and why. I have a Controller, a Model, and a Repository and now I want to Unit Test the Controller. I am just writing a simple test to ensure that the correct view is being returned.
This is my method in the controller:
public ActionResult Selections(SelectionsViewModel model)
{
for (int i = 0; i < model.Sends.Count; i++)
{
Send send = new Send(new SendService(new Database().GetConnectionString()))
{
SendID = model.Sends[i].SendID,
Title = model.Sends[i].Title,
Subject = model.Sends[i].Subject,
SentDate = model.Sends[i].SentDate,
TimesViewed = model.Sends[i].TimesViewed,
Include = model.Sends[i].Include,
Exclude = model.Sends[i].Exclude
};
send.UpdateIncludeExclude();
}
return View(model);
}
Here is my GetConnectionString() method in the Database class that is being sent via my SendService constructor.
public string GetConnectionString()
{
return System.Configuration.ConfigurationManager.ConnectionStrings["DEVConnectionString"].ToString();
}
And lastly, my unit test:
[Test]
public void TestAssignmentSelections()
{
var obj = new AssignmentController();
var actResult = obj.Selections() as ViewResult;
NUnit.Framework.Assert.That(actResult.ViewName, Is.EqualTo("Selections"));
}
Now, my unit test fails, and I get why. My unit test project has no access to the web.config of the project I am testing where my connection string resides.
I've done some research, and apparently just adding a web.config to my unit test project and putting the connection string in there as well will make it work.. but that seems like a hack.
What's the best way to go about this? Is there another way to write my code to accommodate for this?
You want to make your controller unit testable ? Don't do this.
new SendService(
With this code,you are hardcoding your concrete service implementation & your data access code implementation. In your unit test, you should not be really accessing the data from your database. Instead you should be providing a mock data access implementation.
Here comes interfaces, you need to create an interface for your SendService.
public interface ISendService
{
void SomeMethod();
}
now your SendService will be a concrete implementation of this interface
public class SendService : ISendService
{
public void SomeMethod()
{
// Do something
}
}
Now update your controller to have a constructor where we will inject an implementation of ISendService.
public class YourController : Controller
{
private ISendService sendService;
public YourController(ISendService sendService)
{
this.sendService = sendService;
}
public ActionResult YourActionMethod()
{
// use this.sendService.SomeMethod();
}
}
And you may use some dependency injection frameworks to tell the MVC framework which implementation of the interface to use when the code runs. If you are using MVC6,It has an inbuilt dependency injection provider you can use. So go to your Startup class and in your ConfigureServices method, you can map an interface to a concrete implementation.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ISendService, SendService>();
}
}
If you are in a previous version of MVC, You may consider DI frameworks like Unity,Ninject etc. You can do the same approach for your Data access later / Service layers. ie: Create an interface for data access and inject that to your SendService.
public Interface IDataAccess
{
string GetName(int id);
}
and an implementation which uses your specific data access code/ORM
public class EFDataAccess : IDataAccess
{
public string GetName(int id)
{
// return a string from db using EF
}
}
So now your Service class will be
public class SendService : ISendService
{
private IDataAccess dataAccess;
public SendService(IDataAccess dataAccess)
{
this.dataAccess=dataAccess;
}
// to do : Implement methods of your ISendService interface.
// you may use this.dataAccess in those methods as needed.
}
In your unit tests, you can create a mock implementation of your interfaces which returns static data instead of accessing the database.
For example, If you are using Moq mocking framework, you can do this.
var m = new Mock<IDataAccess>();
var m.Setup(s=>s.GetName(It.IsAny<int>())).Returns("Test");
var s = new SendService(m);
var result= s.SomeMethod();

Property injection upon initializing object manually

When using Ninject property injection the following works only if the object is instantiated by the framework (not by developer in code) so that the injection works:
public class SomeController: Controller {
[Inject]
public DbContext db {get; set;}
...
}
However when the object has to be instantiated in the code by the developer the binding does not happen (I don't wanna say fail because it does not happen).
public class DataProvision {
[Inject]
public DbContext db {get; set;}
public List<T> GetList<T>() where T: class, new() {
return db.Set<T>().toList();
}
...
}
public class Test {
public static void Test(){
DataProvision dp = new DataProvision();
var getValue = dp.GetList<Person>();
}
}
Is it even supported by Ninject and if yes what is the solution.
Justification of why we are doing this: Switching between back-up databases and active DBs effortlessly in case emergency
This is expected behavior. The DI framework has no opportunity to inject anything if you new up your own instances. If your code has access to the DI bindings, set up a kernel and use it to instantiate your class:
public class Test {
public static void Test(){
var kernel = new StandardKernel(new YourDiModule());
DataProvision dp = kernel.Get<DataProvision>();
var getValue = dp.GetList<Person>();
}
}
Using the above strategy, you'll probably need to tweak your DI configuration a little so that your context gets disposed when you want it to. (Your web application is probably normally set up to dispose the context after each web request completes, and it doesn't look like your test code is set up to run in the same kind of environment.)
Otherwise, you'll need to manage the dependency injection by hand:
public class Test {
public static void Test(){
using (var context = new DbContext()) // or however you create contexts
{
DataProvision dp = new DataProvision();
dp.db = context;
var getValue = dp.GetList<Person>();
}
}
}

MVC/Ninject Dependency Injection in business layer

I'm fairly new to Dependency Injection, and I'm looking for some advice on best practices. Sorry if this has been asked before, but I haven't been able to find a good solution yet.
Assume I have a MVC4 web app and a separate business layer. The MVC app is already set up using the Ninject NuGet package, so I have NinjectWebCommon, and it works fine.
My question is: How can I use Ninject when I need dependencies set up in other layers?
Assume I have this repository:
public class WidgetRepository : IWidgetRepository
{
// using an entity framework db context.
WidgetDbContext context = new WidgetDbContext();
public IQueryable<Widget> Widgets
{
get
{
return context.Widgets;
}
}
}
Each widget returned by the repository needs to perform calculations using a calculator object that I need to inject:
public class Widget
{
// how can I get Ninject to inject a calculator object
// when Widgets are loaded form the database?
public ICalculator calculator;
public int MyValue { get; set; }
public int CalculateSomething
{
get
{
return calculator.Calculate(MyValue);
}
}
}
What is the best practice to inject an ICalculator into each Widget instance, when Ninject is set up in the MVC web app, but the Widget objects are created in the business layer??
Prevent doing constructor injection or property injection in entities. You should either:
Let the service layer call the calculation on the Widget, like this:
var widget = this.repository.GetById(wigditId);
var value = this.calculator.Calculate(widget.MyValue);
Or use constructor injection into your entities:
var widget = this.repository.GetById(wigditId);
var value = widget.CalculateSomething(this.calculator);
A lot has been written about this. Take a look at these articles for instance:
How not to inject services in entities
Why not use an IoC container to resolve dependencies for entities/business objects?
If Widget and ICalculator are in the same project, just use constructor injection:
public class Widget
{
public Widget(ICalculator calculator)
{
_calculator = calculator;
}
private ICalculator _calculator;
public int MyValue { get; set; }
public int CalculateSomething
{
get
{
return _calculator.Calculate(MyValue);
}
}
}
In NinjectWebCommon, you'll need to register your ICalculator implementation, something like this:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ICalculator>()
.To<Calculator>();
}

Orchard CMS : Creating module for OpenRasta, problems with dependency injection

I'm trying to create an Orchard CMS module that enables a RESTful web service using OpenRasta for a given route (/openrasta/* for example).
I need to get to the Orchard ContentManager to get the content for the service to return, so my OpenRasta handler (ContentHandler) uses a ContentService, which implements IContentService, which inherits IDependency. Normally this would work because Orchard will inject a ContentManager into the constructor:
public class ContentService : IContentService {
public IContentManager content;
public ContentService(IContentManager content) {
this.content = content;
}
public IEnumerable<string> GetContentTypeDefinitionNames() {
return content.GetContentTypeDefinitions().Select(d => d.Name);
}
}
But when I run it I get an error because OpenRasta doesn't know anything about the Orchard dependencies and it's trying to create ContentService, not Orchard, which is fair enough:
OpenRasta.DI.DependencyResolutionException: Could not resolve type
ContentService because its dependencies couldn't be fullfilled
Constructor: Orchard.ContentManagement.IContentManager
Is there a way to achieve this, can I go to an Orchard class somewhere and say "give me an instance of the ContentManager"?
Update: See my comments on #rfcdejong's answer for updates on my progress.
Are u using a ServiceRoute, added in a class implementing IRouteProvider
Look at the ServiceRoute summary, it says "Enables the creation of service routes over HTTP in support of REST scenarios."
public class Routes : IRouteProvider
{
public void GetRoutes(ICollection<RouteDescriptor> routes)
{
foreach (var routeDescriptor in GetRoutes())
routes.Add(routeDescriptor);
}
private static ServiceRoute _rastaService = new ServiceRoute(
"openrasta",
new MyServiceHostFactory<IOpenRastaService>(),
typeof(IOpenRastaService));
public IEnumerable<RouteDescriptor> GetRoutes()
{
return new[]
{
new RouteDescriptor
{
Priority = -1,
Route = _rastaService
}
};
}
}
And want to resolve ContentService? U might have to resolve the interface.
i guess u want the following to work:
var contentService = LifetimeScope.ResolveNew<IContentService>();
I have used HostContainer.Resolve directly and had issues as well. I will describe the solution i'm using at the moment in my own ServiceHostFactory
Do u have a own ServiceHostFactory deriven from OrchardServiceHostFactory?
In that case u can implement the following code to help u resolve instances
private ILifetimeScope _lifetimeScope = null;
private ILifetimeScope LifetimeScope
{
get
{
if (_lifetimeScope == null)
{
IHttpContextAccessor accessor = HostContainer.Resolve<IHttpContextAccessor>();
IRunningShellTable runningShellTable = HostContainer.Resolve<IRunningShellTable>();
ShellSettings shellSettings = runningShellTable.Match(accessor.Current());
IOrchardHost orchardHost = HostContainer.Resolve<IOrchardHost>();
ShellContext shellContext = orchardHost.GetShellContext(shellSettings);
_lifetimeScope = shellContext.LifetimeScope;
}
return _lifetimeScope;
}
}
I also created LifetimeScopeExtensions that has the following code
public static class LifetimeScopeExtensions
{
public static T ResolveNew<T>(this ILifetimeScope scope)
{
IWorkContextAccessor workContextAccessor = scope.Resolve<IWorkContextAccessor>();
WorkContext workContext = workContextAccessor.GetContext();
if (workContext == null)
{
using (IWorkContextScope workContextScope = workContextAccessor.CreateWorkContextScope())
{
ILifetimeScope lifetimeScope = workContextScope.Resolve<ILifetimeScope>();
return lifetimeScope.Resolve<T>();
}
}
else
{
ILifetimeScope lifetimeScope = workContext.Resolve<ILifetimeScope>();
return lifetimeScope.Resolve<T>();
}
}
public static object ResolveNew(this ILifetimeScope scope, Type type)
{
IWorkContextAccessor workContextAccessor = scope.Resolve<IWorkContextAccessor>();
WorkContext workContext = workContextAccessor.GetContext();
if (workContext == null)
{
using (IWorkContextScope workContextScope = workContextAccessor.CreateWorkContextScope())
{
ILifetimeScope lifetimeScope = workContextScope.Resolve<ILifetimeScope>();
return lifetimeScope.Resolve(type);
}
}
else
{
ILifetimeScope lifetimeScope = workContext.Resolve<ILifetimeScope>();
return lifetimeScope.Resolve(type);
}
}
}
var settingsService = LifetimeScope.ResolveNew<ITokenServiceSettingsService>();
So the issue is that your CMS uses its own IoC container. By default OpenRasta does that too.
This means that services that are present in Orchard won't be visible to OpenRasta.
For all other IoC containers, the answer is damn right simple: You use the IoC adaptation layer that lets OpenRasta live in whatever ioc container you want. We support unity, structuremap, castle and ninject. That said, autofac is not supported as no one ever built it.
The cleanest way for you to solve this problem (and any other you may encounter in the future for those issues) would be to build your own autofac ioc adaptation layer for openrasta. If you need help doing that, you can join the openeverything mailing list where the devs would be happy to help you.

Ninject - dynamically specifying a connection string based on a sub domain

I'm trying to specify a connection string dynamically based of the url using ninject.
I'm using the ninject.mvc nuget package that uses the webActivator.
My code is as follows:
my injection:
kernel.Bind<IUnitOfWork>().To<UnitOfWork>()
.WithConstructorArgument("connectionString", MvcApplication.GetConnectionStringName());
my global.asax
private static HttpContext _context;
public static string GetConnectionStringName() {
var subDomain = String.Empty;
if (_context != null) {
subDomain = _context.Request.Url.SubDomain();
}
return String.Format("{0}ConnectionString", subDomain);
}
The problem is the _context (which is set in my Application_BeginRequest) is always null because the WebActivator runs before the application_start.
Is it possible in ninject to specify to call MvcApplication.GetConnectionStringName() when a IUnitOfWork is required rather than on application start?
Is there a better approach to what I'm doing?
Thanks
You should use the Ninject binding like this.
kernel.Bind<IUnitOfWork>().To<UnitOfWork>()
.WithConstructorArgument("connectionString", context => MvcApplication.GetConnectionStringName());
Note that context here is of type Ninject's IContext and so has nothing to do with HttpContext.
Anyway I think you approach is suitable for this.
Sometimes (especially when there are multiple related parameters to be injected) I prefer creating an interface and specific implementations for the configurations and let them injected by standard bindings like this.
public interface IUnitOfWorkConfiguration {
string ConnectionString { get; }
}
public class AppConfigUnitOfWorkConfiguration : IUnitOfWorkConfiguration {
public string ConnectionString { get { ... } }
}
public class UnitOfWork {
public UnitOfWork(IUnitOfWorkConfiguration configuration) {
}
}
Bind<IUnitOfWorkConfiguration>().To<AppConfigUnitOfWorkConfiguration>();
Using this approach you can avoid specifying parameter names as string literals.
One more note about using HttpContext. I do not recommend using it that way because of thread safety issues. You should either mark your private static field _context with the [ThreadStatic] atribute or as a better choice simply use HttpContext.Current everywhere.

Resources