I am doing something like:
private static IServiceProvider serviceProvider;
public Program(IApplicationEnvironment env, IRuntimeEnvironment runtime)
{
var services = new ServiceCollection();
ConfigureServices(services);
serviceProvider = services.BuildServiceProvider();
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; set; }
private void ConfigureServices(IServiceCollection services)
{
//Console.WriteLine(Configuration["Data:DefaultConnection:ConnectionString"]);
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<DbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
}
I am struggling to get to use the program using an injected DbContext. Any idea? How do you instantiate the program and get everything injected? I don't know what to do in the static Main method.
Is there an equivalent for this?
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
Something like?
public static void Main(string[] args) => ConsoleApplication.Run<Program>(args);
This is how I did it:
public class Startup
{
public static IConfigurationRoot Configuration { get; set; }
public static void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<DbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
services.AddSingleton<IMyManager, Manager>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<Program, Program>();
}
public static void Main(string[] args)
{
var services = new ServiceCollection();
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
.AddEnvironmentVariables()
.AddUserSecrets();
Configuration = builder.Build();
ConfigureServices(services);
var provider = services.BuildServiceProvider();
CancellationTokenSource ctSource = new CancellationTokenSource();
CancellationToken ct = ctSource.Token;
Task task = Task.Run(async () =>
{
Program program = provider.GetRequiredService<Program>();
await program.Run(ct);
});
try
{
task.Wait();
}
catch (AggregateException e)
{
throw e.InnerException;
}
ctSource.Cancel();
ctSource.Dispose();
}
}
Then the program is just:
class Program
{
private IMyManager _myManager;
public Program(IMyManager myManager)
{
_myManager = myManager;
}
public async Task Run(CancellationToken cancelationToken)
{
while (true)
{
cancelationToken.ThrowIfCancellationRequested();
// My things using _myManager
await Task.Delay(10000, cancelationToken);
}
}
}
I deleted a bunch of stuff for the example so it probably crashes somewhere, but you get the idea.
Just in case anyone else is looking for a small and simple example to follow.
Here is a small console app I wrote recently for a an example. It"s only a small password generator demonstration of DI in an app with unit tests.
https://github.com/AnthonySB/PasswordApplication
using System;
using Microsoft.Extensions.DependencyInjection;
using PasswordExercise.Interfaces;
using PasswordExercise.Services;
namespace PasswordExercise
{
class Program
{
static void Main(string[] args)
{
//Dependency injection
var serviceProvider = new ServiceCollection()
.AddSingleton<IPasswordGeneratorService, PasswordGenerator>()
.AddSingleton<IPasswordService, PasswordService>()
.BuildServiceProvider();
//Get the required service
var passwordService = serviceProvider.GetService<IPasswordService>();
//For reading from the console
ConsoleKeyInfo key;
//Display the menu
passwordService.Menu();
do
{
//Read the console key, do not display on the screen
key = Console.ReadKey(true);
switch (key.KeyChar.ToString())
{
case "1":
Console.WriteLine("Simple password: {0}", passwordService.SimplePassword());
break;
case "2":
Console.WriteLine("Moderate password: {0}", passwordService.ModeratePassword());
break;
case "3":
Console.WriteLine("Strong password: {0}", passwordService.StrongPassword());
break;
}
} while (key.Key != ConsoleKey.Escape);
}
}
}
Hope this helps someone.
Related
I have .net core 3.1 console application and I want to run it as a windows service, my program.cs looks like
public class Program
{
public static async Task Main(string[] args)
{
var isService = !(Debugger.IsAttached || args.Contains("--console"));
var builder = CreateHostBuilder(args);
if (isService)
{
await builder.RunAsServiceAsync();
}
else
{
await builder.RunConsoleAsync();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker1>();
services.AddHostedService<Worker2>();
});
}
and the .csproj is
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UserSecretsId>dotnet-MyWorkerService-16487890-DF99-45C2-8DC4-5475A21D6B75</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.16" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="3.1.16" />
</ItemGroup>
</Project>
but for RunAsServiceAsync() error is coming like "IHostBuilder does not contain definition for RunAsServiceAsync"
Can anyone please point to me where / what I am missing?
RunAsServiceAsync appears to be 3rd party extension on IHostBuilder.
It does not appear to be a built in function, native to .NET Core.
I found an old implementation on GitHub here that you could probably implement yourself
public static class ServiceBaseLifetimeHostExtensions
{
public static IHostBuilder UseServiceBaseLifetime(this IHostBuilder hostBuilder)
{
return hostBuilder.ConfigureServices((hostContext, services) => services.AddSingleton<IHostLifetime, ServiceBaseLifetime>());
}
public static Task RunAsServiceAsync(this IHostBuilder hostBuilder, CancellationToken cancellationToken = default)
{
return hostBuilder.UseServiceBaseLifetime().Build().RunAsync(cancellationToken);
}
}
public class ServiceBaseLifetime : ServiceBase, IHostLifetime
{
private TaskCompletionSource<object> _delayStart = new TaskCompletionSource<object>();
public ServiceBaseLifetime(IApplicationLifetime applicationLifetime)
{
ApplicationLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime));
}
private IApplicationLifetime ApplicationLifetime { get; }
public Task WaitForStartAsync(CancellationToken cancellationToken)
{
cancellationToken.Register(() => _delayStart.TrySetCanceled());
ApplicationLifetime.ApplicationStopping.Register(Stop);
new Thread(Run).Start(); // Otherwise this would block and prevent IHost.StartAsync from finishing.
return _delayStart.Task;
}
private void Run()
{
try
{
Run(this); // This blocks until the service is stopped.
_delayStart.TrySetException(new InvalidOperationException("Stopped without starting"));
}
catch (Exception ex)
{
_delayStart.TrySetException(ex);
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
Stop();
return Task.CompletedTask;
}
// Called by base.Run when the service is ready to start.
protected override void OnStart(string[] args)
{
_delayStart.TrySetResult(null);
base.OnStart(args);
}
// Called by base.Stop. This may be called multiple times by service Stop, ApplicationStopping, and StopAsync.
// That's OK because StopApplication uses a CancellationTokenSource and prevents any recursion.
protected override void OnStop()
{
ApplicationLifetime.StopApplication();
base.OnStop();
}
}
But it appears that this service based functionality is now built in when UseWindowsService is called on the builder.
So in that case you would need to refactor your code accordingly to get the desired behavior
public class Program {
public static async Task Main(string[] args) {
var isService = !(Debugger.IsAttached || args.Contains("--console"));
var builder = CreateHostBuilder(args);
if (isService) {
await builder.RunAsServiceAsync();
} else {
await builder.RunConsoleAsync();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker1>();
services.AddHostedService<Worker2>();
});
}
public static class ServiceBaseLifetimeHostExtensions {
public static Task RunAsServiceAsync(this IHostBuilder hostBuilder, CancellationToken cancellationToken = default) {
return hostBuilder.UseWindowsService().Build().RunAsync(cancellationToken);
}
}
Will anything bad happen if there are multiple registrations of services in .net core's DI? For example let's say we have the following code
public void ConfigureServices(IServiceCollection services)
{
//....
services.AddHealthChecks();
//...
}
And in another (extension maybe) class we use services.AddHealthChecks() again. Will this mess the DI's container or not?
Thanks in advance
You can register a service several times without an exception has thrown. The problem is when you register a service several times with different scope. consider following example:
public interface IMyInterface
{
void Print();
}
public class MyInterface : IMyInterface
{
public void Print()
{
}
}
And let's register IMyInterface with two different scopes:
internal class Program
{
private static void Main(string[] args)
{
IServiceCollection services = new ServiceCollection();
services.AddScoped<IMyInterface, MyInterface>();
services.AddSingleton<IMyInterface, MyInterface>();
var provider = services.BuildServiceProvider();
for (var i = 0; i < 5; i++)
{
var scope = provider.CreateScope();
using (scope)
{
var myInterface = scope.ServiceProvider.GetService<IMyInterface>();
Console.WriteLine(myInterface.GetHashCode());
}
}
}
}
First, register IMyInterface in the following order:
services.AddScoped<IMyInterface, MyInterface>();
services.AddSingleton<IMyInterface, MyInterface>();
As you can see we get a singleton instance of MyInterface and the hashcode is the same.
Now let's change it to this:
services.AddSingleton<IMyInterface, MyInterface>();
services.AddScoped<IMyInterface, MyInterface>();
Now we get the scoped type of MyInterface and the hashcode is different each time. You always get the last registered scope of your type.
Look at AddHealthChecks code:
public static IHealthChecksBuilder AddHealthChecks(this IServiceCollection services)
{
services.TryAddSingleton<HealthCheckService, DefaultHealthCheckService>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, HealthCheckPublisherHostedService>());
return new HealthChecksBuilder(services);
}
By adding services.AddHealthChecks(); more than one time, you just registering HealthCheckService and IHostedService as a singleton services and I think it doesn't affect the health check functionality.
With the following extension, you can find duplicate registration:
public static class ServiceDescription
{
private static List<IGrouping<Type, ServiceDescriptor>> Descriptors;
public static IHostBuilder ConfigureServiceDescriptionCheck(this IHostBuilder hostBuilder)
{
hostBuilder.ConfigureServices(services =>
{
Descriptors = services.Where(i => !i.ServiceType.Assembly.FullName.Contains("Microsoft"))
.GroupBy(p => p.ServiceType)
.Where(x => x.Count() > 1).ToList();
});
return hostBuilder;
}
public static IHost UseServiceDescriptionCheck(this IHost host)
{
var logger = host.Services.GetRequiredService<ILogger<Program>>();
Descriptors.ForEach(item =>
{
var count = item.Count();
logger.LogWarning("Service of type {Key} has been registered {count} times", item.Key, count);
});
return host;
}
}
And use it in this way:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().UseServiceDescriptionCheck().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureServiceDescriptionCheck();
}
Read this article to get more details.
I'm trying to create a default admin account when I start an application.
Now what I'm interested in is how to seed a database in asp.net core. I have a seed code that I run in the main program. It shows no error but does not update the database. I've been trying to change "Identity rolls" to Application Role in my SeedData, but it has no effect at all.
I wouldn't want to change most of the code and I know it can be done with a model builder, but I don't want it that way. I think the problem is with the main program, but I don't understand what I need to change. My code is shown here.
SeedData.cs
namespace AspNetCoreTodo
{
public static class SeedData
{
public static async Task InitializeAsync(IServiceProvider services)
{
var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
await EnsureRolesAsync(roleManager);
var userManager = services.GetRequiredService<UserManager<ApplicationUser>>();
await EnsureTestAdminAsync(userManager);
}
private static async Task EnsureRolesAsync(RoleManager<IdentityRole> roleManager)
{
var alreadyExists = await roleManager.RoleExistsAsync(Constants.AdministratorRole);
if (alreadyExists) return;
await roleManager.CreateAsync(new IdentityRole(Constants.AdministratorRole));
}
private static async Task EnsureTestAdminAsync(UserManager<ApplicationUser> userManager)
{
var testAdmin = await userManager.Users
.Where(x => x.UserName == "admin#todo.local")
.SingleOrDefaultAsync();
if (testAdmin != null) return;
testAdmin = new ApplicationUser { Email = "admin#todo.local", UserName = "admin#todo.local" };
await userManager.CreateAsync(testAdmin, "NotSecure123!!");
await userManager.AddToRoleAsync(testAdmin, Constants.AdministratorRole);
}
}
}
ApplicationDbContext.cs
namespace AspNetCoreTodo.Data
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<TodoItem> Items {get; set;}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
}
Program.cs
namespace AspNetCoreTodo
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
private static void InitializeDatabase(IWebHost host)
{
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
SeedData.InitializeAsync(services).Wait();
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred seeding the DB.");
}
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Startup.cs //Configuration
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews();
services.AddRazorPages();
services.AddMvc();
services.AddAuthentication();
services.AddScoped<ITodoItemService, TodoItemService>();
}
Does your application ever goes in the method InitializeDatabase(IWebHost host) in Program.cs?
Could you please try to call your method in Main() method:
public static void Main(string[] args)
{
var webHost = CreateWebHostBuilder(args).Build();
InitializeDatabase(webHost);
webHost.Run();
}
Note: You have to create that 'webHost' variable, because your method takes 'IWebHost' as a parameter. And CreateWebHostBuilder(string[] args) method returns type of IWebHostBuilder. Also Run() method will work on type of IWebHost.
Note: As Nilay noticed above, I'd also seed my database in Startup.cs, in the
if(env.isDevelopment){
InitializeDatabase(webHost);
}
Because normally, seeding is a "development" purpose.
I am trying to do some integration tests for my asp.net Core 2.1 project.
I initialise on startup file my connection string but when i run the test it still empty in handler, what is wrong on my code?
[TestMethod]
public async Task Method1()
{
var webHostBuilder = new WebHostBuilder()
.UseEnvironment("Development")
.UseStartup<Startup>();
HttpRequestMessage getRequest = new HttpRequestMessage(HttpMethod.Get, "api/action")
{
};
getRequest.Headers.Add("userId", "4622");
getRequest.Headers.Add("clientId", "889");
using (var server = new TestServer(webHostBuilder))
using (var client = server.CreateClient())
{
var result = await client.SendAsync(getRequest);
...
}
}
Startup
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
...
services.Configure<SqlConfig>(options =>
{
options.ConnectionString = Configuration.GetConnectionString("DefaultConnection");
});
...
}
SqlConfig
public class SqlConfig
{
public string ConnectionString { get; set; }
}
Repository
public abstract class SqlServerQueryHandler<TQuery, TResult> : BaseQueryHandler<TQuery, TResult>
where TQuery : IQuery<TResult>
{
public SqlServerQueryHandler(IOptions<SqlConfig> connectionString)
{
this.ConnectionString = connectionString.Value.ConnectionString;
}
protected string ConnectionString { get; }
}
what solve my probleme is the following code :
public class TestStartup : Startup
{
public TestStartup(IConfiguration configuration, IHostingEnvironment env) : base(configuration)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
this.Configuration = builder.Build();
}
Include the appsettings.json via interface on the output directory
I've a sample asp.net core app with custom Implementation of IServer interface as following:
namespace AspNetCoreAppWithOwinHttpListenerServer
{
public class Program
{
public static void Main(string[] args)
{
IWebHost host = new WebHostBuilder()
.UseHttpListener()
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
public class OwinHttpListenerServer : IServer
{
private IDisposable _HttpListenerServer;
public IFeatureCollection Features { get; } = new FeatureCollection();
public OwinHttpListenerServer()
{
Features.Set<IServerAddressesFeature>(new ServerAddressesFeature());
}
public void Start<TContext>(IHttpApplication<TContext> application)
{
Func<IDictionary<string, object>, Task> appFunc = async env =>
{
FeatureCollection features = new FeatureCollection(new OwinFeatureCollection(env));
TContext context = application.CreateContext(features);
try
{
await application.ProcessRequestAsync(context);
}
catch (Exception ex)
{
application.DisposeContext(context, ex);
throw;
}
application.DisposeContext(context, null);
};
appFunc = OwinWebSocketAcceptAdapter.AdaptWebSockets(appFunc);
Dictionary<string, object> props = new Dictionary<string, object>();
props["host.Addresses"] = Features
.Get<IServerAddressesFeature>()
.Addresses
.Select(add => new Uri(add))
.Select(add => new Address(add.Scheme, add.Host, add.Port.ToString(), add.LocalPath).Dictionary)
.ToList();
OwinServerFactory.Initialize(props);
_HttpListenerServer = OwinServerFactory.Create(appFunc, props);
}
public void Dispose()
{
_HttpListenerServer?.Dispose();
}
}
public static class OwinHttpListenerWebHostBuilderExtensions
{
public static IWebHostBuilder UseHttpListener(this IWebHostBuilder builder)
{
return builder.ConfigureServices(services =>
{
services.AddSingleton<IServer, OwinHttpListenerServer>();
});
}
}
}
It works fine without IIS, but running on IIS or IISExpress results in "did not listen on the given port" by VSIISExeLauncher.exe.
How can I make my custom server compatible with IIS?
Thanks in advance.
GitHub repository: https://github.com/ymoradi/AspNetCoreAppWithOwinHttpListenerServer
AspNetCoreModule (IIS) is incompatible with Http.Sys based servers. See https://github.com/aspnet/AspNetCoreModule/issues/23
OwinHttpListener is based on .NET HttpListenerClass and that class is implemented by help of http.sys.
http.sys is a kernel mode code, and for now, there is no support for that in asp.net core iis module.