This works fine but it’s reparative for configurations and logger and violates DRY principal. A good alternative is this;
Register these assemblies in your startup (startup.cs) to take advantage of dependency feature;
public void ConfigureServices(IServiceCollection services)
{
//Add functionality to inject IOptions<T>
services.AddOptions();
//Add our Config object so it can be injected
services.Configure<ConnectionString>(Configuration.GetSection("ConnectionStrings"));
services.Configure<AppConfig>(Configuration.GetSection("AppConfig"));
}
UPDATE @10/18/2022
For .NET 6, the configuration is;
builder.Services.AddOptions();
builder.Services.Configure<ConnectionString>(builder.Configuration.GetSection("ConnectionStrings"));
builder.Services.Configure<AppConfig>(builder.Configuration.GetSection("AppConfig"));
Create AppConfig POCO class;
public class AppConfig
{
public string ApplicationName { get; set; }
public string Version { get; set; }
}
Create a base class and declare these services as properties.
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ConfigDemo.Models;
public abstract class BaseController<T> : Controller where T : BaseController<T>
{
private ILogger<T> _logger;
private IConfiguration _configuration;
private IOptions<ConnectionString> _appConnectionString;
private IOptions<AppConfig> _appConfiguration;
protected ILogger<T> Logger => _logger ?? (_logger = HttpContext?.RequestServices.GetService<ILogger<T>>());
protected IConfiguration Configuration => _configuration ?? (_configuration = HttpContext?.RequestServices.GetService<IConfiguration>());
protected IOptions<ConnectionString> AppConnectionString => _appConnectionString ?? (_appConnectionString = (HttpContext?.RequestServices.GetService<IOptions<ConnectionString>>()));
protected IOptions<AppConfig> AppConfiguration => _appConfiguration ?? (_appConfiguration = (HttpContext?.RequestServices.GetService<IOptions<AppConfig>>()));
protected string DisplayMessage
{
get { return TempData["DisplayMessage"] == null ? String.Empty : TempData["DisplayMessage"].ToString(); }
set { TempData["DisplayMessage"] = value; }
}
}
We have these values in our appsettings.json file that we would like to use in our application;
Finally create our first HomeController by inheriting from base controller to read config values;
public class HomeController : BaseController<HomeController>
{
//Navigate to URL, for example https://localhost:44320/home/simpleconfig
public string Index()
{
Logger.LogInformation("I am using dependency injection created in the base cotroller");
return "Navigate to URL to show an example";
}
//using configuration
public ViewResult SimpleConfig()
{
var configValue = Configuration.GetSection("AppConfig").GetChildren();
string result = configValue.Select(i => i.Value).Aggregate((i, j) => i + "," + j );
// generate the view
return View("Result",
(object)String.Format("Simple Config value: {0}", result));
}
//using strong type
public ViewResult ConfigValueFromConfig()
{
string configValue = AppConfiguration.Value.ApplicationName;
// generate the view
return View("Result",
(object)String.Format("App Config value: {0}", configValue));
}
Run your application and navigate to action method like this;
Some notes about Angular application structure created using ASP.NET core template in Visual Studio;
ClientApp/src
This folder contains all the TypeScripts files related to Angular app. The whole client-side application source code is here.
The /ClientApp/src/app/assets/ folder keep application images and other asset files. These files will be copied over as-is in the /wwwroot/ folder upon application rebuild.
The /ClientApp/src/app/environment/ folder keep configuration files that target specific environments. For development, it’s environment.ts file and for production it’s environment.prod.ts.
Other root level files are;
1-index.html
The main HTML page that is served when someone visits the site. The CLI automatically adds all JavaScript and CSS files when app is built. Therefore, we don’t’ see any <script> or <link> tags here.
2-karma.conf.js
This is application specific Karma configuration. Karma is a tool used to run Jasmine-based tests.
3-main.ts
The main entry point for application. Compiles the application with the JIT compiler and bootstraps the application’s root modules (AppModule) to run in the browser.
4-polyfills.ts
This provides polyfill scripts for browser support.
5-styles.css
A list of CSS files that supply styles for a project
6-test.ts
The main entry point for the project’s unit tests.
7-tsconfig.*.json
Project specific configuration options for various aspects of app. app.json for application-level, server.json for server level, and specs.json for tests. These options will override tsconfig.json file in the workspace root.
8-tslint.json
The TSLint configuration for the current project.
The ClientApp/src/app/ folder
This contains all of project’s logic and data and includes all Angular modules, services, components, templates and styles.
The basic building blocks of an Angular application is NgModules, which provides compilation context for Components. The role of NgModules is to collect related code into functional sets. Usually the whole Angular app is defined by a set of one or more NgModules.
An Angular app requires a root module, conventionally called AppModule. This tells Angular how to assemble the applications. This enables bootstrapping and starting the initialization life cycle. The remaining modules are known as feature-modules and serve a different purpose. This root module also contains a reference list of all available components.
This is a schema of the standard Angular Initialization Cycle. This can help us better visualize;
The main file bootstraps app.module.ts (AppModule), which then load the app.component.ts file (AppComponent), and later all other components whenever the application needs them.
The /ClientApp/src/app/ folder has the root module of any Angular app. This module is defined within the app.module.ts file. It is listed with a bunch of import statements and some arrays refrencing Components, other modules, providers an so on. We can see all of them here because root module is basically a reference file.
Server-side AppModule for SSR
The /ClientApp/src/app/app.module.ts file contains a BrowserModule.
import { BrowserModule } from '@angular/platform-browser';
This is used to enable the Angular Universal Server-Side Rendering (SSR).
This is a technology that renders Angular applications on the server, provided that the back-end framework support it. .NET Core natively supports such convenient features. This is how the improved Angular initialization schema using SSR;
AppComponent
Angular app is basically a tree of Components working together. Components define views and use services to leverage. Service providers can be injected into Components as dependencies which makes the app code modular, reusable, and efficient. All of these components are conventially located in AppComponent.ts file. This file is located in /app/ root folder according to Angular folder structure conventions. All other components can be placed in sub-folder.
Usually there are three AppComponent files;
1-app.component.ts
This defines the component logic.
2-app.component.html
This defines the HTML templates associated with the AppComponent. Angular component can have an optional HTML file containing its UI layout. This is a good practice unless the component comes with a very minimal UI.
All object properties in JavaScript are public. There is no explicit way to indicate that a property shouldn’t be accessed from outside. Sometime we don’t want the data to be public. For example, when an object uses a value to determine some sort of state, modify that data without the objects’ knowledge throws the state management process.
One way to avoid this is by using naming conventions. For example, it’s quite common to prefix properties with an underscore (such as _name) when they are not intended to be public. However, there are other ways of hiding data that don’t rely on conventions.
The Module Pattern
There are many different ways to create and compose objects in JavaScript. While JavaScript does not include the formal concept of private properties, we can create data or functions that are accessible only from within an object. For singleton objects, we can use the module pattern to hide data from the outside world. we can use an immediately invoked function expression (IIFE) to define local variables and functions that are accessible only by the newly created object. Privileged methods are methods on the object that have access to private data. We can also create constructors that have private data by either defining variables in the constructor function or by using an IIFE to create private data that is shared among all instances.
var yourObject = (function() {
// private data variables
return {
// public methods and properties
};
}());
Here we are defining custom type that are keeping their private properties. This is similar to module pattern inside the constructor to create instance-specific private data.
function Student(name) {
this.name = name;
}
Student.prototype = (function () {
//private variables inside this closure scope
var age = 25;
var myGrades = [93, 95, 88, 0, 55, 91];
function getAge() {
return age;
}
function growOlder() {
age++;
}
function getAverage() {
var total = 0;
for (var i = 0; i < myGrades.length; i++) {
total += myGrades[i];
}
return avg = (total / myGrades.length);
}
function getGradeStatus() {
var failingGrades = myGrades.filter(function (item) {
return item < 70;
});
return 'You failed ' + failingGrades.length + ' times.';
}
//revaling
return {
getAverage: getAverage,
getGradeStatus: getGradeStatus,
getAge: getAge,
growOlder: growOlder
};
})();
var student1 = new Student("shahzad");
var student2 = new Student("ali");
student1.name //returns "shahzad"
student1.getAge(); //returns 25
student1.growOlder(); //increment age
student1.getAge(); //returns 26
student1.getAverage(); //returns 70.33333333333333
student1.getGradeStatus(); //returns "You failed 2 times."
student2.name //returns "ali"
student2.getAge(); //returns 26
student2.getAverage(); //returns 70.33333333333333
student2.getGradeStatus() //returns "You failed 2 times."
If we want private data to be shared across all instances, we can use a hybrid approach that looks like the module pattern but uses a constructor;
var Student = (function() {
//everyone shares the same age
var age = 20;
function IStudent(name){
this.name = name;
}
IStudent.prototype.getAge = function() {
return age;
};
IStudent.prototype.growOlder = function() {
age++;
};
return IStudent;
}());
var student1 = new Student("shahzad");
var student2 = new Student("ali");
console.log(student1.name); //returns shahzad
console.log(student1.getAge()); //returns 20
console.log(student2.name); //returns ali
console.log(student2.getAge()); //returns 20
student1.growOlder(); //increment age
console.log(student1.getAge()); //returns 21
console.log(student2.getAge()); //returns 21
The IStudent constructor is defined inside an IIFE. The variable age is defined outside the constructor but is used for two prototype methods. The IStudent constructor is then returned and becomes the Person constructor in the global scope. All instances of Person end up sharing the age variable, so chaing the value with one instance automatically affects the other instance.
The Mixins Pattern
Mixins are a powerful way to add functionality to objects while avoiding inheritance. A mixin copies properties from one object to another so that the receiving object gains functionality without inheriting from the supplying object. Unlike inheritance, mixins do not allow you to identify where the capabilities came from after the object is created. For this reason, mixins are best used with data properties or small pieces of functionality. Inheritance is still preferable when you want to obtain more functionality and know where that functionality came from.
Scope-safe constructors
Scope-safe constructors are constructors that we can call with or without new to create a new object instance. This pattern takes advantage of the fact that this is an instance of the custom type as soon as the constructor begins to execute, which lets us alter the constructor’s behavior depending on whether or not you used the new operator.
The name is created as a global variable because the Person constructor is called without new. Since this code is running in nonrestrict mode so leaving out new would not throw an error. The fact that constructor begins with capital letter should be preceded by new. What if we want to allow this use case and have the function work without new. We can implement a pattern like this;
function Person(name) {
if (this instanceof Person) {
//called with "new"
} else {
//called without new
}
}
Let’s re-write our constructor logic;
function Person(name) {
if (this instanceof Person) {
this.name = name;
} else {
return new Person(name);
}
}
var person1 = Person("shahzad");
console.log(person1 instanceof Person); //returns true
console.log(typeof person1); //returns object
console.log(person1.name); //returns shahzad
The constructor is called recursively via new if new keyword is not used to create a proper instance of the object. This way following are equivalent;
var person1 = new Person(“shahzad”);
var person2 = Person(“shahzad”);
console.log(person1 instanceof Person); //returns true
console.log(person2 instanceof Person); //returns true
Creating new objects without using the new operator is becoming more common. JavaScript itself has several reference types with scope-sfe constructors, such as Object, Array, RegExp, and Error.