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?
Related
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 have a console application that Autofac DI is used to inject data and service layer from web application project.
here is the setup on console application:
public static class ContainerConfig
{
public static IContainer Configure()
{
var builder = new ContainerBuilder();
builder.RegisterType<DbFactory>().As<IDbFactory>();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>();
builder.RegisterType<Application>().As<IApplication>();
builder.RegisterType<DataRepository>().As<IDataRepository>();
builder.RegisterType<DataService>().As<IDataService>();
return builder.Build();
}
}
public interface IApplication
{
void Run();
}
public class Application : IApplication
{
private readonly IDataService _dataService;
public Application(IDataService dataService)
{
_dataService = dataService;
}
public void Run()
{
var data = _dataService.GetDataById(1);
var task = new TestTask("test");
data.AddTask(task);
_dataService.Update(data);
_dataService.SaveChanges();
}
}
main Program class:
class Program
{
static void Main(string[] args)
{
var container = ContainerConfig.Configure();
using (var scope = container.BeginLifetimeScope())
{
var app = scope.Resolve<IApplication>();
app.Run();
}
}
}
When the application is run loading the data works fine. However, saving a new entry does not seem to do the work.
However, when I remove DI and use simple class initializing in the Run method as below the save works fine:
IDbFactory dbFactory = new DbFactory();
IDataRepository dataRepository = new DataRepository(dbFactory);
var unitOfWork = new UnitOfWork(dbFactory);
IDataService service = new DataService(dataRepository, unitOfWork);
var data = service.GetDataById(1);
var task = new TestTask("test");
data.AddTask(task);
service.Update(data);
service.SaveChanges();
Am I missing something while I setup the autofac? It seems to access the data fine but when it comes to save it does not save the data. I debugged to see any issue but the program runs fine with no error. How can I debug this sort of issues to find more details?
Updated
public interface IDataService
{
void Add(TestTask task);
void SaveChanges();
}
public class DataService : IDataService
{
private readonly IDataRepository _dataRepository;
private readonly IUnitOfWork _unitOfWork;
public DataService(IDataRepository dataRepository, IUnitOfWork unitOfWork)
{
_dataRepository = dataRepository;
_unitOfWork = unitOfWork;
}
public void Add(TestTask task)
{
_dataRepository.Add(task);
}
public void SaveChanges()
{
_unitOfWork.Commit();
}
}
public class UnitOfWork : IUnitOfWork
{
private readonly IDbFactory _dbFactory;
private ApplicationDbContext _dbContext;
public UnitOfWork(IDbFactory dbFactory)
{
this._dbFactory = dbFactory;
}
public ApplicationDbContext DbContext => _dbContext ?? (_dbContext = _dbFactory.Init());
public void Commit()
{
DbContext.Commit();
}
}
After reading autofac scopes here
I found out that default scope is Instance Per Dependency. Which means that a unique instance will be returned from each request for a service. DbFactory should be for InstancePerLifetimeScope.
So changing configuration below fixes the issue:
public static class ContainerConfig
{
public static IContainer Configure()
{
var builder = new ContainerBuilder();
builder.RegisterType<DbFactory>().As<IDbFactory>().InstancePerLifetimeScope();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>();
builder.RegisterType<Application>().As<IApplication>();
builder.RegisterType<DataRepository>().As<IDataRepository>();
builder.RegisterType<DataService>().As<IDataService>();
return builder.Build();
}
}
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
Here i wana to add Unit dependency soi had take 3-Projects as Univers(MVC),Moon(ClassLib),Service(ClassLib)its Responsible for Unit Dependecy
Moon
Here i had Take 1-Interface 2-ClassFile
public interface IMoon
{
string Gotomoon();
}
public class MoonImp : IMoon
{
public string Gotomoon()
{
return"Hello moon how r u.....";
}
}
Service.cs
Here i had Take 1-IService 2-Service 3-ContainerBootstrap.cs
public interface IService
{
string Gotomoon();
}
public class ServiceImp : IService
{
private IMoon Moon;
public ServiceImp(IMoon moons)
{
this.Moon = moons;
}
public string Gotomoon()
{
return Moon.Gotomoon();
}
ContainerBootstrap.cs
public static void RegisterTypes(IUnityContainer container)
{
container
.RegisterType<IMoon, MoonImp>()
.RegisterType<IService, ServiceImp>();
}
Now i Come to Universe its my MVC File
Here i had Take UnityDependencyResolver .cs file and wrote some code
public class UnityDependencyResolver : IDependencyResolver
{
private readonly IUnityContainer container;
public UnityDependencyResolver(IUnityContainer container)
{
this.container = container;
}
public object GetService(Type serviceType)
{
if (!this.container.IsRegistered(serviceType))
{
return null;
}
else
{
return this.container.Resolve(serviceType);
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
if (!this.container.IsRegistered(serviceType))
{
return new List<object>();
}
else
return this.container.ResolveAll(serviceType);
}
**UnityConfig.cs**
Now i had taken a classfile as UnityConfig.cs in App_Start
public class UnityConfig
{
private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(()=>
{
IUnityContainer container = new UnityContainer();
RegisterType(container);
return container;
});
public static IUnityContainer Getconfiguration()
{
return container.Value;
}
public static void RegisterType(IUnityContainer container)
{
ContainerBootstrap.RegisterTypes(container);
}
}
Global.asax
Now I register that File in Global as
DependencyResolver.SetResolver(new UnityDependencyResolver(UnityConfig.Getconfiguration()));
Now I implement in HomeController.cs
private IService serviced;
public HomeController(IService service)
{
this.serviced = service;
}
public ActionResult Index()
{
ViewBag.msg = serviced.Gotomoon();
return View();
}
I faced this Problem already And also i'm with Mr.j...Comment....Here you did not Register your HomeCOntroller
Please Add this
public static void RegisterType(IUnityContainer container)
{
ContainerBootstrap.RegisterTypes(container);
container.RegisterType<HomeController>();
}
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();
});
});