introducing asp.net core 2.0
TRANSCRIPT
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Introducing ASP.NET Core 2.0
@ardalis
Ardalis.com
Steve Smith
(in about an hour)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Weekly Dev TipsPodcast and Newsletter
• Ardalis.com/tips
• WeeklyDevTips.com
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Being updated for 2.0 – should be done this month
https://www.microsoft.com/net/learn/architecture
See also AspNetCoreQuickstart.com online course
Contact me for team training and/or mentoring
Free eBook and Sample
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Agenda – My ASP.NET Core Top 10 List
1. Why ASP.NET Core?
2. Using the CLI
3. Startup
4. Managing Dependencies
5. Managing Middleware
6. ASP.NET Core MVC and Web APIs
7. Tag Helpers
8. Razor Pages
9. Testing ASP.NET Core
10. What’s New in 2.0
Full online course w/demos at:AspNetCoreQuickstart.com
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
ASP.NET Core – What is it?
A new open-source and cross-platform framework for building modern cloud-based Web applications
using .NET
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Modular – NuGet Package-based
Cloud and Container Optimized – Smaller memory footprint
Open-Source with Contributions – even the docs
Fast! - 8x Faster than Node; 3x Faster than Go
Cross-Platform – Windows, Mac, Linux
Choice of Tools and Editors – Any Text Editor plus CLI, or Visual Studio
Goals
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Install SDKhttps://www.microsoft.com/net/download/core
Optional: Install Visual Studio 2017Install VS2017 .NET Core 2.0 Tools
Confirm Version:
Getting the Bits
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Visual Studio .NET Core Templates
Console App
Class Library
Unit Test
xUnit Test
ASP.NET Core
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
ASP.NET Core Project Templates
Empty
Web API
Web App
Web App (MVC)
Angular
React
React and Redux
Docker
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
New Web Projects with dotnet
dotnet new webEmpty Hello World Project
dotnet new mvcMVC Template
--auth Individual adds identity support
--use-local-db true uses localdb instead of SQLite
dotnet new webapi
dotnet new razorpages
dotnet new angular | react | reactredux
Use --help to view options in CLI
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
dotnet CLI Templates
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
dotnet new razor
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Razor Pages
Convention-Based Routes
No Controllers; No Actions
PageModel as CodebehindHandlers replace Actions
PageModel as ViewModel
Leverages existing MVC featuresModel Binding / Validation
Routing
Filters
Maintains Testability and Separation of Concerns
More in my Razor Pages article in September 2017 MSDN Magazine
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Section
Program.cs
ASP.NET Core Startup
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
var host = new WebHostBuilder().UseKestrel().UseContentRoot(Directory.GetCurrentDirectory()).UseIISIntegration().UseStartup<Startup>().Build();
host.Run();
WebHostBuilder (1.x)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
public class Program{
public static void Main(string[] args){
BuildWebHost(args).Run();}
public static IWebHost BuildWebHost(string[] args) =>WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();}
WebHostBuilder (2.x)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
using Microsoft.AspNetCore.Hosting;using Microsoft.AspNetCore.Builder;using Microsoft.AspNetCore.Http;
class Program{
static void Main(string[] args){
new WebHostBuilder().UseKestrel().Configure(a => a.Run(c => c.Response.WriteAsync("Hi!"))).Build().Run();
}}
A Minimal ASP.NET Core App
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
using Microsoft.AspNetCore.Hosting;using Microsoft.AspNetCore.Http;using Microsoft.AspNetCore.Builder;using Microsoft.AspNetCore;
public class Program{
public static void Main(){
WebHost.CreateDefaultBuilder().Configure(app =>
app.Run(async (context) =>{
await context.Response.WriteAsync("Hello World!");}))
.Build()
.Run();}
}
A Minimal ASP.NET Core App (2.0)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Section
Startup.cs
ASP.NET Core Startup
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Startup
Constructor (optional)
ConfigureServices
Configure
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Constructor (optional)
Inject Dependencies (needed by Startup)
Set up configuration (done by default in 2.0)
Set up Startup logging (done by default in 2.0)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
public Startup(IHostingEnvironment env){
var builder = new ConfigurationBuilder().SetBasePath(env.ContentRootPath).AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsDevelopment()){
builder.AddUserSecrets<Startup>();}
builder.AddEnvironmentVariables();Configuration = builder.Build();
}
Startup Constructor (1.x)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
public Startup(IConfiguration configuration){
Configuration = configuration;}
Startup Constructor (2.0)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Startup ConfigureServices
Add framework services needed by app to services collection
Add application services to services collection
Optional
Configure Options used by app components
Configure third-party containerStructureMap, Unity, Autofac, Ninject, Castle Windsor, etc.
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
public void ConfigureServices(IServiceCollection services){
services.AddDbContext<ApplicationDbContext>(options =>options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>().AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();
services.AddMvc();
services.AddSingleton<IEmailSender, EmailSender>();}
Startup ConfigureServices (2.0)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
public IServiceProvider ConfigureServices(IServiceCollection services){
services.AddDbContext<AppDbContext>(options =>options.UseInMemoryDatabase(Guid.NewGuid().ToString()));
services.AddMvc();
var container = new Container();container.Configure(config =>{
config.Scan(_ =>{
_.AssemblyContainingType(typeof(Startup)); // Web_.WithDefaultConventions();
});
config.For(typeof(IRepository<>)).Add(typeof(EfRepository<>));
config.Populate(services);});return container.GetInstance<IServiceProvider>();
}
Custom DI Container (Structuremap)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Startup.Configure
Configure the application request pipeline
Configure and arrange middleware
Optional
Configure logging for the application (done here by convention in 1.x; not required here in 2.x)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
public void Configure(IApplicationBuilder app, IHostingEnvironment env){
if (env.IsDevelopment()){
app.UseDeveloperExceptionPage();app.UseBrowserLink();app.UseDatabaseErrorPage();
}else{
app.UseExceptionHandler("/Error");}
app.UseStaticFiles();app.UseAuthentication();app.UseMvc(routes =>{
routes.MapRoute(name: "default",template: "{controller}/{action=Index}/{id?}");
});}
Startup Configure (2.0)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Section
Built-in and Custom Middleware
Middleware
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
“Middleware are software components that are assembled into an application pipeline to handle requests and responses”
Each component in the pipeline is a request delegate.
Each delegate can invoke the next component in the chain, or short-circuit, returning back up the call chain
What is Middleware?
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
public void Configure(IApplicationBuilder app){
// app.Run terminates the pipeline without calling any later middleware
app.Run(async context =>
{
await context.Response.WriteAsync("Hello, World!");
});}
Creating Middleware in Startup: Run
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
public void Configure(IApplicationBuilder app){
// app.Use can execute code before and after other middleware
app.Use(async (context, next) =>
{
// do some work
// call the next middleware
await next.Invoke();
// do some additional work
});}
Creating Middleware in Startup: Use
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
public void Configure(IApplicationBuilder app){
// app.Map can map a path to a function
app.Map(“hello”, HandleHello);
app.Run(async context =>
{
await context.Response.WriteAsync(“Invalid path!");
});}
public void HandleHello(IApplicationBuilder app){
app.Run(async context =>
{
await context.Response.WriteAsync(“Hello!");
});}
Creating Middleware in Startup: Map
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
public class MyMiddleware{private readonly RequestDelegate _next;public MyMiddleware(RequestDelegate next){_next = next;
}public Task Invoke(HttpContext httpContext){return _next(httpContext);
}}
// Extension method used to add the middleware to the HTTP request pipeline.public static class MyMiddlewareExtensions{public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder){return builder.UseMiddleware<MyMiddleware>();
}}
Moving Middleware to its own classes
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Built-in Middleware : Diagnostics
UseDeveloperExceptionPage
UseDatabaseErrorPage – helps configure database
UseBrowserLink
UseExceptionHandler
UseStatusCodePages
UseWelcomePage
UseElmPage / UseElmCapture (preview)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Built-in Middleware
UseIdentity (UseAuthentication in 2.0)
UseStaticFiles
UseDefaultFiles
UseDirectoryBrowser (requires services)
UseFileServer (replaces/combines previous three)
UseRouter
UseMvc (replaces/wraps UseRouter; requires services)
UseRewriter (1.1)
UseResponseCompression (1.1)
UseResponseCaching (1.1, requires services)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Project Dependencies (1.x)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
New Project Dependencies (2.0)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
<Project Sdk="Microsoft.NET.Sdk.Web"><PropertyGroup><TargetFramework>netcoreapp2.0</TargetFramework><UserSecretsId>aspnet-NewIn2-718CDB27-A711-46D2-8222-38EB73FC1D4B</UserSecretsId>
</PropertyGroup><ItemGroup><PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" /><PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0"
PrivateAssets="All" /><PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.0.0"
PrivateAssets="All" /></ItemGroup><ItemGroup><DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" /><DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.0" /><DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0"
/></ItemGroup><ItemGroup><ProjectReference Include="..\NewIn2.Core\NewIn2.Core.csproj" />
</ItemGroup></Project>
Typical Project File (2.x)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Section
Keeping Code Modular and Loosely Coupled
Injecting Dependencies in ASP.NET Core
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Dependency Injection (DI)
Classes request dependencies instead of referencing directly
Dependent object instances are injected as parameters (or properties)
Allows for flexibility and modularity
Classes follow Explicit Dependencies Principle, Dependency Inversion Principle
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Explicit Dependencies Principle
Classes should express their requirements through their constructor
Direct use of specific collaborators creates hidden dependencies
Classes with hidden dependencies aren’t “honest” about what they require
Constructor should be a contract that specifies what the class requires
Learn more: http://deviq.com/explicit-dependencies-principle/
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Section
ASP.NET Core MVC(and Web APIs – they are ONE THING now!)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Controllers and Actions
Controllers are collections of actionsUseful for grouping actions into cohesive sets
Used by default for routing and URL generation (e.g. /{controllername}/{actioname})
Actions are methods that handle individual requestsEach request is routed to an action method
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
An MVC Action
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
public async Task OnGetAsync(string returnUrl = null){
if (!string.IsNullOrEmpty(ErrorMessage)){
ModelState.AddModelError(string.Empty, ErrorMessage);}
ExternalLogins = (await_signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
ReturnUrl = returnUrl;}
Razor Pages Entry Point
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Routing to Actions
Default route specified in Startup Configure method
Attribute routing (at Controller and/or Action level)[Route(“products/index”)]
[Route(“products/update/{id})]
[Route(“home”)] on Controller and [Route(“about”)] on Action are combined into “home/about” route
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Attribute Routing Tips
Use default convention where possible
Define routes using attributes for non-default cases, especially for APIs
APIs should use HTTP verb attributes to define routes:[HttpGet]
[HttpGet(“{id}”)] // will combine with route specified on controller
Routes can use token replacement for [area], [controller], [action]:[Route(“[controller]/[action]”)]
Especially powerful when used with inheritance:[Route(“api/[controller]/[action]”)]public abstract class BaseApiController : Controller
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Accepting Data in Actions (Model Binding)
MVC uses model binding to convert data sent by requests into action parameters
Model binding takes place before the action is invoked
Parameters may be simple types, or complex objects
Data can come from named form values, route components, or query strings
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Model Validation
Model types can have validation rules specifiedActions should check model validation state before making changes to application state
if(ModelState.IsValid)
Avoid using data model types for model bindingThis can result in exposing more data to user changes than intended
Use [FromBody] to bind to data in the body of the request (JSON by default)
Use [BindProperty] to bind properties to non-GET requests (2.0)
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Tag Helpers
Replaces HTML Helpers (still available, but not recommended)
Render within HTML tags
Declarative, not Imperative
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Tag Helper Example
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Tag Helper Example
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Working With a Database Using EF Core
Add Entity Framework Core (EF Core) in ConfigureServices:
Also supports InMemory – Install this package:
and modify ConfigureServices:
services.AddDbContext<AppDbContext>(options =>options.UseInMemoryDatabase(“DbName”));
//options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Working with EF Core’s AppDbContext
Never directly instantiate your application’s DbContext type(s)
Instead, request from DI using constructor parameters
Recommendation
Follow the Dependency Inversion Principle and avoid having UI types depend on details
Using Entity Framework Core is an implementation detail
Instead depend on an abstraction over your persistence method
Common pattern for this: Repository
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Section
Testing ASP.NET Core AppsOne of my favorite new features!
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Configuring a TestServer
Use a WebHostBuilder
Configure like in your web projectModify for testing if necessary
Create a TestServer from this builder instance
Create an HttpClient using CreateClient()
Make requests to the app using the client instance
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Functional Testing ASP.NET Core Apps
Make a request using the HttpClient instance
Capture the response
Verify status code
Deserialize if necessary
Assert that the result included the values you expected
APIs are easy; views require a little more work to configure for testing
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
[Fact]public async Task ReturnTwoItems(){
var response = await _client.GetAsync("/api/todoitems");response.EnsureSuccessStatusCode();var stringResponse = await response.Content.ReadAsStringAsync();var result = JsonConvert.DeserializeObject<IEnumerable<ToDoItem>>
(stringResponse).ToList();
Assert.Equal(2, result.Count());Assert.Equal(1, result.Count(a => a.Title == "Test Item 1"));Assert.Equal(1, result.Count(a => a.Title == "Test Item 2"));
}
A Functional Test
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
protected HttpClient GetClient(){
var builder = new WebHostBuilder().UseContentRoot(Directory.GetCurrentDirectory()).UseStartup<Startup>().UseEnvironment("Testing"); // ensure ConfigureTesting is called in Startup
var server = new TestServer(builder);var client = server.CreateClient();
// client always expects json resultsclient.DefaultRequestHeaders.Clear();client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
return client;}
Configurating the Client
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Runtime Store – Precompiles meta-package and eliminates need to deploy its packages
.NET Standard 2.0 – Much larger BCL available cross-platform
DI-Enabled Authentication
Kestrel improvements – now supports edge (Internet-facing) deployment
Hosting environment can inject dependencies and code into apps
Automatic CSRF protection
Automatic Razor view compilation
New Tag Helper Components
IHostedService – start/stop services when application starts/stops
VB support (console, class libraries)
EF Core Owned Entities, Global filters, DbContext Pooling
More New Stuff in 2.0
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Demonstration
Razor Pagesand
EF Core New Features
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
Migration from 1.x to 2.0 Tips1. Update TargetFramework to netcoreapp2.0
2. Update global.json to version 2.0.0 (if used)
3. Update package references. Use new MS.AspNetCore.All 2.0.0 package. Includes EF Core 2.0.
4. Update other packages to 2.0.0 if needed.
5. Update .NET Core CLI Tools
6. Change PackageTargetFallback to AssetTargetFallback
7. Update Main in Program.cs
8. Make sure you're not doing DB setup in Startup.Configure. Move it to Main in Program.cs.
9. Remove MvcRazorCompileOnPublish = true from .csproj files -- it is now on by default. If targeting .NET Framework, need to reference ViewCompilation package.
10. If you're not explicitly using App Insights, remove from .csproj, program.cs, and your _Layout file.
11. Migrate Auth/Identity to 2.0
No Content Here(Reserved for Watermark)
Copyright © 2016, DevIQ, Inc.
ASP.NET Core is the future of Microsoft’s platform
Lots of cool demos I didn’t have time to show.
Check out AspNetCoreQuickstart.com
Use qltechcon2017 for 20% off
Subscribe to WeeklyDevTips.com
Connect with me on Twitter: @ardalis
Thanks!
Summary