How to use UnityResolver in Console App? - c#

I have the following resolver in my WebApi project:
config.DependencyResolver = new UnityResolver(container); // HttpConfiguration config
However in a console app, I don't have HttpContiguration. How can I tell my unity container to use this DependencyResolver from my console app?

There might be a better way, but I do my registrations, and then let the container resolve my "first" class.
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Registering dependencies ...");
var container = new UnityContainer();
container.RegisterType<ProgramStarter, ProgramStarter>(); // Register a class that continues your program.
// Do your registrations.
RegisterTypes(container);
// Let Unity resolve ProgramStarter and create a build plan.
var program = container.Resolve<ProgramStarter>();
Console.WriteLine("All done. Starting program...");
program.Run();
}
}
And my ProgramStarter class.
public class ProgramStarter
{
private readonly IService _service;
public ProgramStarter(IService service)
{
// Unity has created this instance and resolved all dependencies.
_service= service;
}
public void Run()
{
// Do what you want to do.
}
}

You don't. The UnityResolver exists to interact with the MVC framework. A console application is no framework and there are no framework types that a Console Application tries to create for you. Since there is no framework, no special hooks are required to use a DI container in a console application. You simply create the container and have one container.Resolve call to construct the object graph and call the method on the resolved object.

Related

Where to put my IoC Container configuration in Service Fabric Service?

I was thinking in placing the IoC Container dependencies configuration under RunAsync method in the Service class but I have a bad feeling that's not the right place to go..
Is there any convention in Azure Service Fabric Services to place this type of configurations without causing any sort of conflicts?
Also, where would you place the dispose call?
Note: I'm using Simple Injector for this but other examples from other IoC containers should do too.
You can create your IoC container in the startup code in program.cs. When you register a service, you can pass it a callback method that is passed the ServiceContext. Once you have the ServiceContext, you can use it to gain access to your application configuration for connection strings and such.
Here's some code I use to create a Ninject kernel.
namespace AccountCommandService
{
internal static class Program
{
private static void Main()
{
try
{
ServiceRuntime.RegisterServiceAsync("AccountCommandServiceType",
context =>
{
// Create IoC container
var kernel = new ServiceKernel(context);
// Create Service
return new AccountCommandService(context,
kernel.Get<IAccountDataContextFactory>(), // Pull a DBContext factory from the IoC
);
}).GetAwaiter().GetResult();
Thread.Sleep(Timeout.Infinite);
}
catch (Exception e)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString());
throw;
}
}
}
}

How to inject with simple injector

I am struggling to determine if this is the correct method for injecting dependencies from my Console Application Main method into my primary application class instance.
I have the following code:
Program Class
static void Main(string[] args)
{
var container = new SimpleInjector.Container();
// Registrations here.
container.Register<ILogger, FileLogger>();
//Verify the container.
container.Verify();
ILogger log = container.GetInstance<ILogger>();
log.Info("Logging From Main Method");
//Start Main Agent
MainAgent agent = new MainAgent(log);
agent.Start();
}
Main Agent Class
public class MainAgent
{
private ILogger log;
public MainAgent(ILogger _log)
{
log = _log;
}
public void Start()
{
//Main Application Code here.
Console.WriteLine("main Agent Started.");
log.Info("Logging through logger in MainAgent Class");
Console.ReadLine();
}
}
I come from a background of writing DotNetCore applications in ASP.Net Core, so I am used to how the DI works with that, registering a service into the pipeline, and them all being available for me to cherry pick in each controller's Constructor.
My worry is I may have 20-30 services, all of which will all need to be injected in as parameters each time I “New Up” a new instance of a class for them to be avaliable to the constructor in my new class.
Am I missing some magical feature which will just make all my registered services available in any newly initialized constructor for me to reference as I do with ASP.Net Core?
No, there is no magic.
What you are missing is that AspNetCore automatically resolves your controller under the covers, which resolves the entire object graph of dependencies that controller has (that is, any dependencies of the controller, dependencies of those dependencies, etc.)
Similarly, in a console app, you need to resolve the entire object graph (usually at startup).
static void Main(string[] args)
{
// Begin Composition Root
var container = new SimpleInjector.Container();
// Registrations here.
container.Register<ILogger, FileLogger>();
container.Register<IMainAgent, MainAgent>();
//Verify the container.
container.Verify();
// End Composition Root
MainAgent agent = container.GetInstance<IMainAgent>();
//Start Main Agent
agent.Start();
}
Effectively the "agent" is should be considered to be the entire application. The console is just a shell to set everything in motion. Do note that it would probably be sensible in most situations to pass in the args from the console app, so they can be parsed and responded to by the "agent" as appropriate.
agent.Start(args);
Am I missing some magical feature which will just make all my registered services available in any
Simple answer yes you are SimpleInjector supports direct object creation
var agent = container.GetInstance<MainAgent>();
With out the need to register the instance at all.
You can make an interface and then register like you do ILogger but making the method virtual and using directly the class name is also fine. You can read more on the subject here

Add Microsoft.Extensions.Logging to .NETStandard project without assuming Dependency Injection available

I want to support logging in my .NETStandard project that will be consumed by a .NET Core Console or Web Client. However I don't want to presume the client uses a constructor that requires a ILogger dependency in the constructor of the classes I wish to log from.
If the logger does not exist, I basically don't want to fail because of this.
So my question is how can I reference ILogger in my code without passing it to the constructor?
using Microsoft.Extensions.Logging;
namespace MyApp
{
public class MyClass
{
//slf4net logger implementation
private static readonly slf4net.ILogger _slf4netLogger = slf4net.LoggerFactory.GetLogger(typeof(MyClass));
//Microsoft.Extensions.Logging???
private static readonly ILogger<MyClass> _logger = ???
public MyClass()
{
//Constructor empty
}
public void MyMethod()
{
//slf4net logger works like this
_slf4netLogger.Trace("This got logged");
//this won't work because the logger was never passed from the constructor
_logger.LogInformation("A message for the log if one is listening");
}
}
}
references:
https://github.com/ef-labs/slf4net
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging?tabs=aspnetcore2x
It seems like I'm not alone with my frustration here
Accessing the Logging API Outside of a MVC Controller
OK, so this is where the new logging API quickly becomes a nightmare.
- https://stackify.com/net-core-loggerfactory-use-correctly/

HangFire with Castle Windsor use of dependencies in background job

I needed a way to run background jobs and found out about HangFire. I succesfully installed everything but I can't seem to get it working together with Windsor.
The problem:
When I use any of my dependencies in my background job function I get the following error in my HangFire dashboard :
System.InvalidOperationException: HttpContext.Current is null.
PerWebRequestLifestyle can only be used in ASP.Net
I searched around and found out that I should use the NuGet package Castle.Windsor.Lifestyles for Hybrid lifestyles. But this does not work for me.
This is my code:
Global.asax:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
// Set up IoC Container
var container = new WindsorContainer(Server.MapPath("~/Configuration/IoC/windsor.config"));
// Set up HangFire with IoC
JobActivator.Current = new WindsorJobActivator(container.Kernel);
}
Startup.cs:
public void Configuration(IAppBuilder app)
{
GlobalConfiguration.Configuration.UseSqlServerStorage("ILVO");
app.UseHangfireServer();
app.UseHangfireDashboard();
}
ServiceInstaller.cs:
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register
(
Component.For<ApplicationContextBuilder>().ImplementedBy<ApplicationContextBuilder>().LifeStyle.HybridPerWebRequestTransient(),
Component.For<IApplicationContextProvider>().ImplementedBy<ApplicationContextProvider>().LifeStyle.HybridPerWebRequestTransient(),
Component.For<ICacheService>().ImplementedBy<CacheService>().LifestyleSingleton(),
Component.For<ISessionProvider>().ImplementedBy<SessionProvider>().LifeStyle.HybridPerWebRequestTransient(),
Component.For<IRepository>().ImplementedBy<Repository>().LifeStyle.HybridPerWebRequestTransient(),
Component.For<IEmployeeService>().ImplementedBy<EmployeeService>().LifeStyle.HybridPerWebRequestTransient(),
Component.For<IGeneralService>().ImplementedBy<GeneralService>().LifeStyle.HybridPerWebRequestTransient(),
Component.For<ITaskService>().ImplementedBy<TaskService>().LifeStyle.HybridPerWebRequestTransient(),
Component.For<ISuggestionService>().ImplementedBy<SuggestionService>().LifeStyle.HybridPerWebRequestTransient(),
Component.For<IAnnouncementService>().ImplementedBy<AnnouncementService>().LifeStyle.HybridPerWebRequestTransient(),
Component.For<IUploadService>().ImplementedBy<UploadService>().LifeStyle.HybridPerWebRequestTransient(),
Component.For<ITaskTrackingService>().ImplementedBy<TaskTrackingService>().LifeStyle.HybridPerWebRequestTransient(),
Component.For<IRequestVpnService>().ImplementedBy<RequestVpnService>().LifeStyle.HybridPerWebRequestTransient(),
Component.For<IEmailService>().ImplementedBy<EmailService>().LifeStyle.HybridPerWebRequestTransient(),
Component.For<IEmployeePlannerService>().ImplementedBy<EmployeePlannerService>().LifeStyle.HybridPerWebRequestTransient(),
Component.For<ISalaryToolService>().ImplementedBy<SalaryToolService>().LifeStyle.HybridPerWebRequestTransient(),
Component.For<IAccessRightService>().ImplementedBy<AccessRightService>().LifeStyle.HybridPerWebRequestTransient()
);
}
Is there any solution for this? I would really like to run database operations in my background job.
Appreciate any help! Thx.
SOLUTION
I made a separate IoC container for HangFire only with the services I need! I also made a class BackroundJobHelper which I store all my functions I need to run in HangFire.
Global.asax
private WindsorContainer _hangFireContainer;
// Set up IoC Container for HangFire
_hangFireContainer = new WindsorContainer();
_hangFireContainer.Register(
Component.For<BackgroundJobHelper>(),
Component.For<ICacheService>().ImplementedBy<CacheService>().LifestylePerThread(),
Component.For<ISessionProvider>().ImplementedBy<SessionProvider>().LifestylePerThread(),
Component.For<IRepository>().ImplementedBy<Repository>().LifestylePerThread(),
Component.For<IEmployeePlannerService>().ImplementedBy<EmployeePlannerService>().LifestylePerThread(),
Component.For<ISalaryToolService>().ImplementedBy<SalaryToolService>().LifestylePerThread()
);
JobActivator.Current = new WindsorJobActivator(_hangFireContainer.Kernel);
BackgroundJobHelper.cs
public class BackgroundJobHelper
{
private readonly IEmployeePlannerService _employeePlannerService;
private readonly ISalaryToolService _salaryToolService;
public BackgroundJobHelper()
{
}
public BackgroundJobHelper(IEmployeePlannerService employeePlannerService, ISalaryToolService salaryToolService)
{
_employeePlannerService = employeePlannerService;
_salaryToolService = salaryToolService;
}
}
Controller
In the controller I call my BackgroundJobHelper class with the function I want to run in HangFire.
BackgroundJob.Enqueue(() => _backgroundJobHelper.Function());
The problem is that Hangfire runs it's own server (thread) independent of the housing app.
Your container is running inside what looks like an MVC application. So at registration time, the HttpContext is available and your services are getting registered with the PerRequest scope.
When you get to running in Hangfire, HttpContext is unavailable. Register any services you want to use in Hangfire with the PerThread scope. If you are sharing these components between the web application and your background threads, this may make things a little wonky. See here
You might want to segregate the components that are going to run in the background and register then PerThread as opposed to sharing PerThread components with both the Hangfire process and your web process.
It tells you here that you can't use PerRequest with Hangfire because of the issue you are seing.
I managed to do it using
https://github.com/BredStik/HangFire.Windsor
and
http://docs.hangfire.io/en/latest/background-methods/using-ioc-containers.html
Basically, you register your Jobs/Services and volia.
Registering is done via you DI provider (Windsor) the usual way.
protected void Application_Start()
{
_container = new WindsorContainer();
/* Register types */
/* _container.Register(Component.For<ISomeInterface>().ImplementedBy<SomeImplementation>()); */
JobActivator.Current = new WindsorJobActivator(_container.Kernel);
}
I basically reused existing stuff and just added Jobs on Container.
windsorContainer.Register(Component.For<SomeJob>().ImplementedBy<SomeJob>());
With windsorContainer being IWindsorContainer. This is done in Global.asax.
In your Startup, you call:
RecurringJob.AddOrUpdate<SomeJob>(x => x.Run(null), Properties.Settings.Default.SomeJobCron);

Creating Instance of a Class using reflection,whose parameters are injected by autofac [duplicate]

I'm trying to figure out how to resolve a instance somewhere in the code.
At the application startup I registered a type
static void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<Foo>().As<IFoo>();
}
Now, how can I resolve an instance somewhere in the code ?
In StructureMAP there is a static object ObjectFactory.GetInstance<IFoo>()
Read up on Getting Started. It should get you started.
First off, what you are looking for is the container. Build it from the ContainerBuilder like in this simple WinForms app:
static void Main()
{
using (var container = builder.Build())
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var mainForm = container.Resolve<MainForm>();
Application.Run(mainForm)
}
}
The general idea is that you only have to resolve the first or topmost instance. The container will handle creating all other instances based on dependency injection through constructor parameters.
If the DI pattern is followed throughout your application you should only have to touch the container once at startup.
How you resolve the topmost instance depends largely on what type of application you are building. If its a web app, the ASP.Net integration and MVC integration will take care of it for you. (After all, the topmost instance in ASP.Net is the Application which is out of our control).
On the other hand, if its a console app or WinForms app you would resolve the first instance manually in Main, like my sample above.

Categories

Resources