very new to Structure-Map. trying to figure it out how it works and how can i benefit from it.
i have got this so far..
Global.asax.cs:
IContainer container = new Container(x =>
{
x.For<IControllerActivator>().Use
<StructureMapControllerActivator>();
x.For<IUserRepo>().Use<UserRepo>();
});
DependencyResolver.SetResolver(new StructureMapDependencyResolver(container));
StructureMapControllerActivator:
public class StructureMapControllerActivator : IControllerActivator
{
private readonly IContainer _container;
public StructureMapControllerActivator(IContainer container )
{
this._container = container;
}
public IController Create(RequestContext requestContext, Type controllerType)
{
return _container.GetInstance(controllerType) as IController;
}
}
StructreMapDependencyResolver:
private readonly IContainer _container;
public StructureMapDependencyResolver(IContainer container )
{
this._container = container;
}
public object GetService(Type serviceType)
{
object instance = _container.TryGetInstance(serviceType);
if(instance == null && !serviceType.IsAbstract)
{
_container.Configure(c => c.AddType(serviceType,serviceType));
instance = _container.TryGetInstance(serviceType);
}
return instance;
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _container.GetAllInstances(serviceType).Cast<object>();
}
}
My AccountController:
public class AccountController : Controller
{
private readonly IUserRepo _userRepo;
private AccountController()
{
_userRepo = ObjectFactory.GetInstance<IUserRepo>();
}
public ActionResult Login()
{
return View();
}
}
Error Code & Description:
StructureMap Exception Code: 202
No Default Instance defined for PluginFamily MBP_Blog.Controllers.AccountController
MBP-Blog, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
I have a Interface Name : IUserRepo and a repository Name: UserRepo
please help as I try to google but dint find any solution within first 3 pages.
New error after using #Martin's code:
StructureMap Exception Code: 180
StructureMap cannot construct objects of Class MBP_Blog.Controllers.AccountController, MBP-Blog, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null because there is no public constructor found.
Take out the StructureMapControllerActivator, I don't think you need it. If you keep it, you need to add a mapping for your AccountController.
Also, use Controller Injection instead, it will make unit testing easier:
public class AccountController : Controller
{
private readonly IUserRepo _userRepo;
public AccountController(IUserRepo userRepo)
{
_userRepo = userRepo;
}
public ActionResult Login()
{
return View();
}
}
Also again, for your Container, you can default the mappings. This will automatically map IService to Service :
IContainer container = new Container(
x =>
{
x.Scan(scan =>
{
scan.Assembly("MBP_Blog");
scan.Assembly("MBP_Blog.Data");
scan.WithDefaultConventions();
});
});
Related
I've used quite a few different DI containers in the past, but never Unity (Specifically Unity 4.0.1).
I'm working with a plain old .NET MVC app with typical 3 tier architecture. Repository -> Domain -> WebUI.
I need to know what I am doing wrong so that I can get my registered dependencies to work on the domain layer. Here is what I have in my global.asax.
protected void Application_Start()
{
// ...
IUnityContainer container = new UnityContainer();
RegisterDependencies(container);
DependencyResolver.SetResolver(new WebApplicationDependencyResolver(container));
}
protected void RegisterDependencies(IUnityContainer container)
{
container.RegisterType<IUnitOfWork, UnitOfWork>();
}
Here is the WebApplicationDependencyResolver used above:
namespace WebApplication1.Infrastructure
{
public class WebApplicationDependencyResolver : IDependencyResolver
{
private IUnityContainer _container;
public WebApplicationDependencyResolver(IUnityContainer container)
{
_container = container;
}
public object GetService(Type serviceType)
{
try
{
return _container.Resolve(serviceType);
}
catch (Exception)
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return _container.ResolveAll(serviceType);
}
catch (Exception)
{
return null;
}
}
}
}
My Domain Layer class CustomerService.cs (which I used both in it's own project and in a folder in the main project):
namespace WebApplication1.Services
{
public class CustomerService
{
private readonly IUnitOfWork _uow;
public CustomerService(IUnitOfWork uow)
{
_uow = uow;
}
}
}
Now, when I try to call the CustomerService class in my controller like so, it doesn't work:
public ActionResult Index()
{
var service = new CustomerService();
return View();
}
But If I use the resolver on the controller itself, it works:
public class HomeController : Controller
{
private IUnitOfWork _unitOfWork;
public HomeController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public ActionResult Index()
{
var service = new CustomerService(_unitOfWork);
return View();
}
}
Can anyone direct me in the right direction, to get DI working on the domain layer?
Try injecting the service in the controller instead of injecting IUnitOfWork. Then use the service instance in the controller methods:
public HomeController(CustomerService service)
{
_service = service
}
public ActionResult Index()
{
var model = _service.GetAllCustomers();
return View(model);
}
This should work, but it is not a good idea to have your class depend on another class. A dependency should be a contract (interface). You should refactor CustomerService to extract an interface ICustomerService and inject that one instead in the controller. Then you need to register it with the container in method RegisterDependencies:
container.RegisterType<ICustomerService, CustomerService>();
Sorry i am new to IOC concepts. I have been trying to implement Unity.WebAPI (5.3.0.) in my web api project but getting following error;
An error occurred when trying to create a controller of type 'TransactionController'. Make sure that the controller has a parameterless public constructor
UnityResolver Class
public class UnityResolver : IDependencyResolver
{
protected IUnityContainer container;
public UnityResolver(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
this.container = container;
}
public object GetService(Type serviceType)
{
try
{
return container.Resolve(serviceType);
}
catch (ResolutionFailedException)
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return container.ResolveAll(serviceType);
}
catch (ResolutionFailedException)
{
return new List<object>();
}
}
public IDependencyScope BeginScope()
{
var child = container.CreateChildContainer();
return new UnityResolver(child);
}
public void Dispose()
{
container.Dispose();
}
}
Web Api Config
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var container = new UnityContainer();
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
container.RegisterType<IFService, FService>(new HierarchicalLifetimeManager());
container.RegisterType<IMService, MService>(new HierarchicalLifetimeManager());
container.RegisterType<ITransactionService, TransactionService>(new HierarchicalLifetimeManager());
container.RegisterType<IMRepository, MRepository>();
config.DependencyResolver = new UnityResolver(container);
}
}
Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
Controller
public class TransactionController : ApiController
{
private readonly ITransactionService _transactionService;
private readonly IMService _mService;
/// <summary>
/// Public constructor to initialize transaction service instance
/// </summary>
public TransactionController(ITransactionService transactiontService, IMService mService)
{
_mService = mService;
_transactionService = transactiontService;
}
[HttpGet]
public IHttpActionResult IsApiAlive()
{
return Ok();
}
TransactionService Class constructor
private readonly IMRepository _mRepository;
public TransactionService(IMRepository mRepository)
{
_mRepository = mRepository;
}
Check to make sure that all dependencies for the object graphs have been registered so that they can be resolved correctly.
You are most likely missing a dependency registration.
For TransactionService, you are passing/injecting the implementations in the constructors
public TransactionService(MRepository mRepository, FService fService) {
_mRepository = mRepository;
_fservice = fService;
}
while you only registered the interfaces.
container.RegisterType<IFService, FService>(new HierarchicalLifetimeManager());
container.RegisterType<IMService, MService>(new HierarchicalLifetimeManager());
container.RegisterType<ITransactionService, TransactionService>(new HierarchicalLifetimeManager());
container.RegisterType<IMRepository, MRepository>();
The resolver will do the actual initialization and injection of the implementations based on the known dependencies.
Change
public TransactionService(MRepository mRepository, FService fService)
To rely on the registered interfaces
private readonly IMRepository mRepository;
private readonly IFService fService;
public TransactionService(IMRepository mRepository, IFService fService) {
this.mRepository = mRepository;
this.fservice = fService;
}
every class involved in creating/resolving the controller needs to be checked to make sure that their dependencies can be resolved without error.
I'm trying to implement UNITY on my WebApi2 application.
The problem is that I'm using an existing SqlConnection, depending on an identifier found in the URL of the resource.
So I need the identifier provided in the request uri, to create my context with.
Is it possible to get the {dbIdentifier} from the request URI, and parse it into the constructor of MyRepo?
The Request usi will look like: /api/{dbIdentifier}/{controller}/{id}
The structure looks like...
Request POST /api/myDbIdentifier/my/ + PAYLOAD data
Controller:
public class MyController : ApiController
{
private readonly IRepo _repo;
public MyController(IRepo repo)
{
_repo = repo;
}
}
Repo:
public class MyRepo : IRepo
{
private readonly MyContext _context;
public MyRepo(string dbIdentifier)
{
_context = new MyContext(GetConnection(dbIdentifier));
}
public void Insert(string s)
{
//Inserting string in context and save changes
}
private DbConnection(string id)
{
//psudo find connecion from pool, and return instance of DbConnection...
}
}
public interface IRepo
{
void Insert(string s);
}
Context:
public class MyContext : DbContext
{
public MyContext(DbConnection exitingConnection) : base(existingConnection, true)
{ }
}
Btw, it's my first time playing around with WebApi and Unity, so please bear with my ignorance.
UPDATED Unity part of my code...
UnityResolver (taken from https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection):
public class UnityResolver : IDependencyResolver
{
protected IUnityContainer Container;
public UnityResolver(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container), "Please provider an IUnityContainer.");
}
Container = container;
}
public void Dispose()
{
Container.Dispose();
}
public object GetService(Type serviceType)
{
try
{
return Container.Resolve(serviceType);
}
catch (ResolutionFailedException)
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return Container.ResolveAll(serviceType);
}
catch (Exception)
{
return new List<object>();
}
}
public IDependencyScope BeginScope()
{
return new UnityResolver(Container.CreateChildContainer());
}
}
Unity Register part in my startup:
public static void Register(HttpConfiguration config)
{
// Configuring DI Container fo IoC (Invert of Control)
var container = new UnityContainer();
container.RegisterType<IRepo, MyRepo>(new HierarchicalLifetimeManager());
config.DependencyResolver = new UnityResolver(container);
}
You can try the following:
1)Create a DelegatingHandler where you can access HtppRequestMessage.RequestUri
2)Extract dbIdentifier from Uri
3)Wrap dbIdentifier with a class (e.g. DbIdentifier) and register it in unity using HierarchicalLifetimeManager
4)Remember to register handler in owin:
httpConfiguration.MessageHandlers.Add(new DbIdentifierHandler());
EDIT.
You can look into this post to find some inspiration :
How to pass Owin context to a Repo being injected into Api controller
I'm going to add in the necessary infrastructure to bind each request to its own nested container, So in this case having a Container Per Request gives us a unique session (Context Per Request), In my code I'm using the new implementation of ObjectFactory:
public static class SmObjectFactory
{
private static readonly Lazy<Container> _containerBuilder =
new Lazy<Container>(defaultContainer, LazyThreadSafetyMode.ExecutionAndPublication);
public static IContainer Container
{
get { return _containerBuilder.Value; }
}
private static Container defaultContainer()
{
return new Container(ioc =>
{
ioc.For<IUnitOfWork>()
.HybridHttpOrThreadLocalScoped()
.Use<ApplicationDbContext>();
// config
});
}
}
So, for example If I just set a break point in the ApplicationDbContext's constructor, each time an instance is created. For example in my case I have these controllers:
public partial class HomeController : Controller
{
private readonly IUnitOfWork _uow;
public HomeController(IUnitOfWork uow)
{
_uow = uow;
}
public virtual ActionResult Index()
{
return View();
}
}
public class TestController : Controller
{
private readonly IUnitOfWork _uow;
public TestController(IUnitOfWork uow)
{
_uow = uow;
}
public ActionResult GetData()
{
return Content("Data");
}
}
So the view returned by Index action uses this code to pull in content from TestController:
#Html.Action("GetData", "Test")
In that example, several instances are created per request!
So I've changed SmObjectFactory this way:
public class NewObjectFactory
{
public static IContainer Container { get; set; }
static NewObjectFactory()
{
Container = new Container();
Container.Configure(ioc =>
{
ioc.For<IUnitOfWork>()
.HybridHttpOrThreadLocalScoped()
.Use<ApplicationDbContext>();
// config
}
}
Then in the Global.asax I've added these lines of code for using nested container:
public IContainer Container
{
get
{
return (IContainer)HttpContext.Current.Items["_Container"];
}
set
{
HttpContext.Current.Items["_Container"] = value;
}
}
public void Application_BeginRequest()
{
Container = NewObjectFactory.Container.GetNestedContainer();
}
public void Application_EndRequest()
{
Container.Dispose();
Container = null;
}
And inside Application_Start:
DependencyResolver.SetResolver(
new StructureMapDependencyResolver(() => Container ?? NewObjectFactory.Container));
And inside of the DependencyResolver I've implemented the factory function this way:
public class StructureMapDependencyResolver : IDependencyResolver
{
private readonly Func<IContainer> _factory;
public StructureMapDependencyResolver(Func<IContainer> factory)
{
_factory = factory;
}
public object GetService(Type serviceType)
{
if (serviceType == null)
{
return null;
}
var factory = _factory();
return serviceType.IsAbstract || serviceType.IsInterface
? factory.TryGetInstance(serviceType)
: factory.GetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _factory().GetAllInstances(serviceType).Cast<object>();
}
}
Finaly when I run the application I get this error:
No default Instance is registered and cannot be automatically
determined for type
'Microsoft.Owin.Security.DataProtection.IDataProtectionProvider'
But when I comment this line in the Startup.cs file:
ConfigureAuth(app)
everything works and this time the ApplicationDbContext is created once and then disposed. So that's what I want: Only a single instance of the context is created now and it is correctly disposed of at the end of the web request, It means that the nested container reused the context to satisfy the dependencies of both controllers. :)
Any idea?
I have been trying to get Castle Windsor to inject my DB Context to my controllers I have been following the tutorials on the Castle Windsor website
my code is as follows
Bootstrapper
internal class IOCContainerBootstrap
{
private static IWindsorContainer container;
public static void Configure()
{
container = new WindsorContainer()
.Install(FromAssembly.This());
var controllerFactory = new GravityClimbingControllerFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
}
#region IDisposable Members
public static void Dispose()
{
container.Dispose();
}
#endregion
}
Installers
public class ControllersInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn<IController>()
.LifestyleTransient());
container.Register(Component.For<DbContext>().ImplementedBy<GravityClimbingEntities>());
}
}
public class APIInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes
.FromThisAssembly()
.BasedOn<IHttpController>()
.ConfigureFor<ApiController>(c => { c.PropertiesIgnore(pi => false); })
.LifestyleTransient());
}
}
And finally
My API Controller
public class ArticalsController : ApiController
{
private readonly DbContext _context;
private readonly Db.Repositories.ArticalRepository repository;
public ArticalsController(DbContext context)
{
_context = context;
repository = new Db.Repositories.ArticalRepository(context);
}
[HttpGet]
public string HelloWorld()
{
return "Hello.world";
}
}
When I Debug I get no errors and it says it can resolve the dependency
But when i try to call the API controller I get the following Error Message
{
"Message" : "An error has occurred.",
"ExceptionMessage" : "Type 'ArticalsController' does not have a default constructor",
"ExceptionType" : "System.ArgumentException"
}
Is there something silly I am doing wrong, that I cannot see?
For people who are facing the same issue I found the issue.
Castle Windsor didn't know about the IHTTPControler (Base for the APIController) so i needed to create an IHttpControllerActivator and attach it to the GlobalConfiguration.Configuration.Services
I found this link to enable this here