Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Loading in …3
×
1 of 66

Introducing ASP.NET Core 2.0

9

Share

Download to read offline

A whirlwind tour of ASP.NET Core 2.0 presented at DogfoodCon 2017 in Columbus, Ohio. October 2017. Learn more at aspnetcorequickstart.com.

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all

Introducing ASP.NET Core 2.0

  1. 1. No Content Here (Reserved for Watermark) Introducing ASP.NET Core 2.0 @ardalis Ardalis.com Steve Smith (in about an hour)
  2. 2. No Content Here (Reserved for Watermark) No Content Here (Reserved for Watermark) Weekly Dev Tips Podcast and Newsletter • Ardalis.com/tips • WeeklyDevTips.com
  3. 3. No Content Here (Reserved for Watermark) 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
  4. 4. No Content Here (Reserved for Watermark) 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
  5. 5. No Content Here (Reserved for Watermark) ASP.NET Core – What is it? A new open-source and cross-platform framework for building modern cloud-based Web applications using .NET
  6. 6. No Content Here (Reserved for Watermark) 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
  7. 7. No Content Here (Reserved for Watermark) Install SDK https://www.microsoft.com/net/download/core Optional: Install Visual Studio 2017 Install VS2017 .NET Core 2.0 Tools Confirm Version: Getting the Bits
  8. 8. No Content Here (Reserved for Watermark) Visual Studio .NET Core Templates Console App Class Library Unit Test xUnit Test ASP.NET Core
  9. 9. No Content Here (Reserved for Watermark) ASP.NET Core Project Templates Empty Web API Web App Web App (MVC) Angular React React and Redux Docker
  10. 10. No Content Here (Reserved for Watermark) New Web Projects with dotnet dotnet new web Empty Hello World Project dotnet new mvc MVC 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
  11. 11. No Content Here (Reserved for Watermark) dotnet CLI Templates
  12. 12. No Content Here (Reserved for Watermark) dotnet new razor
  13. 13. No Content Here (Reserved for Watermark) Razor Pages Convention-Based Routes No Controllers; No Actions PageModel as Codebehind Handlers replace Actions PageModel as ViewModel Leverages existing MVC features Model Binding / Validation Routing Filters Maintains Testability and Separation of Concerns More in my Razor Pages article in September 2017 MSDN Magazine
  14. 14. No Content Here (Reserved for Watermark) Section Program.cs ASP.NET Core Startup
  15. 15. No Content Here (Reserved for Watermark) var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup<Startup>() .Build(); host.Run(); WebHostBuilder (1.x)
  16. 16. No Content Here (Reserved for Watermark) 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)
  17. 17. No Content Here (Reserved for Watermark) 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
  18. 18. No Content Here (Reserved for Watermark) 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)
  19. 19. No Content Here (Reserved for Watermark)
  20. 20. No Content Here (Reserved for Watermark) Section Startup.cs ASP.NET Core Startup
  21. 21. No Content Here (Reserved for Watermark) Startup Constructor (optional) ConfigureServices Configure
  22. 22. No Content Here (Reserved for Watermark) 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)
  23. 23. No Content Here (Reserved for Watermark) 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)
  24. 24. No Content Here (Reserved for Watermark) public Startup(IConfiguration configuration) { Configuration = configuration; } Startup Constructor (2.0)
  25. 25. No Content Here (Reserved for Watermark) 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 container StructureMap, Unity, Autofac, Ninject, Castle Windsor, etc.
  26. 26. No Content Here (Reserved for Watermark) 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)
  27. 27. No Content Here (Reserved for Watermark) 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)
  28. 28. No Content Here (Reserved for Watermark) 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)
  29. 29. No Content Here (Reserved for Watermark) 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)
  30. 30. No Content Here (Reserved for Watermark) Section Built-in and Custom Middleware Middleware
  31. 31. No Content Here (Reserved for Watermark) “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?
  32. 32. No Content Here (Reserved for Watermark)
  33. 33. No Content Here (Reserved for Watermark) 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
  34. 34. No Content Here (Reserved for Watermark) 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
  35. 35. No Content Here (Reserved for Watermark) 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
  36. 36. No Content Here (Reserved for Watermark) 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
  37. 37. No Content Here (Reserved for Watermark) Built-in Middleware : Diagnostics UseDeveloperExceptionPage UseDatabaseErrorPage – helps configure database UseBrowserLink UseExceptionHandler UseStatusCodePages UseWelcomePage UseElmPage / UseElmCapture (preview)
  38. 38. No Content Here (Reserved for Watermark) 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)
  39. 39. No Content Here (Reserved for Watermark) Project Dependencies (1.x)
  40. 40. No Content Here (Reserved for Watermark) New Project Dependencies (2.0)
  41. 41. No Content Here (Reserved for Watermark) <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.CoreNewIn2.Core.csproj" /> </ItemGroup> </Project> Typical Project File (2.x)
  42. 42. No Content Here (Reserved for Watermark) Section Keeping Code Modular and Loosely Coupled Injecting Dependencies in ASP.NET Core
  43. 43. No Content Here (Reserved for Watermark) 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
  44. 44. No Content Here (Reserved for Watermark) 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/
  45. 45. No Content Here (Reserved for Watermark) Section ASP.NET Core MVC (and Web APIs – they are ONE THING now!)
  46. 46. No Content Here (Reserved for Watermark) Controllers and Actions Controllers are collections of actions Useful for grouping actions into cohesive sets Used by default for routing and URL generation (e.g. /{controllername}/{actioname}) Actions are methods that handle individual requests Each request is routed to an action method
  47. 47. No Content Here (Reserved for Watermark) An MVC Action
  48. 48. No Content Here (Reserved for Watermark) 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
  49. 49. No Content Here (Reserved for Watermark) 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
  50. 50. No Content Here (Reserved for Watermark) 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
  51. 51. No Content Here (Reserved for Watermark) 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
  52. 52. No Content Here (Reserved for Watermark) Model Validation Model types can have validation rules specified Actions should check model validation state before making changes to application state if(ModelState.IsValid) Avoid using data model types for model binding This 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)
  53. 53. No Content Here (Reserved for Watermark) Tag Helpers Replaces HTML Helpers (still available, but not recommended) Render within HTML tags Declarative, not Imperative
  54. 54. No Content Here (Reserved for Watermark) Tag Helper Example
  55. 55. No Content Here (Reserved for Watermark) Tag Helper Example
  56. 56. No Content Here (Reserved for Watermark) 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")));
  57. 57. No Content Here (Reserved for Watermark) 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
  58. 58. No Content Here (Reserved for Watermark) Section Testing ASP.NET Core Apps One of my favorite new features!
  59. 59. No Content Here (Reserved for Watermark) Configuring a TestServer Use a WebHostBuilder Configure like in your web project Modify 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
  60. 60. No Content Here (Reserved for Watermark) 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
  61. 61. No Content Here (Reserved for Watermark) [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
  62. 62. No Content Here (Reserved for Watermark) 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 results client.DefaultRequestHeaders.Clear(); client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/json")); return client; } Configurating the Client
  63. 63. No Content Here (Reserved for Watermark) 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
  64. 64. No Content Here (Reserved for Watermark) Demonstration Razor Pages and EF Core New Features
  65. 65. No Content Here (Reserved for Watermark) Migration from 1.x to 2.0 Tips 1. 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
  66. 66. No Content Here (Reserved for Watermark) 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

Editor's Notes

  • Almost all ASP.NET apps use a standard Startup class to configure how the app will work. ASP.NET Core apps run inside of a web host, and even before Startup is called, a WebHostBuilder is used to create a host that then runs.
  • Most ASP.NET Core project templates include a Program.cs file, which holds a public static void Main entry point for the app. It’s where the web host is built and run. You don’t actually need a Startup class at all to create an ASP.NET Core app – you can perform all of the configuration inside of WebHostBuilder if you want.
  • Using WebHostBuilder, which isn’t strictly required.
  • Almost all ASP.NET apps use a standard Startup class to configure how the app will work. ASP.NET Core apps run inside of a web host, and even before Startup is called, a WebHostBuilder is used to create a host that then runs.
  • Using the built-in container…
  • Note that Startup is not a strongly typed contract. Methods can have arbitrary signatures and some support different return types.
  • Logging is no longer done here.
    Data seeding should not be done here, since migrations run after this.
  • Your ASP.NET Core app’s request pipeline is composed of components called middleware. ASP.NET Core ships with a bunch of useful middleware that your apps can use, and it’s pretty straightforward to create your own.
  • For most apps, you won’t have to write any middleware yourself. You’ll probably use a higher-level framework, like MVC, and some of the built-in middleware that ships with ASP.NET Core. There are several interesting pieces of middleware that are very useful when developing your app.

    The DeveloperExceptionPage will let you see details about exceptions that are unhandled by your app. You don’t want this to be enabled in production, as it could reveal sensitive data, but it’s great during development.

    The DatabaseErrorPage is helpful when you’re first creating an app and haven’t yet configured its database. It can help you get that set up.

    BrowserLink works with Visual Studio to speed up how quickly changes you make in your app are reflected in the browser, without always have to restart the app and refresh the browser.

    You can specify an Exception Handler route, so that any unhandled exceptions that occur result in the display of an “oops” or similar page for users in production.

    If you want custom pages for certain status codes, such as a humorous 404 Not Found page, the UseStatusCodePages middleware can provide this functionality.

    The Welcome pages provide some very basic information, and is useful for ensuring the app is functioning at its most basic level

    Finally, the ElmPage and ElmCapture middleware are part of the Error Logging Middleware, or ELM, which can be used to record developer exception pages for later review.
  • UseIdentity is used to allow your app to use the built-in membership and identity services. It’s used with authentication and authorization.

    Static files middleware is required for the app to server up static content, such as script and style files, and images.

    Some apps may require additional file serving functionality, in which case you can add support for default files, like default.aspx or index.htm. You can configure the app to act as a server-side file browser, and if you need all of this functionality you can simply add the FileServer middleware.

    If you need to customize routes and routing in your app, you can use Router middleware. Most apps will work at a higher level than this, though, such as with the Model View Controller, or MVC framework, which uses Router internally.

    Version 1.1 of ASP.NET Core added some additional middleware to support URL rewriting, response compression, and response caching.

  • ASP.NET Core doesn’t just support dependency injection, it uses it internally to achieve its goals of being modular and loosely coupled. Your application code can leverage this capability in the same way ASP.NET Core does.
  • Dependency injection, or DI, is a software design technique in which collaborating classes are injected into the class that needs them, rather than being referenced directly. One could even call it collaborator injection.

    The collaborator, or dependency, is injected into the class by way of parameters, or sometimes by setting properties. The most common technique involves passing the dependencies into the constructor of the class that will need them, which then assigns the instances to local fields.

    This approach produces a more flexible and modular design, since classes do not have direct dependence on specific implementations of their collaborators. This also ensure the classes follow the Explicit Dependencies Principle and the Dependency Inversion Principle.
  • The Explicit Dependencies Principle states that classes should express their requirements through their constructor. If the class cannot function without certain collaborating classes being available, then it should be up front about these dependencies and request them as part of its constructor.

    If instead the class directly instantiates its dependencies, or references them statically, it creates hidden dependencies and tight coupling that may only become apparent at runtime. For example, if you have a class that interacts with a database, and it does so by calling a static helper method, it’s likely this class will fail with an exception at runtime if the database isn’t available. However, if you’re interested in testing the behavior of the class, perhaps swapping in a fake database or using an in memory data store instead for test purposes, it will be very difficult to do so when the dependency is implicit within the class and not exposed as a parameter. This can also be an example of violating the Open/Closed Principle, because the class’s behavior could not be changed without changing its source code.

    Classes that have hidden or implicit dependencies aren’t being honest about their functional requirements. If the class or method needs a database (or some data store) in order to function, it should declare this dependency as a parameter. If only one method has a dependency, it may make sense to only add the parameter at the method rather than the class level. However, many frameworks provide support for automatically populating dependencies on the constructor, so even in the case where only one method has a dependency, it may make sense to inject it through the class constructor.
  • At the end of the last chapter, we created a simple MVC app and looked at how we can use ASP.NET Core’s dependency injection feature to inject services into controllers. In this chapter, we’ll provide an overview of ASP.NET Core MVC that should let you get started building business apps on top of this framework. This will include building Web APIs with ASP.NET Core MVC. In ASP.NET MVC 5 and Web API 2, support for web APIs was in a separate set of classes and packages from MVC. In ASP.NET Core, this is no longer the case, and web API functionality is built directly into ASP.NET Core MVC.
  • The entry point for each request to your ASP.NET Core MVC application is an action. Actions are methods, and collections of actions are grouped together into cohesive sets by Controllers.

    Controllers are useful for routing and URL generation, as well as to support constructor dependency injection.

    In ASP.NET Core MVC, controllers do not need to inherit from any special type – they just need to be named with the Controller suffix. However, the Microsoft.AspNetCore.Mvc.Controller type provides many helpful methods that you can use.

    Here you can see an example of a simple controller that inherits from the Controller base class and defines several action methods.

    Actions are simply methods that handle individual requests. Each request made to the application maps to an action method by way of routing middleware. Individual actions can return raw data, a view, or an action result. Actions can be synchronous or async.

    Here is an example of a simple async action that displays a view for a Login page in an application. Since it is async, its return type is a Task of IActionResult instead of just an IActionResult. It also uses the Await keyword in its body when it calls out to async methods. This action method is also decorated with two attributes. The first one, HttpGet, specifies the HTTP verb this action will respond to, and is used by the routing engine. The second attribute, AllowAnonymous, will override an attribute set at a higher level requiring the user to be authenticated, and will allow an anonymous request to run this action.
  • You can specify default routes for MVC when you configure it in the Configure method in Startup. The default MVC templates add code like this, which defines a default route template. This template should be familiar to you if you’ve used MVC 5 or earlier. It uses the routing convention of controller/action/id, where id is optional. It also specifies default values for the controller and action, so if not present Home and Index are used.

    ASP.NET Core MVC supports attribute routing, which lets you define route information close to the actions that will use be associated with the route. The Route attribute is used to specify the route at the controller and/or action level, and if routes are specified for both they are combined.
  • Here are my tips for working with routing in ASP.NET Core MVC. First, use the default route convention wherever possible. It works well and will be familiar to any developers who must work on your application later, whether they’re coming from Core MVC or legacy ASP.NET MVC.

    Embrace attribute routing and use it wherever you need to stray from the defaults. Microsoft especially recommends this for API methods. API method can define routes directly within their HTTP Verb attributes, so for instance you can specify a route or partial route as part of an [HttpGet] or [HttpPost] attribute. This action-level route will be combined with any controller-specified route.

    To reduce the number of magic strings you need to specify in your routes, you can use token replacements for area, controller, and action. These tokens will be replaced with the actual area, controller, or action name where the route attribute is applied.

    This feature is especially powerful when combined with inheritance. For example, if you want all of your API methods to use a route starting with /api and followed by controllername and actionname, you can specify this route attribute on a base api controller class, as shown.
  • Clients, whether web browsers or other applications, can send data to your application as web requests. MVC uses a process called model binding to convert data sent by requests into parameters on action methods.

    Model binding takes place before the action is invoked. This is important to remember since occasionally there may be errors or other behavior going on during model binding that may not be immediately apparent to you if you’re expecting your action to be the first code to be executed by a request. Don’t forget about model binding!

    Parameters on action methods may be simple primitive types like ints and strings, or they may be complex objects. Model binding can support all of these scenarios.

    Data coming into an action may come from form values, route components, or query strings, in this order.

    Model types can also have validation rules specified, and model validation occurs before the action method is called. Your actions should always check model validity, especially in methods that will make changes to application state.

    You should avoid using data model types in your UI layer, but if you must, you should especially avoid using them in actions that accept data and use model binding. Doing so can allow malicious users to save properties you may not have meant to expose, because they could guess these properties exist, pass them into the request, and model binding will automatically populate these properties (which you might then save in your action).

    Finally, you can use the FromBody attribute on a single parameter to specify that it should be found to the body of the request. By default the expected format is JSON, but you can configure other input formatters to support formats like XML.




  • Clients, whether web browsers or other applications, can send data to your application as web requests. MVC uses a process called model binding to convert data sent by requests into parameters on action methods.

    Model binding takes place before the action is invoked. This is important to remember since occasionally there may be errors or other behavior going on during model binding that may not be immediately apparent to you if you’re expecting your action to be the first code to be executed by a request. Don’t forget about model binding!

    Parameters on action methods may be simple primitive types like ints and strings, or they may be complex objects. Model binding can support all of these scenarios.

    Data coming into an action may come from form values, route components, or query strings, in this order.

    Model types can also have validation rules specified, and model validation occurs before the action method is called. Your actions should always check model validity, especially in methods that will make changes to application state.

    You should avoid using data model types in your UI layer, but if you must, you should especially avoid using them in actions that accept data and use model binding. Doing so can allow malicious users to save properties you may not have meant to expose, because they could guess these properties exist, pass them into the request, and model binding will automatically populate these properties (which you might then save in your action).

    Finally, you can use the FromBody attribute on a single parameter to specify that it should be found to the body of the request. By default the expected format is JSON, but you can configure other input formatters to support formats like XML.




  • The default MVC project template in visual studio uses EF Core if you specify that you want to have individual user accounts. You’ll find a line of code in your startup class that adds support for EF Core to the app by adding it in the ConfigureServices method. This is where you specify your application’s DbContext class, which inherits from the base DbContext class defined by EF Core. You also specify how it will connect to Sql Server.

    EF Core has built-in support for an InMemory database provider. This support requires installing a separate package and modifying the configuration of your DbContext type as shown. You’ll probably also need to specify the Microsoft.EntityFrameworkCore namespace for the UseInMemoryDatabase extension method to work. The in-memory data store is reset each time the application starts.
  • In your ASP.NET Core applications, you should never be instantiating your application’s DbContext implementation directly. This advice applies to previous MVC implementations, too, but it was much easier to instantiate an EF6 dbcontext than it is to instantiate an EF Core one. This is at least partly by design – in EF Core the team has embraced dependency injection and wants code to fall into this pattern by default. Thus, to make use of EF Core in your application, you should simply request your DbContext implementation from the constructor of the type that needs it.

    That said, you should also try to follow the Dependency Inversion Principle, which means you want to avoid having your UI types, like Controllers, depending on details. EF Core is an implementation detail for how your app gets and persists data. Rather than depending on it directly, your classes should depend on abstractions. The common pattern that describes these persistence abstractions is the Repository. You may hear some in the developer community decry this pattern, or say that it’s not needed because EF already implements this pattern. In my experience, not following this pattern will bit you later, and following it is simple and easy, so you’re better off following it. You’ll have more loosely coupled code that’s easier to change and test if you follow this recommendation.
  • If you’re building an app of any complexity, you’ll probably get some value out of testing it so that you know it works as you expect. You can manually test your application, but your time is valuable and computers can do that sort of thing much faster than humans can, so why not automate it?
  • One of my favorite features in ASP.NET Core is the TestServer. The TestServer makes it extremely easy to write integration tests for ASP.NET Core that run through the full ASP.NET Core MVC stack. You configure a TestServer using a WebHostBuilder.

    The configuration might look just like the builder you use for your application, but you can modify it if necessary to make it work for testing. For instance, you might want to change how it gets its data or configure it with test data your tests will expect.

    Once you have a WebHostbuilder configured, use it to create a TestServer instance, and then you can get an HttpClient from the TestServer. This HttpClient is pre-configured to connect to the TestServer, all of which happens without going through your system’s network stack, so you don’t run into issues with firewalls or other problems when using this approach.

    Once you have the client, you can use it to make requests to your application, and then verify the responses.
  • To perform integration testing on ASP.NET core, get an HttpClient that connects to your TestServer, and make a request.

    Capture the response, and verify it’s correct.

    You can start by verifying that the status code was what you expected.

    Then deserialize the response, if required. The response will be a string. If you want to get it back into an object, you can deserialize it using JSON or XML or whatever format it’s in. For view-based responses, the result will typically be a string containing HTML.

    Assert that the response includes the values you’re expecting to complete your test.

    You’ll find using this approach that testing API methods if pretty easy. Testing views requires a little more setup initially, and the assertions on the results can be more brittle depending on how you specific you are about your assertion. We’ll look at both of these scenarios next.
  • Migration Notes:
    https://docs.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/index
    1. 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
  • ×