Until now I have done type registration inside a class within my MVC project but I am now trying to do it with Modules. My project is structured as follows
Project.Data: contains my entityframework classes and DataContext. I have refactored my datacontect to implement an interface (IDbContext) which I register via an autofac module
Project.Business: contains business logic classes into which and IDbContext instance is injected. Classes implement corresponding interfaces which are also registered via an autofac module
Project.Web: asp.net project which uses assembly scanning to register all autofac modules
Sample Code
Project.Data
//IDbContext
public interface IDbContext
{
IDbSet<MyData> MyDatas { get; set; }
....
}
//DataContext
public class DataContext : DbContext, IDbContext
{
public IDbSet<MyData> MyDatas { get; set; }
}
//Module
public class DataInjectionModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<DataContext>().As<IDbContext>();
}
}
Project.Business
public interface IServeDataService
{
IEnumerable<object> GetData();
}
public class ServeDataService : IServeDataService
{
private IDbContext context;
public ServeDataService()
{
this.context = context;
}
public IEnumerable<object> GetData()
{
}
}
public class BusinessInjectionModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ServeDataService>().As<IServeDataService>();
}
}
Project.Web
public partial class Startup
{
public void RegisterContainer()
{
var builder = new ContainerBuilder();
var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>();
builder.RegisterAssemblyModules(assemblies.ToArray());
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration);
}
}
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
RegisterContainer();
}
}
//controller
public class DataController : ApiController
{
private IServeDataService dataService;
public DataController(IServeDataService dataService)
{
this.dataService = dataService;
}
public async Task<IHttpActionResult> Get()
{
var data = dataService.GetData();
return Ok(data);
}
}
however when I try to call the api service, I get the following error
An error occurred when trying to create a controller of type 'DataController'. Make sure that the controller has a parameterless public constructor.
I have debugged and set break points in the RegisterContainer methid and that gets called, I have also looked at the assemblies listed and my Business and Data assemblies are listed, but my module registrations are never run. What am I doing wrong?
It seems that you didn't configure Web API to use your Autofac container.
To get Autofac integrated with Web API you need to reference the Web API integration NuGet package, register your controllers, and set the dependency resolver. You can optionally enable other features as well.
> Autofac - Web API documentation.
In order to make it work you have to add a reference on the Autofac ASP.NET Web API Integration nuget package and add the following line in your RegisterContainer method :
// Set the dependency resolver to be Autofac.
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
Related
I'm using ASP.NET Core Web API. I am having a hard time wrapping my head around instantiating a non-controller class that uses DI. There are a multitude of SO articles related to this, but none that have answered my question (as far as I can understand). These are the most popular and relevant:
Net Core Dependency Injection for Non-Controller
Dependency Injection without a Controller
ASP.NET 5 Non-Controller DI injection
My use case (a contrived example):
I have a class SpeechWriter that has a dependency on IRandomTextService:
public class SpeechWriter
{
private readonly IRandomTextService _textService;
// Constructor with Text Service DI
public SpeechWriter(IRandomTextService textService)
{
_textService = textService;
}
public string WriteSpeech()
{
var speech = _textService.GetText(new Random().Next(5,50));
return speech;
}
}
IRandomTextService interface:
public interface IRandomTextService
{
public string GetText(int wordCount);
}
and the implementation:
public class RandomTextService : IRandomTextService
{
public string GetText(int wordCount)
{
return Lorem.Words(wordCount);
}
}
IRandomTextService is registered as a service in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddScoped<IRandomTextService, RandomTextService>();
}
In my controller action, if I want to instantiate a SpeechWriter like this:
public IActionResult Index()
{
var speech = new SpeechWriter();
return Ok(speech.WriteSpeech());
}
I can't do it because an argument (the injected service) is expected.
The only way I can seem to get DI to inject RandomTextService in SpeechWriter is if SpeechWriter itself is a service and injected in the controller:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddScoped<IRandomTextService, RandomTextService>();
services.AddScoped<SpeechWriter>();
}
public class EchoController : ControllerBase
{
private readonly SpeechWriter _speechWriter;
public EchoController(SpeechWriter speechWriter)
{
_speechWriter = speechWriter;
}
public IActionResult Index()
{
return Ok(_speechWriter.WriteSpeech());
}
}
Is there any way to get RandomTextService injected when SpeechWriter is instantiated as in my first example, like this?
var speech = new SpeechWriter();
If not, what is it about DI that I'm missing? My actual application is more complex than this and I would effectively have to create a chain of DI and services all the way back up to the controller. I could use the ServiceProvider "anti-pattern", but I prefer not to do that because I'd be passing ServiceProvider all over the place.
Please help educate me!
Thanks.
Work on Aspnet core boilerplate framework stuck on one issue, form my controller failed to call services.
Application class library contains IEmployeeService and EmployeeService, how to call them from my EmployeeController.
Service
public interface IEmployeeService
{
int CreateEmployee(CreateEmployeeDto data);
IEnumerable<EmployeeListDto> GetEmployeeList();
}
public class EmployeeService : IEmployeeService
{
}
Controller
[AbpMvcAuthorize]
public class EmployeeController : HRISControllerBase
{
private readonly IEmployeeService _employeeService;
public EmployeeController(
IEmployeeService employeeService
)
{
_employeeService = employeeService;
}
public ActionResult Index()
{
return View();
}
}
Note: Do project need to configure something in ConfigureServices on the Startup.cs file.
You need register it in the ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IEmployeeService, EmployeeService>();
}
Implement ITransientDependency.
public class EmployeeService : IEmployeeService, ITransientDependency
{
// ...
}
From https://aspnetboilerplate.com/Pages/Documents/Dependency-Injection#helper-interfaces:
ASP.NET Boilerplate provides the ITransientDependency, the IPerWebRequestDependency and the ISingletonDependency interfaces as a shortcut.
You can use registered your class in Startup.cs class.here asp..net core provide inbuild DI.
According to the docs
"ASP.NET Boilerplate automatically registers all Repositories, Domain Services, Application Services"
As such all you should need to do is change your IEmployeeService to inherit from IApplicationService:
public interface IEmployeeService : IApplicationService
{
int CreateEmployee(CreateEmployeeDto data);
IEnumerable<EmployeeListDto> GetEmployeeList();
}
I have asp.net core 2.0 Web Api project that uses BLL (Business Logic Layer). I'm getting error when I'm trying to inject any BLL service as Dependancy in my Controller. I have implemented the following Controller:
namespace MyProject.WebApi.Controllers
{
[Route("api/test")]
public class TestController : Controller
{
private readonly ITestService _testService;
public TestController(ITestService testService)
{
_testService = testService;
}
[HttpGet, Route("get-all")]
public List<Test> Get()
{
return _testService.GetTestList();
}
}
}
I have implemented test service in BLL (separate project):
namespace MyProject.Core
{
public class TestService : ITestService
{
private readonly ITestEngine _testEngine;
public TestService(ITestEngine testEngine)
{
_testEngine = testEngine;
}
public List<Test> GetTestList()
{
return _testEngine.GetTestList();
}
}
}
And my interface for TestService looks like this:
namespace MyProject.Core
{
public interface ITestService
{
List<Test> GetTestList();
}
}
The build is successful but when I call Get method of TestController I'm getting the following error:
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[0]
An unhandled exception has occurred while executing the request
System.InvalidOperationException: Unable to resolve service for type 'MyProject.Infrastructure.Interface.ITestEngine' while attempting to activate 'MyProject.Core.TestService'.
Any ideas?
By default, the DI container doesn't know about your services. You need to register them manually in the ConfigureServices method which is in Startup.cs, for example:
public void ConfigureServices(IServiceCollection services)
{
//Snip
services.AddScoped<ITestService, TestService>();
}
See the docs to understand the lifetime you need for the service (i.e. scoped, singleton or transient)
In the Startup class, you need to manually add each service you want into the DI container:
public void ConfigureServices(IServiceCollection services)
{
// your current code goes here
// ...
services.AddScoped<ITestService, TestService>();
}
If you want this to happen automatically, you will need to add a more specialized container (like Autofac or StructureMap).
Is it possible to use dependency injection to inject dependencies into SignalR on ASP.NET Core v2.0?
Assuming the following hub and dependency:
public MyHub : Hub {
private readonly IMyDependency dependency;
public MyHub(IMyDependency dependency) {
this.dependency = dependency;
}
}
public void MyDependency : IDependency
{
public void MyMethod() {
Console.WriteLine("I'm a dependency!");
}
}
I've scoured a bit of the web and there isn't anything obvious out there. I found this tutorial which at first seemed quite promising until I realised it was for Microsoft.AspNetCore.SignalR.Server which didn't ship in the end.
At the moment I have the following setup using Autofac and it's not working:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSignalR();
// Configue Autofac
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterModule<MyModule>();
// Configure SignalR hubs for dependency injection
containerBuilder.RegisterSignalRHubs(typeof(Startup).GetTypeInfo().Assembly);
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
}
}
public static class AutoFacExtensions
{
public static IRegistrationBuilder<object, ScanningActivatorData, DynamicRegistrationStyle> RegisterSignalRHubs(this ContainerBuilder builder, params Assembly[] assemblies)
{
return builder.RegisterAssemblyTypes(assemblies)
.Where(t => typeof(IHub).IsAssignableFrom(t))
.ExternallyOwned();
}
}
public class MyModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<MyDependency>().As<IMyDependency>();
}
}
It looks like the IHub interface doesn't exist anymore. I tried IHubContext<MyHub> in the hope that this might work with the latest version but sadly not.
When I have dependencies in my hub's constructor, the hub isn't created despite all of the dependencies registered with Autofac.
How can I achieve this with the lastest version 1.0.0.0-alpha2-final?
The example given in the question does work with version 1.0.0.0-alpha2-final of Microsoft.AspNetCore.SignalR with one slight tweak, use Hub rather than the now non-existent IHub.
public static class AutoFacExtensions
{
public static IRegistrationBuilder<object, ScanningActivatorData, DynamicRegistrationStyle> RegisterSignalRHubs(this ContainerBuilder builder, params Assembly[] assemblies)
{
// typeof(Hub), not typeof(IHub)
return builder.RegisterAssemblyTypes(assemblies)
.Where(t => typeof(Hub).IsAssignableFrom(t))
.ExternallyOwned();
}
}
Ensure that all of your dependencies are satisfied by assigning them to a controller. I'm not sure at this point how to troubleshoot broken dependencies when injecting into a SignalR hub with this method.
Trying to do dependency injection into my SignalR Hub class using the SignalR-Server which is part of ASP.NET 5 (repo). I tried to figure this out from the tutorial at this link but I can't seem to identify how I can do this given that GlobalHost is no longer available. Here's what I'm trying to do:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddSingleton<IState, State>();
}
public void Configure(IApplicationBuilder app)
{
app.UseSignalR();
}
MyHub.cs
public class MyHub : Hub
{
public IState State { get; set; }
// SignalR accepts this parameterless ctor
public MyHub()
{
}
// SignalR won't use this because it has a parameter
public MyHub(IState state)
{
State = state;
}
}
How can I get SignalR-Server to use the MyHub(IState state) constructor injecting the required dependencies?
the best way (for Asp.Net 5) create a custom resolver to DefaultDependencyResolver that receives IServiceProvider:
public class CustomSignalRDependencyResolver : DefaultDependencyResolver
{
private readonly IServiceProvider _serviceProvider;
public CustomSignalRDependencyResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public override object GetService(Type serviceType)
{
var service = _serviceProvider.GetService(serviceType);
return service ?? base.GetService(serviceType);
}
}
Then on StartUp class
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IState, State>();
//... other services
GlobalHost.DependencyResolver = new CustomSignalRDependencyResolver(services.BuildServiceProvider());
}
I managed to resolve this by adding my State class as a Singleton for IState in Startup.ConfigureServices, and then making a ServiceProvider property publicly available on my Startup.cs class. From there, I was able to GetRequiredService within the constructor of my SignalR Hub class. It isn't the ideal solution and hopefully I'll be able to adjust this to use constructor/property injection as the platform reaches RC.
Here's my code:
Startup.cs
public static IServiceProvider __serviceProvider;
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddSingleton<IState, State>();
__serviceProvider = services.BuildServiceProvider();
}
public void Configure(IApplicationBuilder app)
{
app.UseSignalR();
}
MyHub.cs
public class MyHub : Hub
{
public IState State { get; set; }
public MyHub()
{
State = (IState) Startup.__serviceProvider.GetRequiredService(typeof (IState));
}
public override Task OnConnected()
{
State.Clients = Clients;
State.Groups = Groups;
return base.OnConnected();
}
}
In this way, I was able to set properties and call methods on IState implementing objects from within MyHub, allowing me to persist my app state in memory.
You're very close. You just need:
public class MyHub : Hub
{
readonly IState _state;
public MyHub(IState state)
{
_state = state;
}
}
Ok. Now, I used Autofac, which I am not sure it has ASP.NET 5 integration yet. But if(for now) only target .NET 4.6, you should be fine.
I just published this repository which contains a basic project setup with SignalR and Autofac for dependency injection.
Now, I did the dependency injection setup in order to achieve the following:
be able to inject dependencies into my hub
be able to get the context for my hubs in order to send to clients from outside the hub without using GlobalHost (which is no longer available in .NET 5, but also shouldn't be used since it's a static global object)
I hope you manage to setup your project (even though I don't think you will be able to keep DNX in your build options since Autofac doesn't have the library .NET 5 ready yet.
I hope this helps! Best of luck!
https://github.com/radu-matei/SignalRDependencyInjection
EDIT: If you want to use NInject (and build your own dependency resolver if you want to target DNX, you can follow this repository from the official guys from SignalR (actually from the guy who wrote SignalR):
https://github.com/DamianEdwards/NDCLondon2013/tree/master/DependencyInjection
In this demo they use NInject to create their own dependency resolver, so you shouldn't have any problems targeting DNX if you have NInject libraries.
UPDATE: After reading a little about Dependency Injection in ASP.NET 5, it seems that it is done in an unified manner. If you haven't had a look at this article, I recommend it, even though it doesn't specifically show SignalR DI.
I have simply made constructor with dependencies. For example, I need my IUnitOfWork instance (which was configured in startup) in hub. That is working code
[HubName("receipts")]
public class ReceiptsHub : Hub
{
public IUnitOfWork<string> UnitOfWork { get; set; }
public ReceiptsHub(IUnitOfWork<string> unitOfWork) : base()
{
UnitOfWork = unitOfWork;
}
public override Task OnConnected()
{
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
return base.OnDisconnected(stopCalled);
}
}
In .NET 5 you can directly resolve the IServiceProvider and later you can have the required service. Please check the below codes:
public class MyHub : Hub
{
public IState State { get; set; }
private readonly IServiceProvider _serviceProvider;
public MyHub(IServiceProvider serviceProvider)
{
_serviceProvider=serviceProvider;
State = _serviceProvider.GetRequiredService<IState>();
}
public override Task OnConnected()
{
State.Clients = Clients;
State.Groups = Groups;
return base.OnConnected();
}
}