This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 2 years ago.
I currently do have the following setup these are the different projects
Project.DAL: Data Access Layer
Project.BLL: Business Logic Layer
Project.Core: Interfaces, Models
Project.API: Web API
Project.Console: Simple .NET console app that
in Project.DAL I did setup the db context
public class AppDbContext : DbContext
{
public DbSet<Role> Roles { get; set; }
public DbSet<User> Users { get; set; }
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options)
{ }
}
while I did setup within Project.API all the Dependencies Injections
services.AddTransient<IUserService, UserService>();
services.AddDbContext<AppDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("Default"),x => x.MigrationsAssembly("Project.DAL"));
});
when I try to run Program.cs in Project.Console
class Program
{
private static AppDbContext _appDbContext;
static void Main(string[] args)
{
_appDbContext.Users.ToList();
}
}
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
any idea ?
Thanks In Advance.
You need to do the configurations for all the entry points.
Entry point is yout API
The second entry point is your console application
So you need to modify your main function:
class Program
{
private static AppDbContext _appDbContext;
static void Main(string[] args)
{
// _appDbContext is not initialized right now
// Even if you configure the injection here the _appDbContext will not be initialized.
_appDbContext.Users.ToList();
}
}
Check the net core console app dependency injection guide
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace ConsoleDI.Example
{
class Program
{
static Task Main(string[] args)
{
using IHost host = CreateHostBuilder(args).Build();
ExemplifyScoping(host.Services, "Scope 1");
ExemplifyScoping(host.Services, "Scope 2");
return host.RunAsync();
}
static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((_, services) =>
services.AddTransient<ITransientOperation, DefaultOperation>()
.AddScoped<IScopedOperation, DefaultOperation>()
.AddSingleton<ISingletonOperation, DefaultOperation>()
.AddTransient<OperationLogger>());
static void ExemplifyScoping(IServiceProvider services, string scope)
{
using IServiceScope serviceScope = services.CreateScope();
IServiceProvider provider = serviceScope.ServiceProvider;
OperationLogger logger = provider.GetRequiredService<OperationLogger>();
logger.LogOperations($"{scope}-Call 1 .GetRequiredService<OperationLogger>()");
Console.WriteLine("...");
logger = provider.GetRequiredService<OperationLogger>();
logger.LogOperations($"{scope}-Call 2 .GetRequiredService<OperationLogger>()");
Console.WriteLine();
}
}
}
The key code in the example above is this:
provider.GetRequiredService<OperationLogger>();
This is the one that creates the OperationLogger from the configured DI container.
You should create the AppDbContext DI rules, build CreateHostBuilder(args).Build(); and the create the instance as shown above.
Related
I am using the new top-level statements in .NET 6 to create a simple console application, but I don't understand the advantages/disadvantages of using the "Generic Host".
Can you explain?
My code with Generic Host:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
Console.WriteLine("Hello, World!");
using var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((_, services) =>
{
services.AddTransient<ITestInterface, TestClass>();
})
.Build();
Test();
Console.ReadKey();
void Test()
{
var testClass = host.Services.GetRequiredService<ITestInterface>();
testClass.TestMethod();
}
versus
using Microsoft.Extensions.DependencyInjection;
Console.WriteLine("Hello, World!");
var services = new ServiceCollection();
services.AddTransient<ITestInterface, TestClass>();
var servicesProvider = services.BuildServiceProvider();
Test();
Console.ReadKey();
void Test()
{
var testClass = servicesProvider.GetRequiredService<ITestInterface>();
testClass.TestMethod();
}
The benefits of using the generic host is that by default a lot of services are already setup for you, see the docs.
The CreateDefaultBuilder method:
Sets the content root to the path returned by GetCurrentDirectory().
Loads host configuration from:
Environment variables prefixed with DOTNET_.
Command-line arguments.
Loads app configuration from:
appsettings.json.
appsettings.{Environment}.json.
Secret Manager when the app runs in the Development environment.
Environment variables.
Command-line arguments.
Adds the following logging providers:
Console
Debug
EventSource
EventLog (only when running on Windows)
Enables scope validation and dependency validation when the environment is Development.
The ConfigureServices method exposes the ability to add services to the Microsoft.Extensions.DependencyInjection.IServiceCollection instance. Later, these services can be made available from dependency injection.
You are not using the generic host correctly. For instance: normally one would add a hosted service so you can use proper DI instead of resolving the required services manually.
An example can be found at the docs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
});
}
If we extend this example with an implementation of Worker that takes in a dependency it will look like this:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddTransient<ITestInterface, TestClass>();
services.AddHostedService<Worker>();
});
}
internal class Worker : IHostedService
{
public Worker(ITestInterface testClass)
{
testClass.Foo();
}
public Task StartAsync(CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task StopAsync(CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
public interface ITestInterface
{
void Foo();
}
public class TestClass : ITestInterface
{
public void Foo()
{
}
}
Now you see a new instance of Worker is created and an instance of ITestInterface is injected. There is no need to call servicesProvider.GetRequiredService<ITestInterface>(); which is an anti-pattern.
Decision Tree
If you don't need all those additional services you can choose not to use the Generic Host like in your second code example in the question.
If you do want to make use of services like logging, app configuration etc. you should use the Generic Host.
I have an NUnit3 project and a console app project in .NET 5 and would like to be able to use dependency injection for a class in my NUnit project. In the console app I am creating the HostBuilder to configure the services for the dependency injection, but in the Main method when I am trying to get the service object of my test class it is returning null. I am not too familiar with DI other than a couple small ASP.NET projects I've done so I don't know if I am doing this right exactly or if this is something that can be done. Any help would be greatly appreciated.
namespace SeleniumTest
{
public interface IDriverHelper
{
public AndroidDriver<AndroidElement> Driver { get; }
}
}
namespace SeleniumTest
{
public class DriverHelper : IDriverHelper
{
private AndroidDriver<AndroidElement> driver;
private AppiumLocalService appiumLocalService;
private AppiumOptions appiumOptions;
public AndroidDriver<AndroidElement> Driver
{
get => driver;
set
{
appiumLocalService = new AppiumServiceBuilder().UsingAnyFreePort().Build();
appiumLocalService.Start();
appiumOptions = new AppiumOptions();
// options added here
driver = new AndroidDriver<AndroidElement>(appiumLocalService, appiumOptions);
}
}
}
}
Here is the beginning of the Test Class
namespace SeleniumTest
{
[TestFixture]
public class Tests
{
private readonly IDriverHelper _driverHelper;
public MyMerlin(IDriverHelper driverHelper)
{
_driverHelper = driverHelper;
}
In the code below, var test = host.Services.GetService<Tests>(); returns null so I am unable to run any of the tests in that class, ex: test.PreSetupTest();
public class Program
{
static Task Main(string[] args)
{
Console.WriteLine("Hello World!");
using IHost host = CreateHostBuilder(args).Build();
var test = host.Services.GetService<Tests>();
test.PreSetupTest();
return host.RunAsync();
}
static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((_, services) =>
services.AddSingleton<IDriverHelper, DriverHelper>());
}
Not the solution mentioned in the following question.
Dependency Injection in .NET Core 3.0 for WPF
Instead, by adding <EnableDefaultApplicationDefinition>false</EnableDefaultApplicationDefinition> to the csproj file to prevent App.xaml from automatically generating the Main method, thereby achieving a similar effect to the dependency injection of ASP.NET core.
I have seen a piece of very beautiful code that somehow get an instance of App through IServiceProvider to call Application.Run(), but I can’t remember it now, and I can’t remember the source of the original code.
Can anyone help? Thank you very much.
My current code is as follows.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
class Program
{
[STAThread]
public static void Main()
{
Host.CreateDefaultBuilder()
.ConfigureServices(ConfigureServices)
.Build()
.Run();
}
private static void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<App>();
services.AddSingleton<MainWindow>();
}
}
I need to get an instance of the App class to call Application.Run().
A working solution for me is:
internal class Program
{
[STAThread]
public static void Main()
{
var host = Host.CreateDefaultBuilder()
.ConfigureServices(ConfigureServices)
.Build();
var app = host.Services.GetService<App>();
if(app != null)
{
app.InitializeComponent();
app.Run();
}
}
private static void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<App>();
services.AddSingleton<MainWindow>();
}
}
The IServiceProvider can be found in IHost.Services.
class Program
{
[STAThread]
public static void Main()
{
var host = Host.CreateDefaultBuilder()
.ConfigureServices(ConfigureServices)
.Build();
host.RunAsync();
var app = host.Services.GetService<App>();
app.Run();
}
private static void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<App>();
services.AddSingleton<MainWindow>();
}
}
I tried many ways, but I could not manage to write logs in the console of my .NET Framework 4.6 Console Application. I searched on Stackoverflow and found 2 possible solutions, which did not work:
Registering in ConfigureDI as
services.AddLogging(configure => configure.AddConsole());
This first possible solution I could not even test as apparently
ILoggingBuilder does not contain a definition for AddConsole [...]
Registering in ConfigureDI as
services.AddSingleton< ILoggerFactory, LoggerFactory>();
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
I'm using ILogger with dependency injection, like this:
public class MyApplication
{
private readonly ILogger<MyService> _logger;
public MyApplication(ILogger<MyService> logger)
{
_logger = logger;
}
public void Run()
{
_logger.LogInformation("Application Started at {dateTime}", DateTime.UtcNow);
//...
}
}
My Program.cs is like this:
public static class Program
{
public static void Main(string[] args)
{
var services = new ServiceCollection();
DependencyInjectionConfiguration.ConfigureDI(services);
var serviceProvider = services.BuildServiceProvider();
var receiver = serviceProvider.GetService<IReceiver>();
receiver.MyServiceMethod();
}
}
and my ConfigureDI method is like this:
public static class DependencyInjectionConfiguration
{
public static void ConfigureDI(IServiceCollection services)
{
services.AddScoped<IReceiver, Receiver>();
services.AddHttpClient<MyClient>();
services.AddScoped<HelperXml>();
//services.AddSingleton<ILoggerFactory, LoggerFactory>();
//services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
//services.AddLogging(configure => configure.AddConsole());
}
}
Anyone has a clue on what is wrong here? In addition: Writing on a txt file would be nice too...
Thanks
You'll need to target the .NET Standard framework if you wish to use ILogger (part of Microsoft.Extensions.Logging).
I have problem with understanding source of errors in my code. I try to get throw course about microservices in .net core. After running build solution I get:
------- Project finished: CrossX.Services.Identity. Succeeded: True. Errors: 0. Warnings: 0
But when I run it I get:
/opt/dotnet/dotnet /RiderProjects/crossx/src/CrossX.Services.Identity/bin/Debug/netcoreapp2.2/CrossX.Services.Identity.dll
Unhandled Exception: System.InvalidOperationException: Cannot resolve scoped service 'CrossX.NETCore.Commands.ICommandHandler`1[CrossX.NETCore.Commands.CreateUser]' from root provider.
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator.ValidateResolution(Type serviceType, IServiceScope scope, IServiceScope rootScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at CrossX.NETCore.Services.ServiceHost.BusBuilder.SubscribeToCommand[TCommand]() in /RiderProjects/crossx/src/CrossX.NETCore/Services/ServiceHost.cs:line 78
at CrossX.Services.Identity.Program.Main(String[] args) in /RiderProjects/crossx/src/CrossX.Services.Identity/Program.cs:line 11
When I added to webHostBuilder .UseDefaultServiceProvider(options => options.ValidateScopes = false) my problem was solved. But turning off validations isn't good idea from what I know. Also When I changed AddScope to AddTransient problem was solved (or at least it run).
Problem is that I have no idea where to look for source of this error. I guess I lack of understanding what is wrong, so I would appreciate if someone would help me, or at least give a hint.
Here is my
Startup.cs:
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 void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddRabbitMq(Configuration);
services.AddScoped<ICommandHandler<CreateUser>, CreateUserHandler>();
services.AddScoped<IEncrypter, Encrypter>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
Program.cs
public class Program
{
public static void Main(string[] args)
{
ServiceHost.Create<Startup>(args)
.UseRabbitMq()
.SubscribeToCommand<CreateUser>()
.Build()
.Run();
}
}
ServiceHost.cs
public class ServiceHost : IServiceHost
{
private readonly IWebHost _webHost;
public ServiceHost(IWebHost webHost)
{
_webHost = webHost;
}
public void Run() => _webHost.Run();
public static HostBuilder Create<TStartup>(string[] args) where TStartup : class
{
Console.Title = typeof(TStartup).Namespace;
var config = new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddCommandLine(args)
.Build();
var webHostBuilder = WebHost.CreateDefaultBuilder(args)
.UseConfiguration(config)
// .UseDefaultServiceProvider(options => options.ValidateScopes = false)
.UseStartup<TStartup>();
return new HostBuilder(webHostBuilder.Build());
}
public abstract class BuilderBase
{
public abstract ServiceHost Build();
}
public class HostBuilder : BuilderBase
{
private readonly IWebHost _webHost;
private IBusClient _bus;
public HostBuilder(IWebHost webHost)
{
_webHost = webHost;
}
public BusBuilder UseRabbitMq()
{
_bus = (IBusClient) _webHost.Services.GetService(typeof(IBusClient));
return new BusBuilder(_webHost, _bus);
}
public override ServiceHost Build()
{
return new ServiceHost(_webHost);
}
}
public class BusBuilder : BuilderBase
{
private readonly IWebHost _webHost;
private IBusClient _bus;
public BusBuilder(IWebHost webHost, IBusClient bus)
{
_webHost = webHost;
_bus = bus;
}
public BusBuilder SubscribeToCommand<TCommand>() where TCommand : ICommand
{
var handler = (ICommandHandler<TCommand>) _webHost.Services.GetService(typeof(ICommandHandler<TCommand>));
_bus.WithCommandHandlerAsync(handler);
return this;
}
public BusBuilder SubscribeToEvent<TEvent>() where TEvent : IEvent
{
var handler = (IEventHandler<TEvent>) _webHost.Services.GetService(typeof(IEventHandler<TEvent>));
_bus.WithEventHandlerAsync(handler);
return this;
}
public override ServiceHost Build()
{
return new ServiceHost(_webHost);
}
}
}
Cannot resolve scoped service ICommandHandler<CreateUser> from root provider
As the error says, you cannot create a scoped service instance from the root provider. The root provider is the root service provider that exists outside of service scopes. As such, it cannot resolve services that should only be consumed within service scopes.
If you want to resolve a scoped service from the root provider, for example when you are consuming it from a singleton service, you should create a service scope first using the IServiceScopeFactory:
var serviceScopeFactory = _webHost.Services.GetService<IServiceScopeFactory>();
using (var scope = serviceScopeFactory.CreateScope())
{
var handler = (IEventHandler<TEvent>)scope.ServiceProvider.GetService(typeof(IEventHandler<TEvent>))
// …
}
Note that service scopes are supposed to be short lived, and that you need to dispose them afterwards to clean up.
Looking at your implementation, it seems as if you pass your scoped services to some other service in order to subscribe to events. This generally seems like a bad idea since that means that a reference to a scoped service will be kept by a (likely) singleton service for the whole lifetime of the application. This is generally a bad idea (as I said, scoped services are supposed to live only a short time).
You should ask yourself why you need the services to be scoped there, and whether you cannot make them singleton instead. Or if you actually need the subscribe mechanism to be based on the instance (instead of for example just the type, using a factory pattern or something).