1 . Unexpected outcome of node.js vs ASP.NET Core performance test?

Answer :

As many others have alluded, the comparison lacks context.
At the time of its release, the async approach of node.js was revolutionary. Since then other languages and web frameworks have been adopting the approaches they took mainstream.

To understand what the difference meant, you need to simulate a blocking request that represents some IO workload, such as a database request. In a thread-per-request system, this will exhaust the threadpool and new requests will be put in to a queue waiting for an available thread.
With non-blocking-io frameworks this does not happen.

Consider this node.js server that waits 1 second before responding

const server = http.createServer((req, res) => {
  setTimeout(() => {
    res.statusCode = 200;
    res.end();
  }, 1000);
});

Now let's throw 100 concurrent conenctions at it, for 10s. So we expect about 1000 requests to complete.

$ wrk -t100 -c100 -d10s http://localhost:8000
Running 10s test @ http://localhost:8000
  100 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.01s    10.14ms   1.16s    99.57%
    Req/Sec     0.13      0.34     1.00     86.77%
  922 requests in 10.09s, 89.14KB read
Requests/sec:     91.34
Transfer/sec:      8.83KB

As you can see we get in the ballpark with 922 completed.

Now consider the following asp.net code, written as though async/await were not supported yet, therefore dating us back to the node.js launch era.

app.Run((context) =>
{
    Thread.Sleep(1000);
    context.Response.StatusCode = 200;
    return Task.CompletedTask;
});

$ wrk -t100 -c100 -d10s http://localhost:5000
Running 10s test @ http://localhost:5000
  100 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.08s    74.62ms   1.15s   100.00%
    Req/Sec     0.00      0.00     0.00    100.00%
  62 requests in 10.07s, 5.57KB read
  Socket errors: connect 0, read 0, write 0, timeout 54
Requests/sec:      6.16
Transfer/sec:     566.51B

62! Here we see the limit of the threadpool. By tuning it up we could get more concurrent requests happening, but at the cost of more server resources.

For these IO-bound workloads, the move to avoid blocking the processing threads was that dramatic.

Now let's bring it to today, where that influence has rippled through the industry and allow dotnet to take advantage of its improvements.

app.Run(async (context) =>
{
    await Task.Delay(1000);
    context.Response.StatusCode = 200;
});

$ wrk -t100 -c100 -d10s http://localhost:5000
Running 10s test @ http://localhost:5000
  100 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.01s    19.84ms   1.16s    98.26%
    Req/Sec     0.12      0.32     1.00     88.06%
  921 requests in 10.09s, 82.75KB read
Requests/sec:     91.28
Transfer/sec:      8.20KB

No surprises here, we now match node.js.

So what does all this mean?

Your impressions that node.js is the "fastest" come from an era we are no longer living in. Add to that it was never node/js/v8 that were "fast", it was that they broke the thread-per-request model. Everyone else has been catching up.

If your goal is the fastest possible processing of single requests, then look at the serious benchmarks instead of rolling your own. But if instead what you want is simply something that scales to modern standards, then go for whichever language you like and make sure you are not blocking those threads.


Leave a Comment

Name  
  Email   
Message
1 . ASP.NET MVC (Async) CurrentCulture is not shared between Controller and View?
 

Answer :

You are not. The culture of the current UI thread needs to be set before going into your async action method. If you set the culture while you are inside of an async method, it has no effect on the UI thread (as you have discovered).

But async problems aside, it is too late in the application lifecycle of MVC to be setting the culture inside of an action method, since some important culture-sensitive features of MVC (i.e. model binding) run before that point.

One way to set the culture early enough in the lifecycle is to use an authorization filter, which ensures the culture is set before model binding takes place.

using System.Globalization;
using System.Threading;
using System.Web.Mvc;

public class CultureFilter : IAuthorizationFilter
{
    private readonly string defaultCulture;

    public CultureFilter(string defaultCulture)
    {
        this.defaultCulture = defaultCulture;
    }

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        var values = filterContext.RouteData.Values;

        string culture = (string)values["culture"] ?? this.defaultCulture;

        CultureInfo ci = new CultureInfo(culture);

        Thread.CurrentThread.CurrentCulture = ci;
        Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(ci.Name);
    }
}

And to register it globally:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new CultureFilter(defaultCulture: "fi-FI"));
        filters.Add(new HandleErrorAttribute());
    }
}

The above example assumes that you have setup routing to add the culture as a route value similar to this answer, but you could design a filter to get the culture from somewhere else, if desired.


2 . ASP.Net Core filters with arguments and DI?
 

Answer :

There is, if you look at the source. Never tried though, so you got to try it yourself (can't test it at work and internet issues at home).

The documentation names one of it, for untyped parameters:

[TypeFilter(typeof(AddHeaderAttribute),
    Arguments = new object[] { "Author", "Steve Smith (@ardalis)" })]
public IActionResult Hi(string name)
{
    return Content($"Hi {name}");
}

The xmldoc of TypeFilterAttribute says

    /// <summary>
    /// Gets or sets the non-service arguments to pass to the <see cref="ImplementationType"/> constructor.
    /// </summary>
    /// <remarks>
    /// Service arguments are found in the dependency injection container i.e. this filter supports constructor
    /// injection in addition to passing the given <see cref="Arguments"/>.
    /// </remarks>
    public object[] Arguments { get; set; }

Alternatively, you can add properties to your TestFilterAttribute and assign them in the constructor, but this only works if the parameter is mandatory and hence set via constructor

public class TestFilterAttribute : TypeFilterAttribute
{
    public TestFilterAttribute(string firstArg, string secondArg) : base(typeof(TestFilterFilter))
    {
        this.Arguments = new object[] { firstArg, secondArg }
    }

    private class TestFilterFilter : IActionFilter
    {
        private readonly MainDbContext _mainDbContext;
        private readonly string _firstArg;
        private readonly string _secondArg;

        public TestFilterFilter(string firstArg, string secondArg, MainDbContext mainDbContext)
        {
            _mainDbContext = mainDbContext;
            _firstArg= firstArg;
            _secondArg= secondArg;
        }

        public void OnActionExecuting(ActionExecutingContext context) { }

        public void OnActionExecuted(ActionExecutedContext context) { }
    }
}

3 . ASP.NET Core MVC App Settings?
 

Answer :

The whole configuration approach in .NET Core is really flexible, but not at all obvious at the beginning. It's probably easiest to explain with an example:

Assuming an appsettings.json file that looks like this:

{
  "option1": "value1_from_json",

  "ConnectionStrings": {
    "DefaultConnection": "Server=,\\SQL2016DEV;Database=DBName;Trusted_Connection=True"
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  }
}

To get the data from appsettings.json file you first need to set up a ConfigurationBuilder in Startup.cs as follows:

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

    if (env.IsDevelopment())
    {
        // For more details on using the user secret store see https://go.microsoft.com/fwlink/?LinkID=532709
        builder.AddUserSecrets<Startup>();
    }

    builder.AddEnvironmentVariables();
    Configuration = builder.Build();

You can then access the configuration directly, but it's neater to create Options classes to hold that data, which you can then have injected into your controller or other classes. Each of those options classes represent a different section of the appsettings.json file.

In this code the connections strings are loaded into a ConnectionStringSettings class and the other option is loaded into a MyOptions class. The .GetSection method gets a particular part of the appsettings.json file. Again, this is in Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    ... other code

    // Register the IConfiguration instance which MyOptions binds against.
    services.AddOptions();

    // Load the data from the 'root' of the json file
    services.Configure<MyOptions>(Configuration);

    // load the data from the 'ConnectionStrings' section of the json file
    var connStringSettings = Configuration.GetSection("ConnectionStrings");
    services.Configure<ConnectionStringSettings>(connStringSettings);

These are the classes that the settings data are loaded into. Note how the property names pair up with the settings in the json file:

public class MyOptions
{
    public string Option1 { get; set; }
}

public class ConnectionStringSettings
{
    public string DefaultConnection { get; set; }
}

Finally, you can then access those settings by injecting an OptionsAccessor into the controller as follows:

private readonly MyOptions _myOptions;

public HomeController(IOptions<MyOptions > optionsAccessor)
{
    _myOptions = optionsAccessor.Value;
    var valueOfOpt1 = _myOptions.Option1;
}

4 . Implement checkbox list in ASP.NET Core?
 

Answer :

@model GroupIndexViewModel <form asp-action="Index" asp-controller="Group" method="get"> <ul> @for (var i = 0; i < Model.Filters.Count; i++) { <li> <input type="checkbox" asp-for="@Model.Filters[i].Selected" /> <label asp-for="@Model.Filters[i].Selected">@Model.Filters[i].Name</label> <input type="hidden" asp-for="@Model.Filters[i].Id" /> <input type="hidden" asp-for="@Model.Filters[i].Name" /> </li> } </ul> <button type="submit" name="action">Filtrer</button> </form>

Here I assuming that you have proper implementation of controller and action.


5 . Custom authentication asp.net core web api?

Answer :

I have used this approach in a solution using asp core 1.1. First define a custom scheme:

public static class Authentication
{
    public const string Scheme = "Custom";
}

You then have to inherit AuthenticationHandler<TOptions>. Here is where the logic for validating the header value will go:

public class MyAuthenticationHandler : AuthenticationHandler<MyOptions>
{
    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var authorizationHeader = Context.Request.Headers["Authorization"];
        if (!authorizationHeader.Any())
            return Task.FromResult(AuthenticateResult.Skip());

        var value = authorizationHeader.ToString();
        if (string.IsNullOrWhiteSpace(value))
            return Task.FromResult(AuthenticateResult.Skip());

        // place logic here to validate the header value (decrypt, call db etc)

        var claims = new[]
        {
            new Claim(System.Security.Claims.ClaimTypes.Name, "Bob")
        };

        // create a new claims identity and return an AuthenticationTicket 
        // with the correct scheme
        var claimsIdentity = new ClaimsIdentity(claims, Authentication.Scheme);

        var ticket = new AuthenticationTicket(new ClaimsPrincipal(claimsIdentity), new AuthenticationProperties(), Authentication.Scheme);

        return Task.FromResult(AuthenticateResult.Success(ticket));
    }
}

In order to inherit AuthenticationHandler you must create an options class where you set the AuthenticationScheme-property to the scheme you are using:

public class MyOptions : AuthenticationOptions
{
    AuthenticationScheme = Authentication.Scheme;
}

After this you have to inherit AuthenticationMiddleware<TOptions>. This will create the handler you implemented in the previous step:

public class MyAuthenticationMiddleware : AuthenticationMiddleware<MyOptions>
{
    public MyAuthenticationMiddleware(RequestDelegate next, IOptions<MyOptions> options, ILoggerFactory loggerFactory, UrlEncoder encoder) : base(next, options, loggerFactory, encoder)
    {
    }

    protected override AuthenticationHandler<MyOptions> CreateHandler()
    {
        return new MyAuthenticationHandler();
    }
}

In order to easily plug in your middleware you can define these extension methods:

public static IApplicationBuilder UseMyAuthentication(this IApplicationBuilder app, IConfigurationSection config)
{
    return app.UseMyAuthentication(options => {});
}

private static IApplicationBuilder UseMyAuthentication(this IApplicationBuilder app, Action<MyOptions> configure)
{
    var options = new MyOptions();
    configure?.Invoke(options);

    return app.UseMiddleware<MyAuthenticationMiddleware>(new OptionsWrapper<MyOptions>(options));
}

Then in your Startup class you can finally add your middleware:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseMyAuthentication(Configuration.GetSection("MyAuthenticationOptions"));

    // other stuff

    app.UseMvc();
}

Then add the AuthorizeAttribute on your actions specifying the scheme you just created:

[Authorize(ActiveAuthenticationSchemes = Authentication.Scheme)]
public IActionResult Get()
{
    // stuff ...
}

There are a lot of steps but hopefully this will get you going!



6 . Implement HTTP Cache (ETag) in ASP.NET Core Web API?

Answer :

Here's a more extensive version for MVC (tested with asp.net core 1.1):

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Net.Http.Headers;

namespace WebApplication9.Middleware
{
    // This code is mostly here to generate the ETag from the response body and set 304 as required,
    // but it also adds the default maxage (for client) and s-maxage (for a caching proxy like Varnish) to the cache-control in the response
    //
    // note that controller actions can override this middleware behaviour as needed with [ResponseCache] attribute   
    //
    // (There is actually a Microsoft Middleware for response caching - called "ResponseCachingMiddleware", 
    // but it looks like you still have to generate the ETag yourself, which makes the MS Middleware kinda pointless in its current 1.1.0 form)
    //
    public class ResponseCacheMiddleware
    {
        private readonly RequestDelegate _next;
        // todo load these from appsettings
        const bool ResponseCachingEnabled = true;
        const int ActionMaxAgeDefault = 600; // client cache time
        const int ActionSharedMaxAgeDefault = 259200; // caching proxy cache time 
        const string ErrorPath = "/Home/Error";

        public ResponseCacheMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        // THIS MUST BE FAST - CALLED ON EVERY REQUEST 
        public async Task Invoke(HttpContext context)

7 . How override ASP.NET Core Identity's password policy?
 

Answer :

No need to override any class, you have just to configure the identity settings in your startup class, like this :

services.Configure<IdentityOptions>(options =>
{
    options.Password.RequireDigit = false;
    options.Password.RequiredLength = 5;
    options.Password.RequireLowercase = true;
    options.Password.RequireNonLetterOrDigit = true;
    options.Password.RequireUppercase = false;
});

Or you can configure identity when you add it :

services.AddIdentity<ApplicationUser, IdentityRole>(options=> {
                options.Password.RequireDigit = false;
                options.Password.RequiredLength = 4;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = false;
                options.Password.RequireLowercase = false;
            })
                .AddEntityFrameworkStores<SecurityDbContext>()
                .AddDefaultTokenProviders();

8 . Debug Typescript in VSCode with ASP.Net Core?
 

Answer :

As mentioned you will need the Extension Debugger for Chrome Link to Debugger for Chrome

Then you have to add a configuration in your launch.json. Something like this:

{
    "name": "Launch Chrome",
    "type": "chrome",
    "request": "launch",
    "url": "http://localhost:5000",
    "webRoot": "${workspaceRoot}/wwwroot"
}

Since last November we are able to launch multiple debuggers in VS Code. For this you have to add a "compounds" (source) section to your launch.json where you mention the different configuration names which you want to start all together.

Here is my final launch.json. Be aware of that i have set the "launchBrowser" "enabled" property to false in the ".NET Core Launch (web)" configuration to prevent this configuration from opening a new browser tab since we open a new Broser with the debugger attached in the "Launch Chrome"configuration.

{
 "version": "0.2.0",
 "compounds": [
    {
        "name": "ASP.Net Core & Browser",
        "configurations": [".NET Core Launch (web)", "Launch Chrome"]
    }
 ],
 "configurations": [
    {
        "name": ".NET Core Attach",
        "type": "coreclr",
        "request": "attach",
        "processId": "${command:pickProcess}"
    },
    {
        "name": ".NET Core Launch (web)",
        "type": "coreclr",
        "request": "launch",
        "preLaunchTask": "build",
        "program": "${workspaceRoot}/bin/Debug/netcoreapp1.1/VoteExample.dll",
        "args": [],
        "cwd": "${workspaceRoot}",
        "stopAtEntry": false,
        "launchBrowser": {
            "enabled": false,
            "args": "${auto-detect-url}",
            "windows": {
                "command": "cmd.exe",
                "args": "/C start ${auto-detect-url}"
            },
            "osx": {
                "command": "open"
            },
            "linux": {
                "command": "xdg-open"
            }
        },
        "env": {
            "ASPNETCORE_ENVIRONMENT": "Development"
        },
        "sourceFileMap": {
            "/Views": "${workspaceRoot}/Views"
        }
    },
    {
        "name": "Launch Chrome",
        "type": "chrome",
        "request": "launch",
        "url": "http://localhost:5000",
        "webRoot": "${workspaceRoot}/wwwroot"
    }
 ]
}

Now we are able to debug both, the ASP.NET Core Application and the Client side code within one Visual Studio Code Window. The ".NET Core Attach" Configuration section is not needed, but sometimes i want to attach the debugger on a running proces


9 . How to read ASP.NET Core Response.Body?
 

Answer :

In my original response I had totally misread the question and thought the poster was asking how to read the Request.Body But he had asked how to read the Response.Body. I'm leaving my original answer to preserve history but also updating it to show how I would answer the question once reading it correctly.

Original Answer

If you want a buffered stream that supports reading multiple times you need to set

   context.Request.EnableRewind()

Ideally do this early in the middleware before anything needs to read the body.

So for example you could place the following code in the beginning of the Configure method of the Startup.cs file:

        app.Use(async (context, next) => {
            context.Request.EnableRewind();
            await next();
        });

Prior to enabling Rewind the stream associated with the Request.Body is a forward only stream that doesn't support seeking or reading the stream a second time. This was done to make the default configuration of request handling as lightweight and performant as possible. But once you enable rewind the stream is upgrade to a stream that supports seeking and reading multiple times. You can observe this "upgrade" by setting a breakpoint just before and just after the call to EnableRewindand observing the Request.Body properties. So for example Request.Body.CanSeek will change from false to true.

Then to read the body stream you could for example do this:

   string bodyContent = new StreamReader(Request.Body).ReadToEnd();

Don't wrap the StreamReader creation in a using statement though or it will close the underlying body stream at the conclusion of the using block and code later in the request lifecycle wont be able to read the body.

Also just to be safe, it might be a good idea to follow the above line of code that reads the body content with this line of code to reset the body's stream position back to 0.

request.Body.Position = 0;

That way any code later in the request lifecycle will find the request.Body in a state just like it hasn't been read yet.

Updated Answer

Sorry I originally misread your question. The concept of upgrading the associated steam to be a buffered steam still applies. However you do have to do it manually, I'm unaware of any built in .Net Core functionality that lets you read the response stream once written in the way that EnableRewind() lets a developer reread the request stream after it's been read.

Your "hacky" approach is likely totally appropriate. You are basically converting a stream that can't seek to one that can. At the end of the day the Response.Body stream has to get swapped out with a stream that is buffered and supports seeking. Here is another take on middleware to do that but you will notice it's quite similar to your approach. I did however choose to use a finally block as added protection for putting the original stream back on the Response.Body and I used the Positionproperty of the stream rather than the Seek method since the syntax is a bit simpler but the effect is no different than your approach.

public class ResponseRewindMiddleware {
        private readonly RequestDelegate next;

        public ResponseRewindMiddleware(RequestDelegate next) {
            this.next = next;
        }

        public async Task Invoke(HttpContext context) {

            Stream originalBody = context.Response.Body;

            try {
                using (var memStream = new MemoryStream()) {
                    context.Response.Body = memStream;

                    await next(context);

                    memStream.Position = 0;
                    string responseBody = new StreamReader(memStream).ReadToEnd();

                    memStream.Position = 0;
                    await memStream.CopyToAsync(originalBody);
                }

            } finally {
                context.Response.Body = originalBody;
            }

        } 

10 . Asp.Net Core Post FromBody Always Null?
 

Answer :

You get always null because you need to encapsulate all your post variables inside only one object. Like this:

public class MyPostModel {
    public List<string> userSocs {get; set;}
    public int collegeId {get; set;}
}

and then

public async Task<IActionResult> GetStudentResults([FromBody] MyPostModel postModel)


11 . ASP.NET Core Default route no working?

Answer :

Just remove the [Route("[controller]")] decoration on the controller.

public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}

With the default routing you registered in the UseMvc method with the conventional route patterns, now it should work for yourBaseUrl and yourBaseUrl\Home and yourBaseUrl\Home\Index

Typically you use the [Route("[controller]")] attribute on a controller level as a route prefix for all the routes on that controller to create custom attribute route definitions for your action methods.

[Route("[controller]")]
public class HomeController : Controller
{
    [Route("myseofriendlyurlslug")]
    public IActionResult Index()
    {
        return View();
    }
}

Now your action method will be accessible via yourBaseUrl/Home/myseofriendlyurlslug

Keep in mind that, when using attribute routing like above, the conventional routing pattern won't work.


12 . ASP.NET Core 2.0 Dynamic Authentication?
 

Answer :

I believe you can achieve your goal assigning function to RedirectToIdentityProvider property.

Invoked before redirecting to the identity provider to authenticate. This can be used to set ProtocolMessage.State that will be persisted through the authentication process. The ProtocolMessage can also be used to add or customize parameters sent to the identity provider.

public void ConfigureServices(IServiceCollection services)
{
    services
    .AddAuthentication()
    .AddOpenIdConnect(options =>
        {
            options.Events.OnRedirectToIdentityProvider = context =>
             {
                  // Retrieve identity from current HttpContext
                  var identity = context.HttpContext.User.Identity;

                  // Lookup for your client_id and client_secret
                  var clientId = "find your client id";
                  var clientSecret = "find your client secret";

                  // Assign client_id and client_secret
                  context.ProtocolMessage.ClientId = clientId;
                  context.ProtocolMessage.ClientSecret = clientSecret;

                  return Task.FromResult(0);
              };
         });
}

13 . How to use SqlClient in ASP.NET Core?

Answer :

Instead of referencing System.Data and System.Data.SqlClient you need to grab from Nuget:

System.Data.Common and System.Data.SqlClient.

Currently this creates dependency in project.json –> aspnetcore50 section to these two libraries.

"aspnetcore50": {
       "dependencies": {
           "System.Runtime": "4.0.20-beta-22523",
           "System.Data.Common": "4.0.0.0-beta-22605",
           "System.Data.SqlClient": "4.0.0.0-beta-22605"
       }
}

Getting System.Data.Common and System.Data.SqlClient via Nuget and see if this adds the above dependencies for you, but in a nutshell you are missing System.Runtime.


14 . ASP.NET Core RC2 Area not published?
 

Answer :

You need to configure your publishOptions section of project.json to include the Areas folder which is not included in the default template:

ex:

"publishOptions": {
  "include": [
    "wwwroot",
    "Views",
    "appsettings.json",
    "web.config",
    "Areas"
  ],
  "exclude": [ "bin" ]
}

Update

If you want to ensure that your controllers and other .cs files are not included, you can blacklist with the exclude property of publishOptions like so:

"publishOptions": {
  "include": [ "wwwroot", "Views", "appsettings.json", "web.config", "Areas" ],
  "exclude": [ "**.user", "**.vspscc", "**.cs", "bin" ]
}

If you prefer more restrictive security, you can simply whitelist .cshtml files instead of including the entire Areas folder like so:

"publishOptions": {
  "include": [ "wwwroot", "**.cshtml", "appsettings.json", "web.config" ],
  "exclude": [ "bin" ]
}

Note

Be careful using wildcards like **.cshtml as they will include all files in all subdirectories, including the bin directory. If you have any views in your bin folder from a previous build, they will be duplicated again inside the new build output until the path becomes too long.


15 . Missing “ASP.NET Core Web Application (.NET Framework)” template?
 

Answer :

Click on .Net Core on left side menu. Then you will see all the .net core templates. Select the ASP.NET Core Web Application and click Ok.

enter image description here

After this you will see project template selection dialog. Here you can chose either .Net Core or .Net Framework for project template.

enter image description here