I have an older .NET 4.8 project that needs to use Airbrake. The project is using Unity for its IoC container, implementing the standard Repository Service pattern.
There's very little in the way of ASP.NET examples.
I am looking to do something like this:
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType(typeof(ILogger<>), typeof(ILogger<>));
container.RegisterType<IMyService, MyService();
}
public class MyController
{
private readonly ILogger<MyController> _logger;
private readonly IMyService _myService;
public MyController(ILogger<MyController> logger, IMyService _myService)
{
_logger = logger;
_myService = myService;
}
public MyMethod()
{
try
{
var x = _myService.DoThis();
}
catch (Exception ex)
{
_logger.LogError(e, e.Message);
}
}
}
I believe I need to either somehow register Airbrake with ILogger or perhaps create my own logging service.
public class Logging : ILogging
{
public void LogError(Exception e, string message)
{
var airbrake = new AirbrakeNotifier(new AirbrakeConfig
{
ProjectId = // pulled from web.config somehow
ProjectKey = // pulled from web.config somehow
});
var notice = airbrake.BuildNotice(ex);
airbrake.NotifyAsync(notice).Result;
}
}
I have tried using this as starting point: https://github.com/airbrake/sharpbrake/blob/master/docs/asp-net-http-module.md
This is excellent, but I need to extend it somehow to be able to use it within my services and not just the .Web project.
I know there's the ASP.NET module that will automatically capture the errors but I am wanting to manually log when I see fit, and avoid having to call the airbrake client every time I want to log an error.
Is there a way to do this or am I completely misunderstanding how this should be working?
You don't actually need to wire it up as part of the .NET ILogger. I am sure there is a way (probably via OWIN) but you nothing stops you from writing a basic logging service as you would any other service and using that via bog standard DI. The answer was pretty much in the question to begin with.
Related
I have a multi tenant system with background job. The tenancy details are stored in database and based on the tenant adding request in service bus, I want to resolve the dependencies based on tenant.
For this I would have to add dependencies to service collection before creating scope. When trying to inject IServiceCollection, it gives me error.
I am looking for the best way to inject dependencies from HostedService
public async Task MessageHandler(object sender, Message message)
{
// Inject dependencies
services.AddScoped<IMyService,Myservice>(); // No way to get services here
using (var scope = serviceProvider.CreateScope())
{
var ... = scope.ServiceProvider.GetService<...>();
//...
}
}
I had a similar need a while back. I created my own service bus handler.
You could try something like the below, where you inject a service (here as an example I'm using IMessageService) to the ServiceeBusHandler that itself has a dbcontext injected.
Then where ever you implement IServiceBusHandler you can specify for which tenant (and their queues) you want the connection built.
public class ServiceBusHandler : IServiceBusHandler
{
private readonly ServiceBusSender _serviceBusSender;
private readonly IMessageService _messageService;
public ServiceBusHandler(
ServiceBusSender serviceBusSender,
IMessageService messageService)
{
_serviceBusSender = serviceBusSender;
_messageService = messageService;
}
public async Task PublishMessageAsync<T>(T message)
{
var jsonString = JsonConvert.SerializeObject(message);
var serviceBusMessage = new ServiceBusMessage(jsonString);
await _serviceBusSender.SendMessageAsync(serviceBusMessage);
}
internal static IServiceBusHandler Create(ServiceBusSender sender)
{
return new ServiceBusHandler(sender);
}
}
public class ServiceBusHandlerFactory : IServiceBusHandlerFactory
{
private readonly IAzureClientFactory<ServiceBusClient> _serviceBusClientFactory;
public ServiceBusHandlerFactory(
IAzureClientFactory<ServiceBusClient> serviceBusClientFactory)
{
_serviceBusClientFactory = serviceBusClientFactory;
}
public IServiceBusHandler GetClient(string tenantId)
{
var tenantDetails = _messageService.GetTenantDetails(tenantId); // Call to your DB to get details about the Tenant
var client = GetServiceBusClient(tenantDetails.QueueName);
var sender = client.CreateSender(tenantDetails.QueueName);
return ServiceBusHandler.Create(sender);
}
protected virtual ServiceBusClient GetServiceBusClient(string queueName)
{
var client = _serviceBusClientFactory.CreateClient(queueName);
return client;
}
}
What you are trying to achieve is to change the set of registrations after the Container was built. MS.DI does not support this, and while historically, more mature DI Containers tended to support this behavior, most modern DI Containers stopped supporting this, because there are too many negative consequences in allowing this. Autofac, for instance, obsoleted its Update method in 2016 and described the issues with updating the Container in details. Ninject has gone through a similar process, although development stopped before the final release that removed the possibility to update the Container. The Simple Injector DI Container never supported updating, and its documentation has some clear texts that describe what the issue is.
You might find a DI Container that supports this, but I would urge you to abbondon this path, because of the negative consequences that it can (and probably will) cause, as the previous links described.
Instead, you will have to find a different way to get tenant-specific behavior, with one single set of registrations. The trick here, typically lies in creating a Proxy implementation of your IMyService that can forward the call to the correct tenant implementation.
This might look something like this:
public class ProxyMyService : IMyService
{
public IMyService Service { get; set; }
// IMyService methods
public void SomeMethod() => this.Service.SomeMethod();
}
This proxy class can be registered at startup, together with other IMyService implementations, as follows:
services.AddScoped<IMyService, ProxyMyService>();
services.AddTransient<MyServiceTenant1>();
services.AddTransient<DefaultMyServiceTenant>();
With this, your hosted service can become the following:
private ProxyMyService service;
public MyHostedService(IMyService service)
{
this.service = (ProxyMyService)service;
}
public async Task MessageHandler(object sender, Message message)
{
using (var scope = serviceProvider.CreateScope())
{
var p = scope.ServiceProvider;
var proxy = (ProxyMyService)p.GetRequiredService<IMyService>();
proxy.Service = IsTentant1
? p.GetRequiredService<MyServiceTenant1>()
: p.GetRequiredService<DefaultMyServiceTenant>();
var ... = p.GetRequiredService<...>();
//...
}
}
A more evolved solution would entail a Proxy implementation that allows to switch between tenant-specific implementations internally. That would likely mean moving part of the logic that's currently inside MessageHandler into the ProxyMyService.
Do notice that the solutions I suggested do not require an abstract factory. Abstract factories are typically not needed.
I am using .NET nanoFramework with this sample as a base project to make a REST API that reads and serve my sensors data from ESP32.
using (WebServer server = new WebServer(80, HttpProtocol.Http, new Type[] { typeof(DHTController) }))
{
Debug.WriteLine("Iniciando server...");
var temp = server.Start();
var nis = NetworkInterface.GetAllNetworkInterfaces();
foreach (var ni in nis)
{
Debug.WriteLine("Seu endereço de IP é: " + ni.IPv4Address.ToString());
}
Thread.Sleep(Timeout.Infinite);
}
Everything works fine until i decide to use dependency injection solution for nanoCRL.
The dependency injection seems to work properly but i notice that the constructor Controller dont get called when a request from postman is done. The route responds as spected, but the constructor dont get called and the dependency is not injected as i expected.
private readonly IDHTService service;
public DHTController(IDHTService service)
{
this.service = service;
}
[Route("dht")]
[Method("GET")]
public void Get(WebServerEventArgs e)
{
try
{
var result = service.GetTemperatura();
e.Context.Response.ContentType = "text/plain";
WebServer.OutPutStream(e.Context.Response, result.ToString());
}
catch (Exception)
{
WebServer.OutputHttpCode(e.Context.Response, HttpStatusCode.BadRequest);
}
}
When i make a call from postman, the constructor breakpoint is skiped by the code and the route breakpoint gets called. But without the dependency injected the route dont work properly too.
constructor breakpoint skiped
Can someone help me to understand what is happening in the code? If it is something expected, or a bug. And help me to use dependency injection with nanoFramework, if has another solution.
Seems that you're running into two separate issues.
From your code above it's not obvious what could be the root cause...
Know that work it's underway to offer an official DI library for nanoFramework.
Until that happens you're better raising an issue on the github of the DI library.
I had the same issue. Fortunatly, I found the answer in the nanoframework WebServer sample called "WebServer.DI". (As you stated previously, the simple sample does not allow Dependency Injection because it does not create an instance of the controller)
https://github.com/nanoframework/Samples/tree/main/samples/Webserver/WebServer.DI
You first have to create a class that inherit from the WebServer class and use the IServiceProvider as follow:
internal class WebServerDi : WebServer
{
private readonly IServiceProvider _serviceProvider;
public WebServerDi(int port, HttpProtocol protocol, Type[] controllers, IServiceProvider serviceProvider) : base(port, protocol, controllers)
{
_serviceProvider = serviceProvider;
}
protected override void InvokeRoute(CallbackRoutes route, HttpListenerContext context)
{
route.Callback.Invoke(ActivatorUtilities.CreateInstance(_serviceProvider, route.Callback.DeclaringType), new object[] { new WebServerEventArgs(context) });
}
}
Then, when creating your Web Server, create it using your new class instead of "WebServer":
using (var webServer = new WebServerDi(80, HttpProtocol.Http, new Type[] { typeof(ControllerTest) }, serviceProvider))
{
webServer.Start();
Thread.Sleep(Timeout.Infinite);
}
Now, the instance of the controller will be created using the service provider, allowing some dependency injection magic in your constructor, as we are used to when using the real .Net Framework:
public class ControllerTest
{
private readonly ITextService _textService;
private readonly ITextServiceSingleton _textServiceSingleton;
public ControllerTest(ITextService textService, ITextServiceSingleton textServiceSingleton)
{
_textService = textService;
_textServiceSingleton = textServiceSingleton;
}
[Route("test")]
[Method("GET")]
public void RoutePostTest(WebServerEventArgs e)
{
var content = $"Response from {nameof(ITextService)}: {_textService.GetText()}. Response from {nameof(ITextServiceSingleton)}: {_textServiceSingleton.GetText()}";
e.Context.Response.ContentType = "text/plain";
WebServer.OutPutStream(e.Context.Response, content);
}
}
I wanted to know how to inject ILogger into a function in a ASP.NET Core app that's called by a Java client through Thrift.
So a high level code demo of what I want to do:
// ExecuteRequest is called by java client through Thrift
public class ThriftLayer
{
...
public string ExecuteRequest(...params)
{
...
var result = RequestFunc1(...params);
...do processing
return result;
}
...
}
// Contains request functions called by ExecuteRequest
public class ServerRequestHandler
{
...
public string RequestFunc1(...params)
{
return TaskFunc1(...params);
}
....
}
// Functions in this class are called by both the Thrift layer(called by ServerRequestHandler) as well as a Console application
// In Console applications, we can inject the ILogger at Startup - No issues there.
public class TaskFunctions
{
private readonly ILogger<TaskFunctions> _logger;
public TaskFunctions(ILogger<TaskFunctions> logger)
{
_logger = logger;
}
public string TaskFunc1(...params)
{
_logger.logInfo("<log message>");
...do processing
return stringResult;
}
}
So I wanted to know how can I inject ILogger into TaskFunctions while calling from Thrift?
The accepted answer from this question on StackOverflow will help you.
You need to build a ServiceCollection outside of the ASP.NET Core's Startup class.
the MVC part of your application will add those services in Startup.ConfigureServices method
the other part of your application will need to build and then use the service provider as such var taskFunctionsWithInjected = ActivatorUtilities.CreateInstance<TaskFunctions>(serviceProvider); in order to get the dependencies
Core issue
Every example I see for dependency injection is paired with MVC for some reason as if nothing else exists outside of web projects. I take issue with this because there is a contradiction going on with MVC utilizing dependency injection but it delivers those dependencies through a Dependency Resolver which to me is just another name for a Service Locator.
DI in a simple console application
With all that being said, how do you use DI with a simple console application?
When there isn't a convenient Dependency Resolver ready to use. How do I actually perform the injection part of DI?
I see the disapproving tone around Service Locators, but what else can you do?
You cannot pass the container because that's also bad practice, but again what else can be done?
Common confusion/frustration
I see a lot of programmers making these mistakes and honestly I can't blame them for it. There isn't a clear solution outside of MVC which is clearly using the dreaded Service Locator.
DI introduces its own problems
Something I don't feel good about doing is pass a dependency through a chain of objects to use it in a deeply nested piece of code. This just feels wrong.
Example
This is a watered down example of something I am working on to demonstrate my concern. I don't like passing the SMTP client dependency through a class, just to give it to another class. You might be compelled to say "Inject the SmtpClient into ServiceClass then into EntryPoint". In my example I cannot inject ServiceClass because it actually comes from a Factory pattern.
public static void Main(string[] args)
{
var smtpClient = _container.GetDependency<ISmtpClient>();
//When I do this manually I feel like it defeats the purpose of DI
var ep = new EntryPoint(smtpClient);
ep.RunAProcess();
}
public class EntryPoint
{
private readonly ISmtpClient _smtpClient;
public EntryPoint(ISmtpClient smtpClient)
{
//EntryPoint doesn't use this dependency
_smtpClient = smtpClient;
}
public void RunAProcess()
{
/* More code here */
//ServiceClass actually comes from a Factory, but I didn't
//want to make this example too long
var svc = new ServiceClass(_smtpClient);
svc.Send();
}
}
public class ServiceClass
{
private readonly ISmtpClient _smtpClient;
public ServiceClass(ISmtpClient smtpClient)
{
//ServiceClass uses this dependency
_smtpClient = smtpClient;
}
public void Send()
{
using (var mail = CreateMailMessage(message))
{
_smtpClient.Send(mail);
}
}
}
Almost related existing question
This is the closest SO question I found in relation to my query:
DbContext Dependency Injection outside of MVC project
Outside of MVC you can use HostBuilder see https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-2.2
The general idea is that it works pretty much like the web version ( and will support console, windows services, and linux daemons etc )
public static async Task Main(string[] args)
{
var host = new HostBuilder() .
.ConfigureServices(ConfigureServices)
.UseConsoleLifetime()
.Build();
await host.RunAsync();
}
private static void ConfigureServices(HostBuilderContext context, IServiceCollection services)
{
services
.AddTransient<IThing, Thingy>()
.AddTransient<Stuff>()
.AddHostedService<MyService>();
}
Your Hosted Service is like your main entry point and things from there will be injected....
internal class MyService : IHostedService
{
public MyService(Stuff stuff) // injected stuff
{
}
public Task StartAsync(CancellationToken cancellationToken)
{
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
This is more a matter of misunderstanding the design principles.
Something I don't feel good about passing a dependency through a chain of objects to use it in a deeply nested piece of code. This just feels wrong.
The core of your issue is about understanding how to apply a clean design which allows loose coupling and high cohesion. Whether it is Asp.Net MVC or console application is an implementation detail.
The watered down example in this case is not following a clean design as EntryPoint is tightly coupling to ServiceClass and also violates the Explicit Dependencies Principle.
EntryPoint in this example is not being genuine about its dependencies. If it does not use ISmtpClient directly then it should not explicitly depend on it just to pass it on.
And if ServiceClass is coming from a factory then the factory should be applied at the composition root and then explicitly injected into EntryPoint
Review the following refactoring to see what I am referring to
public static void Main(string[] args) {
//ISmtpClient should be injected into ServiceClass
//when resolved by the container or factoty
IService service = _container.GetDependency<IService>();
var ep = new EntryPoint(service);
ep.RunAProcess();
}
public class EntryPoint {
private readonly IService service;
public EntryPoint(IService service) {
this.service = service;
}
public void RunAProcess() {
/* More code here */
service.Send(message);
}
}
public class ServiceClass : IService {
private readonly ISmtpClient _smtpClient;
public ServiceClass(ISmtpClient smtpClient) {
//ServiceClass uses this dependency
_smtpClient = smtpClient;
}
public void Send(Message message) {
using (var mail = CreateMailMessage(message)) {
_smtpClient.Send(mail);
}
}
}
So even if you apply pure dependency injection at the composition root, only the actual dependencies are injected into the target dependent.
I have an existing Windows Service that starts up, reads some configs, spins up some threads and does some useful work.
Currently I log that work (through TopShelf.Log4Net / Log4Net) to disk (or console).
I was thinking it would be nice to expose an API to allow people to query the service (and in the future maybe even config it on the fly)
I'm having difficulty figuring out an appropriate way to plumb the two together though. My existing windows service has a bunch of worker threads and it knows the context of what work is being done, stats and progress and things like that.
But in the context of an ApiController actually handling a request I can't see an obvious/easy means of getting at those stats. I tried passing some object references in the Properties of the IAppBuilder in my Startup class, but any time I explicitly do the config myself I seem to lose the default MVC routes that I had.
Anyone have any suggestions on integrating OWIN into/on top of an existing service?
EDIT: Added code:
So I have a very basic TopShelf runner;
HostFactory.Run(x =>
{
x.Service<MyService>(s =>
{
s.ConstructUsing(name => new MyService());
s.WhenStarted(lb => lb.Start());
s.WhenStopped(lb => lb.Stop());
});
x.RunAsLocalSystem();
And within MyService I had a method StartWorkers() that goes off, starts up a bunch of workers, and keeps track of them.
public class MyService
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private List<Worker> workers { get; set; }
private List<Thread> _threads { get; set; }
public MyService()
{
StartWorkers();
}
Now, I want to be able to query for the status of those workers from API requests, so I was thinking I could do something like;
public class MyService
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private List<Worker> workers { get; set; }
private List<Thread> _threads { get; set; }
private IDisposable _webServer { get; set; }
public MyService()
{
StartWorkers();
StartWebServer();
}
private void StartWebServer()
{
const string baseAddress = "http://localhost:10281/";
_webServer = WebApp.Start<Startup>(url: baseAddress);
log.DebugFormat("Loaded webserver at address={0}", baseAddress);
}
All of the OWIN code right now is vanilla from the samples.
When I test this code, I can hit the /api/values sample endpoint which is served from the ValuesController, and all is well.
Now where I'm missing a piece of glue is how to access anything useful in my application from my controller. I can do something naive like making the Worker list public static;
public class MyService
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public static List<Worker> workers { get; set; }
In which case from my controller I can do something like;
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
// nonsense/test code
return MyService.workers.Select(i => i.ErrorsEncountered.ToString()).ToArray();
}
There must be a better way of access the internals of my application without manipulation visbility.
What I've seen is that I can pass objects into the Properties of the IAppBuilder when using WebApp.Start(). These are visible then from the route configuration, and I see to be able to access them from within my ApiController, e.g.;
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
var someWorker = base.ControllerContext.Configuration.Properties["someWorkerReference"];
The problem I have when I go down that route is that 'WebApp.Start(url: baseAddress);' works and my routes all function, but using the WebApp.Start() method and passing in an Action causes my routes to break
So instead of spending time fixing the routes, I wanted to see if there's an obvious/known/official means of passing "context" into the web server
So this is where having a container is super helpful. If you have something like public class MyService : IStatusProvider {} then you can register both MyService and IStatusProvider to the same instance you. How to use DI container when OwinStartup talks about using OWIN & dependency injection. And you would add the container setup to start of the program, changing s.ConstructUsing(name => new MyService()); to
s.ConstructUsing(name => {
var container = new Container();
// container setup
return container.resolve<MyService>(); // method depends on your container
});