Adding Structuremap MVC 5 to an ASP.NET MVC project. I would like to have a singleton of my database connection per request - my controllers would share the same database connection. I am implementing the repository pattern here and need each controller to have a copy of its respective repository. I know this is possible but I think I'm missing or mis-interpretting something wrong.
I have a controller, "Bag," that needs a "IBagRepo"
public class BagController : Controller
{
private readonly IBagRepo repo;
public BagController(IBagRepo repo)
{
this.repo = repo;
}
// actions
}
My first attempt was hooking the singleton database connection in the ControllerConvention, as I assume its called once
public class ControllerConvention : IRegistrationConvention {
public void Process(Type type, Registry registry) {
if (type.CanBeCastTo<Controller>() && !type.IsAbstract) {
// Tried something like
registry.For(type).Singleton().Is(new ApplicationDbContext()); // this
registry.For(type).LifecycleIs(new UniquePerRequestLifecycle());
}
}
}
But it came clear that this isn't the right file to make this change. I went into the registry class that was automatically generated upon installing the nuget package and tried fiddling around with this.
public class DefaultRegistry : Registry {
#region Constructors and Destructors
public DefaultRegistry() {
Scan(
scan => {
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.With(new ControllerConvention());
});
// httpContext is null if I use the line below
// For<IBagRepo>().Use<BagRepo>().Ctor<ApplicationDbContext>().Is(new ApplicationDbContext());
}
#endregion
}
I haven't seen a problem like this out here yet. Am I passing in the right types within my DefaultRegistry class?
What you're wanting is effectively the default behavior if you had been using the StructureMap.MVC5 nuget: https://www.nuget.org/packages/StructureMap.MVC5/. As long as your DbContext is registered with the default lifecycle, that package is using a nested container per http request which effectively scopes a DbContext to an HTTP request for unit of work scoping.
Different tooling than MVC & EF, but I described similar mechanics for FubuMVC + RavenDb w/ StructureMap in this blog post: http://jeremydmiller.com/2014/11/03/transaction-scoping-in-fubumvc-with-ravendb-and-structuremap/
I ended overriding the default controller factory and not using structuremap
Related
i have a simple controller where i am using the interfaec like this ,
public class HomeController : Controller
{
// GET: Home
private IHotelService hotelService;
public HomeController(IHotelService _hotelService)
{
hotelService = _hotelService;
}
}
its working fine, but when i use same thing with API controller like
public class RoomController : BaseApiController
{
private IHotelService hotelService;
public RoomController(IHotelService _hotelService)
{
hotelService = _hotelService;
}
it gives me error
As pointed out here (and in several other answers on SO), you have most likely not registered your DI container with Web API. Web API is a separate framework than MVC and therefore it has a separate configuration, including dependency injection.
So, you need to set
GlobalConfiguration.Configuration.DependencyResolver = MyDependencyResovler(container);
at application startup. The details of how to do this depend on what container you are actually using and whether you use a stock dependency resolver or roll your own as shown in Dependency Injection in ASP.NET Web API 2.
I've created two projects:
Web Project, that contains all the viewmodels/data/controllers etc. And a Web Api project to allow form capture.
I simply want to capture the data in the web Api and save it to the database where it will become accessible to the front end.
I am experiencing an issue initialzing the DBcontext within the Api controller and need help.
namespace ZebraCRM.API2.Controllers
{
[Route("api/[controller]")]
public class LeadsController : Controller
{
private readonly ApplicationDbContext _context;
public LeadController(ApplicationDbContext context)
{
_context = context;
}
// POST api/values
[HttpPost]
public void Post(Lead formData)
{
formData.DateCreated = DateTime.Now;
_context.Lead.Add(formData);
_context.SaveChanges();
}
}
The above idea was taken from the controller in the main web project, but is obviously not the right approach in this situation.
the debug outputs the following
System.InvalidOperationException: Unable to resolve service for type 'ZebraCRM.Web.Data.ApplicationDbContext' while attempting to activate 'ZebraCRM.API2.Controllers.LeadsController'.
The framework doesn't know how to constructor a LeadController because it doesn't know how to satisfy the ApplicationDbContext context parameter when it calls the constructor. To solve this, you could simply assign the value as part of your constructor, eliminating the parameter.
namespace ZebraCRM.API2.Controllers
{
[Route("api/[controller]")]
public class LeadsController : Controller
{
private readonly ApplicationDbContext _context;
public LeadController()
{
_context = new ApplicationDbContext();
}
// POST api/values
[HttpPost]
public void Post(Lead formData)
{
formData.DateCreated = DateTime.Now;
_context.Lead.Add(formData);
_context.SaveChanges();
}
}
}
Or, if you do want to leave the constructor parameter in place, you'll need a DI container such as AutoFac or Ninject. If you're new to DI, I suggest you watch this video. When you set things up for Dependency Injection, you will basically pass something to the framework that says "When constructing an object that needs an X, here's how you give it an X". This allows you to better follow SOLID principles. An object can demand an IRepository (an interface) and as part of your DI setup you can say "when an object demands an IRepository, I want to pass it a SqlServerRepository (which would implement IRepository)". Then if you later decided to switch to MySQL, you could modify the setup to use a MySqlRepository instead of SqlServerRepository, without needing to modify anything about the controller, since the controller would use the repository via the interface.
My C# MVC Website uses StructureMap for Dependency Injection.
The constructor for my DbContext used to be as follows.
public class PortalEntityModel : DbContext
{
public PortalEntityModel() : base("name=PortalEntityModel")
{
}
}
Now I have added Effort.EF6 in order to unit test, I had to change my DbContext constructor to this.
public class PortalEntityModel : DbContext
{
public PortalEntityModel(DbConnection connection) : base(connection, true)
{
}
}
While I can now unit test my code. I can't run the website anymore because StructureMap cannot create since the change.
No default Instance is registered and cannot be automatically determined for type 'System.Data.Common.DbConnection'
How can I tell StructureMap how to initialise my DBContext as before, while allowing Effort.EF6 to also work?
EDIT --
StructureMap is started when the website is started as follows...
public static void Start()
{
IContainer container = IoC.Initialize();
StructureMapDependencyScope = new StructureMapDependencyScope(container);
DependencyResolver.SetResolver(StructureMapDependencyScope);
DynamicModuleUtility.RegisterModule(typeof(StructureMapScopeModule));
}
It is started automatically when the website starts
[assembly: PreApplicationStartMethod(typeof(StructuremapMvc), "Start")]
[assembly: ApplicationShutdownMethod(typeof(StructuremapMvc), "End")]
I managed to figure it out. When changing the constructor for the DBContext structuremap was unable to pass it a DbConnection.
So I have both constructors now and added a bit of logic to the structuremap setup that tells it which constructor the website should use.
c.For().Use().SelectConstructor(() => new PortalEntityModel());
How can I inject different implementation of object for a specific class?
For example, in Unity, I can define two implementations of IRepository
container.RegisterType<IRepository, TestSuiteRepositor("TestSuiteRepository");
container.RegisterType<IRepository, BaseRepository>();
and call the needed implementation
public BaselineManager([Dependency("TestSuiteRepository")]IRepository repository)
As #Tseng pointed, there is no built-in solution for named binding. However using factory method may be helpful for your case. Example should be something like below:
Create a repository resolver:
public interface IRepositoryResolver
{
IRepository GetRepositoryByName(string name);
}
public class RepositoryResolver : IRepositoryResolver
{
private readonly IServiceProvider _serviceProvider;
public RepositoryResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IRepository GetRepositoryByName(string name)
{
if(name == "TestSuiteRepository")
return _serviceProvider.GetService<TestSuiteRepositor>();
//... other condition
else
return _serviceProvider.GetService<BaseRepositor>();
}
}
Register needed services in ConfigureServices.cs
services.AddSingleton<IRepositoryResolver, RepositoryResolver>();
services.AddTransient<TestSuiteRepository>();
services.AddTransient<BaseRepository>();
Finally use it in any class:
public class BaselineManager
{
private readonly IRepository _repository;
public BaselineManager(IRepositoryResolver repositoryResolver)
{
_repository = repositoryResolver.GetRepositoryByName("TestSuiteRepository");
}
}
In addition to #adem-caglin answer I'd like to post here some reusable code I've created for name-based registrations.
UPDATE Now it's available as nuget package.
In order to register your services you'll need to add following code to your Startup class:
services.AddTransient<ServiceA>();
services.AddTransient<ServiceB>();
services.AddTransient<ServiceC>();
services.AddByName<IService>()
.Add<ServiceA>("key1")
.Add<ServiceB>("key2")
.Add<ServiceC>("key3")
.Build();
Then you can use it via IServiceByNameFactory interface:
public AccountController(IServiceByNameFactory<IService> factory) {
_service = factory.GetByName("key2");
}
Or you can use factory registration to keep the client code clean (which I prefer)
_container.AddScoped<AccountController>(s => new AccountController(s.GetByName<IService>("key2")));
Full code of the extension is in github.
You can't with the built-in ASP.NET Core IoC container.
This is by design. The built-in container is intentionally kept simple and easily extensible, so you can plug third-party containers in if you need more features.
You have to use a third-party container to do this, like Autofac (see docs).
public BaselineManager([WithKey("TestSuiteRepository")]IRepository repository)
After having read the official documentation for dependency injection, I don't think you can do it in this way.
But the question I have is: do you need these two implementations at the same time? Because if you don't, you can create multiple environments through environment variables and have specific functionality in the Startup class based on the current environment, or even create multiple Startup{EnvironmentName} classes.
When an ASP.NET Core application starts, the Startup class is used to bootstrap the application, load its configuration settings, etc. (learn more about ASP.NET startup). However, if a class exists named Startup{EnvironmentName} (for example StartupDevelopment), and the ASPNETCORE_ENVIRONMENT environment variable matches that name, then that Startup class is used instead. Thus, you could configure Startup for development, but have a separate StartupProduction that would be used when the app is run in production. Or vice versa.
I also wrote an article about injecting dependencies from a JSON file so you don't have to recompile the entire application every time you want to switch between implementations. Basically, you keep a JSON array with services like this:
"services": [
{
"serviceType": "ITest",
"implementationType": "Test",
"lifetime": "Transient"
}
]
Then you can modify the desired implementation in this file and not have to recompile or change environment variables.
Hope this helps!
First up, this is probably still a bad idea. What you're trying to achieve is a separation between how the dependencies are used and how they are defined. But you want to work with the dependency injection framework, instead of against it. Avoiding the poor discover-ability of the service locator anti-pattern. Why not use generics in a similar way to ILogger<T> / IOptions<T>?
public BaselineManager(RepositoryMapping<BaselineManager> repository){
_repository = repository.Repository;
}
public class RepositoryMapping<T>{
private IServiceProvider _provider;
private Type _implementationType;
public RepositoryMapping(IServiceProvider provider, Type implementationType){
_provider = provider;
_implementationType = implementationType;
}
public IRepository Repository => (IRepository)_provider.GetService(_implementationType);
}
public static IServiceCollection MapRepository<T,R>(this IServiceCollection services) where R : IRepository =>
services.AddTransient(p => new RepositoryMapping<T>(p, typeof(R)));
services.AddScoped<BaselineManager>();
services.MapRepository<BaselineManager, BaseRepository>();
Since .net core 3, a validation error should be raised if you have failed to define a mapping.
I have a problem that I don't know how to solve. I have a container which contains different interfaces for different services that I expose with a WebApi. The problem is that I need to get that dependencies inside my controller and I must avoid the use of static. I read about this
http://beletsky.net/2011/10/inside-aspnet-mvc-idependencyresolver.html
http://www.asp.net/web-api/overview/advanced/dependency-injection
In the asp.net I read that I can implement my own IDependencyResolver. Is this madness? because I searched a lot and I only found examples using Unity. If I don't want to use that dependency injector? What it's the best way to achieve this?.
public class MyController: ApiController
{
private InterfaceService m_interfaceService; //This is the dependency I need
public MyController()
{
}
[HttpGet]
[Route("myServices/")]
public List<IServiceCategory> GetServiceObjectsList()
{
return m_interfaceServices.GetObjectsList();
}
}
So you have an pre-existing container/dependency mechanism! You should ask your team why it was a good idea to make something like that instead of using all the good ones out in the .net world.
Nonetheless, from the docs:
http://www.asp.net/web-api/overview/advanced/dependency-injection
Although you could write a complete IDependencyResolver implementation
from scratch, the interface is really designed to act as bridge
between Web API and existing IoC containers.
The Unity example on that page shows what must be done to bridge the gap between Unity DI framework and the web mvc. You just need to do the same thing with your home-rolled one. It's matter of implementing just a few methods. Go for it!
Here is an implementation that meets your requirements:
namespace AdvancedDI.Controllers
{
public class ProductController : ApiController
{
public IFactory iFactory { get; set; }
protected override void Initialize(HttpControllerContext controllerContext)
{
DIAPP.GetContainer(this);
base.Initialize(controllerContext);
}
public IHttpActionResult Get()
{
var response = iFactory.DoWork();
return Ok(response);
}
protected override void Dispose(bool disposing)
{
DIAPP.Dispose(this);
base.Dispose(disposing);
}
}
}
This is one kind of property based injection. It is possible with UnityContainerExtensions. The Initialize method will get called before Get.
Step 1. DIAPP.GetContainer(this) carries the entire productController context.
Step 2. GetContainer receives the IFactory property information from this.
Step 3. Next, you have a chance to receive the unity IBuilderContext for this IFactory.
I've used both Ninject and AutoFac for dependency injection. This is not madness, it's common practice.