European ASP.NET 4.5 Hosting BLOG

BLOG about ASP.NET 4, ASP.NET 4.5 Hosting and Its Technology - Dedicated to European Windows Hosting Customer

European ASP.NET Core Hosting :: Error When Published ASP.NET Core? See Below Tips!

clock November 5, 2019 05:38 by author Scott

At the past few years, we have discussed about common error that you can find when published .NET Core, the most common error is 502.5 – process failure error.

Startup errors with ASP.NET Core don’t provide much information either, at least not in a production environment. Here are 7 tips for understanding and fixing those errors.

1. There are two types of startup errors.

There are unhandled exceptions originating outside of the Startup class, and exceptions from inside of Startup. These two error types can produce different behavior and may require different troubleshooting techniques.

2. ASP.NET Core will handle exceptions from inside the Startup class.

If code in the ConfigureServices or Configure methods throw an exception, the framework will catch the exception and continue execution.

Although the process continues to run after the exception, every incoming request will generate a 500 response with the message “An error occurred while starting the application”.

Two additional pieces of information about this behavior:

- If you want the process to fail in this scenario, call CaptureStartupErrors on the web host builder and pass the value false.

- In a production environment, the “error occurred” message is all the information you’ll see in a web browser. The framework follows the practice of not giving away error details in a response because error details might give an attacker too much information. You can change the environment setting using the environment variable ASPNETCORE_ENVIRONMENT, but see the next two tips first. You don’t have to change the entire environment to see more error details.

3. Set detailedErrors in code to see a stack trace.

The following bit of code allows for detailed error message, even in production, so use with caution.

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
           .CaptureStartupErrors(true) // the default
           .UseSetting("detailedErrors", "true")
           .UseStartup<Startup>();

4. Alternatively, set the ASPNETCORE_DETAILEDERRORS environment variable.

Set the value to true and you’ll also see a stack trace, even in production, so use with caution.

5. Unhandled exceptions outside of the Startup class will kill the process.

Perhaps you have code inside of Program.cs to run schema migrations or perform other initialization tasks which fail, or perhaps the application cannot bind to the desired ports. If you are running behind IIS, this is the scenario where you’ll see a generic 502.5 Process Failure error message.

These types of errors can be a bit more difficult to track down, but the following two tips should help.

6. For IIS, turn on standard output logging in web.config.

If you are carefully logging using other tools, you might be able to capture output there, too, but if all else fails, ASP.NET will write exception information to stdout by default. By turning the log flag to true, and creating the output directory, you’ll have a file with exception information and a stack trace inside to help track down the problem.

The following shows the web.config file created by dotnet publish and is typically the config file in use when hosting .NET Core in IIS. The attribute to change is the stdoutLogEnabled flag.

<system.webServer>
  <handlers>
    <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />

  </handlers>
  <aspNetCore processPath="dotnet" arguments=".\codes.dll"
              stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" />
</system.webServer>

Important: Make sure to create the logging output directory.

Important: Make sure to turn logging off after troubleshooting is complete.

7. Use the dotnet CLI to run the application on your server.

If you have access to the server, it is sometimes easier to go directly to the server and use dotnet to witness the exception in real time. There’s no need to turn on logging or set and unset environment variables.

Summary

Debugging startup errors in ASP.NET Core is a simple case of finding the exception. In many cases, #7 is the simplest approach that doesn’t require code or environment changes. FYI, we also have support latest ASP.NET Core on our hosting environment. Feel free to visit our site at https://www.hostforlife.eu.



European ASP.NET Core 3 Hosting :: Simple Steps to Migrate Your ASP.NET Core 2 to ASP.NET Core 3

clock October 25, 2019 07:18 by author Scott

.NET Core always interesting. In the past few months, we have just launched ASP.NET Core 2.2 on our hosting environment, and now Microsoft has support latest ASP.NET Core 3. This post will be taking the Contacts project used in the ASP.NET Basics series and migrating it from .NET Core 2.2 to .NET Core 3.0.

Installation

If you are a Visual Studio user you can get .NET Core 3.0 by installing at least Visual Studio 16.3. For those not using Visual Studio, you can download and install .NET Core 3.0 SDK from here. As with previous versions, the SDK is available for Windows, Linux, and Mac.

After installation is complete you can runt the following command from a command prompt to see all the versions of the .NET Core SDK you have installed.

dotnet --list-sdks

You should see 3.0.100 listed. If you are like me you might also see a few preview versions of the SDK that can be uninstalled at this point.

Project File Changes

Right-click on the project and select Edit projectName.csproj.

Change the TargetFramework to netcoreapp3.0.

Before:
<TargetFramework>netcoreapp2.2</TargetFramework>

After:
<TargetFramework>netcoreapp3.0</TargetFramework>

The packages section has a lot of changes. Microsoft.AspNetCore.App is now gone and part of .NET Core without needing a specific reference. The other thing to note is that Entity Framework Core is no longer “in the box” so you will see a lot of references add to make Entity Framework Core usable.

Before:
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="4.0.1" />

After:
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="3.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.0.0" PrivateAssets="All" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc4" />

The last thing to note is that Swashbuckle doesn’t have a final version ready for .NET Core 3 so you will have to make sure you are using version 5 rc2 at a minimum.

The following is my full project file for reference.

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
    <UserSecretsId>aspnet-Contacts-cd2c7b27-e79c-43c7-b3ef-1ecb04374b70</UserSecretsId>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="3.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="3.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.0.0" />
    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.0.0" PrivateAssets="All" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc4" />
  </ItemGroup>
</Project>

Program Changes

In Program.cs some changes to the way the host is constructed. The over version may or may not have worked, but I created a new app and pulled this out of it just to make sure I’m using the current set up.

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Contacts
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                                          {
                                              webBuilder.UseStartup<Startup>();
                                          });
    }
}

Startup Changes

In Startup.cs we have quite a few changes to make. As long as you haven’t do any customization in the constructor you can replace it with the following.

public Startup(IConfiguration configuration)
{
        Configuration = configuration;
}

Next, they type on the configuration property changed from IConfigurationRoot to IConfiguration.

Before:
public IConfigurationRoot Configuration { get; }

After:
public IConfiguration Configuration { get; }

Moving on to the ConfigureServices function has a couple of changes to make. The first is a result of updating to the newer version of the Swagger package where the Info class has been replaced with OpenApiInfo.

Before:
services.AddSwaggerGen(c =>
{
        c.SwaggerDoc("v1", new Info { Title = "Contacts API", Version = "v1"});
});

After:
services.AddSwaggerGen(c =>
{
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "Contacts API", Version = "v1" })
});

Next, we are going to move from using UserMvc to the new AddControllersWithViews which is one of the new more targeted ways to add just the bits of the framework you need.

Before:
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

After:
services.AddControllersWithViews();

Now in the Configure function, the function signature needs to be updated and the logging factory bits removed. If you do need to configure logging that should be handled as part of the HostBuilder.

Before:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
      loggerFactory.AddDebug();

After:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{

For the next set of changes, I’m just going to show the result and not the before. The UseCors may or may not apply but the addition of UserRouting and the replacement of UseMvc with UserEndpoint will if you want to use the new endpoint routing features.

app.UseStaticFiles();
app.UseRouting();
app.UseCors(builder =>
            {
                builder.AllowAnyHeader();
                builder.AllowAnyMethod();
                builder.AllowAnyOrigin();
            }
           );
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
                 {
                     endpoints.MapControllerRoute(
                                                  name: "default",
                                                  pattern: "{controller=Home}/{action=Index}/{id?}");
                     endpoints.MapRazorPages();
                 });

Other Miscellaneous Changes

The only other change I had was the removal of @using Microsoft.AspNetCore.Http.Authentication in a few cshtml files related to login.

 



European ASP.NET Core 3 Hosting :: How to Enable GRPC Compression in ASP.NET Core 3

clock October 22, 2019 09:41 by author Scott

How Do I Enable Response Compression with GRPC?

There are two main approaches that I’ve found so far to enable the compression of gRPC responses. You can either configure this at the server level so that all gRPC services apply compression to responses, or at a per-service level.

Server Level Options

services.AddGrpc(o =>
{
    o.ResponseCompressionLevel = CompressionLevel.Optimal;
    o.ResponseCompressionAlgorithm = "gzip";
});

When registering the gRPC services into the dependency injection container with the AddGrpc method inside ConfigureServices, it’s possible to set properties on the GrpcServiceOptions. At this level, the options affect all gRPC services which the server implements.

Using the overload of the AddGrpc extension method, we can supply an Action<GrpcServiceOptions>. In the above snippet we’ve set the compression algorithm to “gzip”. We can also optionally control the CompressionLevel which trades the time needed to compress the data against the final size that is achieved by compression. If not specified the current implementation defaults to using CompressionLevel.Fastest. In the preceding snippet, we’ve chosen to allow more time for the compression to reduce the bytes to the smallest possible size.

Service Level Options

services.AddGrpc()
    .AddServiceOptions<WeatherService>(o =>
        {
            o.ResponseCompressionLevel = CompressionLevel.Optimal;
            o.ResponseCompressionAlgorithm = "gzip";
        });

After calling AddGrpc, an IGrpcServerBuilder is returned. We can call an extension method on that builder called AddServiceOptions to provide per service options. This method is generic and accepts the type of the gRPC service that the options should apply.

In the preceding example, we have decided to provide options specifically for calls that are handled by the WeatherService implementation. The same options are available at this level as we discussed for the server level configuration. In this scenario, if we mapped other gRPC services within this server, they would not receive the compression options.

Making Requests from A GRPC Client

Now that response compression is enabled, we need to ensure our requests state that our client accepts compressed content. In fact, this is enabled by default when using a GrpcChannel created using the ForAddress method so we have nothing to do in our client code.

var channel = GrpcChannel.ForAddress("https://localhost:5005");

Channels created in this way already send a “grpc-accept-encoding” header which includes the gzip compression type. The server reads this header and determines that the client allows gzipped responses to be returned.

One way to visualise the effect of compression is to enable trace level logging for our application while in development. We can achieve this by modifying the appsettings.Development.json file as follows:

{
  "Logging": {
    "LogLevel": {
        "Default": "Debug",
        "System": "Information",
        "Grpc": "Trace",
        "Microsoft": "Trace"
    }
  }
}

When running our server, we now get much more verbose console logging.

info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
      Executing endpoint 'gRPC - /WeatherForecast.WeatherForecasts/GetWeather'
dbug: Grpc.AspNetCore.Server.ServerCallHandler[1]
      Reading message.
dbug: Microsoft.AspNetCore.Server.Kestrel[25]
      Connection id "0HLQB6EMBPUIA", Request id "0HLQB6EMBPUIA:00000001": started reading request body.
dbug: Microsoft.AspNetCore.Server.Kestrel[26]
      Connection id "0HLQB6EMBPUIA", Request id "0HLQB6EMBPUIA:00000001": done reading request body.
trce: Grpc.AspNetCore.Server.ServerCallHandler[3]
      Deserializing 0 byte message to 'Google.Protobuf.WellKnownTypes.Empty'.
trce: Grpc.AspNetCore.Server.ServerCallHandler[4]
      Received message.
dbug: Grpc.AspNetCore.Server.ServerCallHandler[6]
      Sending message.
trce: Grpc.AspNetCore.Server.ServerCallHandler[9]
      Serialized 'WeatherForecast.WeatherReply' to 2851 byte message.
trce: Microsoft.AspNetCore.Server.Kestrel[37]
      Connection id "0HLQB6EMBPUIA" sending HEADERS frame for stream ID 1 with length 104 and flags END_HEADERS
trce: Grpc.AspNetCore.Server.ServerCallHandler[10]
      Compressing message with 'gzip' encoding.
trce: Grpc.AspNetCore.Server.ServerCallHandler[7]
      Message sent.
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
      Executed endpoint 'gRPC - /WeatherForecast.WeatherForecasts/GetWeather'
trce: Microsoft.AspNetCore.Server.Kestrel[37]
      Connection id "0HLQB6EMBPUIA" sending DATA frame for stream ID 1 with length 978 and flags NONE
trce: Microsoft.AspNetCore.Server.Kestrel[37]
      Connection id "0HLQB6EMBPUIA" sending HEADERS frame for stream ID 1 with length 15 and flags END_STREAM, END_HEADERS
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 2158.9035ms 200 application/grpc

On line 16 of this log, we can see that the WeatherReply, essentially an array of 100 WeatherData items in this sample, has been serialised to protocol buffers and has a size of 2851 bytes.

Later, in line 20, we can see that the message has been compressed with gzip encoding and on line 26, we can see the size of the data frame for this call which is 978 bytes. The data, in this case, has compressed quite well (66% reduction) because the repeated WeatherData items contain text and many of the values repeat within the message.

In this example, gzip compression has a good effect on the over the wire size of the data.

Disable Response Compression within A Service Method Implementation

It’s possible to control the response compression on a per-method basis. At this time, I’ve only found a way to do this on an opt-out approach. When compression is enabled for a service or server, we can opt-out of compression within the service method implementation.

Let’s look at the server log output when calling a service method which streams WeatherData messages from the server.

info: WeatherForecast.Grpc.Server.Services.WeatherService[0]
      Sending WeatherData response
dbug: Grpc.AspNetCore.Server.ServerCallHandler[6]
      Sending message.
trce: Grpc.AspNetCore.Server.ServerCallHandler[9]
      Serialized 'WeatherForecast.WeatherData' to 30 byte message.
trce: Grpc.AspNetCore.Server.ServerCallHandler[10]
      Compressing message with 'gzip' encoding.
trce: Microsoft.AspNetCore.Server.Kestrel[37]
      Connection id "0HLQBMRRH10JQ" sending DATA frame for stream ID 1 with length 50 and flags NONE
trce: Grpc.AspNetCore.Server.ServerCallHandler[7]
      Message sent.

On line 6, we can see that an individual WeatherData message is 30 bytes in size. On line 8, this gets compressed, and on line 10, we can see that the data length is now 50 bytes, larger than the original message. In this case, there is no gain from gzip compression, and we see an increase in the overall message size sent over the wire.

We can avoid compression for a particular message by setting the WriteOptions for the call within the service method.

public override async Task GetWeatherStream(Empty _, IServerStreamWriter<WeatherData> responseStream, ServerCallContext context)
{
    context.WriteOptions = new WriteOptions(WriteFlags.NoCompress);

    // implementation of the method which writes to the stream
}

At the top of our service method, we can set the WriteOptions on the ServerCallContext. We pass in a new WriteOptions instance which the WriteFlags value set to NoCompress. These write options are used for the next write.

With streaming responses, it’s also possible to set this value on the IServerStreamWriter.

public override async Task GetWeatherStream(Empty _, IServerStreamWriter<WeatherData> responseStream, ServerCallContext context)
{   
    responseStream.WriteOptions = new WriteOptions(WriteFlags.NoCompress);

    // implementation of the method which writes to the stream
}

When this option is applied, the logs now show that compression for calls to this service method is not applied.

info: WeatherForecast.Grpc.Server.Services.WeatherService[0]
      Sending WeatherData response
dbug: Grpc.AspNetCore.Server.ServerCallHandler[6]
      Sending message.
trce: Grpc.AspNetCore.Server.ServerCallHandler[9]
      Serialized 'WeatherForecast.WeatherData' to 30 byte message.
trce: Microsoft.AspNetCore.Server.Kestrel[37]
      Connection id "0HLQBMTL1HLM8" sending DATA frame for stream ID 1 with length 35 and flags NONE
trce: Grpc.AspNetCore.Server.ServerCallHandler[7]
      Message sent.

Now the 30 byte message has a length of 35 bytes in the DATA frame. There is a small overhead which accounts for the extra 5 bytes which we don’t need to concern ourselves with here.

Disable Response Compression from A GRPC Client

By default, the gRPC channel includes options that control which encodings it accepts. It is possible to configure these when creating the channel if you wish to disable compression of responses from your client. Generally, I would avoid this and let the server decide what to do, since it knows best what can and cannot be compressed. That said, you may sometimes need to control this from the client.

The only way I’ve found to do this in my exploration of the API to date is to configure the channel by passing in a GrpcChannelOptions instance. One of the properties on this options class is for the CompressionProviders, an IList<ICompressionProvider>. By default, when this is null, the client implementation adds the Gzip compression provider for you automatically. This means that the server can choose to gzip the response message(s) as we have already seen.

private static async Task Main()
{
    using var channel = GrpcChannel.ForAddress("https://localhost:5005", new GrpcChannelOptions { CompressionProviders = new List<ICompressionProvider>() });

    var client = new WeatherForecastsClient(channel);

    var reply = await client.GetWeatherAsync(new Empty());

    foreach (var forecast in reply.WeatherData)
    {
        Console.WriteLine($"{forecast.DateTimeStamp.ToDateTime():s} | {forecast.Summary} | {forecast.TemperatureC} C");
    }

    Console.WriteLine("Press a key to exit");
    Console.ReadKey();
}

In this sample client code, we establish the GrpcChannel and pass in a new instance of GrpcChannelOptions. We set the CompressionProviders property to an empty list. Since we now specify no providers in our channel, when the calls are created and sent via this channel, they won’t include any compression encodings in the “grpc-accept-encoding” header. The server acknowledges this and not apply gzip compression to the response.

Summary

In this post, we’ve explored the possibility of compressing response messages from a gRPC server. We’ve identified that in some cases, but crucially not all, this may result in smaller payloads. We’ve seen that by default clients calls include the gzip “grpc-accept-encoding” value in the headers. If the server is configured to apply compression, it only does so if a supported encoding type is matched from the request header.

We can configure the GrpcChannelOptions when creating a channel for the client, to disable the inclusion of the gzip compression encoding. On the server, we can configure the whole server, or a specific service to enable compression for responses. We can override and disable that on a per service-method level as well.



European ASP.NET Core 3 Hosting :: Custom JSONConverter ASP.NET Core 3

clock October 17, 2019 07:17 by author Scott

With the introduction of ASP.NET Core 3.0 the default JSON serializer has been changed from Newtonsoft.Json to System.Text.Json. For projects and libraries switching to the new JSON serializer this change means more performance and the opportunity to rewrite our JsonConverters.

Serialization of concrete classes

Let's start with a simple one that can (de)serialize a concrete class Category. In our example we (de)serialize the property Name only.

public class Category
{
   public string Name { get; }

   public Category(string name)
   {
      Name = name;
   }
}

To implement a custom JSON converter we have to derive from the generic class JsonConverter<T> and to implement 2 methods: Read and Write.

public class CategoryJsonConverter : JsonConverter<Category>
{
   public override Category Read(ref Utf8JsonReader reader,
                                 Type typeToConvert,
                                 JsonSerializerOptions options)
   {
      var name = reader.GetString();

      return new Category(name);
   }

   public override void Write(Utf8JsonWriter writer,
                              Category value,
                              JsonSerializerOptions options)
   {
      writer.WriteStringValue(value.Name);
   }
}

The method Read is using the Utf8JsonReader to fetch a string, i.e. the name, and the method Write is writing a string using an instance of Utf8JsonWriter.

In both cases (i.e. during serialization and deserialization) the converter is not being called if the value is null so I skipped the null checks. The .NET team doesn't do null checks either, see JsonKeyValuePairConverter<TKey, TValue>.

Let's test the new JSON converter. For that we create an instance of JsonSerializerOptions and add our CategoryJsonConverter to the Converters collection. Next, we use the static class JsonSerializer to serialize and to deserialize an instance of Category.

Category category = new Category("my category");

var serializerOptions = new JsonSerializerOptions
{
    Converters = { new CategoryJsonConverter() }
};

// json = "my category"
var json = JsonSerializer.Serialize(category, serializerOptions);

// deserializedCategory.Name = "my category"
var deserializedCategory = JsonSerializer.Deserialize<Category>(json, serializerOptions);

Serialization of generic classes

The next example is slightly more complex. The property we are serializing is a generic type argument, i.e. we can't use methods like reader.GetString() or writer.WriteStringValue(name) because we don't know the type at compile time.

In this example I've changed the class Category to a generic type and renamed the property Name to Key:

public class Category<T>
{
   public T Key { get; }

   public Category(T key)
   {
      Key = key;
   }
}

For serialization of the generic property Key we need to fetch a JsonSerializer<T> using the instance of JsonSerializerOptions.

public class CategoryJsonConverter<T> : JsonConverter<Category<T>>
{
   public override Category<T> Read(ref Utf8JsonReader reader,
                                    Type typeToConvert,
                                    JsonSerializerOptions options)
   {
      var converter = GetKeyConverter(options);
      var key = converter.Read(ref reader, typeToConvert, options);

      return new Category<T>(key);
   }

   public override void Write(Utf8JsonWriter writer,
                              Category<T> value,
                              JsonSerializerOptions options)
   {
      var converter = GetKeyConverter(options);
      converter.Write(writer, value.Key, options);
   }

   private static JsonConverter<T> GetKeyConverter(JsonSerializerOptions options)
   {
      var converter = options.GetConverter(typeof(T)) as JsonConverter<T>;

      if (converter is null)
         throw new JsonException("...");

      return converter;
   }
}

The behavior of the generic JSON converter is the same as before especially if the Key is of type string.

Deciding the concrete JSON converter at runtime

Having several categories with different key types, say, string and int, we need to register them all with the JsonSerializerOptions.

var serializerOptions = new JsonSerializerOptions
                        {
                           Converters =
                           {
                              new CategoryJsonConverter<string>(),
                              new CategoryJsonConverter<int>()
                           }
                        };

If the number of required CategoryJsonConverters grows to big or the concrete types of the Key are not known at compile time then this approach is not an option. To make this decision at runtime we need to implement a JsonConverterFactory. The factory has 2 method: CanConvert(type) that returns true if the factory is responsible for the serialization of the provided type; and CreateConverter(type, options) that should return an instance of type JsonConverter.

public class CategoryJsonConverterFactory : JsonConverterFactory
{
   public override bool CanConvert(Type typeToConvert)
   {
      if (!typeToConvert.IsGenericType)
         return false;

      var type = typeToConvert;

      if (!type.IsGenericTypeDefinition)
         type = type.GetGenericTypeDefinition();

      return type == typeof(Category<>);
   }

   public override JsonConverter CreateConverter(Type typeToConvert,
                                                 JsonSerializerOptions options)
   {
      var keyType = typeToConvert.GenericTypeArguments[0];
      var converterType = typeof(CategoryJsonConverter<>).MakeGenericType(keyType);

      return (JsonConverter)Activator.CreateInstance(converterType);
   }
}

Now, we can remove all registrations of the CategoryJsonConverter<T> from the options and add the newly implemented factory.

Category<int> category = new Category<int>(42);

var serializerOptions = new JsonSerializerOptions
{
    Converters = { new CategoryJsonConverterFactory() }
};

// json = 42
var json = JsonSerializer.Serialize(category, serializerOptions);

// deserialized.Key = 42
var deserialized = JsonSerializer.Deserialize<Category<int>>(json, serializerOptions);

In the end the implementation of a custom converter for System.Text.Json is very similar to the one for Newtonsoft.Json. The biggest difference here is the non-existence of a non-generic JsonConverter but for that we've got the JsonConverterFactory.

Actually, there is a non-generic JsonConverter which is the base class of the JsonConverter<T> and the JsonConverterFactory but we cannot (and should not) use this class directly because its constructor is internal.



European ASP.NET Core Hosting :: Knowing about WinForms and .NET Core 3.0

clock October 11, 2019 09:29 by author Scott

Desktop support for .NET Core 3.0 has been in preview for some time, it is slated to be released later this year. As per indicators by Microsoft, .NET Core will receive more updates compared to full .NET Framework, keeping this in mind, it makes sense to start thinking about migrating existing applications and creating new ones on .NET Core if the application still has some years to go.

Desktop support in .NET Core has not been completely migrated yet; however, it is possible to create and migrate WinForms applications using the preview bits. In this article, we'll consider the creation and migration of a WinForms application in .NET Core 3.0 that uses ComponentOne controls.

Creating a New .NET Core 3.0 Application

To create a new .NET Core 3.0 application, it's recommended to download VS 2019 and the nightly build of the .NET Core 3.0 SDK. Make sure that you’ve installed the .NET Framework 4.7.2 and .NET Core 2.1 development tools in the Visual Studio Installer. This software will be necessary whether you plan on creating a new project or migrating an existing one to .NET Core 3.0.

The simplest way to work with .NET Core 3.0 (for now) is to use dotnet.exe command line utility. It can be used to create a new project, add/restore dependencies, build, etc.

.NET Core 3 - Using x64 and x86 Versions

Visual Studio uses the appropriate version depending on your .NET Core 3 project’s target platform – x86 or x64. It uses last installed version for both platforms. So, if you intend to build your project for both platforms, you must download and install both platform SDK versions with the same version.

These are the typical paths for .NET Core 3 command line utility:

  • c:\Program Files\dotnet\dotnet.exe - for x64 version of .Net Core 3
  • c:\Program Files (x86)\dotnet\dotnet.exe- for x86 version of .Net Core 3

Let’s use x64 version of dotnet.exe to simplify it for further description.

Create a New Visual Studio Project

First, you’ll need to open the Visual Studio Developer Command Prompt to create a project through the command line. Currently, the tooling for Visual Studio is very limited in its support of .NET Core 3.0, so it’s easiest to create a project through the command line interface.

First, we’ll create the project:

“c:\Program Files\dotnet\dotnet.exe” **new** winforms -o TestAppWinFormsCore

Once this is finished, we’ll navigate into the project directory:

cd TestAppWinFormsCore

Finally, we can run the project to make sure that it works:

“c:\Program Files\dotnet\dotnet.exe” run

Now that the project has been created, we can use Visual Studio 2019 to open it and modify its contents. 

In the Visual Studio 2019, open the TestAppWinFormsCore.csproj file we just created.

You should see something like this in Visual Studio:

.NET Core 3.0 uses a new project type and has no design-time support currently, so the process of adding controls will be a little different than what you may be accustomed to. We need to add C1 libraries manually to the project to make use of them, also, we should add the controls manually to Form1.cs because of missing design-time support.

Because of this, let’s use a trick. We'll add the classic .NET Framework WinForms project in order to use its Form designer:

1. Right-click on Solution node and Add – New Project… - Windows Forms Desktop project (with .Net Framework, say version 4.0). The name of new project is by default WindowsFormsApp1.

2. Remove the existing Form1 from WindowsFormsApp1 project

3. Add Form1.cs from TestAppWinFoirmsCore project as link:

After that, the following view of Solution Explorer appears:

Now, we can edit Form1 form in the usual way:

Now, remove unneeded “Hello .NET Core!” label and add some C1 control, for example C1DockingTab:

Launch WindowsFormsApp1 application and observe C1DockingTab on Form1. But this will be a .Net Framework 4 application.

Adding C1DockingTab caused adding C1.Win.C1Command.4 reference to WindowsFormsApp1 project. Therefore, the same reference should be added to TestAppWinFormsCore project also. For this let’s take a look in the properties of the C1.Win.C1Command.4 reference (right-click – Properties…) and find the path to the assembly. It looks like C:\Program Files (x86)\ComponentOne\WinForms Edition\bin\v4.0\C1.Win.C1Command.4.dll. Add this reference to the TestAppWinFormsCore project (right-click on Dependencies – Add reference… - Browse… - choose C:\Program Files (x86)\ComponentOne\WinForms Edition\bin\v4.0\C1.Win.C1Command.4.dll).

You should be able to run the code at this point, though you’ll notice a ComponentOne nag screen since the TestAppWinFormsCore project doesn’t contain any licensing information.

We can correct this by adding a licenses.licx file to the project. To do so, right-click on TestAppWinFormsCore project and select Add -> Existing Items. And browse the licenses.licx file from WindowsFormsApp1/Properties folder.

Build and rerun the app and you should no longer see a nag screen.

And now this is a genuine .NET Core 3.0 WinForms app. You can see this in the Modules window. All system references are from C:\Program Files\dotnet... folder.

Migrating an Existing Project to .NET Core 3.0

It is possible to migrate an existing project to .NET Core 3.0, though the process may require a few more steps than you would expect. Also, before trying to migrate a project, it’s worth running Microsoft’s portability analyzer tool on the project you want to convert.

The tool can give you an idea (ahead of time) how compatible your project will be with .NET Core 3.0, and it points out potential problems you may run into (included unsupported APIs).

If you’ve made up your mind to try to migrate a project, it’s easiest to begin the process by creating a project through dotnet.exe as described above.Let’s look at the WeatherChart sample migration. This sample demonstrates the C1FlexChart control possibilities.

First, we’ll create a new DotNetCore3FlexChart project from the command line:

“c:\Program Files\dotnet\dotnet.exe” **new** winforms -o DotNetCore3FlexChart

Once this is finished, we’ll navigate into the project directory and open DotNetCore3FlexChart.csproj from Visual Studio 2019.

Next, add the WeatherChart project to the DotNetCore3FlexChart solution. WeatherChart project is placed in Documents\ComponentOne Samples\WinForms\C1FlexChart\CS\WeatherChart\WeatherChart\WeatherChart.csproj by default.

Here, we'll use the WeatherChart project in our .NET Core 3.0 project. Therefore, the WeatherChart project reference should be added to DotNetCore3FlexChart dependencies (right-click on Dependencies – Add Reference… - Projects - WeatherChart)

After that, open Project.cs from DotNetCore3FlexChart project and change the following line:

Application.Run(new Form1()); 

by 

Application.Run(new WeatherChart.Form1()); 

It means that the .NET Core 3.0 app (DotNetCore3FlexChart) will run WeatherChart.Form1 from WeatherChart assembly instead of own Form1\.

That’s all!

If you run the WeatherChart project it will be launched as .NET Framework 4.0 app.

But if you run the DotNetCore3FlexChart project then the WeatherChart assembly will be launched in .NET Core 3.0 environment.

.NET Core 3.0 Preview Caveats

Using the .NET Core 3.0 Preview for any kind of day-to-day work isn’t recommended at this point, as it’s obviously still work in progress. As the steps above illustrate, much of the project creation and migration processes require a lot of manual configuration from the user, and it’s possible to find some bugs and unfinished implementations presently.The lack of designer support also makes working with the preview somewhat difficult, though Microsoft has committed to adding this feature in the coming months.

Overall, .NET Core 3.0 will be an important change for developers, though this preview is merely the first step.



European ASP.NET Core 3 Hosting :: ASP.NET Core 3 Certificate Authentication

clock October 8, 2019 12:04 by author Scott

This article shows how Certificate Authentication can be implemented in ASP.NET Core 3.0. In this example, a shared self signed certificate is used to authenticate one application calling an API on a second ASP.NET Core application.

Setting up the Server

Add the Certificate Authentication using the Microsoft.AspNetCore.Authentication.Certificate NuGet package to the server ASP.NET Core application.

 

This can also be added directly in the csproj file.

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Authentication.Certificate"
      Version="3.0.0-preview6.19307.2" />
  </ItemGroup>

  <ItemGroup>
    <None Update="sts_dev_cert.pfx">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>

</Project>

The authentication can be added in the ConfigureServices method in the Startup class. This example was built using the ASP.NET Core documentation. The AddAuthentication extension method is used to define the default scheme as “Certificate” using the CertificateAuthenticationDefaults.AuthenticationScheme string. The AddCertificate method then adds the configuration for the certificate authentication. At present, all certificates are excepted which is not good and the MyCertificateValidationService class is used to do extra validation of the client certificate. If the validation fails, the request is failed and the request for the resource will be rejected.

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<MyCertificateValidationService>(); 

    services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
        .AddCertificate(options => // code from ASP.NET Core sample
        {
            options.AllowedCertificateTypes = CertificateTypes.All;
            options.Events = new CertificateAuthenticationEvents
            {
                OnCertificateValidated = context =>
                {
                    var validationService =
                        context.HttpContext.RequestServices.GetService<MyCertificateValidationService>(); 
                    if (validationService.ValidateCertificate(context.ClientCertificate))
                    {
                        var claims = new[]
                        {
                            new Claim(ClaimTypes.NameIdentifier, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer),
                            new Claim(ClaimTypes.Name, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer)
                        }; 

                        context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
                        context.Success();
                    }
                    else
                    {
                        context.Fail("invalid cert");
                    } 

                    return Task.CompletedTask;
                }
            };
        }); 

    services.AddAuthorization(); 

    services.AddControllers();
}

The AddCertificateForwarding method is used so that the client header can be specified and how the certificate is to be loaded using the HeaderConverter option. When sending the certificate with the HttpClient using the default settings, the ClientCertificate was always be null. The X-ARR-ClientCert header is used to pass the client certificate, and the cert is passed as a string to work around this.

services.AddCertificateForwarding(options =>
{
    options.CertificateHeader = "X-ARR-ClientCert";
    options.HeaderConverter = (headerValue) =>
    {
        X509Certificate2 clientCertificate = null;
        if(!string.IsNullOrWhiteSpace(headerValue))
        {
            byte[] bytes = StringToByteArray(headerValue);
            clientCertificate = new X509Certificate2(bytes);
        }

        return clientCertificate;
    };
});

The Configure method then adds the middleware. UseCertificateForwarding is added before the UseAuthentication and the UseAuthorization.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    ...     
    app.UseRouting(); 

    app.UseCertificateForwarding();
    app.UseAuthentication();
    app.UseAuthorization(); 

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

The MyCertificateValidationService is used to implement validation logic. Because we are using self signed certificates, we need to ensure that only our certificate can be used. We validate that the thumbprints of the client certificate and also the server one match, otherwise any certificate can be used and will be be enough to authenticate.

using System.IO;
using System.Security.Cryptography.X509Certificates; 

namespace AspNetCoreCertificateAuthApi
{
    public class MyCertificateValidationService
    {
        public bool ValidateCertificate(X509Certificate2 clientCertificate)
        {
            var cert = new X509Certificate2(Path.Combine("sts_dev_cert.pfx"), "1234");
            if (clientCertificate.Thumbprint == cert.Thumbprint)
            {
                return true;
            } 

            return false;
        }
    }
}

The API ValuesController is then secured using the Authorize attribute.

[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ValuesController : ControllerBase


...

The ASP.NET Core server project is deployed in this example as an out of process application using kestrel. To use the service, a certificate is required. This is defined using the ClientCertificateMode.RequireCertificate option.

public static IWebHost BuildWebHost(string[] args)
  => WebHost.CreateDefaultBuilder(args)
  .UseStartup<Startup>()
  .ConfigureKestrel(options =>
  {
    var cert = new X509Certificate2(Path.Combine("sts_dev_cert.pfx"), "1234");
    options.ConfigureHttpsDefaults(o =>
    {
        o.ServerCertificate = cert;
        o.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
    });
  })
  .Build();

Implementing the HttpClient

The client of the API uses a HttpClient which was create using an instance of the IHttpClientFactory. This does not provide a way to define a handler for the HttpClient and so we use a HttpRequestMessage to add the Certificate to the “X-ARR-ClientCert” request header. The cert is added as a string using the GetRawCertDataString method.

private async Task<JArray> GetApiDataAsync()
{
    try
    {
        var cert = new X509Certificate2(Path.Combine(_environment.ContentRootPath, "sts_dev_cert.pfx"), "1234"); 

        var client = _clientFactory.CreateClient(); 

        var request = new HttpRequestMessage()
        {
            RequestUri = new Uri("
https://localhost:44379/api/values"),
            Method = HttpMethod.Get,
        }; 

        request.Headers.Add("X-ARR-ClientCert", cert.GetRawCertDataString());
        var response = await client.SendAsync(request); 

        if (response.IsSuccessStatusCode)
        {
            var responseContent = await response.Content.ReadAsStringAsync();
            var data = JArray.Parse(responseContent); 

            return data;
        } 

        throw new ApplicationException($"Status code: {response.StatusCode}, Error: {response.ReasonPhrase}");
    }
    catch (Exception e)
    {
        throw new ApplicationException($"Exception {e}");
    }
}

If the correct certificate is sent to the server, the data will be returned. If no certificate is sent, or the wrong certificate, then a 403 will be returned. It would be nice if the IHttpClientFactory would have a way of defining a handler for the HttpClient. I also believe a non valid certificates should fail per default and not require extra validation for this. The AddCertificateForwarding should also not be required to use for a default HTTPClient client calling the service.

Certificate Authentication is great, and helps add another security layer which can be used together with other solutions. See the code and ASP.NET Core src code for further documentation and examples. Links underneath. 



European ASP.NET Core Hosting :: File Upload in Angular 7

clock September 20, 2019 10:52 by author Scott

In this article, we will see how to upload a file into the database using the Angular 7 app in an ASP.NET project. Let us create a new project in Visual Studio 2017. We are using ASP.NET Core 2 and Angular 7 for this project.

Prerequisites:

  • Node JS must be installed
  • Angular CLI must be installed
  • Basic knowledge of Angular

Let’s get started.

Create a new project and name it as FileUploadInAngular7

Select a new project with Angular in .NET Core and uncheck the configure HTTPS

Your project will be created. Left click on ClientApp and open it in file explorer

Type cmd as shown in the picture.

Type npm install in the cmd

Now it will take time and all the node_modules will be installed in the project.

Create a Web API controller as UploadController.

Add the following code in it.

using System.IO;
using System.Net.Http.Headers;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
namespace FileUploadInAngular7.Controllers
{
    [Produces("application/json")]
    [Route("api/[controller]")]
    public class UploadController : Controller
    {
        private IHostingEnvironment _hostingEnvironment;
        public UploadController(IHostingEnvironment hostingEnvironment)
        {
            _hostingEnvironment = hostingEnvironment;
        }
        [HttpPost, DisableRequestSizeLimit]
        public ActionResult UploadFile()
        {
            try
            {
                var file = Request.Form.Files[0];
                string folderName = "Upload";
                string webRootPath = _hostingEnvironment.WebRootPath;
                string newPath = Path.Combine(webRootPath, folderName);
                if (!Directory.Exists(newPath))
                {
                    Directory.CreateDirectory(newPath);
                }
                if (file.Length > 0)
                {
                    string fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
                    string fullPath = Path.Combine(newPath, fileName);
                    using (var stream = new FileStream(fullPath, FileMode.Create))
                    {
                        file.CopyTo(stream);
                    }
                }
                return Json("Upload Successful.");
            }
            catch (System.Exception ex)
            {
                return Json("Upload Failed: " + ex.Message);
            }
        }
    }
}

Navigate to the following path and open both the files.

In home.component.html write the following code.

<h1>File Upload Using Angular 7 and ASP.NET Core Web API</h1>
<input #file type="file" multiple (change)="upload(file.files)" />
<br />
<span style="font-weight:bold;color:green;" *ngIf="progress > 0 && progress < 100">
  {{progress}}%
</span>
<span style="font-weight:bold;color:green;" *ngIf="message">
  {{message}}
</span>

In home.component.ts file write the following code in it.

import { Component, OnInit } from '@angular/core';
import { HttpClient, HttpRequest, HttpEventType, HttpResponse } from '@angular/common/http';
@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
})
export class HomeComponent {
  public progress: number;
  public message: string;
  constructor(private http: HttpClient) { }
  upload(files) {
    if (files.length === 0)
      return;
    const formData = new FormData();
    for (let file of files)
      formData.append(file.name, file);
    const uploadReq = new HttpRequest('POST', `api/upload`, formData, {
      reportProgress: true,
    });
    this.http.request(uploadReq).subscribe(event => {
      if (event.type === HttpEventType.UploadProgress)
        this.progress = Math.round(100 * event.loaded / event.total);
      else if (event.type === HttpEventType.Response)
        this.message = event.body.toString();
    });
  }
}

Run the project and you will see the following output.



European ASP.NET Core Hosting :: InProcess and OutOfProcess Hosting Model In ASP.NET Core

clock September 17, 2019 11:37 by author Scott

Once you finish developing an ASP.NET Core web application, you need to deploy it on a server so that end users can start using it. When it comes to deployment to IIS, ASP.NET Core offers two hosting models namely InProcess and OutOfProcess. In this article you learn about these hosting models and how to configure them.

When you deploy your web application to IIS, various requests to the application are handled by what is known as ASP.NET Core Module. Under default settings the hosting model for your application is InProcess. This means ASP.NET Core Module forwards the requests to IIS HTTP Server (IISHttpServer). The IIS HTTP Server is a server that runs in-process with IIS. This results in great performance as compared to Out Of Process model. In-process models bypasses built-in Kestrel web server of ASP.NET Core.

If you decide to use Out-Of-Process hosting model then IIS HTTP Server won't be used. Instead Kestrel web server is used to process your requests. So, ASP.NET Core Module forwards your requests to Kestrel web server. This communication is out-of-process communication and is therefore slower than the in-process model.

Now that you have some basic idea about the in-process and out-of-process hosting models let's see how to configure them.

In order to understand the hosting models discussed in this article you first need to create an ASP.NET Core web application using Visual Studio. So, go ahead and do so based on any of the web application project templates. Make sure to use ASP.NET Core version 2.2 or later.

Once you create the project, click on the Build > Publish menu and Web Deploy the output (or manually copy to IIS) to IIS. Once you finish deploying the application locate the web.config file generated during the deployment process. In this web.config you will find a section like this:

<system.webServer>
  <handlers>
    <add name="aspNetCore" path="*" verb="*"
modules="AspNetCoreModuleV2" />
  </handlers>
  <aspNetCore processPath="dotnet"
              arguments=".\MyWebApp.dll"  
              stdoutLogEnabled="false"
              stdoutLogFile=".\logs\stdout"
              hostingModel="inprocess" />
</system.webServer>

As you can see the <aspNetCore> tag has hostingModel attribute that is set to inprocess by default.

Now, run the application from the browser with this default setting in place and observe the HTTP headers. The following figure shows one such sample run of the application.

As you can see the Server is Microsoft IIS.

Now, change the hostingModel attribute from inprocess to outofprocess. Run the application again. This time you will get the following output:

As you can see the Server is now Kestrel indicating that Out-Of-Process model is active.

In the preceding example you change the hosting model in the web.config generated during the publish operation. You can also specify the hosting model in project's .csproj file. Consider the following markup from .csproj file that does that.

<PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
</PropertyGroup>

The <AspNetCoreHostingModel> element sets the hosting model to InProcess. To set it to Out-Of-Process you need to set it like this:

<AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>

Run the application with these settings and confirm whether you get the outcome as before.

That's it for now! Keep coding!!



European ASP.NET Core Hosting :: How to Use Serilog with ASP.NET Core 2

clock August 7, 2019 08:48 by author Scott

Today, I will discuss about Serilog. What is Serilog? Serilog is an open source event library for .NET. Serilog gives you two important components: loggers and sinks (outputs). Most applications will have a single static logger and several sinks, so in this example I’ll use two: the console and a rolling file sink.

Starting with a new ASP.NET Core 2.0 Web Application running on .NET Framework (as in the image to the right), begin by grabbing a few packages off NuGet:

  • Serilog
  • Serilog.AspNetCore
  • Serilog.Settings.Configuration
  • Serilog.Sinks.Console
  • Serilog.Sinks.RollingFile

Next, you will need to modify some files.

Startup.cs

Startup constructor

Create the static Log.Logger by reading from the configuration (see appsettings.json below). The constructor should now look like this:

public Startup(IConfiguration configuration)
{
   Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(configuration).CreateLogger();
Configuration = configuration;
}

BuildWebHost method

Next, add the .UseSerilog() extension to the BuildWebHost method. It should now look like this:

public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseSerilog() // <-- Add this line
.Build();

The BuildWebHost method might look strange because the body of this method is called an expression body rather than a traditional method with a statement body.

Configure method

At the start of the configure method, add one line at the top to enable the Serilog middleware. You can optionally remove the other loggers as this point as well. Your Configure method should start like this:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
     loggerFactory.AddSerilog(); // <-- Add this line
     ...

appsettings.json

At the root level of the appsettings.json (or one of the environment-specific settings files), add something like this:

"Serilog": {
     "Using": [ "Serilog.Sinks.Console" ],
     "MinimumLevel": "Debug",
     "WriteTo": [
            { "Name": "Console" },
            {
                    "Name": "RollingFile",
                    "Args": {
                          "pathFormat": "logs\\log-{Date}.txt",
                          "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}"
                    }
            }
     ],
     "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
     "Properties": {
     "Application": "My Application"
            }
}

If you’re wondering about the pathFormat and what other parameters you can use, there aren’t that many. {Date} is the only “switch” you can use with this particular sink. But you can use any environment variable (things like %TEMP%), allowing for a pathFormat of:

"%TEMP%\\logs\\log-{Date}.txt"

The outputTemplate has a LOT of options that I won’t get into here because the official documentation does a great job of explaining it.

As for the event levels, I’ve copied the list below from the official documentation for reference.

  1. Verbose – tracing information and debugging minutiae; generally only switched on in unusual situations
  2. Debug – internal control flow and diagnostic state dumps to facilitate pinpointing of recognised problems
  3. Information – events of interest or that have relevance to outside observers; the default enabled minimum logging level
  4. Warning – indicators of possible issues or service/functionality degradation
  5. Error – indicating a failure within the application or connected system
  6. Fatal – critical errors causing complete failure of the application

You’ll also notice in the above JSON that the “Using” property is set to an array containing “Serilog.Sinks.Console” but not “Serilog.Sinks.RollingFile”. Everything appears to work, so I am not sure what impact this property has.  



European ASP.NET Core Hosting :: How to Fix “An error occurred while starting the application” in ASP.NET Core on IIS

clock July 5, 2019 07:33 by author Scott

.NET Core is the latest Microsoft product and Microsoft always keep update their technologies. We have written tutorial about how to publish ASP.NET Core on IIS server, but we know that some of you sometimes receive error when deploying your ASP.NET Core. Feel frustrated to fix the issue? Yeah, not only you have headache, but some of our clients also experience same problem like you. That’s why we write this tutorial and hope it can help to fix your issue!

Anyone see this error? Have problem to solve it? We want to help you here!

The Problem

 

It basically means something bad happened with your application. Things you need to check

  • You might not have the correct .NET Core version installed on the server.
  • You might be missing DLL’s
  • Something went wrong in your Program.cs or Startup.cs before any exception handling kicked in

If you use Windows Server, then I believe that you can’t find anything on your Event Viewer too. You’ll notice that there is no error on your Event Viewer log. Why? This is because Event Logging must be wired up explicitly and you’ll need to use the Microsoft.Extensions.Logging.EventLog package, and depending on the error, you might not have a chance to even catch it to log to the Event Viewer.

How to Fix it?

So, how to fix error above? The followings are the steps to fix the error:

1. Open your web.config

2. Change stdoutLogEnabled=true

3. Create a logs folder

  • Unfortunately, the AspNetCoreModule doesn’t create the folder for you by default
  • If you forget to create the logs folder, an error will be logged to the Event Viewer that says: Warning: Could not create stdoutLogFile \\?\YourPath\logs\stdout_timestamp.log, ErrorCode = -2147024893.
  • The “stdout” part of  the value “.\logs\stdout” actually references the filename not the folder.  Which is a bit confusing.

4. Run your request again, then open the \logs\stdout_*.log file

Note – you will want to turn this off after you’re done troubleshooting, as it is a performance hit.

So your web.config’s aspNetCore element should look something like this

 <aspNetCore processPath=”.\YourProjectName.exe” stdoutLogEnabled=”true” stdoutLogFile=”.\logs\stdout” />

Doing this will log all the requests out to this file and when the exception occurs, it will give you the full stack trace of what happened in the \logs\stdout_*.log file

  

 

 

Hope this helps!

Looking for ASP.NET Core hosting with affordable cost?

We know it is quite hard to find reliable hosting that support ASP.NET Core. We believe that some of you have signed up for 1 hosting, but they are below your expectation. Maybe they don’t support latest ASP.NET Core or they might support .NET Core but their support are not competent. We are here to help you. If you are looking for cheap, reliable with best ASP.NET Core hosting support, then you can visit our site at https://www.hostforlife.eu.



About HostForLIFE

HostForLIFE is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2019 Hosting, ASP.NET 5 Hosting, ASP.NET MVC 6 Hosting and SQL 2019 Hosting.


Month List

Tag cloud

Sign in