I am trying to get an instance of the DbContext (so I can do some additional work upon startup with it), I get the following error when trying to get an instance in the Configure method:
System.InvalidOperationException: 'Cannot resolve scoped service 'MyApp.Data.MyDbContext' from root provider.'
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyDbContext>(
options => options.UseSqlServer(Configuration.GetConnectionString("MyDbContext")));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
var dbContext = app.ApplicationServices.GetService(typeof(MyDbContext)) as MyDbContext;
}
I can access an instance of the DbContext fine via the controller, etc
Paul Hiles comment is correct but that method works better in .NET Core 1.0.
In ASP.NET Core 2.0 it's generally a bad idea to run any database setup in Startup.cs. This is because if you run any migrations from the CLI or Visual Studio it will run all of Startup.cs and try to run your configuration which will fail. Of course if you don't use Entity-Framework then this isn't a problem however its still not the recommended way of doing it in 2.0. It's now recommended to do it in Program.cs.
For example you can create a extension method of IWebHost that will run any setup you need.
public static IWebHost MigrateDatabase(this IWebHost webHost)
{
var serviceScopeFactory = (IServiceScopeFactory)webHost.Services.GetService(typeof(IServiceScopeFactory));
using (var scope = serviceScopeFactory.CreateScope())
{
var services = scope.ServiceProvider;
var dbContext = services.GetRequiredService<YourDbContext>();
dbContext.Database.Migrate();
}
return webHost;
}
And then in Program.cs you can then call that method before running.
public static void Main(string[] args)
{
BuildWebHost(args)
.MigrateDatabase()
.Run();
}
Update for Core 2.1 onwards
Just to add to #Travis Boatman's excellent answer, the preferred Main method syntax has changed slightly from Core 2.1 onwards and the default Main method now has CreateWebHostBuilder instead of BuildWebHost.
The revised code to call the extension method is shown below.
NB: the order is important here, the Build method returns a WebHost, which is what the extension method is extending, so you need to call the migrate method after Build() and before Run()):
public static void Main(string[] args)
{
CreateWebHostBuilder(args)
.Build()
.MigrateDatabase()
.Run();
}
Migrating more than one DbContext
We have more than one DbContext in our project, so I changed the extension method to a generic method that can take any type of DbContext:
public static IWebHost MigrateDatabase<T>(this IWebHost webHost) where T:DbContext
{
var serviceScopeFactory = (IServiceScopeFactory)webHost
.Services.GetService(typeof(IServiceScopeFactory));
using (var scope = serviceScopeFactory.CreateScope())
{
var services = scope.ServiceProvider;
var dbContext = services.GetRequiredService<T>();
dbContext.Database.Migrate();
}
return webHost;
}
You can then chain the calls to migrate the different contexts:
CreateWebHostBuilder(args)
.Build()
.MigrateDatabase<ApiAuthDbContext>()
.MigrateDatabase<MainDbContext>()
.MigrateDatabase<SomeOtherDbContext>()
.Run();
see this question and he answerd himself in the 'Update' section
Add in program.cs at CreateWebHostBuilder method
.UseDefaultServiceProvider(options => {
options.ValidateScopes = false;//to use any scoped
option.validateOnBuild = false;//to use dbContext
})
The full code:
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
Host.CreateWebHostBuilder(args).ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>().UseDefaultServiceProvider(options =>
{
options.ValidateScopes = false;
option.ValidateOnBuild = false;
});
})
}
Related
I want to add this to my program.cs without having to use a startup class.
I've read the Microsoft docs but can't seem to get it to work.
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args).Build();
InitializeDatabase(host);
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
public 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 database.");
}
}
}
Firstly, the documentation doesn't suggest/guide on upgrade from 2.1 to .net6.
you can try refer to follow the steps based on the side menu below 'migration' menu.
https://learn.microsoft.com/en-us/aspnet/core/migration/31-to-60?view=aspnetcore-6.0&tabs=visual-studio
However, there are few breaking changes in between, I am not sure if you want to go for a version by version upgrade. Anyhow, for the initialize classes, you can declare without access modifier, in the program.cs for .net6
and if you want to drop startup.cs , you actually do not need most of classic setup code the old code in startup.cs too.
Eg in program.cs:
var builder = WebApplication.CreateBuilder(args);
....
....
MyInitializeMethod(app);
app.MapRazorPages();
app.Run();
static void MyInitializeMethod(IApplicationBuilder app)
{
.....
....
}
But if you like to keep the startup class, maybe you shall consider for .net5 because we still have startup.cs in .net 5, and good news is .net6 will still understand it.
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 asp.net core 3.1 web application
I have an Interface which is implemented by 3 classes to configure database mapping. I want to call the method automatically during application configuration setup.
Following is my interface and their implementation.
public interface IMongoMapper
{
void Configure();
}
class TenantMap : IMongoMapper
{
public void Configure()
{
BsonClassMap.RegisterClassMap<Entities.Tenant>(cm =>
{
cm.AutoMap();
});
}
}
class CourseMap : IMongoMapper
{
public void Configure()
{
BsonClassMap.RegisterClassMap<Course>(cm =>
{
cm.AutoMap();
});
}
}
How to get all the classes that implement interface and call Configure
method appropriately?
You can use scope.ServiceProvider.GetServices<IMongoMapper>(); to get all classes that implement IMongoMapper interface.
You can use an extension method and call it in Configure method in startup class.
public static void IntializeMapping(this IApplicationBuilder app)
{
using (var scope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var mappers = scope.ServiceProvider.GetServices<IMongoMapper>();
foreach (var map in mappers)
{
map.Configure();
}
}
}
and use it in startup class
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.IntializeMapping();
}
Update
According to Microsoft documentation better way is use this
public static async Task Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
try
{
var mappers = scope.ServiceProvider.GetServices<IMongoMapper>();
foreach (var map in mappers)
{
map.Configure();
}
}
catch (Exception ex)
{
var logger = service.GetService<ILogger<Program>>();
logger.LogError(ex, "An error occurred mapping");
}
}
await host.RunAsync();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
Microsoft Documentation
In older tutorials, you may see similar code in the Configure method in Startup.cs. We recommend that you use the Configure method only to set up the request pipeline. Application startup code belongs in the Main method.
Now the first time you run the application, the database will be created and seeded with test data. Whenever you change your data model, you can delete the database, update your seed method, and start afresh with a new database the same way. In later tutorials, you'll see how to modify the database when the data model changes, without deleting and re-creating it.
Assuming that you have empty constructor for the derived classes as mentioned in your example,you can do the below code you can get the interface by reflection and check which type can be assignable and !c.IsInterface so as it doesn't return the interface itself:
var result = typeof("Any Class").Assembly.GetTypes().Where(c => typeof(IMongoMapper).IsAssignableFrom(c) && !c.IsInterface);
foreach (var i in result)
{
((IMongoMapper)Activator.CreateInstance(i)).Configure();
}
You can do it via Reflection.
I just tried the code below and it works in dotnet core 3.1.
It works with the interface and the implementation class:
in the same assembly
in separate assemblies
var asm = Assembly.GetAssembly(typeof(YourClass));
var mapperTypes = asm.GetTypes().Where(x => x.GetInterface(nameof(IMongoMapper)) != null);
foreach(var mapperType in mapperTypes)
{
var mapper = (IMongoMapper)Activator.CreateInstance(mapperType);
mapper.Configure();
}
You can also plug any parameters you need to create an instance of your objects:
IConfiguration _configuration;
public Startup(IConfiguration configuration)
{
_configuration = configuration;
}
//other code
foreach(var mapperType in mapperTypes)
{
var mapper = (IMongoMapper)Activator.CreateInstance(mapperType, _configuration);
mapper.Configure();
}
There is also this question that has lots of examples (some do not work in dotnet core anymore): Getting all types that implement an interface
In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IPersonals, Personals>();
}
I am writing a new ASP.NET Core Application and I am using the inbuilt DI Framework.
I have a service that I need to run an Initaliaze method as part of the DI - is this possible with the in built Framework DI?
I have done something like this before with Simple Injector using the following code:
container.RegisterInitializer<MyService>(instance =>
{
instance.Initialize("Parameter to Initialize method");
});
I am registering most of my service in the .NET Core as below:
public static void RegisterServiceDependencies(this IServiceCollection services)
{
services.AddTransient<IServiceA, ServiceA>();
services.AddTransient<IServiceB, ServiceB>();
//etc etc
However looking at the services intellisense I don't see anything like RegisterInitializer.
Something like this?
public static void RegisterServiceDependencies(this IServiceCollection services)
{
services.AddTransient(sp =>
{
var instance = sp.GetService<MyClass>(); /* depends on your type */
instance.Initialize("Parameter to Initialize method");
return instance;
});
});
I would like to write integration tests for my Asp .net core application, but I don't want my tests to use real implemetation of some services.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
services.AddTransient<IExternalService,ExternalService>();
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
}
}
public interface IExternalService
{
bool Verify(int id);
}
public class ExternalService : IExternalService
{
public bool Verify(int id)
{
//Implemetation is here.
//I want to fake this implemetation during testing.
}
}
[Fact]
public void TestCase()
{
//Stub out service
var myExtService = new Mock<IExternalService>();
//Setup response by stub
myExtService
.Setup(p => p.Verify(It.IsAny<int>()))
.Returns(false);
var host = new WebHostBuilder()
.UseStartup<Startup>()
.ConfigureServices((services) =>
{
//Setup injection
services.AddTransient<IExternalService>((a) =>
{
return myExtService.Object;
});
});
var server = new TestServer(host);
var client = server.CreateClient();
var response = client.GetAsync("/").Result;
var responseString = response.Content.ReadAsStringAsync().Result;
Assert.Contains("Your service returned: False", responseString);
}
Current injection setup in test case does not work, because ExternalService is injected over the mock.
However the test will pass when I remove services.AddTransient<IExternalService,ExternalService>; from Startup.
Most likely the one in Startup is called later and all the setup in that class is preferred by application.
What options do I have to setup some dependecies in tests, but use everything else as they are declared in Startup?
UPDATE
Application code should be unaware of tests.
Tests should be aware of:
(weakly typed) Endpoint - if this changes then test should fail
IExternalService interface
Tests should not care if application has razor pages or uses mvc or how the application is wired between endpoint and IExternalService.
Tests should not have to setup or configure application (apart from stubbing IExternalService) in order to make it work.
I understand that WebHostBuilder still has to be created, but my point is that configuration should be bare minimum in test case and majority of configuration should still be described on application side.
The only option I know of is to setup WebHostBuilder with UseEnvironment:
var host = new WebHostBuilder()
.UseStartup<Startup>()
.ConfigureServices(services =>
{
//Setup injection
services.AddTransient<IExternalService>(provider =>
{
return myExtService.Object;
});
})
.UseEnvironment("IntegrationTest");
And then add a condition in the ConfigureServices method in the Startup:
public void ConfigureServices(IServiceCollection services)
{
if (Configuration["Environment"] != "IntegrationTest")
{
services.AddTransient<IExternalService, ExternalService>();
}
services.AddMvc();
// ...
}
UPDATE:
I did some more poking around and another option is to not use UseStartup extension method but rather configure the WebHostBuilder directly. You can do this a number of ways but I thought that you could possibly create your own extension method to create a template in your tests:
public static class WebHostBuilderExt
{
public static WebHostBuilder ConfigureServicesTest(this WebHostBuilder #this, Action<IServiceCollection> configureServices)
{
#this.ConfigureServices(services =>
{
configureServices(services);
services.AddMvc();
})
.Configure(builder =>
{
builder.UseMvc();
});
return #this;
}
}
Now your tests can be setup like the following:
var host = new WebHostBuilder()
.ConfigureServicesTest(services =>
{
//Setup injection
services.AddTransient<IInternalService>(provider =>
{
return myExtService.Object;
});
});
var server = new TestServer(host);
This means that you will have to explicitly setup all the implementations that the container will resolve for the specific endpoint you are calling. You can choose to mock or use the the concrete implementations.
The only thing yo need to change is to use ConfigureTestServices instead of ConfigureServices. ConfigureTestServices runs after your Startup, therefor you can override real implementations with mocks/stubs. ConfigureServices was newer intended for that purpose, rather, it configures "host services", which are used during the host-building phase of the application, and copied into the application's DI container.
ConfigureTestServices is available in ASP Core version 2.1 and higher.
var host = new WebHostBuilder()
.UseStartup<Startup>()
.ConfigureTestServices((services) =>
{
//Setup injection
services.AddTransient<IExternalService>((a) =>
{
return myExtService.Object;
});
});
So after hours of research I found a solution.
I could not find a way to solely use built-in dependency injection solution, so I opted to choose 3rd party DI solution - Autofac
Idea is to use WebHostBuilder (declared Main Program) and add necessary options so I can fake some services during testing.
Something that I learned:
If you use Startup as host.UseStartup<Startup> it will be created after host.ConfigureServices()
You cannot inject something to Startup like host.UseStartup<Startup>(new Dependency())
However if you have registred dependency in host.ConfigureServices(services => services.AddTransient<IDependency, MyDependency>()), then it will be resolved before Startup is created and constructor public Startup(IDependency dependency) is used to create Startup.
My application side:
public class Program
{
public static void Main(string[] args)
{
CreateWebHost(args)
.Build()
.Run();
}
public static IWebHostBuilder CreateWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureServices((services) =>
{
//Setup autofac.
services.AddAutofac();
//Register module dependency that Startup requires.
services.AddTransient<Module, MyAutofacModule>();
////It would a bit cleaner to use autofac to setup Startup dependency,
////but dependency did not get resolved for Startup.
//services.AddAutofac((builder) =>
//{
// builder.RegisterModule(new AutofacModule());
//});
})
.UseStartup<Startup>();
}
public class MyAutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
//Register all application dependencies in this module.
builder.Register((c) => new ExternalService()).As<IExternalService>();
}
}
public class Startup
{
private Module applicationDIModule;
public Startup(Module applicationDIModule)
{
this.applicationDIModule = applicationDIModule;
}
public void ConfigureServices(IServiceCollection services)
{
//We can add build-in services such as mvc and authorization,
//but I would not use Add(Transient/Scoped/Singleton) here.
//You should register domain specific dependecies in MyAutofacModule,
//since it will be added after this method call.
services.AddMvc();
}
//This method is called after ConfigureServices (refer to Autofac link).
public void ConfigureContainer(ContainerBuilder builder)
{
//We will register injected module.
builder.RegisterModule(applicationDIModule);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvcWithDefaultRoute();
}
}
Test case:
public class IntegrationTests
{
[Fact]
public void TestCase()
{
//Create and setup moq object as usual.
var service = new Mock<IExternalService>();
service
.Setup(p => p.Verify(It.IsAny<int>()))
.Returns(false);
//Bundle moq objects together for registration.
var attachFakes = new Action<ContainerBuilder>((builder) =>
{
builder.Register(c => service.Object);
});
//Use host builder that application uses.
var host = Program.CreateWebHost(new string[] { })
.UseContentRoot(GetContentRoot()) //Adjust content root since testproject.csproj is not in same folder as application.csproj
.ConfigureServices((services) =>
{
//We re-configure Module registration,
//so Startup is injected with our TestModule.
services.AddTransient<Module>((a) =>
{
return new TestModule(attachFakes);
});
});
//Create server to use our host and continue to test.
var server = new TestServer(host);
var client = server.CreateClient();
var response = client.GetAsync("/").Result;
var responseString = response.Content.ReadAsStringAsync().Result;
Assert.Contains("External service result: False", responseString);
}
private static string GetContentRoot()
{
var current = Directory.GetCurrentDirectory();
var parent = Directory.GetParent(current).Parent.Parent.Parent;
return Path.Combine(parent.FullName, "src");
}
}
public class TestModule : MyAutofacModule
{
private Action<ContainerBuilder> attachFakes;
public TestModule(Action<ContainerBuilder> attachFakes)
{
this.attachFakes = attachFakes;
}
protected override void Load(ContainerBuilder builder)
{
//We register everything in MyAutoFacModule before adding our fakes.
base.Load(builder);
//We add fakes and everything that is re-registered here will be used instead.
attachFakes.Invoke(builder);
}
}
Although it feels a bit brittle, but I still prefer this solution over what #ODawg suggested. His solution would work, but I see it would cause troubles in future when new test cases are added.