npm i simplejsparsermatharithmetic
Supported expressions (examples):
2+3 should evaluate to 5
12 * 5–(5 * (32 + 4)) + 3 should evalute to -117
This is a great example of how to write a parser of your own.
npm i simplejsparsermatharithmetic
Supported expressions (examples):
2+3 should evaluate to 5
12 * 5–(5 * (32 + 4)) + 3 should evalute to -117
This is a great example of how to write a parser of your own.
using System.Web;
using System.Web.Mvc;
namespace HelloTagBuilderDemo.Helpers
{
/// <summary>
/// Sample usage of TagBuilder in MVC
/// </summary>
public static class HtmlHelpers
{
/// <summary>
/// Generates an IMG element which is self-closing and uses as src the given path pointing to an image file with a relative path within the web application
/// and with alternate text
/// </summary>
/// <param name="path"></param>
/// <param name="alternateText"></param>
/// <returns></returns>
// ReSharper disable once UnusedParameter.Global
// ReSharper disable once InvalidXmlDocComment
public static IHtmlString Image(this HtmlHelper htmlHelper, string path, string alternateText)
{
var builder = new TagBuilder("img");
// ReSharper disable once StringLiteralTypo
builder.Attributes.Add("src", VirtualPathUtility.ToAbsolute(path));
builder.Attributes.Add("alt", alternateText);
var markupResult = builder.ToString(TagRenderMode.SelfClosing);
return new MvcHtmlString(markupResult);
}
}
}
We make use of the VirtualPathUtility.ToAbsolute method to convert a virtual path to an absolute path.
Then we make use of the html helper inside a MVC view like this:
@{
ViewBag.Title = "Home Page";
}
@using System.Web.Mvc.Html
@using HelloTagBuilderDemo.Helpers
<div class="jumbotron">
<h1>ASP.NET</h1>
<p class="lead">ASP.NET is a free web framework for building great Web sites and Web applications using HTML, CSS and JavaScript.</p>
<p><a href="https://asp.net" class="btn btn-primary btn-lg">Learn more »</a></p>
</div>
<div class="row">
<div class="col-md-4">
<h2>Getting started</h2>
<p>Harold is at it again, making clones of himself to inflict multiple pain.</p>
</div>
<div class="col-md-4">
@Html.Image(@"~/Images/haroldatitagain.jpg", "Harold")
</div>
</div>
And now we have our resulting HTML helper at display, showing also in the screen grab the markup the HTML helper generated for us.
[1] Tagbuilder on MSDN: https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.tagbuilder?view=aspnet-webpages-3.2
use master go declare @tablestoNuke as table(db nvarchar(100)) insert into @tablestoNuke select name from sys.databases where name like '%SomeSimilarDbNameSet%' declare @nukedb as nvarchar(100) declare @nukesql as nvarchar(150) declare nuker cursor for select db from @tablestoNuke open nuker fetch next from nuker into @nukedb while @@FETCH_STATUS = 0 begin set @nukesql = 'drop database ' + @nukedb exec sp_executesql @nukesql fetch next from nuker into @nukedb end close nuker deallocate nuker print 'All done nuking'The T-SQL uses a cursor to loop through the database names fetched from sys.databases view on master db and then uses exec sp_executesql to delete the databases, by dropping them.
///A tip is to add a file called Chutzpah.json and ignoring well known Javascript libraries to only run code coverage on your own code:/// /// /// /// /// /// describe('someController works', function () { beforeEach(module('app')); var $scope; var $rootScope; var $controller; var $bootstrappedData; var $repository; var ctrl; var provide; beforeEach(module(function ($provide) { provide = $provide; })); beforeEach(module(function ($provide) { $provide.factory('repository', function () { return { model: { } }; }); })); beforeEach(inject(function (_$controller_, _$rootScope_) { $controller = _$controller_; $rootScope = _$rootScope_; scope = $rootScope.$new(); })); it('Creates the AngularJs controller someController', function () { provide.factory('bootstrappedData', function () { return { model: { } }; }); ctrl = $controller('someController', { $scope: scope }); expect(ctrl).not.toBe(null); }); it('Method someproperty returns expected', function () { provide.factory('bootstrappedData', function () { return { model: { SomeProperty: '3' } }; }); ctrl = $controller('KontrollskjemaController', { $scope: scope }); var someprop = scope.isSomeConditionalPropertyReturningTrueIfSomePropertyIsThree; expect(someprop).toBe(true); }); });
{
"CodeCoverageExcludes": [ "*\\jquery.js", "*\\angular.js", "*\\bootstrap.min.js", "*\\jquery*", "*\\angular-animate.min.js" ]
//"CodeCoverageIncludes": [ "*\\*Spec.js" ]
}
[Test]
[Category(TestCategories.IntegrationTest)]
public void OutputCircularDependencies()
{
var compositionParts = new List>CompositionPart<();
foreach (var part in _aggregateCatalog.Parts)
{
var importList = new List>string<();
foreach (var import in part.ImportDefinitions)
{
if (import != null)
{
importList.Add(import.ContractName);
}
}
foreach (var export in part.ExportDefinitions)
{
string exportType = null;
if (export.Metadata.ContainsKey("ExportTypeIdentity"))
exportType = export.Metadata["ExportTypeIdentity"].ToString();
string creationPolicy = null;
if (export.Metadata.ContainsKey("System.ComponentModel.Composition.CreationPolicy"))
creationPolicy = export.Metadata["System.ComponentModel.Composition.CreationPolicy"].ToString();
compositionParts.Add(new CompositionPart(part.ToString(), exportType, creationPolicy, importList));
}
}
foreach (var part in compositionParts)
{
//check each import if it imports this part
foreach (var importPart in part.Imports)
{
var matchingPart = compositionParts.FirstOrDefault(c =< c.Identity == importPart);
if (matchingPart != null)
{
if (matchingPart.Imports.Any(i =< i == part.Identity))
{
//Circular reference detected!
Console.WriteLine(@"Component {0} is circular dependent of component {1}", part.Name, matchingPart.Name);
}
}
}
}
}
The code loops through the ComposablePart parts of the AggregateCatalog.Each part has ExportDefinitions (usually one export) and ImportDefinitions (often multiple imports). The first pass will then just gather up information for all the parts of
the AssemblyCatalog and then loop through each part again and loop though its imports in an inner loop. If the imported part imports the part itself, we have a circular dpeendency. The test just outputs the circular dependencies to the console.
I use the class CompositionPart to have a entity to contain some information about each composition part of the AssemblyCatalag instance. It is just a regular AssemblyCatalog in MEF, created like this:
readonly AggregateCatalog _aggregateCatalog = new AggregateCatalog();
CompositionContainer _container;
[SetUp]
public void CommonInitialize()
{
_aggregateCatalog.Catalogs.Add(new AssemblyCatalog(System.Reflection.Assembly.GetAssembly(typeof(SomeFeatureModule))));
_aggregateCatalog.Catalogs.Add(new AssemblyCatalog(System.Reflection.Assembly.GetAssembly(typeof(SomeFeatureServiceAgent))));
_aggregateCatalog.Catalogs.Add(new AssemblyCatalog(System.Reflection.Assembly.GetAssembly(typeof(SomeCommonUtil))));
_aggregateCatalog.Catalogs.Add(new AssemblyCatalog(System.Reflection.Assembly.GetAssembly(typeof(SomeProvider))));
_container = new CompositionContainer(_aggregateCatalog);
_container.ComposeParts();
}
As we can see, the AggregateCatalog just consists of several assemblies in .NET.
I rewrite the software to not use MEF and property based imports (i.e. properties decorated with the import attribute of System.ComponentModel.Composition.ImportAttribute) by looping through the parts of the AggregateCatalog
and looking at the export definitions. The target is Autofac, which is a DI framework that offers dependency injection IMHO better than MEF ImportingConstructor, so analyzing the export defiitions, I can create a list of statements to migrate from MEF to Autofac.
[Test]
[Category(TestCategories.IntegrationTest)]
public void OutputAutofacRegistrations()
{
var sb = new StringBuilder();
foreach (var part in _aggregateCatalog.Parts)
{
string partName = part.ToString();
if (part.ExportDefinitions != null)
{
foreach (var export in part.ExportDefinitions)
{
string exportType = null;
if (export.Metadata.ContainsKey("ExportTypeIdentity"))
exportType = export.Metadata["ExportTypeIdentity"].ToString();
string creationPolicy = null;
if (export.Metadata.ContainsKey("System.ComponentModel.Composition.CreationPolicy"))
creationPolicy = export.Metadata["System.ComponentModel.Composition.CreationPolicy"].ToString();
string autofacExportDefinition = string.Format("builder.RegisterType>{0}<(){1}{2};", partName,
!string.IsNullOrEmpty(exportType) ? ".As>" + exportType + "<()" : string.Empty,
!string.IsNullOrEmpty(creationPolicy) && creationPolicy.ToLower().Contains("nonshared") ?
".InstancePerDependency()" : ".SingleInstance()");
sb.AppendLine(autofacExportDefinition);
Console.WriteLine(autofacExportDefinition);
}
}
}
I also used Ndepend as a supporting tool to look at visualizations of these dependencies. I had trouble detecting ImportingAttribute on properties (properties are methods in C# known as 'property getters' and 'property setters'), but at least I came up with the following CQLinq statements to
look for all types that are decorated with the ExportAttribute (exporting parts) and Ndepend then got nice visualization of something called 'View internal dependency cycles on graph'. As Ndepend or my CQLinq skills lacking could not find the importing attribute decorated on properties (Ndepend
does not fully support attribute detection on methods in an easy way yet - detecting attributes on types is easier), I ended up with the CQLinq below to at least list up the exporting classes and launching the graphical tool to look if the parts (classes) with circular dependencies was a hotspot in the
source base, i.e. a class used by many other classes. The CQLinq below shows how to generate such a graph for revealing class interdepenencies - quite easy using Ndepend.
let exportingTypes = from type in JustMyCode.Types
where type.IsClass && type.HasAttribute("System.ComponentModel.Composition.ExportAttribute")
&& (type.FullNameLike("SomeAcme"))
let f = Types.ChildMethods().Where(m =< m.IsPropertyGetter)
select type
let typesAttributes = Types
from m in exportingTypes.UsingAny(typesAttributes).ChildMethods().Where(x =< x.IsPropertySetter || x.IsPropertyGetter)
let mAttributes = typesAttributes.Where(t =< m.HasAttribute(t)).ToArray()
where mAttributes .Length < 0
select new { m, mAttributes}
So there you have some tips around how to migrate from MEF to Autofac and detect cyclic dependencies in your source code. Ndepend will be a good tool to have as a companion to the refactoring job when you want to migrate. I will suggest to first rewrite your application using importing constructors instead
of property based imports and then fix up the cyclic dependencies. You can use the Lazy initializer for example. It will delay constructing a part of type T specified to fix up such circular dependencies. Or you could of course refactor the code such that part A and part B that imports eachother instead imports some other part C both.. There are different ways to fix it up. Once you have rewritten the software to use importing constructors and there are no circular dependencies, you can switch to Autofac. I showed you in the unit test OutputAutofacRegistrations how to do that. It outputs ContainerBuilder statements to build up a working Autofac IContainer with same kind of mesh of dependencies as in the MEF based application.
ng build --prod --sourceMapIn addition, the vendor chunk is now baked into the main chunk. To create a separate vendor chunk, run this: Angular 8 will put the vendor chunk into the main for optimizing the js code.
ng build --prod --sourceMap --vendor-chunk=trueIn addition, it is recommended to analyze the bundle size with for example Webpack Bundle Analyzer like this:
npm install -g webpack-bundle-analyzer
Then add the following Npm run scripts to package.json:
"buildwithstats": "ng b --sourceMap --prod --stats-json",
"analyze": "webpack-bundle-analyzer --port 9901 dist/stats.json",
Now we have an interactive TreeMap view we can zoom into and see what is taking up space in our bundle!
DECLARE @sql nvarchar(1024)
DECLARE @column_name varchar(200)
DECLARE @table_name varchar(200)
DECLARE @sp_out varchar(2048)
DECLARE @x int
create table #tmp(sp_out varchar(2048), table_name varchar(255), column_name varchar(2048), id uniqueidentifier)
DECLARE tn_cursor CURSOR FOR
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.tables
open tn_cursor
FETCH NEXT FROM tn_cursor
INTO @table_name
WHILE @@FETCH_STATUS = 0
BEGIN
print @table_name
DECLARE cn_cursor CURSOR FOR
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.columns
WHERE TABLE_NAME = @table_name
open cn_cursor
FETCH NEXT FROM cn_cursor
INTO @column_name
WHILE @@FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM cn_cursor
INTO @column_name
--print @column_name
set @sql =
N'insert into #tmp SELECT TOP (1000) ' + @column_name + ', ''' + @table_name + ''', ''' + @column_name + ''', Id' +
N' FROM [dbo].[' + @table_name + N']' +
N' WHERE ' + @column_name + N' like ''%? % ?%'''
--print @sql
exec sp_executesql @sql
END
CLOSE cn_cursor;
DEALLOCATE cn_cursor;
FETCH NEXT FROM tn_cursor
INTO @table_name
END
CLOSE tn_cursor;
DEALLOCATE tn_cursor;
select * from #tmp
drop table #tmp
This query will look for all fields in the DB with the contents with pattern '%? % %?' that matches Angularized values. An Angularized value is generated by AngularJs when autogenerated values are made when the contents of a drop down does not match the HTML.
import { fas } from '@fortawesome/free-solid-svg-icons';
import { far } from '@fortawesome/free-regular-svg-icons';
import { fab } from '@fortawesome/free-brands-svg-icons';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { library } from '@fortawesome/fontawesome-svg-core';
Importing the FontAwesomeModule also into the imports section.
Add the following in to your constructor of app module:
constructor(){
library.add(fab, far, fas);
}
Now you can reference the Font Awesome icons from inside any component like in this markup example:
<div class="crop"
(click)="onClick()"
[style.width.px]="starWidth"
[title]="rating">
<div style="width: 75px">
<span><fa-icon [icon]="['far', 'star']"></fa-icon></span>
<span><fa-icon [icon]="['far', 'star']"></fa-icon></span>
<span><fa-icon [icon]="['far', 'star']"></fa-icon></span>
<span><fa-icon [icon]="['far', 'star']"></fa-icon></span>
<span><fa-icon [icon]="['far', 'star']"></fa-icon></span>
</div>
</div>
Note that if you do not use the solid icons from the 'fas' library, you must specify the type of Font Awesome icon library, such as 'far' for the regular icons.
I ended up with using the following npm packages: "@fortawesome/angular-fontawesome": "^0.3.0", "@fortawesome/fontawesome-svg-core": "^1.2.21", "@fortawesome/free-brands-svg-icons": "^5.10.1", "@fortawesome/free-regular-svg-icons": "^5.10.1", "@fortawesome/free-solid-svg-icons": "^5.10.1",
Note: I did a downgrade to version 0.3.0 of the angular-fontawesome package.
Tested out in Angular 8.
Note that adding the entire library is not suggested in most cases, as this will increase the bundle size Webpack builds up. Instead, add the necessary icons one by one. However, in a development period, it is neat to have all the (Free) icons from Font Awesome readily available until it is production/deploying time! Happy Angular 8 coding!
Example of a shared module you can set up to import in Angular 8 supporting Font Awesome five is below:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { StarComponent } from "src/app/shared/star.component";
import { FormsModule } from "@angular/forms";
import { fas } from '@fortawesome/free-solid-svg-icons';
import { far } from '@fortawesome/free-regular-svg-icons';
import { fab } from '@fortawesome/free-brands-svg-icons';
import { library } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
@NgModule({
declarations: [
StarComponent
],
imports: [
CommonModule,
FontAwesomeModule
],
exports: [
StarComponent,
CommonModule,
FormsModule,
FontAwesomeModule,
]
})
export class SharedModule {
constructor () {
library.add(fab, far, fas);
}
}
import { NgModule, LOCALE_ID } from '@angular/core';
import { registerLocaleData } from '@angular/common';
import localeNor from '@angular/common/locales/nb';
import localeNorExtra from '@angular/common/locales/nb';
Note here that this setup targets Norwegian locale. You can see a list of these locales in the parent folder of this url:
https://github.com/angular/angular/blob/master/packages/common/locales/nb.ts
We also import the 'extra' information for locales - the Norwegian locale here. The parent folder show the available locales in this url:
https://github.com/angular/angular/blob/master/packages/common/locales/extra/nb.ts
Then we set up the Norwegian locale using the method registerLocaleData below the imports of our app module with a call to the method.
registerLocaleData(localeNor, 'no', localeNorExtra);
We also set up the providers for the app module to use the 'no' variable for LOCALE_ID
A complete sample of the app module looks like this then:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, LOCALE_ID } from '@angular/core';
import { registerLocaleData } from '@angular/common';
import localeNor from '@angular/common/locales/nb';
import localeNorExtra from '@angular/common/locales/nb';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { ProductListComponent } from "src/app/products/product-list.component";
import { WelcomeComponent } from "src/app/home/welcome.component";
registerLocaleData(localeNor, 'no', localeNorExtra);
@NgModule({
declarations: [
AppComponent,
ProductListComponent,
],
providers: [
{provide: LOCALE_ID, useValue: 'no'
],
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot([
{ path: '', component: AppComponent }
])
],
bootstrap: [AppComponent]
})
export class AppModule { }
An example of using the locale setup is to be observed in the use of the pipe called currency. Sample markup:
<td>{{ product.productCode | lowercase }}</td>
<td>{{ product.releaseDate }}</td>
<td>{{ product.price | currency:'NOK':'symbol':'1.2-2' }}</td>
<td>
Note the syntax here: Inside the interpolation expression in Angular with {{ My_Expression }} as the notation we pipe with the '|' sign to the currency pipe then we add a ':' sign
and the 'NOK' denotes the Norwegian currency. If we want 'øre' (Norwegian cents) afterwards, we can add that outside the interpolation expression. Note that we also add another ':' sign and then 'symbol':'1.2-2'
This states that we show at least one digit for the integer part and between 2 and 2 decimals (i.e. just 2 decimals please).
This shows how we can set up the standard locale of an Angular app. Supporting multiple aplications should then be a matter of importing additional locales for Angular and have some way of switching between locales.
Probably it is best to be sure to usually addition both the locale and the 'locale extra' when setting this up.
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { catchError } from 'rxjs/operators';
import { UserManager, User, WebStorageStateStore, Log } from 'oidc-client';
import { Constants } from '../constants';
import { Utils } from './utils';
import { AuthContext } from '../model/auth-context';
@Injectable()
export class AuthService {
private _userManager: UserManager;
private _user: User;
authContext: AuthContext;
constructor(private httpClient: HttpClient) {
Log.logger = console;
const config = {
authority: Constants.stsAuthority,
client_id: Constants.clientId,
redirect_uri: `${Constants.clientRoot}assets/oidc-login-redirect.html`,
scope: 'openid projects-api profile',
response_type: 'id_token token',
post_logout_redirect_uri: `${Constants.clientRoot}?postLogout=true`,
userStore: new WebStorageStateStore({ store: window.localStorage }),
automaticSilentRenew: true,
silent_redirect_uri: `${Constants.clientRoot}assets/silent-redirect.html`
};
this._userManager = new UserManager(config);
this._userManager.getUser().then(user => {
if (user && !user.expired) {
this._user = user;
this.loadSecurityContext();
}
});
this._userManager.events.addUserLoaded(args => {
this._userManager.getUser().then(user => {
this._user = user;
this.loadSecurityContext();
});
});
}
login(): Promise<any> {
console.log('Inside the new auth service!');
return this._userManager.signinRedirect();
}
logout(): Promise<any> {
return this._userManager.signoutRedirect();
}
isLoggedIn(): boolean {
return this._user && this._user.access_token && !this._user.expired;
}
getAccessToken(): string {
return this._user ? this._user.access_token : '';
}
signoutRedirectCallback(): Promise<any> {
return this._userManager.signoutRedirectCallback();
}
loadSecurityContext() {
this.httpClient.get<AuthContext>(`${Constants.apiRoot}Account/AuthContext`).subscribe(context => {
this.authContext = context;
}, error => console.error(Utils.formatError(error)));
}
}
The login redirect file oidc-login-redirect.html goes into the assets folder of Angular 8 project.
<script src="https://cdnjs.cloudflare.com/ajax/libs/oidc-client/1.4.1/oidc-client.min.js"></script>
<script>
var config = {
userStore: new Oidc.WebStorageStateStore({ store: window.localStorage })
};
var mgr = new Oidc.UserManager(config);
mgr.signinRedirectCallback().then(() => {
window.history.replaceState({},
window.document.title,
window.location.origin);
window.location = "/";
}, error => {
console.error(error);
});
</script>
The silent redirect that automatically renews the token is in the file silent-redirect.html in the assets folder:
<script src="https://cdnjs.cloudflare.com/ajax/libs/oidc-client/1.5.1/oidc-client.min.js"></script>
<script>
var config = {
userStore: new Oidc.WebStorageStateStore({ store: window.localStorage })
};
new Oidc.UserManager(config).signinSilentCallback()
.catch((err) => {
console.log(err);
});
</script>
Then, the config in the ThinkTecture IdentityServer looks somewhat like this:
using System.Collections.Generic;
using IdentityServer4;
using IdentityServer4.Models;
namespace SecuringAngularApps.STS
{
public class Config
{
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("projects-api", "Projects API")
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "spa-client",
ClientName = "Projects SPA",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RequireConsent = false,
RedirectUris = { "http://localhost:4200/assets/oidc-login-redirect.html","http://localhost:4200/assets/silent-redirect.html" },
PostLogoutRedirectUris = { "http://localhost:4200/?postLogout=true" },
AllowedCorsOrigins = { "http://localhost:4200/" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"projects-api"
},
IdentityTokenLifetime=120,
AccessTokenLifetime=120
},
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "http://localhost:4201/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:4201/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile
},
AllowOfflineAccess = true
}
};
}
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
}
}
}
And in the API backend we wire up the STS like this in Asp.net Core in the Startup.cs:
using System;
using System.Linq;
using SecuringAngularApps.STS.Data;
using SecuringAngularApps.STS.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authentication.Cookies;
using IdentityServer4.Services;
using SecuringAngularApps.STS.Quickstart.Account;
namespace SecuringAngularApps.STS
{
public class Startup
{
public IConfiguration Configuration { get; }
public IHostingEnvironment Environment { get; }
public Startup(IConfiguration configuration, IHostingEnvironment environment)
{
Configuration = configuration;
Environment = environment;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", corsBuilder =>
{
corsBuilder.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin()
.AllowCredentials();
});
});
services.AddTransient<IProfileService, CustomProfileService>();
services.AddMvc();
var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
})
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddAspNetIdentity<ApplicationUser>()
.AddProfileService<CustomProfileService>();
if (Environment.IsDevelopment())
{
builder.AddDeveloperSigningCredential();
}
else
{
throw new Exception("need to configure key material");
}
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseCors("CorsPolicy");
app.UseIdentityServer();
app.UseMvcWithDefaultRoute();
}
}
}