ASP.NET Core updates in .NET 7 Preview 2

Daniel Roth

.NET 7 Preview 2 is now available and includes many great new improvements to ASP.NET Core.

Here’s a summary of what’s new in this preview release:

  • Infer API controller action parameters that come from services
  • Dependency injection for SignalR hub methods
  • Provide endpoint descriptions and summaries for minimal APIs
  • Binding arrays and StringValues from headers and query strings in minimal APIs
  • Customize the cookie consent value

For more details on the ASP.NET Core work planned for .NET 7 see the full ASP.NET Core roadmap for .NET 7 on GitHub.

Get started

To get started with ASP.NET Core in .NET 7 Preview 2, install the .NET 7 SDK.

If you’re on Windows using Visual Studio, we recommend installing the latest Visual Studio 2022 preview. Visual Studio for Mac support for .NET 7 previews isn’t available yet but is coming soon.

To install the latest .NET WebAssembly build tools, run the following command from an elevated command prompt:

dotnet workload install wasm-tools

Upgrade an existing project

To upgrade an existing ASP.NET Core app from .NET 7 Preview 1 to .NET 7 Preview 2:

  • Update all Microsoft.AspNetCore.* package references to 7.0.0-preview.2.*.
  • Update all Microsoft.Extensions.* package references to 7.0.0-preview.2.*.

See also the full list of breaking changes in ASP.NET Core for .NET 7.

Infer API controller action parameters that come from services

Parameter binding for API controller actions now binds parameters through dependency injection when the type is configured as a service. This means it’s no longer required to explicitly apply the [FromServices] attribute to a parameter.

Services.AddScoped<SomeCustomType>();

[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
    // Both actions will bound the SomeCustomType from the DI container
    public ActionResult GetWithAttribute([FromServices]SomeCustomType service) => Ok();
    public ActionResult Get(SomeCustomType service) => Ok();
}

You can disable the feature by setting DisableImplicitFromServicesParameters:

Services.Configure<ApiBehaviorOptions>(options =>
{
     options.DisableImplicitFromServicesParameters = true;
})

Dependency injection for SignalR hub methods

SignalR hub methods now support injecting services through dependency injection (DI).

Services.AddScoped<SomeCustomType>();

public class MyHub : Hub
{
    // SomeCustomType comes from DI by default now
    public Task Method(string text, SomeCustomType type) => Task.CompletedTask;
}

You can disable the feature by setting DisableImplicitFromServicesParameters:

services.AddSignalR(options =>
{
    options.DisableImplicitFromServicesParameters = true;
});

To explicitly mark a parameter to be bound from configured services, use the [FromServices] attribute:

public class MyHub : Hub
{
    public Task Method(string arguments, [FromServices] SomeCustomType type);
}

Provide endpoint descriptions and summaries for minimal APIs

Minimal APIs now support annotating operations with descriptions and summaries used for OpenAPI spec generation. You can set these descriptions and summaries for route handlers in your minimal API apps using an extension methods:

app.MapGet("/hello", () => ...)
  .WithDescription("Sends a request to the backend HelloService to process a greeting request.");

Or set the description or summary via attributes on the route handler delegate:

app.MapGet("/hello", [EndpointSummary("Sends a Hello request to the backend")]() => ...)

Binding arrays and StringValues from headers and query strings in minimal APIs

With this release, you can now bind values from HTTPS headers and query strings to arrays of primitive types, string arrays, or StringValues:

// Bind query string values to a primitive type array
// GET  /tags?q=1&q=2&q=3
app.MapGet("/tags", (int[] q) => $"tag1: {q[0]} , tag2: {q[1]}, tag3: {q[2]}")

// Bind to a string array
// GET /tags?names=john&names=jack&names=jane
app.MapGet("/tags", (string[] names) => $"tag1: {names[0]} , tag2: {names[1]}, tag3: {names[2]}")

// Bind to StringValues
// GET /tags?names=john&names=jack&names=jane
app.MapGet("/tags", (StringValues names) => $"tag1: {names[0]} , tag2: {names[1]}, tag3: {names[2]}")

You can also bind query strings or header values to an array of a complex type as long as the type has TryParse implementation as demonstrated in the example below.

// Bind to an array of a complex type
// GET /tags?tags=trendy&tags=hot&tags=spicy
app.MapGet("/tags", (Tag[] tags) =>
{
    return Results.Ok(tags);
});

...

class Tag 
{
    public string? TagName { get; init; }

    public static bool TryParse(string? tagName, out Tag tag)
    {
        if (tagName is null) 
        {
            tag = default;
            return false;
        }

        tag = new Tag { TagName = tagName };
        return true;
    }
}

You can now specify the value used to track if the user consented to the cookie use policy using the new CookiePolicyOptions.ConsentCookieValue property.

Thank you @daviddesmet for contributing this improvement!

Request for feedback on shadow copying for IIS

In .NET 6 we added experimental support for shadow copying app assemblies to the ASP.NET Core Module (ANCM) for IIS. When an ASP.NET Core app is running on Windows, the binaries are locked so that they cannot be modified or replaced. You can stop the app by deploying an app offline file, but sometimes doing so is inconvenient or impossible. Shadow copying enables the app assemblies to be updated while the app is running by making a copy of the assemblies.

You can enable shadow copying by customizing the ANCM handler settings in web.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <remove name="aspNetCore"/>
      <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified"/>
    </handlers>
    <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".logsstdout">
      <handlerSettings>
        <handlerSetting name="experimentalEnableShadowCopy" value="true" />
        <handlerSetting name="shadowCopyDirectory" value="../ShadowCopyDirectory/" />
      </handlerSettings>
    </aspNetCore>
  </system.webServer>
</configuration>

We’re investigating making shadow copying in IIS a feature of ASP.NET Core in .NET 7, and we’re seeking additional feedback on whether the feature satisfies user requirements. If you deploy ASP.NET Core to IIS, please give shadow copying a try and share with us your feedback on GitHub.

Give feedback

We hope you enjoy this preview release of ASP.NET Core in .NET 7. Let us know what you think about these new improvements by filing issues on GitHub.

Thanks for trying out ASP.NET Core!

14 comments

Comments are closed. Login to edit/delete your existing comments

  • Dawid Haładus

    when will you do advanced routing support for blazor?
    The dynamic option is missing right after the domain, example:
    domain.com/{userName}

  • Michael Peter

    +1 for adding shadow copying again like in ASP.NET Days!!!

    Hint: You wrote: In .NET 6 we added experimental support for shadow copying – I guess you mean .NET 7 ?

    Also happy about the documenation feature in the minimal api!

    • Daniel RothMicrosoft employee

      Hi Michael. Shadow copying for IIS was included in .NET 6, but only as an experimental feature. We’re looking into including shadow copying in .NET 7 as a fully supported feature. Part of that investigation is validating with users that the feature satisfies their requirements. Please give the feature a try and let us know if it works for you by commenting on https://github.com/dotnet/AspNetCore.Docs/issues/23733.

  • Nikola Petrovic

    Great work! 😀

    These implicit FromServices parameters seem quite handy for doing things just a bit quicker.
    Would you happen to know how some of the preview users feel about this feature?
    E.g. would they be willing to start using this implicit approach over the explicit on their future projects, or is it a factor of something?

    • Daniel RothMicrosoft employee

      Hi Nikola. It’s great to hear that you like the implicit FromServices feature! We’re providing this functionality in the .NET 7 previews so that we can hear from users (like you!) what they think about them. So far, as far as I know, the reaction has been positive.

  • André R.

    Allowing arrays in query strings out of the box is great improvement 👍

    However as HTTP doesn’t seem to have a clear standard definition for arrays: (even for HTTP headers it’s inconsistent, some software uses “,” others several headers)
    Has it been considered to use already supported “[]” notation for arrays in query string? (PHP, jQuery, misc frameworks in other languages)

    Example:
    Instead of: ?tags=trendy&tags=hot&tags=spicy
    You would have: ?tags[]=trendy&tags[]=hot&tags[]=spicy

    Benefit: Several javascript frameworks out there already supports this, and while for instance Rails seems to use the former syntax, backend languages like PHP supports the latter and has for years.

    Either way this should be taken as a chance to improve standardization for arrays in HTTP headers and query-string, so reverse proxies* and other solutions in between can know which query strings to be considered as duplicates and not.

    * One examples: Varnish Cache has for years had several different types of vmods to allow you to cleanup and sort query string to ensure efficient cache hits

    • David FowlerMicrosoft employee

      We went with the simplest thing that was also supported in MVC (and the most obvious thing). Could we add more syntax? Yes, as to the decision process behind how we decide, I think its about popularity and need. Everyone supports the multiple query string syntax because it is the default thing you would use in HTTP (multiple values for a single query string key). The other syntaxes have evolved over the years based on frameworks making up more advanced binding syntax var various reasons (ways to encapsulate type information in the query string).

      Here’s what’s supported by MVC https://docs.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-6.0#collections. We don’t have plans to add support for any of these syntaxes as we want to keep things minimal. We’ll look out for feedback though.

      Either way this should be taken as a chance to improve standardization for arrays in HTTP headers and query-string, so reverse proxies* and other solutions in between can know which query strings to be considered as duplicates and not.

      Unfortunately, this is probably a lost cause at this point. There are too many assumptions in both the proxies and frameworks to change anything.

  • Luigi De Rosa

    i am using Visual Studio 2019 with ASP.NET Framework 7.2, when i use textbox with enabled=”false” in my HTML during test Web page i found added code
    disabled=”disabled” and class=”aspNetDisabled” in my HTML, this code override my css class code.

    I try to use WebControl.DisabledCssClass = “mycss” in vb.net code and also in Global.asax file, but during ReBuild code from VS2019 i receive this message:

    error BC30456: ‘DisabledCssClass’ is not member of ‘System.Web.UI.WebControls.WebControl’.

    i have used Add Reference for “System.web.dll” but the error occur too.

    what can i do? THANKS very much!

    • Daniel RothMicrosoft employee

      Hi Luigi. This post is about ASP.NET Core, not ASP.NET. For ASP.NET related questions you’re better off posting to StackOverflow.

  • Jon Miller

    The default for everything should be server-side apps and port Web Forms to the new .NET. JavaScript hell and serialization of everything over web APIs is a waste of time. Have you ever noticed how slow client-side web apps are? And how many bugs they have? I have. Microsoft, stop following other people’s bad examples. You did that with MVC when you copied Ruby On Rails and it’s been all down hill from there. You aren’t making things easier. You making things more complicated and wasting people’s time. If you were smart, you would build robust UI controls into Blazor and eliminate people having to purchase third party products to do this. The whole industry is on the completely wrong path if you ask me.