What is the proper way of injecting this within a test case? ASP Core 7.0
public IndexModel(DbContext context, IConfiguration configuration, IToastNotification toastNotification)
{
_context = context;
_configuration = configuration;
_toastNotification = toastNotification;
}
[Fact, Priority(1)]
public async Task OnPostAddSectionAsyncTest()
{
var indexModel = new IndexModel(_context, _configuration, ???);
}
Related
I want to read my data from database and control it, and I need to do this in the request pipeline at startup.
So I have to do dependency injection at startup.
This is my (DI)
public Startup(IConfiguration configuration,IAuthHelper authHelper)
{
Configuration = configuration;
AuthHelper = authHelper;
}
public IConfiguration Configuration { get; }
public IAuthHelper AuthHelper;
I encounter this error
An error occurred while starting the application.
InvalidOperationException: Unable to resolve service for type 'Laboratory.Core.Services.Interfaces.IAuthHelper' while attempting to activate 'Laboratory.Startup'.
I used service like this
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var siteDirectory = AuthHelper.GetSiteSetting().MediaPath;
var fileServerOptions = new FileServerOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine
(env.WebRootPath, $#"{siteDirectory}User Picture\")),
RequestPath = "/ServerFiles"
};
app.UseFileServer(fileServerOptions);
}
This is my service
public class AuthHelper : IAuthHelper
{
private readonly LaboratoryContext _context;
private readonly IRazorPartialToStringRenderer _renderer;
private readonly IHttpContextAccessor _httpContext;
private readonly IHttpClientFactory _clientFactory;
public AuthHelper(LaboratoryContext context, IRazorPartialToStringRenderer renderer, IHttpContextAccessor httpContext, IHttpClientFactory clientFactory)
{
_context = context;
_renderer = renderer;
_httpContext = httpContext;
_clientFactory = clientFactory;
}
public TableSiteSetting GetSiteSetting()
{
try
{
return _context.TableSiteSettings.AsNoTracking().FirstOrDefault();
}
catch (SqlException)
{
return new TableSiteSetting() { StaticIp = "ServerConnectionError" };
}
catch (Exception)
{
return new TableSiteSetting() { StaticIp = "ServerError" };
}
}
}
Thanks for any help.
Your service can't be injected in Startup constructor because it has not been added yet to the dependency injection container. But you can inject it to the Configure method.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IAuthHelper authHelper)
{
...
}
I assume you have already registered the service in ConfigureServices
services.AddSingleton<IAuthHelper, AuthHelper>(); // Or scoped/transient depends what your service does.
You can get dbcontext service in program.cs and do what ever you like to your database data.
for example I use this approach to seed my database:
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<ApplicationDbContext>();
await ApplicationDbContextSeed.SeedSampleDataAsync(context)
}
host.Run();
I have created an ASP.NET Core Web API using Entity Framework Core and Simple Injector.
I would like unit tests using xUnit to test my controllers.
I'm not sure where to begin. I believe that I have to mock a container object in my unit tests.
Here is the start up code where the container gets initialized:
public class Startup
{
private Container container;
public IConfiguration Configuration { get; }
private IConfigurationRoot configurationRoot;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
// Build configuration info
configurationRoot = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
InitializeContainer();
services.AddSimpleInjector(container, options =>
{
options.AddAspNetCore()
.AddControllerActivation();
options.AddLogging();
});
}
private void InitializeContainer()
{
container = new SimpleInjector.Container();
container.Options.ResolveUnregisteredConcreteTypes = false;
container.ConfigureServices();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseSimpleInjector(container);
AppSettingsHelper.AppConfig = configurationRoot;
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Here is the code for my services installer:
public static class ServicesInstaller
{
public static void ConfigureServices(this Container container)
{
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
//Assembly.Load will not re-load already loaded Assemblies
container.Register<IFooContext, FooContext>(Lifestyle.Scoped);
container.Register<FooContext>(Lifestyle.Scoped);
}
}
Here is a sample controller:
[Route("[controller]")]
[ApiController]
public class SomeController : ControllerBase
{
private readonly ILogger<SomeController> _logger;
private readonly Container _container;
public SomeController(ILogger<SomeController> p_Logger, Container p_Container)
{
_logger = p_Logger;
_container = p_Container;
}
[HttpGet]
[Route("{p_SomeId}")]
public Some GetOwnerByOwnerId(Guid p_SomeId)
{
Some some;
using (Scope scope = AsyncScopedLifestyle.BeginScope(_container))
{
var dbContext = _container.GetInstance<FooContext>();
some = dbContext.Somes.Where(x => x.SomeId == p_SomeId).FirstOrDefault();
}
return some;
}
}
I'm relatively new to using SimpleInjector.
How would I mock up a container to use for testing?
The controller in the provided example should not be coupled to anything container specific.
Explicitly inject the necessary dependencies into the controller.
[Route("[controller]")]
[ApiController]
public class SomeController : ControllerBase {
private readonly ILogger<SomeController> _logger;
private readonly IFooContext dbContext;
public SomeController(ILogger<SomeController> p_Logger, IFooContext dbContext) {
_logger = p_Logger;
this.dbContext = dbContext;
}
[HttpGet]
[Route("{p_SomeId}")]
public Some GetOwnerByOwnerId(Guid p_SomeId) {
Some some = dbContext.Somes.Where(x => x.SomeId == p_SomeId).FirstOrDefault();
return some;
}
}
Now there is no need to mock the container, which would be seen as an implementation detail code smell.
Mock the dependency abstractions and verify the expected behavior when exercising your unit test(s).
Controllers should also be kept as lean as possible since most other cross-cutting concerns, like making sure the injected context is scoped, are handled by the framework via the configured container at startup.
Postgres database has multiple schemes like company1, company2, ... companyN
Browser sends cookie containing scheme name . Data access operations should occur in this scheme. Web application user can select different scheme. In this case different cookie value is set.
Npgsql EF Core Data provider is used.
ASP NET MVC 5 Core application registers factory in StartUp.cs :
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddScoped<IEevaContextFactory, EevaContextFactory>();
....
Home controller tries to use it:
public class HomeController : EevaController
{
public ActionResult Index()
{
var sm = new SchemeManager();
sm.PerformInsert();
....
This throws exception since factory member is null. How to fix this ?
public interface IEevaContextFactory
{
EevaContext Create();
}
public class EevaContextFactory : IEevaContextFactory
{
private IHttpContextAccessor httpContextAccessor;
private IConfiguration configuration;
public EevaContextFactory(IHttpContextAccessor httpContextAccessor, IConfiguration configuration)
{
this.httpContextAccessor = httpContextAccessor;
this.configuration = configuration;
}
public EevaContext Create()
{
var builder = new DbContextOptionsBuilder<EevaContext>();
var pathbase = httpContextAccessor.HttpContext.Request.PathBase.Value;
var scheme = httpContextAccessor.HttpContext.Request.Cookies["Scheme"];
var csb = new NpgsqlConnectionStringBuilder()
{
Host = pathbase,
SearchPath = scheme
};
builder.UseNpgsql(csb.ConnectionString);
return new EevaContext(builder.Options);
}
}
Scheme data acess methods:
public class SchemeManager
{
readonly IEevaContextFactory factory;
public SchemeManager(IEevaContextFactory factory)
{
this.factory = factory;
}
public SchemeManager()
{
}
public void PerformInsert()
{
using (var context = factory.Create())
{
var commandText = "INSERT into maksetin(maksetin) VALUES (CategoryName)";
context.Database.ExecuteSqlRaw(commandText);
}
}
}
var sm = new SchemeManager()
... will call the no-parameter constructor on SchemeManager so the IEevaContextFactory is not injected. You should inject your factory into your controller and pass it into your SchemeManager.
Remove your no-parameter constructor. It's not needed.
public class HomeController : EevaController
{
private IEevaContextFactor eevaFactory;
public HomeController(IEevaContextFactory factory)
{
eevaFactory = factory;
}
public ActionResult Index()
{
var sm = new SchemeManager(eevaFactory);
sm.PerformInsert();
....
}
}
Your other option is to put the SchemeManager in the DI container and then the DI container will auto-resolve IEevaContextFactory on the constructor and then just inject SchemeManager into your controller.
Either way, remove that no-parameter constructor.
when I try to access the API via Postman,
Send:
localhost:5050/api/Auth/token
Body:
{ "UserName": "jouverc", "Password": "P#ssw0rd!" }
to this method:
[Produces("application/json")]
[Route("api/Auth")]
public class AuthController : Controller
{
#region constructor injection
private readonly IPasswordHasher<User> _hasher;
private readonly UserManager<User> _userManager;
private readonly IConfigurationRoot _config;
private readonly SignInManager<User> _signInManager;
public AuthController(IPasswordHasher<User> hasher, UserManager<User> userManager, SignInManager<User> signInManager, IConfigurationRoot config)
{
_hasher = hasher;
_userManager = userManager;
_signInManager = signInManager;
_config = config;
}
#endregion
#region createToken
[HttpPost("token")]
public async Task<IActionResult> CreateToken([FromBody] CredentialModel model)
{
try
{
var user = await _userManager.FindByNameAsync(model.UserName);
if (user != null)
{
if (_hasher.VerifyHashedPassword(user, user.PasswordHash, model.Password) == PasswordVerificationResult.Success)
{
return Ok(CreateToken(user));
}
}
}
catch (Exception)
{
//log
}
return null;
}
private async Task<JwtPacket> CreateToken(User user)
{
var userClaims = await _userManager.GetClaimsAsync(user);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString())
}.Union(userClaims);
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
var cred = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(issuer: _config["Tokens:Issuer"],
audience: _config["Tokens:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddDays(2),
signingCredentials: cred
);
return new JwtPacket
{
Token = new JwtSecurityTokenHandler().WriteToken(token),
Expiration = token.ValidTo.ToString(),
UserName = user.UserName
};
}
public class JwtPacket
{
public string Token;
public string UserName;
public string Expiration;
}
#endregion
}
I receive a 500 Internal Server Error:
Unable to resolve service for type 'Microsoft.Extensions.Configuration.IConfigurationRoot' while attempting to activate 'WebAPI.Controllers.AuthController
how should i configurate the 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.
In .net core 2.0
IConfigurationRoot is now just IConfiguration.
Explained in this document: Migrating from 1.x to 2.0.
In 2.0 projects, the boilerplate configuration code inherent to 1.x projects runs behind-the-scenes. For example, environment variables and app settings are loaded at startup. The equivalent Startup.cs code is reduced to IConfiguration initialization with the injected instance:
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
Just change IConfigurationRoot to IConfiguration on the constructor for the controller.
But what might be better is to use the IOpions pattern for injecting settings into your controller.
If you are just reading values from the appsettings.json then use the IConfiguration interface instead.
Here's how to implement it in the Startup class
public Startup(IApplicationEnvironment appEnv)
{
var builder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddEnvironmentVariables()
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfiguration>(Configuration);
}
I'm trying to read connection strings from appsettings.json and I'm using:
services.AddSingleton(Configuration);
This line from startup throws null. I'm pretty new to core2.0. Can someone tell what I'm missing?
My startup:
public class Startup
{
public static string ConnectionString { get; private set; }
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton(Configuration);
services.AddSingleton<IConfiguration>(Configuration);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
}
My controller:
public class CreateController : Controller
{
public IConfiguration _ConnectionString;
public CreateController(IConfiguration configuration)
{
_ConnectionString = configuration;
}
public IEnumerable<string> Get()
{
Markets();
}
public string Markets()
{
using(SqlConnection con = new SqlConnection(_ConnectionString.GetSection("Data").GetSection("ConnectionString").Value))
{
return con.Database;
}
}
}
I've noticed your Startup is missing a constructor. In ASP.NET Core 2, when it calls startup (based on a typical WebHost.CreateDefaultBuilder(args).UseStartup<Startup>() inside a vanilla Program.BuildWebHost) will automatically pass the configuration into the Startup constructor:
public class Startup
{
public IConfiguration Configuration { get; }
// This configuration is automatic, if WebHost.CreateDefaultBuilder(args) is used in Program.cs
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
Just adding that will solve your IConfiguration is null issue.
Now, you should not have to add that Configuration into DI because with the defaults it should already be in there and you can add it as-is to your controller constructor. However, there's no harm in doing so.
Lastly, to join the chorus, using IConfiguration directly in you controllers is not a good idea. Rather look into strongly typed configuration settings. There are tutorials out there that can help - here's the first link I found - but the gist is your controller will end up looking sort of like this:
public class CreateController : Controller
{
public ConnectionStrings _ConnectionStrings;
public CreateController(IOptions<ConnectionStrings> connectionStrings)
{
_ConnectionStrings = connectionStrings.Value;
...
You shouldn't be calling services.AddSingleton(Configuration) in ConfigureServices. It is already in the DI container by default.
You simply need to reference it within your Controler:
public class CreateController : Controller
{
public IConfiguration _configuration;
public CreateController(IConfiguration configuration)
{
_configuration = configuration;
}
public IEnumerable<string> Get()
{
Markets();
}
public string Markets()
{
var connectionString = _configuration.GetConnectionString("ConnectionStringName");
using( SqlConnection con = new SqlConnection(connectionString) )
{
return con.Database;
}
}
}
It is null because it hasn't been set. You need to build your configuration first which is best done in the constructor. As others have pointed out it is not recommended to do this.
Example
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
if (env.IsDevelopment())
{
builder.AddUserSecrets<Startup>();
}
Configuration = builder.Build();
}
private IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(Config);
}
}