ASP.NET Core WebApplicationFactory Server.Host is not accessible - asp.net-core

I am using Microsoft.AspNetCore.Testing.WebApplicationFactory<Startup> in my integration tests. I'm trying to access _fixture.Server.Host property, but it's throwing an exception
"The TestServer constructor was not called with a IWebHostBuilder so IWebHost is not available".
Could anyone tell, why is it so?
Here is my code:
public class OneTest:IClassFixture<WebApplicationFactory<Startup>>{
private readonly WebApplicationFactory<Startup> _fixture;
public OneTest(WebApplicationFactory<Startup> fixture){
_fixture=fixture;
}
[Fact]
public async Task TestCanBeAuthenticated(){
var host=_fixture.Server.Host; // Exception is being thrown
// ... code ...
}
}

If you check the TestServer class,you could found serval different constructors:
The error just indicates the server created by WebApplicationFactory doesn't contain the instance of IWebHostBuilder in it's constructor
So,what's the purpose of accessing the host of TestServer in your test?

Related

ObjectDisposedException on DbContext

I am getting the following error when using DbContext injected into a service from another service:
ObjectDisposedException: Cannot access a disposed object.
The lifetime of my DbContext is Scoped and the lifetimes of my services is Transient, but changing these other than to Singleton (which we don't want) does not solve the issue.
Interestingly, the error occurs seemingly at random. Sometimes there are no errors and everything runs fine.
In relation to this error, I am (also randomly) getting a InvalidOperationException right after startup, when my Angular app starts firing requests to the backend.
"An attempt was made to use the context while it is being configured. A DbContext instance cannot be used inside OnConfiguring since it is still being configured at this point."
My code:
public class MyService1 {
private static IMyService2 _myService2;
public MyService1(IMyService2 myService2){
_myService2 = myService2;
}
public async Task DoSomethingWithMyService2() {
await _myService2.DoSomething(new MyEntity());
}
}
public class MyService2 : IMyService2 {
private MyDbContext _dbContext;
public MyService2(MyDbContext myDbContext) {
_dbContext = myDbContext;
}
public async Task DoSomething(MyEntity myEntity) {
await _dbContext.MySet.AddAsync(myEntity); // <-- ObjectDisposedException
await _dbContext.SaveChangesAsync();
}
}
To answer my own question: the culprit is that MyService2 is stored in a static field after injection into MyService1.
Because the lifetime of DbContext is Scoped, it will be disposed after an initial request to the service. However, the service will live on during the lifetime of the entire app, with a reference to its disposed DbContext.
(I am not entirely sure about the latter (regarding lifetime of the app), because MyService1 itself is also Transient. Perhaps someone else can explain how that works.)

How to configure hangfire with unity?

I have ASP.NET Web API application. The application is using Unity as IoC container. The application is also using Hangfire and I am trying to configure Hangfire to use Unity.
So based on documentation i am using Hangfire.Unity which registers the unity container as a current job activator in Hangfire.
I have a class which has dependency on IBackgroundJobClient
public class MyService
{
private MyDBContext _dbContext = null;
private IBackgroundJobClient _backgroundJobClient = null;
public MyService(MyDbContext dbContext, IBackgroundJobClient backgroundJobClient)
{
_dbContext = dbContext;
_backgroundJobClient = backgroundJobClient;
}
}
However even after configuring Hangfire.Unity it could not create & pass instance of BackgroundJobClient
So i had to register every dependency of BackgroundJobClient with unity container.
Unity Registration
public class UnityConfig
{
private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
public static IUnityContainer GetConfiguredContainer()
{
return container.Value;
}
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<MyDbContext>(new HierarchicalLifetimeManager(), new InjectionFactory(x => new MyDbContext()));
// register hangfire dependencies
container.RegisterType<IBackgroundJobClient, BackgroundJobClient>();
container.RegisterType<JobStorage, SqlServerStorage>(new InjectionConstructor("HangfireConnectionString"));
container.RegisterType<IJobFilterProvider, JobFilterAttributeFilterProvider>(new InjectionConstructor(true));
container.RegisterType<IBackgroundJobFactory, BackgroundJobFactory>();
container.RegisterType<IRecurringJobManager, RecurringJobManager>();
container.RegisterType<IBackgroundJobStateChanger, BackgroundJobStateChanger>();
}
}
OWIN Startup
public class Startup
{
public void Configuration(IAppBuilder app)
{
var container = UnityConfig.GetConfiguredContainer();
Hangfire.GlobalConfiguration.Configuration.UseSqlServerStorage("HangfireConnectionString");
Hangfire.GlobalConfiguration.Configuration.UseUnityActivator(container);
// if i dont call UseSqlServerStorage() above then UseHangfireDashboard() method fails with exception
//JobStorage.Current property value has not been initialized. You must set it before using Hangfire Client or Server API.
app.UseHangfireDashboard();
app.UseHangfireServer();
RecurringJob.AddOrUpdate<MyService>(x => x.Prepare(), Cron.MinuteInterval(10));
}
}
Code is working with such configuration. However i have questions:
Is this the correct way of configuring Unity with Hangfire?
Why do i need to invoke Hangfire.GlobalConfiguration.Configuration.UseSqlServerStorage("HangfireConnectionString") in OWIN startup even though SqlServerStorage is already registered with Unity container as JobStorage?
If i dont invoke UseSqlServerStorage() method in OWIN startup then i get exception on app.UseHangfireDashboard() method.
JobStorage.Current property value has not been initialized. You must
set it before using Hangfire Client or Server API.
I believe there is a problem where you want to kick off Hangfire outside of the Unity ecosystem, but also want Unity to understand how to instantiate the appropriate Hangfire interfaces with the associated implementations. Since Hangfire itself doesn't use Unity, you will need to start up Hangfire with the appropriate configuration, such as the SQL Server connection string, and then use that configuration to inform Unity how to instantiate the Hangfire interfaces. I was able to solve this problem by setting the global Hangfire configuration for SQL and then use that same Hangfire static instance to set up Unity.
Here's example code where first you will see how I start the hangfire dashboard and server with a connection string:
public void Configuration(IAppBuilder app)
{
var configuration = new Configuration(); // whatever this is for you
GlobalConfiguration.Configuration.UseSqlServerStorage(
configuration.GetConnectionString());
GlobalConfiguration.Configuration.UseActivator(
new HangfireContainerActivator(UnityConfig.GetConfiguredContainer()));
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] {new HangfireAuthorizationFilter()}
});
app.UseHangfireServer();
}
As the second example, here's the configuration of Unity for Hangfire; notice how this code is using the static JobStorage Hangfire object to instantiate any requests for JobStorage.
public static void RegisterHangfire(IUnityContainer container)
{
container.RegisterType<JobStorage>(new InjectionFactory(c => JobStorage.Current));
container.RegisterType<IJobFilterProvider, JobFilterAttributeFilterProvider>(new InjectionConstructor(true));
container.RegisterType<IBackgroundJobFactory, BackgroundJobFactory>();
container.RegisterType<IRecurringJobManager, RecurringJobManager>();
container.RegisterType<IBackgroundJobClient, BackgroundJobClient>();
container.RegisterType<IBackgroundJobStateChanger, BackgroundJobStateChanger>();
}
I believe this approach gives you the best of both worlds where you only set up your SQL Server connection once and you do it early to kick off Hangfire, but then you use that instance to tell Unity how to behave.

Resolving dependencies in Integration test in ASP.NET Core

I have ASP.NET Core API. I have already gone through documentation here that shows how to do integration testing in asp.net core. The example sets up a test server and then invoke controller method.
However I want to test a particular class method directly (not a controller method)? For example:
public class MyService : IMyService
{
private readonly DbContext _dbContext;
public MyService(DbContext dbContext)
{
_dbContext = dbContext;
}
public void DoSomething()
{
//do something here
}
}
When the test starts I want startup.cs to be called so all the dependencies will get register. (like dbcontext) but I am not sure in integration test how do I resolve IMyService?
Note: The reason I want to test DoSomething() method directly because this method will not get invoked by any controller. I am using Hangfire inside this API for background processing. The Hangfire's background processing job will call DoSomething() method. So for integration test I want to avoid using Hangfire and just directly call DoSomething() method
You already have a TestServer when you run integration tests, from here you can easily access the application wide container. You can't access the RequestServices for obvious reason (it's only available in HttpContext, which is created once per request).
var testServer = new TestServer(new WebHostBuilder()
.UseStartup<Startup>()
.UseEnvironment("DevelopmentOrTestingOrWhateverElse"));
var myService = testServer.Host.Services.GetRequiredService<IMyService>();

Windsor Dependencies injection in mvc 4 rc into parametereized apicontroller

So, i used Mark Seemann's example to do dependency injection with Windsor in MVC 4 RC Web Api, but i get an exception saying that it can't resolve the dependencies to my ApiController
public class StatisticsController : ApiController
{
private readonly ILogger _logger;
private readonly IClickMessageProducer _producer;
public StatisticsController(ILogger logger,
IClickMessageProducer clickMsgProducer)
{
_logger = logger;
_producer = clickMsgProducer;
}
public string Get(string msg, string con) {...}
}
My Global.asax looks like this:
protected void Application_Start()
{
// different configs removed for brevity
BootstrapContainer();
}
private static IWindsorContainer _container;
private static void BootstrapContainer()
{
_container = new WindsorContainer()
.Install(FromAssembly.This(), new ProducerInstaller())
.Install(FromAssembly.This(), new WebWindsorInstaller());
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator),
new WindsorHttpControllerActivator(_container));
}
The Installers gives Windsor the references needed to IClickMessageProducer. I have it working with IController in a genuine MVC 4 project so i'm confident that part is working.
To specify, this is the error message i get, when trying to access a method in StatisticsController with a GET call to the API:
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Can't create component 'APIMVC.Controllers.StatisticsController'
as it has dependencies to be satisfied.
'APIMVC.Controllers.StatisticsController' is waiting for the following
dependencies: - Service 'Castle.Core.Logging.ILogger' which was not registered.
</ExceptionMessage>
<ExceptionType>Castle.MicroKernel.Handlers.HandlerException</ExceptionType>
<StackTrace>...</StackTrace>
</Error>
The call being something like this:
"http://localhost:60000/api/statistics?msg=apitest&con=apimvc"
If anyone has a working example or just a comment to the problem about my Windsor implementation i'll be happy to see it.
Your ILogger implementation isn't registered with Windsor. Remove the ILogger parameter from StatisticsController and try again. If it works, you're going to need to register an ILogger implementation.
_container = new WindsorContainer().Install(FromAssembly.This(), new ProducerInstaller()).Install(FromAssembly.This(), new WebWindsorInstaller());
this was the part at fault. As you can see I call Install(FromAssembly.This()) twice witch caused the LoggerInstaller to try to add a LoggingFacilitytwice causing an error.
The new implementation would look like this:
_container = new WindsorContainer().Install(FromAssembly.This(), new ProducerInstaller(), new WebWindsorInstaller());

When using Ninject in my MVC3 I get a Ninject.ActivationException: Error activating IRepository{Ranking} installed via Nuget

I installed Ninject via nuget, and I registered my binds in NinjectMVC3's RegisterServices method (created by nuget). Follow my code:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IUnitOfWork>().To<UnitOfWork>();
kernel.Bind<IRepository<Action>>().To<ActionRepository>();
kernel.Bind<IRepository<ActionType>>().To<ActionTypeRepository>();
kernel.Bind<IRepository<City>>().To<CityRepository>();
kernel.Bind<IRepository<Country>>().To<CountryRepository>();
kernel.Bind<IRepository<Goods>>().To<GoodsRepository>();
kernel.Bind<IRepository<Media>>().To<MediaRepository>();
kernel.Bind<IRepository<MediaType>>().To<MediaTypeRepository>();
kernel.Bind<IRepository<Ranking>>().To<RankingRepository>();
kernel.Bind<IRepository<Role>>().To<RoleRepository>();
kernel.Bind<IRepository<Sponsor>>().To<SponsorRepository>();
kernel.Bind<IRepository<State>>().To<StateRepository>();
kernel.Bind<IRepository<UserAccountInfo>>().To<UserAccountInfoRepository>();
kernel.Bind<IRepository<UserAction>>().To<UserActionRepository>();
kernel.Bind<IRepository<UserDeservesGoods>>().To<UserDeservesGoodsRepository>();
kernel.Bind<IRepository<UserGoods>>().To<UserGoodsRepository>();
kernel.Bind<IRepository<User>>().To<UserRepository>();
kernel.Bind<IUserService>().To<UserService>();
kernel.Bind<IAccountService>().To<AccountService>();
DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
}
here is my HomeController:
readonly IRepository<Ranking> repoRanking;
public HomeController(IRepository<Ranking> repoRanking)
{
this.repoRanking = repoRanking;
}
When I execute HomeController, I get follow exception:
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: Ninject.ActivationException: Error activating IRepository{Ranking}
No matching bindings are available, and the type is not self-bindable.
Activation path:
2) Injection of dependency IRepository{Ranking} into parameter repoRanking of constructor of type HomeController
1) Request for HomeController
Suggestions:
1) Ensure that you have defined a binding for IRepository{Ranking}.
2) If the binding was defined in a module, ensure that the module has been loaded into the kernel.
3) Ensure you have not accidentally created more than one kernel.
4) If you are using constructor arguments, ensure that the parameter name matches the constructors parameter name.
5) If you are using automatic module loading, ensure the search path and filters are correct.
What I doing wrong?
Have you created a default module where you set your bindings instead of in the register services method?
public class DefaultModule : NinjectModule {
public override void Load() {
Bind<IProductService>().To<ProductService>().InTransientScope();
}
}
and then in your Global.asax file do this:
protected override IKernel CreateKernel() {
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
return kernel;
}