I have an Asp.Net Core 3.1 Razor Pages website in which I have a static Repository class holding the most used items. I search these items a lot and it takes around 4 minutes to initialize them.
public static class Repository
{
public static Dictionary<int, RepositoryPerson> People { get; private set; }
public static async Task InitAsync(INoSqlSettings settings)
{
if (People != null || loading)
{
return;
}
loading = true;
var people = await db.People.ToDictionaryAsync(p => p.Id);
People = ConvertToRepository(people);
//..and lots of other stuff
loading = false;
}
}
At first, I tried to load this with a hosted service but it fails because it takes too long. Now I load it in the Index.cshtml.cs file's OnGetAsync(). But the problem is that every once in awhile, it seems like the .exe file closes because the website initializes again. Is this normal? How can I make the program run only once and share the in-memory repository forever?
Why have you declared the class as static? The common way, as described in the docs, is to use Dependency Injection mechanism of ASP.Net Core.
You can implement it by registering your instance of your class as a Singleton in your Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
//...
var myRepo = new Repository();
repo.InitAsync(someSettings); //Not async now
services.AddSingleton<Repository>(myRepo);
//...
}
Afterwards retrieve the Instance with Dependency Injection, like this:
public class MyPageModel : PageModel
{
private readonly Repository _repo;
public MyPageModel(Repository repo)
{
_repo = repo;
}
See docs for Razor page dependency injection.
Related
I have a Controller where a FeatureManager is injected and the code branches depending on the values read from _featureManager.IsEnabledAsync("NameOfFeature"). I am using SpecFlow and want to create a Given attribute that modifies a particular FeatureManager feature so I can test with the feature enabled and disabled.
At the same time, I would like to control some of the features from the environment (e.g. set them in appsettings.json) because they will change depending on how the tests are executed (locally or in docker in a CI-pipeleine). This is an ASP.NET Core application set up with dependency injection.
The appsettings.json file contains features in section "FeatureManagement". The features that are used by the application projects can be mocked, they don't have to be read from this file, but some features are to be used by the SpecFlow project and have to be taken from this file.
Question
How can I control what the IFeatureManager returns in the tests while also reading some other features from appsettings.json? It is acceptable to mock the FeatureManager, as well as defining a custom one.
Code
Hooks.cs (SpecFlow project)
[Binding]
public sealed class Hooks
{
private static IHost _host;
[BeforeTestRun]
public static void BeforeTestRun()
{
// Program is the app being tested. It contains a Startup class with
// services.AddFeatureManagement(configuration.GetSection("FeatureManagement"));
var hostBuilder = Program.CreateHostBuilder(Array.Empty<string>());
hostBuilder.ConfigureServices((hostBuilderContext, services) => {
// Test services that we wish to inject in the app are added here
});
_host = hostBuilder.Build();
_host.Start();
}
[ScenarioDependencies]
public static IServiceCollection CreateServices()
{
var services = new ServiceCollection();
// Services that are injected in the SpecFlow step definitions are added here
services.AddFeatureManagement(configuration.GetSection("FeatureManagement"));
return services;
}
}
StepDefinitions.cs (SpecFlow project)
[Given(#"the feature '([^']*)' is enabled")]
public void GivenTheFeatureIsEnabled(string feature)
{
// Set up the FeatureManager to return true for the feature
}
// And similar for when the feature is disabled
Startup.cs (Application) Simplified for brevity
public class Startup
{
private readonly IConfiguration _configuration;
public Startup(IConfiguration configuration)
{
_configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddFeatureManagement(_configuration.GetSection("FeatureManagement"));
// Other services added...
}
}
I'm having trouble injecting the dependency I pass into the constructor of my Asegurador class.
When I want to instantiate, _instance = new Asegurador(); I don't have the parameter required by the constructor (IGeneralRepository), how can I solve this problem?
Note that my Asegurador class is a singleton.
private Asegurador(IGeneralRepository generalRepository)
{
_token = GetTokenAsync().Result;
_repository = generalRepository;
}
public static Asegurador Instance
{
get
{
if (_instance == null)
{
_local = System.Environment.GetEnvironmentVariable("SEGUROS_LOCAL") ?? "local";
_instance = new Asegurador();
}
return _instance;
}
}
When using a DI container you can (and should) let it take care of handling the Lifetime of a dependency.
.Net core's dependency injection lets you define 3 different lifetimes for your services (Docs):
Transient: a transient service is recreated each time it is injected
Scoped: a scoped service is created once for each request
Singleton: a singleton is created once in the whole application lifetime.
The best approach to achieve what you are trying to do is the following:
Amend your Asegurador class so that it has a public constructor and get rid of the static Instance property
public class Asegurador {
public Asegurador(IGeneralRepository generalRepository)
{
_token = GetTokenAsync().Result; //I know too few about it but I would try to pass it as a dependency as well
_repository = generalRepository;
}
}
instead of calling Asegurador.Instance inject the dependency in the client class
public class IUseTheAsegurador {
private Asegurador _asegurador;
public IUseTheAsegurador(Asegurador asegurador)
{
_asegurador = asegurador;
}
}
Register all in the DI in your Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSingleton<Asegurador>();
services.AddScoped<IUseAsegurador>(); //This can be Singleton or Transient as well, depending on your needs
...
}
I (a lot of people actually :D) prefer this approach because it leaves the responsability of guaranteeing a single instance to the DI and also because lets you write unit tests for the client class (IUseTheAsegurador in the example) in an easier way.
I'm currently following a .Net Core Angular 8 tutorial in Udemy. I'm able do get/post requests in Postman and I can also see what I've posted in a .db file using sqlite as my database and viewing the data through Db Browser. Everything seems to be working great but is all for nothing if I can't comprehend what's going on in some areas of the application. I would really appreciate it if someone could help me answer a few questions.
My entire project is in GitHub: https://github.com/cjtejada/ASP.NetCoreAngular8/tree/master/DatingApp.API
Problem 1: I have the following the following controller:
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly IAuthRepository _repo;
private readonly IConfiguration _config;
public AuthController(IAuthRepository repo, IConfiguration config)
{
_repo = repo;
_config = config;
}
[HttpPost("register")]
public async Task<IActionResult> Register(UserForRegisterDto userForRegisterDto)
{
// validate request
userForRegisterDto.Username = userForRegisterDto.Username.ToLower();
if (await _repo.UserExists(userForRegisterDto.Username))
return BadRequest("User already exists");
var userToCreate = new User
{
Username = userForRegisterDto.Username
};
var createdUser = await _repo.Register(userToCreate, userForRegisterDto.Password);
return StatusCode(201);
}
}
I know that when the client makes a request to register, the register() method will be called and the Username that gets passed in will set the Username from DTO userForRegisterDto. After this then we call method UserExists() to check if the user exists in our database.
Question 1:
How is _repo aware of the logic in method UserExists() when it is only using the interface IAuthRepository? I know that IAuthRepository and class AuthRepository are somehow linked but I don't see anywhere in the app where Constructor DI is happening. My suspicion is that it has something to do with this line in startup.cs under the ConfigureServices method :
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DataContext>(x => x.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddCors();
services.AddScoped<IAuthRepository, AuthRepository>(); //<---- This Line
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration.GetSection("AppSettings:Token").Value)),
ValidateIssuer = false,
ValidateAudience = false
};
});
}
After these two are "linked up", then the UserExists() method can be accessed through the AuthRepository class:
public class AuthRepository : IAuthRepository
{
private readonly DataContext _context;
public AuthRepository(DataContext context)
{
_context = context;
}
public async Task<User> Login(string username, string password)
{
}
private bool VerifyPasswordHash(string password, byte[] passwordHash, byte[] passwordSalt)
{
}
public async Task<User> Register(User user, string password)
{
byte[] passwordHash, passwordSalt;
CreatePasswordHash(password, out passwordHash, out passwordSalt);
user.PasswordHash = passwordHash;
user.PasswordSalt = passwordSalt;
await _context.Users.AddAsync(user);
await _context.SaveChangesAsync();
return user;
}
private void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt)
{
}
public async Task<bool> UserExists(string username)
{
if (await _context.Users.AnyAsync(x => x.Username == username))
return true;
return false;
}
}
I've been reading about the AddScoped method and what it does but this is not clear to me that this is the case. Any clarification as to how this works would be great.
Problem 2:
This one is more or less the same. If we keep following the path of the request we will hit the register() method in the AuthRepository class.
Question 2:
How does this class have access to the properties of DataContext _context when I also can't spot any instances of constructor DI anywhere?
Here are the rest of my project files if needed:
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.AddDbContext<DataContext>(x => x.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddCors();
services.AddScoped<IAuthRepository, AuthRepository>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration.GetSection("AppSettings:Token").Value)),
ValidateIssuer = false,
ValidateAudience = false
};
});
}
// 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
{
//app.UseHsts();
}
//app.UseHttpsRedirection();
app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
app.UseAuthentication();
app.UseMvc();
}
}
DataContext.cs
public class DataContext : DbContext
{
public DataContext(DbContextOptions<DataContext> options) : base (options){}
public DbSet<Value> Values { get; set; }
public DbSet<User> Users { get; set; }
}
Any clarifications and suggestions are greatly appreciated. Thanks, all.
You are correct. The line services.AddScoped<IAuthRepository, AuthRepository>(); simply instructs the ASP.NET Core service container to substitute an instance of concrete class AuthRepository wherever it sees a reference to IAuthRepository at runtime.
The various Add* methods all do the same thing under the hood regarding registering the mapping of interfaces => classes, the key difference is the scope of the created class, i.e. how long it persists for:
AddScoped classes will be created at the beginning of every request to the server, and destroyed at the end of every request. In other words, every request results in a new instance of that class being created.
AddSingleton classes are created when your ASP.NET Core application starts up, and are destroyed when it shuts down. In other words, only a single instance of that class exists within your application.
AddTransient classes are recreated whenever they are requested. In other words, if a page on your site used the same service transient twice, there would be two instances created. (Contrast this with a scoped service, where only a single instance would be created, as each page is a single request.)
A fuller explanation, including examples: https://stackoverflow.com/a/38139500/70345
In order to fulfill (1) by creating an instance of your class AuthRepository, the service container needs to call that class's constructor. The container inspects your class to find the first public constructor and retrieves any arguments to that constructor, in this case an instance of the DataContext class. The container then searches its internal class mappings for that class and, because you have registered that mapping via services.AddDbContext<DataContext>(...), is able to construct and return the class instance. Thus it's able to pass that instance to AuthRepository, so AuthRepository is constructed successfully.
The AddDbContext method is simply a wrapper around AddScoped, that performs some additional scaffolding to allow Entity Framework DbContexts to work correctly.
For the official explanation, refer to Microsoft's official page on DI and IoC.
Question 1 - You've right this line in Startup.cs provide creating a new object AuthRepository. For this example you must to know that DI container creates an AuthRepository object for you based on the interface and his own implementation and you only need to pass an interface in properly constructor. AddScope() is related with lifetime of created objects. When you register object by method AddScope() then the object will be created for a single request and after the request, the object will be disposed.
Question 2 - Your dbContext is registered in DI container. AddDbContext() is a specific extension method provided to registration of entity framework dbContextes. This line of code registers your dbContext with connection strings got from the appSetting.json file.
services.AddDbContext<DataContext>(x =>
x.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
This DbContext is injected into the constructor of the AuthRepository class and when you use this class DI container created DbContext instance for you.
private readonly DataContext _context;
public AuthRepository(DataContext context)
{
_context = context;
}
I am using Autofac for an Inversion of Control container which is configured like this
public void Configuration(IAppBuilder app) {
configureIoC(app);
configureAuth(app);
}
void configureIoC(IAppBuilder app) {
var b = new ContainerBuilder();
//...
b.Register(c => HttpContext.Current?.User?.Identity
?? new NullIdentity()).InstancePerLifetimeScope();
var container = b.Build();
app.UseAutofacMiddleware(container);
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
I believe the fact that this is Autofac versus some other container is probably irrelevant to what I'm seing. They key line here is the one configuring any dependency on IIdentity to be plucked from HttpContext.Current.
I use it like this so that I can have stub-able access to the current user anywhere I want.
public interface ICurrentUser {
Task<AppUser> Get();
}
public class CurrentUserProvider : ICurrentUser {
public async Task<AppUser> Get() => await users.FindByNameAsync(currentLogin.GetUserId());
public CurrentUserProvider(AppUserManager users, IIdentity currentLogin) {
this.users = users;
this.currentLogin = currentLogin;
}
}
I've used this pattern on past projects and it works fine. I'm currently applying it to an existing project and seeing a very strange thing.
When an Asp.net Mvc controller depends on ICurrentUser everything works fine
When a WebApi controller gets an instance of ICurrentUser the Get operation fails since the instance of IIdentity has not been parsed from the cookie and does not yet have Claims loaded into it (AuthenticationType == null)! Oddly, if I pause the debugger after the WebApi controller is instantiated I can hit HttpContext.Current.User.Identity and see that AuthenticationType == "Cookie" and all claims are there.
What this leads me to conclude is that somehow things are happening in the following order
If this is a web api route, the Web Api controller creates an instance
Asp.Net Identity fills out the current HttpContext Identity
If this is an mvc route, the mvc controller creates an instance
Any actions are executed
This of course makes no sense at all!
So the questions are as follows
Is my inference of the order of things in the pipeline correct?
How can I control it to work properly? Why would this have worked on other projects but be causing problems here? Am I wiring something up in the wrong order?
Please don't suggest that I create an IdentityProvider to late-resolve IIdentity. I understand how I can fix the issue, what I don't understand is why this is happening to begin with and how to control the pipeline order of things.
I modified your code just a little, since I don't have NullIdentity() and your CurrentUserProvider wasn't compiling here.
I'm installed these packages:
Autofac
Autofac.Owin
Autofac.Owin
Autofac.Mvc5
Autofac.Mvc5.Owin
Autofac.WebApi2
Autofac.WebApi2.Owin
My Startup.cs looks like this:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
configureIoC(app);
ConfigureAuth(app);
}
void configureIoC(IAppBuilder app) {
var b = new ContainerBuilder();
//...
b.RegisterType<CurrentUserProvider>().As <ICurrentUser>().InstancePerLifetimeScope();
b.Register(c => HttpContext.Current.User.Identity).InstancePerLifetimeScope();
b.RegisterControllers(typeof(MvcApplication).Assembly);
b.RegisterApiControllers(typeof(MvcApplication).Assembly);
var x = new ApplicationDbContext();
b.Register<ApplicationDbContext>(c => x).InstancePerLifetimeScope();
b.Register<UserStore<ApplicationUser>>(c => new UserStore<ApplicationUser>(x)).AsImplementedInterfaces().InstancePerLifetimeScope();
b.RegisterType<ApplicationUserManager>().InstancePerLifetimeScope();
b.RegisterType<ApplicationSignInManager>().InstancePerLifetimeScope();
var container = b.Build();
app.UseAutofacMiddleware(container);
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
}
Your ICurrentUser stuff:
public interface ICurrentUser
{
Task <ApplicationUser> Get();
}
public class CurrentUserProvider : ICurrentUser
{
private ApplicationUserManager users;
private IIdentity currentLogin;
public async Task<ApplicationUser> Get()
{
return await users.FindByNameAsync(currentLogin.GetUserId());
}
public CurrentUserProvider(ApplicationUserManager users, IIdentity currentLogin)
{
this.users = users;
this.currentLogin = currentLogin;
}
}
Therefore Global.asax:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
My HomeController which is quite simple:
public class HomeController : Controller
{
private ICurrentUser current;
public HomeController(ICurrentUser current)
{
this.current = current;
}
public ActionResult Index()
{
var user = current.Get();
if (user == null)
throw new Exception("user is null");
return View();
}
}
...and finally a simple ApiController, which I access by typing localhost/api/TestApi/5:
public class TestApiController : ApiController
{
private ICurrentUser current;
public TestApiController(ICurrentUser current)
{
this.current = current;
}
public string Get(int id)
{
var user = current.Get();
if (user == null)
throw new Exception("user is null");
return "";
}
}
If I just start the project (without even logging in), I receive a GenericIdentity object to support IIdentity interface, look at this:
And when I step in (F11) in the Get() method, the IIdentity is properly set with that GenericIdentity, because actually there is no one Logged in the application. That's why I think you don't actually need that NullableIdentity.
Try comparing your code with mine and fix yours so we can see if it works, then eventually you'll find out what was the real cause of the problem, rather than just fixing it (we developers like to know why something just got working).
I am playing with ASP.NET 5. I am trying to understand the new configuration model. I have read several articles. However, I am still unsuccessful in loading a configuration setting. My config.json file looks like this:
{
"App" : {
"Info" : {
"Version":"1.0.0",
"ReleaseDate":"03-15-2015"
}
}
}
My Startup.cs file looks like this:
public class Startup
{
public IConfiguration Configuration { get; private set; }
public Startup()
{
Configuration = new Configuration()
.AddJsonFile("config.json");
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseErrorPage();
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index" });
});
app.UseMvc();
app.UseWelcomePage();
}
}
In one of my controllers, I have the following
MyController.cs
using System;
using Microsoft.AspNet.Mvc;
namespace MyOrg.MyApp
{
public class MyController : Controller
{
[HttpGet()]
public ActionResult Index()
{
var version = Configuration.Get("App:Info:Version");
return new HttpStatusCodeResult(200);
}
}
}
When I start the app, I get an error that says:
error CS0103: The name 'Configuration' does not exist in the current context
at Microsoft.Framework.Runtime.Roslyn.RoslynProjectReference.Load(IAssemblyLo
adContext loadContext)
at Microsoft.Framework.Runtime.Loader.ProjectAssemblyLoader.Load(String name,
IAssemblyLoadContext loadContext)
at Microsoft.Framework.Runtime.Loader.ProjectAssemblyLoader.Load(String name)
at kre.host.LoaderContainer.Load(String name)
at kre.hosting.RuntimeBootstrapper.<>c__DisplayClass6_0.<ExecuteAsync>b__4(As
semblyName assemblyName)
at kre.hosting.RuntimeBootstrapper.<>c__DisplayClass6_0.<ExecuteAsync>b__7(Ob
ject sender, ResolveEventArgs a)
at System.AppDomain.OnAssemblyResolveEvent(RuntimeAssembly assembly, String assemblyFullName)
What am I doing wrong? I feel like I've followed the examples I've seen. Yet, I can figure out what I'm doing wrong.
Clearly you want to access Configuration property in your Startup class. And the error method says it doesn't know what Configuration is. So you need a using statement or a fully qualified name. Also, you should avoid naming things the same thing as stuff found in the framework. Your Startup class has a Configuration property, but it also tries to use the Configuration class from Microsoft.Framework.ConfigurationModel. How confusing is that?
Your Configure() method in Startup needs a using statement or fully qualified name so it knows what the Configuration class is.
using Microsoft.Framework.ConfigurationModel; //at the top of your class
Configuration = new Configuration(); //later in the code, we can access without fully qualifying name
or
Configuration = new Microsoft.Framework.ConfigurationModel.Configuration();
In your controller, you may have a similar issue. Replace MyOrg.MyApp.Startup in the example below with whatever the namespace is for your Startup class.
using MyOrg.MyApp.Startup //at the top of your class
Startup.Configuration.Get("App:Info:Version"); //later in the code, we can access without fully qualifying name
or
MyOrg.MyApp.Startup.Startup.Configuration.Get("App:Info:Version");
Better way of doing things
That should be enough to get you started. However, accessing the Startup class to retrieve your configuration isn't ideal, because now your controller's action methods depend on having the Startup class there. That's not very unit testable. Ideally your controllers should be isolated from each other. You should define some sort of interface to hold the configuration info you want, then have the controller depend on that interface. When you're in your site, you'll respond with a class specific to the site's configuration. When unit testing, you can have tight control over the test values by using a different class.
interface ISiteConfig
{
string Version {get; set;}
DateTime ReleaseDate {get; set;}
}
public class SiteConfig : ISiteConfig
{
public string Version {get; set;}
public DateTime ReleaseDate {get; set;}
public SiteConfig()
{
var c = new Configuration()
.AddJsonFile("config.json");
Version = c.Get("App:Info:Version");
ReleaseDate = c.Get("App:Info:ReleaseDate"); //may need to parse here
}
}
public class TestConfig : ISiteConfig
{
public string Version {get; set;}
public DateTime ReleaseDate {get; set;}
public TestConfig(string version, DateTime releaseDate)
{
Version = version;
ReleaseDate = releaseDate;
}
}
Then you'd use Dependency Injection to inject instances of your configuration into the Controller.
public class MyController : Controller
{
private readonly ISiteConfig Config;
public MyController(ISiteConfig config)
{
Config = config;
}
[HttpGet()]
public HttpStatusCodeResult Index()
{
var version = Config.Version;
return new HttpStatusCodeResult(200);
}
}
public class Startup
{
public void Configure(IBuilder app)
{
...
app.UseServices(services =>
{
...
// Set up the dependencies
services.AddTransient<ISiteConfig, SiteConfig>();
...
});
...
}
}
Now you can more easily unit test your action methods, because your unit tests can use the TestConfig class while the site can use the SiteConfig class. And also if you want to change how your configuration is done, you don't have to replace strings in a bunch of different places. You'll have one class where you do so, the rest is strongly typed and easy to change without blowing up your application.
Your unit test might look like this:
//Arrange
var testConfig = new TestConfig("1.0", DateTime.Now );
var controller = new MyController(testConfig );
//Act
var response = controller.Index();
//Assert
Assert.AreEqual(200, response.StatusCode);
As of Beta 5 the accepted answer is no longer correct. There is no longer a Get method on IConfiguration. Also the way of constructing the configuration object is also changed.
The following code works on Beta 7:
// showing using statements here since this is new from Beta 5
using Microsoft.Dnx.Runtime; // renamed was Microsoft.Framework.Runtime
using Microsoft.Framework.Configuration; // renamed was Microsoft.Framework.ConfigurationModel
// other using statements here
// Startup constructor. Note: now takes IApplicationEnvironment
// this is required in order to get base path
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
// Setup configuration sources.
var builder = new ConfigurationBuilder(appEnv.ApplicationBasePath)
.AddJsonFile("config.json")
.AddJsonFile("dbconfig.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
}
// property to hold configuration object created in constructor
public IConfiguration Configuration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
// this will bind to an IOptions<AppSettings> instance
// where AppSettings is a class you define that has a set of
// properties that match your configuration section loaded from the
// json file
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
// here I am loading a connection string from a json file and passing into an
// new EF 6.x DB Context class
services.AddInstance<TalentAgencyContainer>(new TalentAgencyContainer(Configuration["ConnectionStrings:TalentAgencyContainer"]));
// Add MVC services to the services container.
services.AddMvc();
}