Posted on Leave a comment

Introducing Project Tye

Amiee Lo

Amiee

Project Tye is an experimental developer tool that makes developing, testing, and deploying microservices and distributed applications easier.

When building an app made up of multiple projects, you often want to run more than one at a time, such as a website that communicates with a backend API or several services all communicating with each other. Today, this can be difficult to setup and not as smooth as it could be, and it’s only the very first step in trying to get started with something like building out a distributed application. Once you have an inner-loop experience there is then a, sometimes steep, learning curve to get your distributed app onto a platform such as Kubernetes.

The project has two main goals:

  1. Making development of microservices easier by:
    • Running many services with one command
    • Using dependencies in containers
    • Discovering addresses of other services using simple conventions
  2. Automating deployment of .NET applications to Kubernetes by:
    • Automatically containerizing .NET applications
    • Generating Kubernetes manifests with minimal knowledge or configuration
    • Using a single configuration file

If you have an app that talks to a database, or an app that is made up of a couple of different processes that communicate with each other, then we think Tye will help ease some of the common pain points you’ve experienced.

We have recently demonstrated Tye in a few Build sessions that we encourage you to watch, Cloud Native Apps with .NET and AKS and Journey to one .NET

Installation

To get started with Tye, you will first need to have .NET Core 3.1 installed on your machine.

Tye can then be installed as a global tool using the following command:

dotnet tool install -g Microsoft.Tye --version "0.2.0-alpha.20258.3"

Running a single service

Tye makes it very easy to run single applications. To demonstrate this:

1. Make a new folder called microservices and navigate to it:

mkdir microservices
cd microservices

2. Then create a frontend project:

dotnet new razor -n frontend

3. Now run this project using tye run:

tye run frontend

Image tye run output The above displays how Tye is building, running, and monitoring the frontend application.

One key feature from tye run is a dashboard to view the state of your application. Navigate to http://localhost:8000 to see the dashboard running.

Image tye dashboard

The dashboard is the UI for Tye that displays a list of all of your services. The Bindings column has links to the listening URLs of the service. The Logs column allows you to view the streaming logs for the service.

Image tye logs

Services written using ASP.NET Core will have their listening ports assigned randomly if not explicitly configured. This is useful to avoid common issues like port conflicts.

Running multiple services

Instead of just a single application, suppose we have a multi-application scenario where our frontend project now needs to communicate with a backend project. If you haven’t already, stop the existing tye run command using Ctrl + C.

1. Create a backend API that the frontend will call inside of the microservices/ folder.

dotnet new webapi -n backend

2. Then create a solution file and add both projects:

dotnet new sln
dotnet sln add frontend backend

You should now have a solution called microservices.sln that references the frontend and backend projects.

3. Run tye in the folder with the solution.

tye run

The dashboard should show both the frontend and backend services. You can navigate to both of them through either the dashboard of the url outputted by tye run.

The backend service in this example was created using the webapi project template and will return an HTTP 404 for its root URL.

Getting the frontend to communicate with the backend

Now that we have two applications running, let’s make them communicate.

To get both of these applications communicating with each other, Tye utilizes service discovery. In general terms, service discovery describes the process by which one service figures out the address of another service. Tye uses environment variables for specifying connection strings and URIs of services.

The simplest way to use Tye’s service discovery is through the Microsoft.Extensions.Configuration system – available by default in ASP.NET Core or .NET Core Worker projects. In addition to this, we provide the Microsoft.Tye.Extensions.Configuration package with some Tye-specific extensions layered on top of the configuration system.

If you want to learn more about Tye’s philosophy on service discovery and see detailed usage examples, check out this reference document.

1. If you haven’t already, stop the existing tye run command using Ctrl + C. Open the solution in your editor of choice.

2. Add a file WeatherForecast.cs to the frontend project.

using System; namespace frontend { public class WeatherForecast { public DateTime Date { get; set; } public int TemperatureC { get; set; } public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); public string Summary { get; set; } } }

This will match the backend WeatherForecast.cs.

3. Add a file WeatherClient.cs to the frontend project with the following contents:

using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks; namespace frontend
{ public class WeatherClient { private readonly JsonSerializerOptions options = new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, }; private readonly HttpClient client; public WeatherClient(HttpClient client) { this.client = client; } public async Task<WeatherForecast[]> GetWeatherAsync() { var responseMessage = await this.client.GetAsync("/weatherforecast"); var stream = await responseMessage.Content.ReadAsStreamAsync(); return await JsonSerializer.DeserializeAsync<WeatherForecast[]>(stream, options); } }
}

4. Add a reference to the Microsoft.Tye.Extensions.Configuration package to the frontend project

dotnet add frontend/frontend.csproj package Microsoft.Tye.Extensions.Configuration --version "0.2.0-*"

5. Now register this client in frontend by adding the following to the existing ConfigureServices method to the existing Startup.cs file:

...
public void ConfigureServices(IServiceCollection services)
{ services.AddRazorPages(); /** Add the following to wire the client to the backend **/ services.AddHttpClient<WeatherClient>(client => { client.BaseAddress = Configuration.GetServiceUri("backend"); }); /** End added code **/
}
...

This will wire up the WeatherClient to use the correct URL for the backend service.

6. Add a Forecasts property to the Index page model under Pages\Index.cshtml.cs in the frontend project.

... public WeatherForecast[] Forecasts { get; set; }
...

7. Change the OnGet method to take the WeatherClient to call the backend service and store the result in the Forecasts property:

... public async Task OnGet([FromServices]WeatherClient client) { Forecasts = await client.GetWeatherAsync(); }
...

8. Change the Index.cshtml razor view to render the Forecasts property in the razor page:

@page
@model IndexModel
@{ ViewData["Title"] = "Home page";
} <div class="text-center"> <h1 class="display-4">Welcome</h1> <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div> Weather Forecast: <table class="table"> <thead> <tr> <th>Date</th> <th>Temp. (C)</th> <th>Temp. (F)</th> <th>Summary</th> </tr> </thead> <tbody> @foreach (var forecast in @Model.Forecasts) { <tr> <td>@forecast.Date.ToShortDateString()</td> <td>@forecast.TemperatureC</td> <td>@forecast.TemperatureF</td> <td>@forecast.Summary</td> </tr> } </tbody>
</table>

9. Run the project with tye run and the frontend service should be able to successfully call the backend service!

When you visit the frontend service you should see a table of weather data. This data was produced randomly in the backend service. The fact that you’re seeing it in a web UI in the frontend means that the services are able to communicate. Unfortunately, this doesn’t work out of the box on Linux right now due to how self-signed certificates are handled, please see the workaround here

Tye’s configuration schema

Tye has a optional configuration file (tye.yaml) to enable customizing settings. This file contains all of your projects and external dependencies. If you have an existing solution, Tye will automatically populate this with all of your current projects.

To initalize this file, you will need to run the following command in the microservices directory to generate a default tye.yaml file:

tye init

The contents of the tye.yaml should look like this:

Image tye yaml

The top level scope (like the name node) is where global settings are applied.

tye.yaml lists all of the application’s services under the services node. This is the place for per-service configuration.

To learn more about Tye’s yaml specifications and schema, you can check it out here in Tye’s repository on Github.

We provide a json-schema for tye.yaml and some editors support json-schema for completion and validation of yaml files. See json-schema for instructions.

Adding external dependencies (Redis)

Not only does Tye make it easy to run and deploy your applications to Kubernetes, it’s also fairly simple to add external dependencies to your applications as well. We will now add redis to the frontend and backend application to store data.

Tye can use Docker to run images that run as part of your application. Make sure that Docker is installed on your machine.

1. Change the WeatherForecastController.Get() method in the backend project to cache the weather information in redis using an IDistributedCache.

2. Add the following using‘s to the top of the file:

using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;

3. Update Get():

[HttpGet]
public async Task<string> Get([FromServices]IDistributedCache cache)
{ var weather = await cache.GetStringAsync("weather"); if (weather == null) { var rng = new Random(); var forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }) .ToArray(); weather = JsonSerializer.Serialize(forecasts); await cache.SetStringAsync("weather", weather, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(5) }); } return weather;
}

This will store the weather data in Redis with an expiration time of 5 seconds.

4. Add a package reference to Microsoft.Extensions.Caching.StackExchangeRedis in the backend project:

cd backend/
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
cd ..

5. Modify Startup.ConfigureServices in the backend project to add the redis IDistributedCache implementation.

 public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddStackExchangeRedisCache(o => { o.Configuration = Configuration.GetConnectionString("redis"); }); }

The above configures redis to the configuration string for the redis service injected by the tye host.

6. Modify tye.yaml to include redis as a dependency.

name: microservice
services:
- name: backend project: backend\backend.csproj
- name: frontend project: frontend\frontend.csproj
- name: redis image: redis bindings: - port: 6379 connectionString: "${host}:${port}"
- name: redis-cli image: redis args: "redis-cli -h redis MONITOR"

We’ve added 2 services to the tye.yaml file. The redis service itself and a redis-cli service that we will use to watch the data being sent to and retrieved from redis.

The "${host}:${port}" format in the connectionString property will substitute the values of the host and port number to produce a connection string that can be used with StackExchange.Redis.

7. Run the tye command line in the solution root

Make sure your command-line is in the microservices/ directory. One of the previous steps had you change directories to edit a specific project.

tye run

Navigate to http://localhost:8000 to see the dashboard running. Now you will see both redis and the redis-cli running listed in the dashboard.

Navigate to the frontend application and verify that the data returned is the same after refreshing the page multiple times. New content will be loaded every 5 seconds, so if you wait that long and refresh again, you should see new data. You can also look at the redis-cli logs using the dashboard and see what data is being cached in redis.

The "${host}:${port}" format in the connectionString property will substitute the values of the host and port number to produce a connection string that can be used with StackExchange.Redis.

Deploying to Kubernetes

Tye makes the process of deploying your application to Kubernetes very simple with minimal knowlege or configuration required.

Tye will use your current credentials for pushing Docker images and accessing Kubernetes clusters. If you have configured kubectl with a context already, that’s what tye deploy is going to use!

Prior to deploying your application, make sure to have the following:

  1. Docker installed based off on your operating system
  2. A container registry. Docker by default will create a container registry on DockerHub. You could also use Azure Container Registry (ACR) or another container registry of your choice.
  3. A Kubernetes Cluster. There are many different options here, including:

If you choose a container registry provided by a cloud provider (other than Dockerhub), you will likely have to take some steps to configure your kubernetes cluster to allow access. Follow the instructions provided by your cloud provider.

Deploying Redis

tye deploy will not deploy the redis configuration, so you need to deploy it first by running:

kubectl apply -f https://raw.githubusercontent.com/dotnet/tye/master/docs/tutorials/hello-tye/redis.yaml

This will create a deployment and service for redis.

Tye deploy

You can deploy your application by running the follow command:

tye deploy --interactive

Enter the Container Registry (ex: example.azurecr.io for Azure or example for dockerhub):

You will be prompted to enter your container registry. This is needed to tag images, and to push them to a location accessible by kubernetes.

Image tye deploy output

If you are using dockerhub, the registry name will be your dockerhub username. If you are using a standalone container registry (for instance from your cloud provider), the registry name will look like a hostname, eg: example.azurecr.io.

You’ll also be prompted for the connection string for redis.

Image redis connection string

Enter the following to use the instance that you just deployed:

redis:6379

tye deploy will create Kubernetes secret to store the connection string.

–interactive is needed here to create the secret. This is a one-time configuration step. In a CI/CD scenario you would not want to have to specify connection strings over and over, deployment would rely on the existing configuration in the cluster.

Tye uses Kubernetes secrets to store connection information about dependencies like redis that might live outside the cluster. Tye will automatically generate mappings between service names, binding names, and secret names.

tye deploy does many different things to deploy an application to Kubernetes. It will:

  • Create a docker image for each project in your application.
  • Push each docker image to your container registry.
  • Generate a Kubernetes Deployment and Service for each project.
  • Apply the generated Deployment and Service to your current Kubernetes context.

Image tye deploy building images

You should now see three pods running after deploying.

kubectl get pods NAME READY STATUS RESTARTS AGE
backend-ccfcd756f-xk2q9 1/1 Running 0 85m
frontend-84bbdf4f7d-6r5zp 1/1 Running 0 85m
redis-5f554bd8bd-rv26p 1/1 Running 0 98m

You can visit the frontend application, you will need to port-forward to access the frontend from outside the cluster.

kubectl port-forward svc/frontend 5000:80

Now navigate to http://localhost:5000 to view the frontend application working on Kubernetes.

Image kubernetes portforward

Currently tye does not automatically enable TLS within the cluster, and so communication takes place over HTTP instead of HTTPS. This is typical way to deploy services in kubernetes – we may look to enable TLS as an option or by default in the future.

Adding a registry to tye.yaml

If you want to use tye deploy as part of a CI/CD system, it’s expected that you’ll have a tye.yaml file initialized. You will then need to add a container registry to tye.yaml. Based on what container registry you configured, add the following line in the tye.yaml file:

registry: <registry_name>

Now it’s possible to use tye deploy without --interactive since the registry is stored as part of configuration.

This step may not make much sense if you’re using tye.yaml to store a personal Dockerhub username. A more typical use case would storing the name of a private registry for use in a CI/CD system.

For a conceptual overview of how Tye behaves when using tye deploy for deployment, check out this document.

Undeploying your application

After deploying and playing around with the application, you may want to remove all resources associated from the Kubernetes cluster. You can remove resources by running:

tye undeploy

This will remove all deployed resources. If you’d like to see what resources would be deleted, you can run:

tye undeploy --what-if

If you want to experiment more with using Tye, we have a variety of different sample applications and tutorials that you can walk through, check them out down below:

We have been diligently working on adding new capabilities and integrations to continuously improve Tye. Here are some integrations below that we have recently released. There is also information provided on how to get started for each of these:

  • Ingressto expose pods/services created to the public internet.
  • Redisto store data, cache, or as a message broker.
  • Daprfor integrating a Dapr application with Tye.
  • Zipkinusing Zipkin for distributed tracing.
  • Elastic Stackusing Elastic Stack for logging.

While we are excited about the promise Tye holds, it’s an experimental project and not a committed product. During this experimental phase we expect to engage deeply with anyone trying out Tye to hear feedback and suggestions. The point of doing experiments in the open is to help us explore the space as much as we can and use what we learn to determine what we should be building and shipping in the future.

Project Tye is currently commited as an experiment until .NET 5 ships. At which point we will be evaluating what we have and all that we’ve learnt to decide what we should do in the future.

Our goal is to ship every month, and some new capabilities that we are looking into for Tye include:

  • More deployment targets
  • Sidecar support
  • Connected development
  • Database migrations

We are excited by the potential Tye has to make developing distributed applications easier and we need your feedback to make sure it reaches that potential. We’d really love for you to try it out and tell us what you think, there is a link to a survey on the Tye dashboard that you can fill out or you can create issues and talk to us on GitHub. Either way we’d love to hear what you think.

Posted on Leave a comment

Blazor WebAssembly 3.2.0 now available

Daniel Roth

Daniel

I’m thrilled to announce that Blazor WebAssembly is now officially released. This is a fully-featured and supported release of Blazor WebAssembly that is ready for production use. Full stack web development with .NET is now here!

Get started

Getting started with Blazor WebAssembly is easy: simply go to https://blazor.net and install the latest .NET Core SDK (3.1.300 or later), which includes everything you need to build and run Blazor WebAssembly apps.

You can then create and run your first Blazor WebAssembly app by running:

dotnet new blazorwasm -o BlazorApp1
cd BlazorApp1
dotnet run

Browse to https://localhost:5001 and voilà! You’ve just built and run your first Blazor WebAssembly app!

Running Blazor WebAssembly app

To maximize your Blazor productivity, be sure to install a supported version of Visual Studio for your platform of choice:

You can find additional docs and samples on https://blazor.net.

Upgrade an existing project

If you already have an existing Blazor WebAssembly project, you can upgrade it from the 3.2.0 Release Candidate to the official 3.2.0 release by doing the following:

  • Update all Microsoft.AspNetCore.Components.WebAssembly.* and System.Net.Http.Json package references to version 3.2.0.

That’s it, you’re all set!

What is Blazor WebAssembly?

In case this is your first time learning about Blazor, let me introduce you to what Blazor WebAssembly is all about.

Blazor is an open source and cross-platform web UI framework for building single-page apps using .NET and C# instead of JavaScript. Blazor is based on a powerful and flexible component model for building rich interactive web UI. You implement Blazor UI components using a combination of .NET code and Razor syntax: an elegant melding of HTML and C#. Blazor components can seamlessly handle UI events, bind to user input, and efficiently render UI updates.

Blazor components can then be hosted in different ways to create your web app. The first supported way is called Blazor Server. In a Blazor Server app, the components run on the server using .NET Core. All UI interactions and updates are handled using a real-time WebSocket connection with the browser. Blazor Server apps are fast to load and simple to implement. Support for Blazor Server is available with .NET Core 3.1 LTS.

Blazor WebAssembly is now the second supported way to host your Blazor components: client-side in the browser using a WebAssembly-based .NET runtime. Blazor WebAssembly includes a proper .NET runtime implemented in WebAssembly, a standardized bytecode for the web. This .NET runtime is downloaded with your Blazor WebAssembly app and enables running normal .NET code directly in the browser. No plugins or code transpilation are required. Blazor WebAssembly works with all modern web browsers, both desktop and mobile. Similar to JavaScript, Blazor WebAssembly apps run securely on the user’s device from within the browser’s security sandbox. These apps can be deployed as completely standalone static sites without any .NET server component at all, or they can be paired with ASP.NET Core to enable full stack web development with .NET, where code can be effortlessly shared with the client and server.

Fully-featured

Blazor WebAssembly comes packed with features to keep you productive on your next web app project:

Blazor in action

Blazor WebAssembly has everything you need to build fully-featured production web apps. To see all these Blazor WebAssembly features in action, checkout Steve Sanderson’s on-demand BUILD session (link should be live after 12pm PT): Modern Web UI with Blazor WebAssembly.

Ready-made components

Of course, any web app is going to need beautiful and feature rich components. A variety of Blazor UI components are available from our fantastic partners that work great in any Blazor app, including Blazor WebAssembly apps:

Open-source community

Blazor also has a thriving open-source community and ecosystem. Members of the community, (folks just like you!) have built lots of great component libraries, interop libraries, test frameworks, and more, and then made them freely available for you to use. Some great examples include:

You can find these community projects and many others listed on the Awesome Blazor GitHub repo.

LTS or Current?

Blazor WebAssembly 3.2.0 is a fully supported release under the .NET Core Support Policy. Since this is the first release of Blazor WebAssembly, it is a Current release, not an LTS release; it does not the inherit LTS status of .NET Core 3.1. This means that once Blazor WebAssembly ships with .NET 5 later this year, you will need to upgrade to .NET 5 to stay in support. We expect Blazor in .NET 5 to be a highly compatible release.

What’s next?

Now that we have shipped Blazor WebAssembly, we are shifting our attention to .NET 5. Work has already started on making Blazor WebAssembly available with .NET 5, which we expect to complete for preview next month.

We also have a number of Blazor features and improvements that we are investigating for the .NET 5 & 6 wave. You can see the list of core deliverables that we are considering in the Blazor Roadmap for .NET 5 issue on GitHub. Please note that we consider this list to be highly aspirational. While we hope to deliver all of the improvements listed, there are still many unknown and plans will certainly change as we go. We also expect that there will be plenty of smaller improvements that we will deliver as well.

We are also continuing to collaborate with our friends on the Xamarin team on experimental support for building native UI using Blazor through the Mobile Blazor Bindings project. This includes some early efforts to explore building hybrid UI for native apps, which we hope to share more about soon.

Thank you

We sincerely appreciate all the enthusiastic support we have received from the Blazor community as we’ve worked to make the release a reality. The number of Blazor articles, blog posts, docs, sample apps, libraries, books, videos, presentations, workshops, courses, meetups, feature suggestions, and feedback issues that have been contributed by the community to the Blazor ecosystem even while it was still in preview has been truly outstanding. To everyone who helped make this release possible, thank you! We couldn’t have done it without you.

Try Blazor today

We hope you enjoy this release of Blazor WebAssembly. Give Blazor a try today by going to https://blazor.net. We look forward to seeing what you create with it.

As always, if you have any questions of feedback about Blazor please let us know by filing an issue on GitHub.

Posted on Leave a comment

Blazor WebAssembly 3.2.0 Release Candidate now available

Daniel Roth

Daniel

The Blazor WebAssembly Release Candidate is here! This release contains all of the features and improvements that we expect to release for the upcoming Blazor WebAssembly release. There are no more breaking changes planned at this point. Please give the Blazor WebAssembly Release Candidate a try and let us know what you think!

Here’s what’s new in this release:

  • Custom boot resource loading
  • API reference docs

Get started

To get started with Blazor WebAssembly 3.2.0 Release Candidate, install the latest .NET Core 3.1 SDK.

NOTE: Version 3.1.201 or later of the .NET Core SDK is required to use this Blazor WebAssembly release! Make sure you have the correct .NET Core SDK version by running dotnet --version from a command prompt.

Once you have the appropriate .NET Core SDK installed, run the following command to install the updated Blazor WebAssembly template:

dotnet new -i Microsoft.AspNetCore.Components.WebAssembly.Templates::3.2.0-rc1.20223.4

If you’re on Windows using Visual Studio, we recommend installing the latest preview of Visual Studio 2019 16.6. For this preview, you should still install the template from the command-line as described above to ensure that the Blazor WebAssembly template shows up correctly in Visual Studio and on the command-line.

That’s it! You can find additional docs and samples on https://blazor.net.

Upgrade an existing project

To upgrade an existing Blazor WebAssembly app from 3.2.0 Preview 5 to the 3.2.0 Release Candidate:

  • Update all Microsoft.AspNetCore.Components.WebAssembly.* package references to version 3.2.0-rc1.20223.4.
  • Update any Microsoft.AspNetCore.Components.WebAssembly.Runtime package references to version 3.2.0-rc1.20222.2 (having a reference to this package is uncommon).
  • Update all System.Net.Http.Json package references to 3.2.0-rc1.20217.1

You’re all set!

Custom boot resource loading

When a Blazor WebAssembly app loads in the browser, it first downloads all of the required boot resources from the server, including the .NET runtime, the bootstrapping JavaScript code, locale specific data, and the .NET assemblies that make up the app. You can now customize how these boot resources are loading using the loadBootResource API. You can use this API to make any needed modifications to how these specific outbound requests are constructed. For example, you might want to load some of the resources from an external CDN. Although Microsoft does not currently host the Blazor framework files on any specific public Content Delivery Network (CDN), you are free to add them to your own CDN if you wish. For example, if you have published the _framework\wasm files to a CDN within the base URL https://mycdn.example.com/blazorwebassembly/3.2.0-rc1/, then you could configure your application to use those resources as follows:

<script src="_framework/blazor.webassembly.js" autostart="false"></script>
<script> Blazor.start({ loadBootResource: function (type, name, defaultUri, integrity) { console.log(`Will load '${type}' with name '${name}' from URI '${defaultUri}' and integrity '${integrity}'`); switch (type) { case 'dotnetjs': case 'dotnetwasm': case 'timezonedata': return `https://mycdn.external.cdn/blazorwebassembly/3.2.0-rc1/${name}`; } // Other types are 'assembly' and 'pdb' but you probably wouldn't want to fetch those from a CDN as they would be custom-linked for your app // By returning undefined for other types, we let the framework use its normal strategy for those types } });
</script>

If you want to customize more than just the URLs that are being used, then your loadBootResource function can call fetch directly and return the result. For example:

<script src="_framework/blazor.webassembly.js" autostart="false"></script>
<script> Blazor.start({ loadBootResource: function (type, name, defaultUri, integrity) { // Adds a custom HTTP header to the outbound requests // To retain the default integrity checking behavior, it's necessary to pass through the 'integrity' parameter return fetch(defaultUri, { cache: 'no-cache', integrity: integrity, headers: { 'MyCustomHeader': 'My custom value' } }); } });
</script>

API reference docs

API reference docs for the Blazor WebAssembly namespaces (Microsoft.AspNetCore.Components.WebAssembly.*) are now available in the .NET API browser as part of the ASP.NET Core 3.1 API docs.

Known issues

  • When publishing a ASP.NET Core hosted Blazor WebAssembly app using Visual Studio, satellite assemblies from the client application do not get copied to the publish folder.

    This issue will be addressed in the upcoming release. To workaround this issue, publish the app from the command line using dotnet publish.

Feedback

This is our last planned preview release of Blazor WebAssembly 3.2! We need your help to make sure that we’ve addressed any remaining blocking issues for the upcoming release. Please give it a try and let us know what you think by filing issues on GitHub.

We hope you enjoy the Blazor WebAssembly Release Candidate and thanks for trying out Blazor!

Posted on Leave a comment

Blazor WebAssembly 3.2.0 Preview 5 release now available

Daniel Roth

Daniel

A new preview update of Blazor WebAssembly is now available! Here’s what’s new in this release:

  • Read configuration during startup
  • Configure HTTP fetch request options
  • Honor existing web.config when publishing
  • Attach tokens to outgoing requests
  • Support for time zones

Get started

To get started with Blazor WebAssembly 3.2.0 Preview 5 install the latest .NET Core 3.1 SDK.

NOTE: Version 3.1.201 or later of the .NET Core SDK is required to use this Blazor WebAssembly release! Make sure you have the correct .NET Core SDK version by running dotnet --version from a command prompt.

Once you have the appropriate .NET Core SDK installed, run the following command to install the updated Blazor WebAssembly template:

dotnet new -i Microsoft.AspNetCore.Components.WebAssembly.Templates::3.2.0-preview5.20216.8

If you’re on Windows using Visual Studio, we recommend installing the latest preview of Visual Studio 2019 16.6. For this preview, you should still install the template from the command-line as described above to ensure that the Blazor WebAssembly template shows up correctly in Visual Studio and on the command-line.

That’s it! You can find additional docs and samples on https://blazor.net.

Upgrade an existing project

To upgrade an existing Blazor WebAssembly app from 3.2.0 Preview 4 to 3.2.0 Preview 5:

  • Update all Microsoft.AspNetCore.Components.WebAssembly.* package references to version 3.2.0-preview5.20216.8.
  • Update any Microsoft.AspNetCore.Components.WebAssembly.Runtime package references to version 3.2.0-preview5.20216.1.
  • Remove any calls to set WebAssemblyHttpMessageHandlerOptions.DefaultCredentials and instead call SetBrowserRequestCredentials on individual requests (see “Configure HTTP fetch request options” section below).
  • Remove the redirect parameter from calls to TryGetToken on AccessTokenResult.

You’re all set!

Read configuration during startup

Configuration data is now available during app startup in Program.Main using the Configuration property on WebAssemblyHostBuilder. This property can now be used both to add configuration sources and to access the current configuration data.

You can see this feature in action in the project templates when you enable authentication with Azure AD, Azure AD B2C, or an OpenID Connect provider of your choice. The authentication settings are stored in appsettings.json and then read from configuration when the app starts up:

Program.cs

public class Program
{ public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("app"); builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); builder.Services.AddOidcAuthentication(options => { // Configure your authentication provider options here. // For more information, see https://aka.ms/blazor-standalone-auth builder.Configuration.Bind("Local", options.ProviderOptions); }); await builder.Build().RunAsync(); }
}

appsettings.json

{ "Local": { "Authority": "https:login.microsoftonline.com/", "ClientId": "33333333-3333-3333-33333333333333333" }
}

Configure HTTP fetch request options

HTTP requests issued from a Blazor WebAssembly app using HttpClient are handled using the browser fetch API. In this release, we’ve added a set of extension methods for HttpRequestMessage that configure various fetch related options. These extension methods live in the Microsoft.AspNetCore.Components.WebAssembly.Http namespace:

HttpRequestMessage extension method Fetch request property
SetBrowserRequestCredentials credentials
SetBrowserRequestCache cache
SetBrowserRequestMode mode
SetBrowserRequestIntegrity integrity

You can set additional options using the more generic SetBrowserRequestOption extension method.

The HTTP response is typically buffered in a Blazor WebAssembly app to enable support for sync reads on the response content. To enable support for response streaming, use the SetBrowserResponseStreamingEnabled extension method on the request.

Honor existing web.config when publishing

When publishing a standalone Blazor WebAssembly app, a web.config is automatically generated for the app that handles configuring IIS appropriately. You can now specify your own web.config in the project, which will get used instead of the generated one.

Attach tokens to outgoing requests

Configuring authentication now adds an AuthorizationMessageHandler as a service that can be used with HttpClient to attach access tokens to outgoing requests. Tokens are acquired using the existing IAccessTokenProvider service. If a token cannot be acquired, an AccessTokenNotAvailableException is thrown. This exception has a Redirect method that can be used to navigate the user to the identity provider to acquire a new token. The AuthorizationMessageHandler can be configured with the authorized URLs, scopes, and return URL using the ConfigureHandler method.

For example, you can configure an HttpClient to use the AuthorizationMessageHandler like this:

builder.Services.AddSingleton(sp =>
{ return new HttpClient(sp.GetRequiredService<AuthorizationMessageHandler>() .ConfigureHandler( new [] { "https://www.example.com/base" }, scopes: new[] {"example.read", "example.write"})) { BaseAddress = new Uri("https://www.example.com/base") };
});

For convenience, a BaseAddressAuthorizationMessageHandler is also included that is preconfigured with the app base address as an authorized URL. The authentication enabled Blazor WebAssembly templates now use IHttpClientFactory to set up an HttpClient with the BaseAddressAuthorizationMessageHandler:

builder.Services.AddHttpClient("BlazorWithIdentityApp1.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)) .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>(); // Supply HttpClient instances that include access tokens when making requests to the server project
builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("BlazorWithIdentityApp1.ServerAPI"));

You can use the configured HttpClient to make authorized requests using a simple try-catch pattern. For example, here’s the updated code in the FetchData component for requesting the weather forecast data:

protected override async Task OnInitializedAsync()
{ try { forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast"); } catch (AccessTokenNotAvailableException exception) { exception.Redirect(); }
}

Alternatively, you can simplify things even further by defining a strongly-typed client that handles all of the HTTP and token acquisition concerns within a single class:

WeatherClient.cs

public class WeatherClient
{ private readonly HttpClient httpClient; public WeatherClient(HttpClient httpClient) { this.httpClient = httpClient; } public async Task<IEnumerable<WeatherForecast>> GetWeatherForeacasts() { IEnumerable<WeatherForecast> forecasts = new WeatherForecast[0]; try { forecasts = await httpClient.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast"); } catch (AccessTokenNotAvailableException exception) { exception.Redirect(); } return forecasts; }
}

Program.cs

builder.Services.AddHttpClient<WeatherClient>(client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)) .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

FetchData.razor

protected override async Task OnInitializedAsync()
{ forecasts = await WeatherClient.GetWeatherForeacasts();
}

Support for time zones

Blazor now infers the user’s time zone and uses it in date and time calculations. In addition, APIs on System.TimeZoneInfo that previously returned incomplete results now report correct results.

Help improve the Blazor docs!

Thank you everyone who has taken the time to give feedback on how we can best improve the Blazor docs!

If you haven’t already, please join in with helping us improve the docs by doing the following:

  • As you read the Blazor docs, let us know where we should focus our efforts by telling us if you find a topic helpful or not using the helpfulness widget at the top of each doc page:

    Doc helpfulness

  • Use the Feedback section at the bottom of each doc page to let us know when a particular topic is unclear, inaccurate, or incomplete.

    Doc feedback

  • Comment on our Improve the Blazor docs GitHub issue with your suggestions for new content and ways to improve the existing content.

Feedback

We hope you enjoy the new features in this preview release of Blazor WebAssembly! Please let us know what you think by filing issues on GitHub.

Thanks for trying out Blazor!

Posted on Leave a comment

Blazor WebAssembly 3.2.0 Preview 4 release now available

Daniel Roth

Daniel

A new preview update of Blazor WebAssembly is now available! Here’s what’s new in this release:

  • Access host environment during startup
  • Logging improvements
  • Brotli precompression
  • Load assemblies and runtime in parallel
  • Simplify IL linker config for apps
  • Localization support
  • API docs in IntelliSense

Get started

To get started with Blazor WebAssembly 3.2.0 Preview 4 install the latest .NET Core 3.1 SDK.

NOTE: Version 3.1.201 or later of the .NET Core SDK is required to use this Blazor WebAssembly release! Make sure you have the correct .NET Core SDK version by running dotnet --version from a command prompt.

Once you have the appropriate .NET Core SDK installed, run the following command to install the updated Blazor WebAssembly template:

dotnet new -i Microsoft.AspNetCore.Components.WebAssembly.Templates::3.2.0-preview4.20210.8

If you’re on Windows using Visual Studio, we recommend installing the latest preview of Visual Studio 2019 16.6. For this preview you should still install the template from the command-line as described above to ensure that the Blazor WebAssembly template shows up correctly in Visual Studio and on the command-line.

That’s it! You can find additional docs and samples on https://blazor.net.

Upgrade an existing project

To upgrade an existing Blazor WebAssembly app from 3.2.0 Preview 3 to 3.2.0 Preview 4:

  • Update all Microsoft.AspNetCore.Components.WebAssembly.* package references to version 3.2.0-preview4.20210.8.
  • Update any Microsoft.AspNetCore.Components.WebAssembly.Runtime package references to version 3.2.0-preview5.20210.1
  • Replace package references to Microsoft.AspNetCore.Blazor.HttpClient with System.Net.Http.Json and update all existing System.Net.Http.Json package references to 3.2.0-preview5.20210.3.
  • Add @using System.Net.Http.Json to your _Imports.razor file and update your code as follows:

    Microsoft.AspNetCore.Blazor.HttpClient System.Net.Http.Json
    GetJsonAsync GetFromJsonAsync
    PostJsonAsync PostAsJsonAsync
    PutJsonAsync PutAsJsonAsync

    Calls to PostAsJsonAsync and PutAsJsonAsync return an HttpResponseMessage instead of the deserialized response content. To deserialize the JSON content from the response message, use the ReadFromJsonAsync<T> extension method: response.content.ReadFromJsonAsync<WeatherForecast>().

  • Replace calls to AddBaseAddressHttpClient in Program.cs with builder.Services.AddSingleton(new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });.

You’re all set!

Access host environment during startup

The WebAssemblyHostBuilder now exposes IWebAssemblyHostEnvironment through the HostEnvironment property, which surfaces details about the app environment (Development, Staging, Production, etc.) during startup. If the app is hosted in an ASP.NET Core app, the environment reflects the ASP.NET Core environment. If the app is a standalone Blazor WebAssembly app, the environment is specified using the blazor-environment HTTP header, which is set to Development when served by the Blazor dev server. Otherwise, the default environment is Production.

New convenience extension methods on IWebAssemblyHostEnvironment make it easy to check the current environment: IsProduction(), IsDevelopment(), IsStaging(). We’ve also added a BaseAddress property to IWebAssemblyHostEnvironment for getting the app base address during startup when the NavigationManager service isn’t yet readily available.

Logging improvements

The WebAssemblyHostBuilder now exposes a Logging property of type ILoggingBuilder that can be used to configure logging for the app, similar to how you would configure Logging in an ASP.NET Core app on the server. You can use the ILoggingBuilder to set the minimum logging level and configure custom logging providers using extension methods in the Microsoft.Extensions.Logging namespace.

Brotli precompression

When you publish a Blazor WebAssembly app, the published and linked output is now precompressed using Brotli at the highest level to further reduce the app size and remove the need for runtime compression. ASP.NET Core hosted apps seamlessly take advantage of these precompressed files. For standalone apps, you can configure the host server to redirect requests to the precompressed files. Using the precompressed files, a published Blazor WebAssembly is now 1.8MB, down from 2MB in the previous preview. A minimal app without Bootstrap CSS reduces to 1.6MB.

Load assemblies and runtime in parallel

Blazor WebAssembly apps now load the assemblies and runtime in parallel saving some precious milliseconds off the app load time.

Simplify .NET IL linker config for apps

You can optionally provide a .NET IL linker config file for a Blazor WebAssembly app to customize the behavior of the linker. Previously, specifying a linker config file for your app would override the customizations built into Blazor that are necessary for apps to function property. App specific linker configuration is now treated as additive to the linker configuration provided by Blazor.

Localization support

Blazor WebAssembly apps now support localization using .NET resource files (.resx) and satellite assemblies. Blazor WebAssembly apps set the current culture using the user’s language preference. The appropriate satellite assemblies are then loaded from the server. Components can then be localized using the ASP.NET Core localization APIs, like IStringLocalizer<TResource> and friends. For more details on localizing Blazor WebAssembly apps, see Globalization and localization.

API docs in IntelliSense

The API docs for the various Blazor WebAssembly APIs are now available through IntelliSense:

API docs in IntelliSense

Known issues

Debugging limitations

Thank you everyone who has been trying out the new Blazor WebAssembly debugging support and sending us your feedback! We’ve made some progress in this release, but there are still a number of limitations with the current debugging experience in Visual Studio and Visual Studio Code. The following debugging features are still not yet fully implemented:

  • Inspecting arrays
  • Hovering to inspect members
  • Step debugging into or out of managed code
  • Full support for inspecting value types
  • Breaking on unhandled exceptions
  • Hitting breakpoints during app startup

We expect to continue to improve the debugging experience in future releases.

Help improve the Blazor docs!

We’ve received a some feedback from the in-product Blazor survey that the Blazor docs could use some improvement. Thank you for this feedback! We know that docs are a critical part of any software development framework, and we are committed to making the Blazor docs as helpful as we can.

We need your help to understand how to best improve the Blazor docs! If you’d like to help make the Blazor docs better, please do the following:

  • As you read the Blazor docs, let us know where we should focus our efforts by telling us if you find a topic helpful or not using the helpfulness widget at the top of each doc page:

    Doc helpfulness

  • Use the Feedback section at the bottom of each doc page to let us know when a particular topic is unclear, inaccurate, or incomplete.

    Doc feedback

  • Comment on our Improve the Blazor docs GitHub issue with your suggestions for new content and ways to improve the existing content.

Feedback

We hope you enjoy the new features in this preview release of Blazor WebAssembly! Please let us know what you think by filing issues on GitHub.

Thanks for trying out Blazor!

Posted on Leave a comment

Blazor WebAssembly 3.2.0 Preview 3 release now available

Daniel Roth

Daniel

A new preview update of Blazor WebAssembly is now available! Here’s what’s new in this release:

  • Debugging in Visual Studio and Visual Studio Code
  • Auto-rebuild in Visual Studio
  • Configuration
  • New HttpClient extension methods for JSON handling

Get started

To get started with Blazor WebAssembly 3.2.0 Preview 3 install the latest .NET Core 3.1 SDK.

NOTE: Version 3.1.201 or later of the .NET Core SDK is required to use this Blazor WebAssembly release! Make sure you have the correct .NET Core SDK version by running dotnet --version from a command prompt.

Once you have the appropriate .NET Core SDK installed, run the following command to install the updated Blazor WebAssembly template:

dotnet new -i Microsoft.AspNetCore.Components.WebAssembly.Templates::3.2.0-preview3.20168.3

If you’re on Windows using Visual Studio, we recommend installing the latest preview of Visual Studio 2019 16.6. Installing Visual Studio 2019 16.6 Preview 2 or later will also install an updated version of the .NET Core 3.1 SDK that includes the Blazor WebAssembly template, so you don’t need to separately install it.

That’s it! You can find additional docs and samples on https://blazor.net.

Upgrade an existing project

To upgrade an existing Blazor WebAssembly app from 3.2.0 Preview 2 to 3.2.0 Preview 3:

  • Update all Microsoft.AspNetCore.Components.WebAssembly.* package references to version 3.2.0-preview3.20168.3.
  • Update all Microsoft.AspNetCore.Blazor.* package references to version 3.2.0-preview3.20168.3.

You’re all set – easy peasy!

Debugging

You can now debug Blazor WebAssembly apps directly from Visual Studio and Visual Studio Code. You can set breakpoints, inspect locals, and step through your code. You can also simultaneously debug your Blazor WebAssembly app and any .NET code running on the server. Using the browser dev tools to debug your Blazor WebAssembly apps is also still supported.

Enable debugging

To enable debugging in an existing Blazor WebAssembly app, update launchSettings.json in the startup project of your app to include the following inspectUri property in each launch profile:

"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}"

This property enables the IDE to detect that this is a Blazor WebAssembly app and instructs the script debugging infrastructure to connect to the browser through Blazor’s debugging proxy.

Once updated, your launchSettings.json file should look something like this:

{ "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:50454", "sslPort": 44399 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "BlazorApp1.Server": { "commandName": "Project", "launchBrowser": true, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } } 

Visual Studio

To debug a Blazor WebAssembly app in Visual Studio:

  1. Ensure you have installed the latest preview release of Visual Studio 2019 16.6 or later.
  2. Create a new ASP.NET Core hosted Blazor WebAssembly app.
  3. Hit F5 to run the app in the debugger.
  4. Set a breakpoint in Counter.razor in the IncrementCount method.
  5. Browser to the Counter tab and click the button to hit the breakpoint:

    Debug Counter

  6. Check out the value of the currentCount field in the locals window:

    View locals

  7. Hit F5 to continue execution.

While debugging your Blazor WebAssembly app you can also debug your server code:

  1. Set a breakpoint in the FetchData.razor page in OnInitializedAsync.
  2. Set a breakpoint in the WeatherForecastController in the Get action method.
  3. Browser to the Fetch Data tab to hit the first breakpoint in the FetchData component just before it issues an HTTP request to the server:

    Debug Fetch Data

  4. Hit F5 to continue execution and then hit the breakpoint on the server in the WeatherForecastController:

    Debug server

  5. Hit F5 again to let execution continue and see the weather forecast table rendered.

Visual Studio Code

To debug a Blazor WebAssembly app in Visual Studio Code:

  1. Install the C# extension and the JavaScript Debugger (Nightly) extension with the debug.javascript.usePreview setting set to true.

    Extensions

    JS preview debugger

  2. Open an existing Blazor WebAssembly app with debugging enabled.

    a. If you get the following notification that additional setup is required to enable debugging, recheck that you have the correct extensions installed and JavaScript preview debugging enabled and then reload the window:

    Additional setup requried

    b. A notification should offer to add required assets for building and debugging to the app. Select “Yes”.

    Add required assets

  3. Starting the app in the debugger is then a two-step process:

    a. Start the app first using the “.NET Core Launch (Blazor Standalone)” launch configuration.

    b. Then start the browser using the “.NET Core Debug Blazor Web Assembly in Chrome” launch configuration (requires Chrome). To use the latest stable release of Edge instead of Chrome, change the type of the launch configuration in .vscode/launch.json from pwa-chrome to pwa-msedge.

  4. Set a breakpoint in the IncrementCount method in the Counter component and then select the button to hit the breakpoint:

    Debug Counter in VS Code

Known limitations

There are a number of limitations with the current debugging experience in Visual Studio and Visual Studio Code. The following debugging features are not yet fully implemented:

  • Inspecting arrays
  • Hovering to inspect members
  • Step debugging into or out of managed code
  • Full support for inspecting value types
  • Breaking on unhandled exceptions
  • Hitting breakpoints during app startup
  • Debugging an app with a service worker

We expect to continue to improve the debugging experience in future releases. We appreciate your feedback to help us get the Blazor WebAssembly debugging experience right!

Auto-rebuild in Visual Studio

Visual Studio 2019 16.6 will watch for file changes in .cs and .razor files across the solution and automatically rebuild and restart the app so that the changes can be seen by simply refreshing the browser. This enables auto-rebuild support for Blazor WebAssembly projects and Razor Class Libraries. Instead of manually rebuilding and restarting the app when making code changes, just edit, save, and then refresh the browser.

Configuration

Blazor WebAssembly apps now have built-in support for loading configuration data from appsettings.json and environment specific configuration data from appsettings.{environment}.json.

To add configuration data to your Blazor WebAssembly app:

  1. Add an appsettings.json file in the wwwroot folder of your Blazor WebAssembly app:
{ "message": "Hello from config!"
}
  1. Inject an IConfiguration instance into your components to access the configuration data.
@page "/"
@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration <h1>Configuration example</h1> <p>@Configuration["message"]</p>
  1. Run the app to see the configured message displayed on the home page.

  2. To optionally override this configuration with values specific to the Development environment, add an appsettings.Development.json to your wwwroot folder:

{ "message": "Hello from Development config!"
}
  1. If you now run the app in Development, you’ll see the new message.

Note: Blazor WebAssembly apps load the configuration data by downloading the JSON files to the browser, so these configuration files must be publicly addressable. Do not store secrets in these configuration files, as they are public and can be viewed by anyone.

New HttpClient extension methods for JSON handling

The .NET team has been hard at work creating a full set of new extension methods for HttpClient that handle JSON serialization and deserialization using System.Text.Json. These extension methods are now available in preview with the System.Net.Http.Json package and they will replace the existing helper methods in the Microsoft.AspNetCore.Blazor.HttpClient package. We haven’t updated the Blazor WebAssembly template yet to use the new extension methods, but we will in our next Blazor WebAssembly preview update.

You can try the new extension methods yourself by replacing the Microsoft.AspNetCore.Blazor.HttpClient package with the newer System.Net.Http.Json package. Then add @using System.Net.Http.Json to your _Imports.razor file and update your code as follows:

Microsoft.AspNetCore.Blazor.HttpClient System.Net.Http.Json
GetJsonAsync GetFromJsonAsync
PostJsonAsync PostAsJsonAsync
PutJsonAsync PutAsJsonAsync

The updated implementation of the FetchData component in the default Blazor WebAssembly template looks like this:

@code { private WeatherForecast[] forecasts; protected override async Task OnInitializedAsync() { forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast"); }
}

System.Net.Http.Json also provides a JsonContent class that can be used for sending serialized JSON, as well as convenient helper methods for reading JSON from an HttpContent instance.

Look for more details on System.Net.Http.Json to be published soon on the .NET blog.

Known issues

There are a few known issues with this release that you may run into:

  • When building a Blazor WebAssembly app using an older .NET Core SDK you may see the following build error:

    error MSB4018: The "ResolveBlazorRuntimeDependencies" task failed unexpectedly.
    error MSB4018: System.IO.FileNotFoundException: Could not load file or assembly '\BlazorApp1\obj\Debug\netstandard2.1\BlazorApp1.dll'. The system cannot find the file specified.
    error MSB4018: File name: '\BlazorApp1\obj\Debug\netstandard2.1\BlazorApp1.dll' error MSB4018: at System.Reflection.AssemblyName.nGetFileInformation(String s)
    error MSB4018: at System.Reflection.AssemblyName.GetAssemblyName(String assemblyFile)
    error MSB4018: at Microsoft.AspNetCore.Components.WebAssembly.Build.ResolveBlazorRuntimeDependencies.GetAssemblyName(String assemblyPath)
    error MSB4018: at Microsoft.AspNetCore.Components.WebAssembly.Build.ResolveBlazorRuntimeDependencies.ResolveRuntimeDependenciesCore(String entryPoint, IEnumerable`1 applicationDependencies, IEnumerable`1 monoBclAssemblies)
    error MSB4018: at Microsoft.AspNetCore.Components.WebAssembly.Build.ResolveBlazorRuntimeDependencies.Execute()
    error MSB4018: at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
    error MSB4018: at Microsoft.Build.BackEnd.TaskBuilder.ExecuteInstantiatedTask(ITaskExecutionHost taskExecutionHost, TaskLoggingContext taskLoggingContext, TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask)
    

    To address this issue, update to version 3.1.201 or later of the .NET Core 3.1 SDK.

  • You may see the following warning when building from the command-line:

    CSC : warning CS8034: Unable to load Analyzer assembly C:\Users\user\.nuget\packages\microsoft.aspnetcore.components.analyzers\3.1.0\analyzers\dotnet\cs\Microsoft.AspNetCore.Components.Analyzers.dll : Assembly with same name is already loaded
    

    To address this issue, update to your package reference to Microsoft.AspNetCore.Components to 3.1.3 or newer. If your project reference the Microsoft.AspNetCore.Components package through a transitive package reference that has not been updated, you can add a reference in your project to resolve the issue in your project.

  • The following error may occur when publishing an ASP.NET Core hosted Blazor app with the .NET IL linker disabled:

    An assembly specified in the application dependencies manifest (BlazorApp1.Server.deps.json) was not found
    

    This error occurs when assemblies shared by the server and Blazor client project get removed during publish (see https://github.com/dotnet/aspnetcore/issues/19926).

    To workaround this issue, ensure that you publish with the .NET IL linker enabled. To publish with the linker enabled:

    • Publish using a Release build configuration: dotnet publish -c Release. The .NET IL linker is automatically run for Release builds, but not for Debug builds.
    • Don’t set BlazorWebAssemblyEnableLinking to false in your client project file.

    If you’re hitting issues running with the linker disabled, you may need to configure the linker to preserve code that is being called using reflection. See https://docs.microsoft.com/aspnet/core/host-and-deploy/blazor/configure-linker for details.

Feedback

We hope you enjoy the new features in this preview release of Blazor WebAssembly! Please let us know what you think by filing issues on GitHub.

Thanks for trying out Blazor!

Posted on Leave a comment

ASP.NET Core updates in .NET 5 Preview 1

Avatar

Sourabh

.NET 5 Preview1 is now available and is ready for evaluation! .NET 5 will be a current release.

Get started

To get started with ASP.NET Core in .NET 5.0 install the .NET 5.0 SDK.

If you’re on Windows using Visual Studio, we recommend installing the latest preview of Visual Studio 2019 16.6.

Upgrade an existing project

To upgrade an existing ASP.NET Core 3.1 app to .NET 5:

  • Update the TargetFramework property to netcoreapp5.0
  • Update all Microsoft.AspNetCore.* package references to 5.0.0-preview.1.20124.5.
  • Update all Microsoft.Extensions.* package references to 5.0.0-preview.1.20120.4.

See the full list of breaking changes in ASP.NET Core 5.0.

That’s it! You should now be all set to use .NET 5.

What’s new?

ASP.NET Core in .NET 5 Preview 1 doesn’t include any major new features just yet, but it does include plenty of minor bug fixes. We expect to announce new features in upcoming preview releases.

See the release notes for additional details and known issues.

Give feedback

We hope you enjoy this release of ASP.NET Core in .NET 5! We are eager to hear about your experiences with this latest .NET 5 release. Let us know what you think by filing issues on GitHub.

Thanks for trying out ASP.NET Core!

Posted on Leave a comment

Blazor WebAssembly 3.2.0 Preview 2 release now available

Daniel Roth

Daniel

A new preview update of Blazor WebAssembly is now available! Here’s what’s new in this release:

  • Integration with ASP.NET Core static web assets
  • Token-based authentication
  • Improved framework caching
  • Updated linker configuration
  • Build Progressive Web Apps

Get started

To get started with Blazor WebAssembly 3.2.0 Preview 2 install the latest .NET Core 3.1 SDK.

NOTE: Version 3.1.102 or later of the .NET Core SDK is required to use this Blazor WebAssembly release! Make sure you have the correct .NET Core SDK version by running dotnet --version from a command prompt.

Once you have the appropriate .NET Core SDK installed, run the following command to install the update Blazor WebAssembly template:

dotnet new -i Microsoft.AspNetCore.Components.WebAssembly.Templates::3.2.0-preview2.20160.5

That’s it! You can find additional docs and samples on https://blazor.net.

Upgrade an existing project

To upgrade an existing Blazor WebAssembly app from 3.2.0 Preview 1 to 3.2.0 Preview 2:

  • Update your package references and namespaces as described below:
    • Microsoft.AspNetCore.Blazor -> Microsoft.AspNetCore.Components.WebAssembly
    • Microsoft.AspNetCore.Blazor.Build -> Microsoft.AspNetCore.Components.WebAssembly.Build
    • Microsoft.AspNetCore.Blazor.DevServer -> Microsoft.AspNetCore.Components.WebAssembly.DevServer
    • Microsoft.AspNetCore.Blazor.Server -> Microsoft.AspNetCore.Components.WebAssembly.Server
    • Microsoft.AspNetCore.Blazor.HttpClient -> Microsoft.AspNetCore.Blazor.HttpClient (unchanged)
    • Microsoft.AspNetCore.Blazor.DataAnnotations.Validation -> Microsoft.AspNetCore.Components.DataAnnotations.Validation
    • Microsoft.AspNetCore.Blazor.Mono -> Microsoft.AspNetCore.Components.WebAssembly.Runtime
    • Mono.WebAssembly.Interop -> Microsoft.JSInterop.WebAssembly
  • Update all Microsoft.AspNetCore.Components.WebAssembly.* package references to version 3.2.0-preview2.20160.5.
  • In Program.cs add a call to builder.Services.AddBaseAddressHttpClient().
  • Rename BlazorLinkOnBuild in your project files to BlazorWebAssemblyEnableLinking.
  • If your Blazor WebAssembly app is hosted using ASP.NET Core, make the following updates in Startup.cs in your Server project:
    • Rename UseBlazorDebugging to UseWebAssemblyDebugging.
    • Remove the call to services.AddResponseCompression (response compression is now handled by the Blazor framework).
    • Replace the call to app.UseClientSideBlazorFiles<Client.Program>() with app.UseBlazorFrameworkFiles().
    • Replace the call to endpoints.MapFallbackToClientSideBlazor<Client.Program>("index.html") with endpoints.MapFallbackToFile("index.html").

Hopefully that wasn’t too painful!

Integration with ASP.NET Core static web assets

Blazor WebAssembly apps now integrate seamlessly with how ASP.NET Core handles static web assets. Blazor WebAssembly apps can use the standard ASP.NET Core convention for consuming static web assets from referenced projects and packages: _content/{LIBRARY NAME}/{path}. This allows you to easily pick up static assets from referenced component libraries and JavaScript interop libraries just like you can in a Blazor Server app or any other ASP.NET Core web app.

Blazor WebAssembly apps are also now hosted in ASP.NET Core web apps using the same static web assets infrastructure. After all, a Blazor WebAssembly app is just a bunch of static files!

This integration simplifies the startup code for ASP.NET Core hosted Blazor WebAssembly apps and removes the need to have an assembly reference from the server project to the client project. Only the project reference is needed, so setting ReferenceOutputAssembly to false for the client project reference is now supported.

Building on the static web assets support in ASP.NET Core also enables new scenarios like hosting ASP.NET Core hosted Blazor WebAssembly apps in Docker containers. In Visual Studio you can add docker support to your Blazor WebAssembly app by right-clicking on the Server project and selecting Add > Docker support.

Token-based authentication

Blazor WebAssembly now has built-in support for token-based authentication.

Blazor WebAssembly apps are secured in the same manner as Single Page Applications (SPAs). There are several approaches for authenticating users to SPAs, but the most common and comprehensive approach is to use an implementation based on the OAuth 2.0 protocol, such as OpenID Connect (OIDC). OIDC allows client apps, like a Blazor WebAssembly app, to verify the user identity and obtain basic profile information using a trusted provider.

Using the Blazor WebAssembly project template you can now quickly create apps setup for authentication using:

  • ASP.NET Core Identity and IdentityServer
  • An existing OpenID Connect provider
  • Azure Active Directory

Authenticate using ASP.NET Core Identity and IdentityServer

Authentication for Blazor WebAssembly apps can be handled using ASP.NET Core Identity and IdentityServer. ASP.NET Core Identity handles authenticating users while IdentityServer handles the necessary protocol endpoints.

To create a Blazor WebAssembly app setup with authentication using ASP.NET Core Identity and IdentityServer run the following command:

dotnet new blazorwasm --hosted --auth Individual -o BlazorAppWithAuth1

If you’re using Visual Studio, you can create the project by selecting the “ASP.NET Core hosted” option and the selecting “Change Authentication” > “Individual user accounts”.

Blazor authentication

Run the app and try to access the Fetch Data page. You’ll get redirected to the login page.

Blazor login

Register a new user and log in. You can now access the Fetch Data page.

Fetch data authorized

The Server project is configured to use the default ASP.NET Core Identity UI, as well as IdentityServer, and JWT authentication:

// Add the default ASP.NET Core Identity UI
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>(); // Add IdentityServer with support for API authorization
services.AddIdentityServer() .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(); // Add JWT authentication
services.AddAuthentication() .AddIdentityServerJwt();

The Client app is registered with IdentityServer in the appsettings.json file:

"IdentityServer": { "Clients": { "BlazorAppWithAuth1.Client": { "Profile": "IdentityServerSPA" } }
},

In the Client project, the services needed for API authorization are added in Program.cs:

builder.Services.AddApiAuthorization();

In FetchData.razor the IAccessTokenProvider service is used to acquire an access token from the server. The token may be cached or acquired without the need of user interaction. If acquiring the token succeeds, it is then applied to the request for weather forecast data using the standard HTTP Authorization header. If acquiring the token silently fails, the user is redirected to the login page:

protected override async Task OnInitializedAsync()
{ var httpClient = new HttpClient(); httpClient.BaseAddress = new Uri(Navigation.BaseUri); var tokenResult = await AuthenticationService.RequestAccessToken(); if (tokenResult.TryGetToken(out var token)) { httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token.Value}"); forecasts = await httpClient.GetJsonAsync<WeatherForecast[]>("WeatherForecast"); } else { Navigation.NavigateTo(tokenResult.RedirectUrl); }
}

For additional details on using Blazor WebAssembly with ASP.NET Core Identity and IdentityServer see Secure an ASP.NET Core Blazor WebAssembly hosted app with Identity Server

Authenticate using an existing OpenID Connect provider

You can setup authentication for a standalone Blazor WebAssembly using any valid OIDC provider. Once you’ve registered your app with the OIDC provider you configure the Blazor WebAssembly app to use that provider by calling AddOidcAuthentication in Program.cs:

builder.Services.AddOidcAuthentication(options =>
{ options.ProviderOptions.Authority = "{AUTHORITY}"; options.ProviderOptions.ClientId = "{CLIENT ID}";
});

You can create a standalone Blazor WebAssembly app that uses a specific OIDC provider for authentication using the following command:

dotnet new blazorwasm --auth Individual --client-id "{CLIENT ID}" --authority "{AUTHORITY}"

For more details on configuring authentication for a standalone Blazor WebAssembly app see Secure an ASP.NET Core Blazor WebAssembly standalone app with the Authentication library

Authenticate using Azure AD & Azure AD B2C

You can also setup Blazor WebAssembly apps to use Azure Active Directory (Azure AD) or Azure Active Directory Business-to-Customer (Azure AD B2C) for authentication. When authenticating using Azure AD or Azure AD B2C authentication is handled using the new Microsoft.Authentication.WebAssembly.Msal library, which is based on the Microsoft Authentication Library (MSAL.js).

To learn how to setup authentication for Blazor WebAssembly app using Azure AD or Azure AD B2C see:

Additional authentication resources

This is just a sampling of the new authentication capabilities in Blazor WebAssembly. To learn more about how Blazor WebAssembly supports authentication see Secure ASP.NET Core Blazor WebAssembly.

Improved framework caching

If you look at the network trace of what’s being download for a Blazor WebAssembly app after it’s initially loaded, you might think that Blazor WebAssembly has been put on some sort of extreme diet:

Blazor network

Whoa! Only 159kB? What’s going on here?

When a Blazor WebAssembly app is initially loaded, the runtime and framework files are now stored in the browser cache storage:

Blazor cache

When the app loads, it first uses the contents of the blazor.boot.json to check if it already has all of the runtime and framework files it needs in the cache. If it does, then no additional network requests are necessary.

You can still see what the true size of the app is during development by checking the browser console:

Blazor loaded resources

Updated linker configuration

You may notice with this preview release that the download size of the app during development is now a bit larger, but build times are faster. This is because we no longer run the .NET IL linker during development to remove unused code. In previous Blazor previews we ran the linker on every build, which slowed down development. Now we only run the linker for release builds, which are typically done as part of publishing the app. When publishing the app with a release build (dotnet publish -c Release), the linker removes any unnecessary code and the download size is much more reasonable (~2MB for the default template).

If you prefer to still run the .NET IL linker on each build during development, you can turn it on by adding <BlazorWebAssemblyEnableLinking>true<BlazorWebAssemblyEnableLinking> to your project file.

Build Progressive Web Apps with Blazor

A Progressive Web App (PWA) is a web-based app that uses modern browser APIs and capabilities to behave like a native app. These capabilities can include:

  • Working offline and always loading instantly, independently of network speed
  • Being able to run in its own app window, not just a browser window
  • Being launched from the host operating system (OS) start menu, dock, or home screen
  • Receiving push notifications from a backend server, even while the user is not using the app
  • Automatically updating in the background

A user might first discover and use the app within their web browser like any other single-page app (SPA), then later progress to installing it in their OS and enabling push notifications.

Blazor WebAssembly is a true standards-based client-side web app platform, so it can use any browser API, including the APIs needed for PWA functionality.

Using the PWA template

When creating a new Blazor WebAssembly app, you’re offered the option to add PWA features. In Visual Studio, the option is given as a checkbox in the project creation dialog:

image

If you’re creating the project on the command line, you can use the --pwa flag. For example,

dotnet new blazorwasm --pwa -o MyNewProject

In both cases, you’re free to also use the “ASP.NET Core hosted” option if you wish, but don’t have to do so. PWA features are independent of how the app is hosted.

Installation and app manifest

When visiting an app created using the PWA template option, users have the option to install the app into their OS’s start menu, dock, or home screen.

The way this option is presented depends on the user’s browser. For example, when using desktop Chromium-based browsers such as Edge or Chrome, an Add button appears within the address bar:

image

On iOS, visitors can install the PWA using Safari’s Share button and its Add to Homescreen option. On Chrome for Android, users should tap the Menu button in the upper-right corner, then choose Add to Home screen.

Once installed, the app appears in its own window, without any address bar.

image

To customize the window’s title, color scheme, icon, or other details, see the file manifest.json in your project’s wwwroot directory. The schema of this file is defined by web standards. For detailed documentation, see https://developer.mozilla.org/en-US/docs/Web/Manifest.

Offline support

By default, apps created using the PWA template option have support for running offline. A user must first visit the app while they are online, then the browser will automatically download and cache all the resources needed to operate offline.

Important: Offline support is only enabled for published apps. It is not enabled during development. This is because it would interfere with the usual development cycle of making changes and testing them.

Warning: If you intend to ship an offline-enabled PWA, there are several important warnings and caveats you need to understand. These are inherent to offline PWAs, and not specific to Blazor. Be sure to read and understand these caveats before making assumptions about how your offline-enabled app will work.

To see how offline support works, first publish your app, and host it on a server supporting HTTPS. When you visit the app, you should be able to open the browser’s dev tools and verify that a Service Worker is registered for your host:

image

Additionally, if you reload the page, then on the Network tab you should see that all resources needed to load your page are being retrieved from the Service Worker or Memory Cache:

image

This shows that the browser is not dependent on network access to load your app. To verify this, you can either shut down your web server, or instruct the browser to simulate offline mode:

image

Now, even without access to your web server, you should be able to reload the page and see that your app still loads and runs. Likewise, even if you simulate a very slow network connection, your page will still load almost immediately since it’s loaded independently of the network.

To learn more about building PWAs with Blazor, check out the documentation.

Known issues

There are a few known issues with this release that you may run into:

  • When building a Blazor WebAssembly app using an older .NET Core SDK you may see the following build error:

    error MSB4018: The "ResolveBlazorRuntimeDependencies" task failed unexpectedly.
    error MSB4018: System.IO.FileNotFoundException: Could not load file or assembly '\BlazorApp1\obj\Debug\netstandard2.1\BlazorApp1.dll'. The system cannot find the file specified.
    error MSB4018: File name: '\BlazorApp1\obj\Debug\netstandard2.1\BlazorApp1.dll' error MSB4018: at System.Reflection.AssemblyName.nGetFileInformation(String s)
    error MSB4018: at System.Reflection.AssemblyName.GetAssemblyName(String assemblyFile)
    error MSB4018: at Microsoft.AspNetCore.Components.WebAssembly.Build.ResolveBlazorRuntimeDependencies.GetAssemblyName(String assemblyPath)
    error MSB4018: at Microsoft.AspNetCore.Components.WebAssembly.Build.ResolveBlazorRuntimeDependencies.ResolveRuntimeDependenciesCore(String entryPoint, IEnumerable`1 applicationDependencies, IEnumerable`1 monoBclAssemblies)
    error MSB4018: at Microsoft.AspNetCore.Components.WebAssembly.Build.ResolveBlazorRuntimeDependencies.Execute()
    error MSB4018: at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
    error MSB4018: at Microsoft.Build.BackEnd.TaskBuilder.ExecuteInstantiatedTask(ITaskExecutionHost taskExecutionHost, TaskLoggingContext taskLoggingContext, TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask)
    

    To address this issue, upgrade to version 3.1.102 or later of the .NET Core 3.1 SDK.

  • You may see the following warning when building from the command-line:

    CSC : warning CS8034: Unable to load Analyzer assembly C:\Users\user\.nuget\packages\microsoft.aspnetcore.components.analyzers\3.1.0\analyzers\dotnet\cs\Microsoft.AspNetCore.Components.Analyzers.dll : Assembly with same name is already loaded
    

    This issue will be fixed in a future update to the .NET Core SDK. To workaround this issue, add the <DisableImplicitComponentsAnalyzers>true</DisableImplicitComponentsAnalyzers> property to the project file.

Feedback

We hope you enjoy the new features in this preview release of Blazor WebAssembly! Please let us know what you think by filing issues on GitHub.

Thanks for trying out Blazor!

Daniel Roth
Daniel Roth

Principal Program Manager, ASP.NET

Follow Daniel   

Posted on Leave a comment

ASP.NET Core Apps Observability

Francisco Beltrao

Francisco

Thank you Sergey Kanzhelev for the support and review of this ASP.NET Core Apps Observability article.

Modern software development practices value quick and continuous updates, following processes that minimize the impact of software failures. As important as identifying bugs early, finding out if changes are improving business value are equally important. These practices can only work when a monitoring solution is in place. This article explores options for adding observability to .NET Core apps. They have been collected based on interactions with customers using .NET Core in different environments. We will be looking into OpenTelemetry and Application Insights SDKs to add observability to a sample distributed application.

Identifying software error and business impact require a monitoring solution with the ability to observe and report how the system and users behave. The collected data must provide the required information to analyze and identify a bad update. Answering questions such as:

  • Are we observing more errors than before?
  • Were there new error types?
  • Did the request duration unexpectedly increase compared to previous versions?
  • Has the throughput (req/sec) decreased?
  • Has the CPU and/or Memory usage increased?
  • Were there changes in our KPIs?
  • Is it selling less than before?
  • Did our visitor count decrease?

The impact of a bad system update can be minimized by combining the monitoring information with progressive deployment strategies. Such as canary, mirroring, rings, blue/green, etc.

Observability is Built on 3 Pillars:

  • Logging: collects information about events happening in the system. Helping the team analyze unexpected application behavior. Searching through the logs of suspect services can provide the necessary hint to identify the problem root cause. Such as: service throwing out of memory exceptions and app configuration not reflecting expected values. As well as calls to external service with incorrect address, calls to external service returns with unexpected results, and incoming requests with unexpected input.

  • Tracing: collects information to create an end-to-end view of how transactions are executed in a distributed system. A trace is like a stack trace spanning multiple applications. Once a problem has been recognized, traces are a good starting point in identifying the source in distributed operations. Like calls from service A to B are taking longer than normal, service payment calls are failing, etc.

  • Metrics: provide a real-time indication of how the system is running. It can be leveraged to build alerts, allowing proactive reactance to unexpected values. As opposed to logs and traces, the amount of data collected using metrics remains constant as the system load increases. Application problems are often first detected through abnormal metric values. Such as CPU usage being higher than before, payment error count spiking, and queued item count keeps growing.

Adding Observability to a .NET Core Application

There are many ways to add observability aspects to an application. Dapr for example, is a runtime to build distributed applications, transparently adding distribute tracing. Another example is through the usage of service meshes in Kubernetes (Istio, Linkerd).

Built-in and transparent tracing are typically covering basic scenarios and answering generic questions, such as observed request duration or CPU trends. Other questions, such as custom KPIs or user behavior, require adding instrumentation to your code.

To illustrate how observability can be added to a .NET Core application we will be using the following asynchronous distributed transaction example:

Sample Observability Application Overview

  1. Main Api receives a request from a “source”.
  2. Main Api enriches the request body with current day, obtained from Time Api.
  3. Main Api enqueues enriched request to a RabbitMQ queue for asynchronous processing.
  4. RabbitMQProcessor dequeues request.
  5. RabbitMQProcessor, as part of the request processing, calls Time Api to get dbtime.
  6. Time Api calls SQL Server to get current time.

To run the sample application locally (including dependencies and observability tools), follow this guide. The article will walkthrough adding each observability pillar (logging, tracing, metrics) into the sample asynchronous distributed transaction.

Note: for information on bootstrapping OpenTelemetry or Application Insights SDK please refer to the documentation: OpenTelemetry and Application Insights.

Logging was redesigned in .NET Core, bringing an integrated and extensible API. Built-in and external logging providers allow the collection of logs in multiple formats and targets. When deciding a logging platform, consider the following features:

  • Centralized: allowing the collection/storage of all system logs in a central location.
  • Structured logging: allows you to add searchable metadata to logs.
  • Searchable: allows searching by multiple criteria (app version, date, category, level, text, metadata, etc.)
  • Configurable: allows changing verbosity without code changes (based on log level and/or scope).
  • Integrated: integrated into tracing, facilitating analysis of traces and logs in the same tool.

The sample application uses the ILogger interface for logging. The snippet below demonstrates an example of structure logging. Which captures events using message template and generates information that is human and machine readable.

var result = await repository.GetTimeFromSqlAsync();
logger.LogInformation("{operation} result is {result}", nameof(repository.GetTimeFromSqlAsync), result);

When using a logging backend that understands structured logs, such as Application Insights, search instances of the example log items where “operation” is equal to “GetTimeForSqlAsync”:

Observability Application Insights structured log search

Tracing collects required information to enable the observation of a transaction as it “walks” through the system. It must be implemented in every service taking part of the transaction to be effective.

.NET Core defines a common way in which traces can be defined through the System.Diagnostics.Activity class. Through the usage of this class, dependency implementations (i.e. HTTP, SQL, Azure, EF Core, StackExchange.Redis, etc.) can create traces in a neutral way, independent of the monitoring tool used.

It is important to notice that those activities will not be available automatically in a monitoring system. Publishing them is responsibility of the monitoring SDK used. Typically, SDKs have built-in collectors to common activities, transferring them to the destination platform automatically.

In the last quarter of 2019 OpenTelemetry was announced, promising to standardize telemetry instrumentation and collection across languages and tools. Before OpenTelemetry (or its predecessors OpenCensus and OpenTracing), adding observability would often mean adding proprietary SDKs (in)directly to the code base.

The OpenTelemetry .NET SDK is currently in alpha. The Azure Monitor Application Insights team is investing in OpenTelemetry as a next step of Azure Monitor SDKs evolution.

Quick Intro on Tracing with OpenTelemetry

In a nutshell, OpenTelemetry collects traces using spans. A span delimits an operation (HTTP request processing, dependency call). It contains start and end time (among other properties). It has a unique identifier (SpanId, 16 characters, 8 bytes) and a trace identifier (TraceId, 32 characters, 16 bytes). The trace identifier is used to correlate all spans for a given transaction. A span can contain children spans (as calls in a stack trace). If you are familiar with Azure Application Insights, the following table might be helpful to understand OpenTelemetry terms:

Application Insights OpenTelemetry
Request, PageView Span with span.kind = server
Dependency Span with span.kind = client
Id of Request and Dependency SpanId
Operation_Id TraceId
Operation_ParentId ParentId

Adding Tracing to a .NET Core Application

As mentioned previously, an SDK is needed in order to collect and publish distributed tracing in a .NET Core application. Application Insights SDK sends traces to its centralized database while OpenTelemetry supports multiple exporters (including Application Insights). When configured to use OpenTelemetry, the sample application sends traces to a Jaeger instance.

In the asynchronous distributed transaction scenario, track the following operations:

HTTP Requests between microservices

HTTP correlation propagation is part of both SDKs. With the only requirement of setting activity id format to W3C at application start:

public static void Main(string[] args)
{ Activity.DefaultIdFormat = ActivityIdFormat.W3C; Activity.ForceDefaultIdFormat = true; // rest is omitted
}

Dependency calls (SQL, RabbitMQ)

Unlike Application Insights SDK, OpenTelemetry (in early alpha) does not yet have support for SQL Server trace collection. A simple way to track dependencies with OpenTelemetry is to wrap the call like the following example:

var span = this.tracer.StartSpan("My external dependency", SpanKind.Client);
try
{ return CallToMyDependency();
}
catch (Exception ex)
{ span.Status = Status.Internal.WithDescription(ex.ToString()); throw;
}
finally
{ span.End();
}

Asynchronous Processing / Queued Items

There is no built-in trace correlation between publishing and processing a RabbitMQ message. Custom code is required, creating the publishing activity (optional) and referencing the parent trace during the item dequeuing.

We covered previously creating traces by wrapping the dependency call. This option allows expressing additional semantic information such as links between spans for batching and other fan-in patterns. Another option is to use System.Diagnostics.Activity, which is a SDK independent way to create traces. This option has limited set of features, however, is built-in into .NET.

These two options work well with each other and .NET team is working on making .NET Activity and OpenTelemetry spans integration better.

Creating an Operation Trace

The snippet below demonstrates how the publish operation trace can be created. It adds the trace information to the enqueued message header, which will later be used to link both operations.

Activity activity = null;
if (diagnosticSource.IsEnabled("Sample.RabbitMQ"))
{ // Generates the Publishing to RabbitMQ trace // Only generated if there is an actual listener activity = new Activity("Publish to RabbitMQ"); diagnosticSource.StartActivity(activity, null);
} // Add current activity identifier to the RabbitMQ message
basicProperties.Headers.Add("traceparent", Activity.Current.Id); channel.BasicPublish(...) if (activity != null)
{ // Signal the end of the activity diagnosticSource.StopActivity(activity, null);
}

A collector, which subscribes to target activities, is required to publish the trace to a backend. Implementing a collector is not a straightforward task and is intended to be used by SDK implementors. The snippet below is taken from the sample application, where a simplified and not production-ready, RabbitMQ collector for OpenTelemetry was implemented:

public class RabbitMQListener : ListenerHandler
{ public override void OnStartActivity(Activity activity, object payload) { var span = this.Tracer.StartSpanFromActivity(activity.OperationName, activity); foreach (var kv in activity.Tags) span.SetAttribute(kv.Key, kv.Value); } public override void OnStopActivity(Activity activity, object payload) { var span = this.Tracer.CurrentSpan; span.End(); if (span is IDisposable disposableSpan) { disposableSpan.Dispose(); } }
} var subscriber = new DiagnosticSourceSubscriber(new RabbitMQListener("Sample.RabbitMQ", tracer), DefaultFilter);
subscriber.Subscribe();

For more information on how to build collectors, please refer to OpenTelemetry/Application Insights built-in collectors as well as this user guide.

Activity

As mentioned, HTTP requests in ASP.NET have built-in activity correlation injected by the framework. That is not the case for the RabbitMQ consumer. In order to continue the distributed transaction, we must create the span referencing the parent trace. This was injected into the message by the publisher. The snippet below uses an extension method to build the activity:

public static Activity ExtractActivity(this BasicDeliverEventArgs source, string name)
{ var activity = new Activity(name ?? Constants.RabbitMQMessageActivityName); if (source.BasicProperties.Headers.TryGetValue("traceparent", out var rawTraceParent) && rawTraceParent is byte[] binRawTraceParent) { activity.SetParentId(Encoding.UTF8.GetString(binRawTraceParent)); } return activity;
}

The activity is then used to create the concrete trace. In OpenTelemetry the code looks like this:

// Note: OpenTelemetry requires the activity to be started
activity.Start();
tracer.StartActiveSpanFromActivity(activity.OperationName, activity, SpanKind.Consumer, out span);

The snippet below creates the telemetry using Application Insights SDK:

// Note: Application Insights will start the activity
var operation = telemetryClient.StartOperation<Dependencytelemetry>(activity);

The usage of activities gives flexibility in terms of SDK used, as it is a neutral way to create traces. Once instrumented the distributed end-to-end transaction in Jaeger looks like this:

Distributed Trace in Jaeger

The same transaction in Application Insights looks like this:

Distributed Trace in Application Insights

When using single monitoring solution for traces and logs, such as Application Insights, the logs become part of the end-to-end transaction:

Observability Application Insights: traces and logs

Metrics

There are common metrics applicable to most applications, like CPU usage, allocated memory, and request time. As well as business specific like visitors, page views, sold items, and sent items. Exposing business metrics in a .NET Core application typically requires using an SDK.

Collection metrics in .NET Core happens through 3rd-party SDKs which aggregate values locally, before sending to a backend. Most libraries have built-in collection for common application metrics. However, business specific metrics need to be built in the application logic, since they are created based on events that occur in the application domain.

In the sample application we are using metric counters for: enqueued items, successfully processed items and unsuccessfully processed items. The implementation in both SDKs is similar, requiring setting up a metric, dimensions and finally, tracking the counter values.

OpenTelemetry supports multiple exporters and we will be using Prometheus exporter. Prometheus combined with Grafana, for visualization and alerting, is a popular choice for open source monitoring. Application Insights supports metrics as any other instrumentation type, requiring no additional SDK or tool.

Defining a metric and tracking values using OpenTelemetry looks like this:

// Create counter
var simpleProcessor = new UngroupedBatcher(exporter, TimeSpan.FromSeconds(5));
var meterFactory = MeterFactory.Create(simpleProcessor);
var meter = meterFactory.GetMeter("Sample App");
var enqueuedCounter = meter.CreateInt64Counter("Enqueued Item"); // Incrementing counter for specific source
var labelSet = new Dictionary<string, string>() { { "Source", source } }; enqueuedCounter.Add(context, 1L, this.meter.GetLabelSet(labelSet));

The visualization with Grafana is illustrated in the image below:

Metrics with Grafana/Prometheus

The snippet below demonstrates how to define a metric and track its values using the Application Insights SDK:

// create counter
var enqueuedCounter = telemetryClient.GetMetric(new MetricIdentifier("Sample App", "Enqueued Item", "Source")); // Incrementing counter for specific source
enqueuedCounter.TrackValue(metricValue, source);

The visualization in Application Insights is illustrated below:

Observability Application Insights custom metrics

Troubleshooting

Now that we have added the 3 observability pillars to a sample application, let’s use them to troubleshoot a scenario where the application is experiencing problems.

The first signals of an application problems are usually detected by anomalies in metrics. The snapshot below illustrates such a scenario, where the number of failed processed items spikes (red line).

Metrics indicating failure

A possible next step is to look for hints in distributed traces. This should help us identify where the problem is happening. In Jaeger, searching with the tag “error=true” filters the results, listing transaction where at least one error happened.

Jaeger traces with error

In Application Insights, we can search for errors in end-to-end transactions by looking in the Failures/Dependencies or Failures/Exceptions.

Search traces with error in Application Insights

Application Insights error details in trace

The problem seems to be related to the Sample.RabbitMQProcessor service. Logs of this service can help us identify the problem. When using Application Insights logging provider, log and traces are correlated, being displayed in the same view:

Observability Application Insights errors and logs

Looking at the details, we discover that the exception InvalidEventNameException is being raised. Since we are logging the message payload, details of the failed message are available in the monitoring tool. It appears the message being processed has the eventName value of “error”, which is causing the exception to be raised.

When introducing observability into a .NET Core application, two decisions need to be taken:

  • The backend(s) where collected data will be stored and analyzed.
  • How instrumentation will be added to the application code.

Depending on your organization, the monitoring tool might already be selected. However, if you do have the chance to make this decision, consider the following:

  • Centralized: having all data in a single place makes it simple to correlate information. For example, logs, distribute traces and CPU usage. If they are split, more effort is required.
  • Manageability: how simple is to manage the monitoring tool? Is it hosted in the same machines/VMs where your application is running? In that case, shared infrastructure unavailability might leave you in the dark. When monitoring is not working, alerts won’t be triggered and metrics won’t be collected.
  • Vendor Locking: if you need to run the same application in different environments (i.e. on premises and cloud), choosing a solution that can run everywhere might be favored.
  • Application Dependencies: parts of your infrastructure or tooling that might require you to use a specific monitoring vendor. For example, Kubernetes scaling and/or progressive deployment based on Prometheus metrics.

Once the monitoring tool has been defined, choosing an SDK is limited to two options. Using the one provided by the monitoring vendor or a library capable of integrating to multiple backends.

Vendor SDKs typically yield little/no surprises regarding stability and functionality. That is the case with Application Insights, for example. It is stable with a rich feature set, including live stream, which is a feature-specific to this specific monitoring system.

OpenTelemetry

Using OpenTelemetry SDK gives you more flexibility, offering integration with multiple monitoring backends. You can even mesh them: a centralized monitoring solution for all collected data, while having a subset sent to Prometheus to fulfill a requirement. If you are unsure whether OpenTelemetry is a good fit for your project, consider the following:

  • When is your project going to production? The SDK is currently in alpha, meaning breaking changes and non-production-ready is expected.
  • Are you using vendor specific features not yet available through the OpenTelemetry SDK (specific collectors, etc.)?
  • Is your monitoring backend supported by the SDK?
  • Are you replacing a vendor SDK with OpenTelemetry? Plan some time to compare both SDKs, OpenTelemetry exporters might have differences compared to how the vendor SDK collects data.

Source code with the sample application can be found in this GitHub Repository.

Francisco Beltrao

Posted on Leave a comment

Blazor WebAssembly 3.2.0 Preview 1 release now available

Daniel Roth

Daniel

Today we released a new preview update for Blazor WebAssembly with a bunch of great new features and improvements.

Here’s what’s new in this release:

  • Version updated to 3.2
  • Simplified startup
  • Download size improvements
  • Support for .NET SignalR client

Get started

To get started with Blazor WebAssembly 3.2.0 Preview 1 install the .NET Core 3.1 SDK and then run the following command:

dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.2.0-preview1.20073.1

That’s it! You can find additional docs and samples on https://blazor.net.

Upgrade an existing project

To upgrade an existing Blazor WebAssembly app from 3.1.0 Preview 4 to 3.2.0 Preview 1:

  • Update all Microsoft.AspNetCore.Blazor.* package references to 3.2.0-preview1.20073.1.
  • In Program.cs in the Blazor WebAssembly client project replace BlazorWebAssemblyHost.CreateDefaultBuilder() with WebAssemblyHostBuilder.CreateDefault().
  • Move the root component registrations in the Blazor WebAssembly client project from Startup.Configure to Program.cs by calling builder.RootComponents.Add<TComponent>(string selector).
  • Move the configured services in the Blazor WebAssembly client project from Startup.ConfigureServices to Program.cs by adding services to the builder.Services collection.
  • Remove Startup.cs from the Blazor WebAssembly client project.
  • If you’re hosting Blazor WebAssembly with ASP.NET Core, in your Server project replace the call to app.UseClientSideBlazorFiles<Client.Startup>(...) with app.UseClientSideBlazorFiles<Client.Program>(...).

Version updated to 3.2

In this release we updated the versions of the Blazor WebAssembly packages to 3.2 to distinguish them from the recent .NET Core 3.1 Long Term Support (LTS) release. There is no corresponding .NET Core 3.2 release – the new 3.2 version applies only to Blazor WebAssembly. Blazor WebAssembly is currently based on .NET Core 3.1, but it doesn’t inherit the .NET Core 3.1 LTS status. Instead, the initial release of Blazor WebAssembly scheduled for May of this year will be a Current release, which “are supported for three months after a subsequent Current or LTS release” as described in the .NET Core support policy. The next planned release for Blazor WebAssembly after the 3.2 release in May will be with .NET 5. This means that once .NET 5 ships you’ll need to update your Blazor WebAssembly apps to .NET 5 to stay in support.

Simplified startup

We’ve simplified the startup and hosting APIs for Blazor WebAssembly in this release. Originally the startup and hosting APIs for Blazor WebAssembly were designed to mirror the patterns used by ASP.NET Core, but not all of the concepts were relevant. The updated APIs also enable some new scenarios.

Here’s what the new startup code in Program.cs looks like:

public class Program
{ public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("app"); await builder.Build().RunAsync(); }
}

Blazor WebAssembly apps now support async Main methods for the app entry point.

To a create a default host builder, call WebAssemblyHostBuilder.CreateDefault(). Root components and services are configured using the builder; a separate Startup class is no longer needed.

The following example adds a WeatherService so it’s available through dependency injection (DI):

public class Program
{ public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.Services.AddSingleton<WeatherService>(); builder.RootComponents.Add<App>("app"); await builder.Build().RunAsync(); }
}

Once the host is built, you can access services from the root DI scope before any components have been rendered. This can be useful if you need to run some initialization logic before anything is rendered:

public class Program
{ public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.Services.AddSingleton<WeatherService>(); builder.RootComponents.Add<App>("app"); var host = builder.Build(); var weatherService = host.Services.GetRequiredService<WeatherService>(); await weatherService.InitializeWeatherAsync(); await host.RunAsync(); }
}

The host also now provides a central configuration instance for the app. The configuration isn’t populated with any data by default, but you can populate it as required in your app.

public class Program
{ public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.Services.AddSingleton<WeatherService>(); builder.RootComponents.Add<App>("app"); var host = builder.Build(); var weatherService = host.Services.GetRequiredService<WeatherService>(); await weatherService.InitializeWeatherAsync(host.Configuration["WeatherServiceUrl"]); await host.RunAsync(); }
}

Download size improvements

Blazor WebAssembly apps run the .NET IL linker on every build to trim unused code from the app. In previous releases only the core framework libraries were trimmed. Starting with this release the Blazor framework assemblies are trimmed as well resulting in a modest size reduction of about 100 KB transferred. As before, if you ever need to turn off linking, add the <BlazorLinkOnBuild>false</BlazorLinkOnBuild> property to your project file.

Support for the .NET SignalR client

You can now use SignalR from your Blazor WebAssembly apps using the .NET SignalR client.

To give SignalR a try from your Blazor WebAssembly app:

  1. Create an ASP.NET Core hosted Blazor WebAssembly app.

    dotnet new blazorwasm -ho -o BlazorSignalRApp
    
  2. Add the ASP.NET Core SignalR Client package to the Client project.

    cd BlazorSignalRApp
    dotnet add Client package Microsoft.AspNetCore.SignalR.Client
    
  3. In the Server project, add the following Hub/ChatHub.cs class.

    using System.Threading.Tasks;
    using Microsoft.AspNetCore.SignalR; namespace BlazorSignalRApp.Server.Hubs
    { public class ChatHub : Hub { public async Task SendMessage(string user, string message) { await Clients.All.SendAsync("ReceiveMessage", user, message); } }
    }
    
  4. In the Server project, add the SignalR services in the Startup.ConfigureServices method.

    services.AddSignalR();
    
  5. Also add an endpoint for the ChatHub in Startup.Configure.

    .UseEndpoints(endpoints =>
    { endpoints.MapDefaultControllerRoute(); endpoints.MapHub<ChatHub>("/chatHub"); endpoints.MapFallbackToClientSideBlazor<Client.Program>("index.html");
    });
    
  6. Update Pages/Index.razor in the Client project with the following markup.

    @using Microsoft.AspNetCore.SignalR.Client
    @page "/"
    @inject NavigationManager NavigationManager <div> <label for="userInput">User:</label> <input id="userInput" @bind="userInput" />
    </div>
    <div class="form-group"> <label for="messageInput">Message:</label> <input id="messageInput" @bind="messageInput" />
    </div>
    <button @onclick="Send" disabled="@(!IsConnected)">Send Message</button> <hr /> <ul id="messagesList"> @foreach (var message in messages) { <li>@message</li> }
    </ul> @code { HubConnection hubConnection; List<string> messages = new List<string>(); string userInput; string messageInput; protected override async Task OnInitializedAsync() { hubConnection = new HubConnectionBuilder() .WithUrl(NavigationManager.ToAbsoluteUri("/chatHub")) .Build(); hubConnection.On<string, string>("ReceiveMessage", (user, message) => { var encodedMsg = user + " says " + message; messages.Add(encodedMsg); StateHasChanged(); }); await hubConnection.StartAsync(); } Task Send() => hubConnection.SendAsync("SendMessage", userInput, messageInput); public bool IsConnected => hubConnection.State == HubConnectionState.Connected;
    }
    
  7. Build and run the Server project

    cd Server
    dotnet run
    
  8. Open the app in two separate browser tabs to chat in real time over SignalR.

Known issues

Below is the list of known issues with this release that will get addressed in a future update.

  • Running a new ASP.NET Core hosted Blazor WebAssembly app from the command-line results in the warning: CSC : warning CS8034: Unable to load Analyzer assembly C:\Users\user\.nuget\packages\microsoft.aspnetcore.components.analyzers\3.1.0\analyzers\dotnet\cs\Microsoft.AspNetCore.Components.Analyzers.dll : Assembly with same name is already loaded.

    • Workaround: This warning can be ignored or suppressed using the <DisableImplicitComponentsAnalyzers>true</DisableImplicitComponentsAnalyzers> MSBuild property.

Feedback

We hope you enjoy the new features in this preview release of Blazor WebAssembly! Please let us know what you think by filing issues on GitHub.

Thanks for trying out Blazor!

Daniel Roth
Daniel Roth

Principal Program Manager, ASP.NET

Follow Daniel