Category Archives: Development

HxInputFile – Blazor InputFile extension with direct (native) upload and progress indication

Starting with .NET 5 there is a InputFile component in Blazor which allows you to access content of the corresponding file(s) in form of a Stream (IBrowserFile.OpenReadStream).

If you run Blazor on server, the InputFile implementation sends the file content from browser to server through a SignalR connection almost at native speed (approx. 30% slower). In case of Blazor WebAssembly, you have to transfer the file to server on your own.

Due to unmarshalled interop Blazor is able to copy the file to WASM memory extremely quickly (approx. 100MB/sec). You can process the file on client-side (resize image etc.), but if you just want to upload the file, the cost of WASM is relatively high (in comparison to direct HTTP upload) and not suitable for larger files:

Direct (native) upload

To get over the limits of InputFile component in WASM, we can upload the files to server directly from JavaScript using XMLHttpRequest:

var data = new FormData();
data.append('file', file,;

var request = new XMLHttpRequest();'POST', uploadEndpointUrl, true);

(Unlike XMLHttpRequest the new fetch() API does not support progress indication.)

Progress indicator

XMLHttpRequest gives you nice progress indication by using the onprogress event:

request.upload.onprogress = function (e) {
    // e.loaded - bytes already uploaded
    // - total upload size (slightly bigger than the file size)

HxInputFile / HxInputFileCore

Let’s put this all together and create a new component. We will call it HxInputFileCore (+ the HxInputFile which is a ready-made Bootstrap derivative).

The most important portions follow:

public partial class HxInputFileCore : InputFile, IAsyncDisposable
	[Parameter] public string UploadUrl { get; set; }
	[Parameter] public EventCallback<UploadProgressEventArgs> OnProgress { get; set; }
	[Parameter] public EventCallback<FileUploadedEventArgs> OnFileUploaded { get; set; }
	[Parameter] public EventCallback<UploadCompletedEventArgs> OnUploadCompleted { get; set; }
	[Parameter] public bool Multiple { get; set; }
	[Parameter] public string Id { get; set; } = "hx" + Guid.NewGuid().ToString("N");

	[Inject] protected IJSRuntime JSRuntime { get; set; }

	private DotNetObjectReference<HxInputFileCore> dotnetObjectReference;
	private IJSObjectReference jsModule;

	public HxInputFileCore()
		dotnetObjectReference = DotNetObjectReference.Create(this);

	protected override void OnParametersSet()

		// TODO Temporary hack as base implementation of InputFile does not expose ElementReference (vNext:
		AdditionalAttributes ??= new Dictionary<string, object>();
		AdditionalAttributes["id"] = this.Id;
		AdditionalAttributes["multiple"] = this.Multiple;

	public async Task StartUploadAsync(string accessToken = null)
		jsModule ??= await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./_content/Havit.Blazor.Components.Web/hxinputfilecore.js");
		await jsModule.InvokeVoidAsync("upload", Id, dotnetObjectReference, this.UploadUrl, accessToken);

	public async Task HandleUploadProgress(int fileIndex, string fileName, long loaded, long total)
		var uploadProgress = new UploadProgressEventArgs() { /*...*/	};
		await OnProgress.InvokeAsync(uploadProgress);

	public async Task HandleFileUploaded(int fileIndex, string fileName, long fileSize, string fileType, long fileLastModified, int responseStatus, string responseText)
		var fileUploaded = new FileUploadedEventArgs() { /* ... */ };
		await OnFileUploaded.InvokeAsync(fileUploaded);

	public async Task HandleUploadCompleted(int fileCount, long totalSize)
		var uploadCompleted = new UploadCompletedEventArgs() { /* ... */
		await OnUploadCompleted.InvokeAsync(uploadCompleted);

	public async ValueTask DisposeAsync()
		// ...

…and the supportive JavaScript is:

export function upload(inputElementId, hxInputFileDotnetObjectReference, uploadEndpointUrl, accessToken) {
	var inputElement = document.getElementById(inputElementId);
	var dotnetReference = hxInputFileDotnetObjectReference;
	var files = inputElement.files;
	var totalSize = 0;
	var uploadedCounter = 0;

	for (var i = 0; i < files.length; i++) {
		(function (curr) {
			var index = curr;
			var file = files[curr];
			totalSize = totalSize + file.size;

			var data = new FormData();
			data.append('file', file,;

			var request = new XMLHttpRequest();'POST', uploadEndpointUrl, true);

			if (accessToken) {
				request.setRequestHeader('Authorization', 'Bearer ' + accessToken);

			request.upload.onprogress = function (e) {
				dotnetReference.invokeMethodAsync('HxInputFileCore_HandleUploadProgress', index,, e.loaded,;
			request.onreadystatechange = function () {
				if (request.readyState === 4) {
					dotnetReference.invokeMethodAsync('HxInputFileCore_HandleFileUploaded', index,, file.size, file.type, file.lastModified, request.status, request.responseText);

				if (uploadedCounter === files.length) {
					dotnetReference.invokeMethodAsync('HxInputFileCore_HandleUploadCompleted', files.length, totalSize);


The component is part of open-sourced library Havit.Blazor published on GitHub.


<HxInputFile @ref="hxInputFileComponent" Label="HxInputFile" UploadUrl="/file-upload-streamed/" OnProgress="HandleProgress" OnFileUploaded="HandleFileUploaded" OnUploadCompleted="HandleUploadCompleted" Multiple="true" />

<HxButton Text="Upload" OnClick="HandleUploadClick" />

	private HxInputFile hxInputFileComponent;

	private async Task HandleUploadClick()

		string accessToken = null;
		var accessTokenResult = await ... // use IAccessTokenProvider
		await hxInputFileComponent.StartUploadAsync(accessToken);

	private Task HandleProgress(UploadProgressEventArgs progress)
		// indicate progress here

	private Task HandleFileUploaded(FileUploadedEventArgs fileUploaded)
		// individual file uploaded

	private Task HandleUploadCompleted(UploadCompletedEventArgs uploadCompleted)
		// all files uploaded


The presented component if not feature complete. There is some more work to do:

  • Maximum file size limit
  • Limit number of files being uploaded in parallel
  • Better error-handling
  • …?


See also

SQL LocalDB: Upgrade to 2019 (15.0.2000)

For me it was quite confusing to find the 2019 version of LocalDB and it is not a streamline process to upgrade your local default instance.

The easiest way to upgrade your LocalDB instance to 2019 is:

  1. Download the LocalDB 2019 installer by using the SQL Server Express installer.
    1. Run the installer and select “Download Media”.
    2. Select “LocalDB” + click Download.
  2. Before running the SqlLocalDB.msi installer, delete your current MSSQLLocalDB instance:
sqllocaldb stop MSSQLLocalDB
sqllocaldb delete MSSQLLocalDB
  1. Run the new SqlLocalDB.msi (2019) installer. It will create a new MSSQLLocalDB instance.
  2. RESTART YOUR PC! (Otherwise MS SQL Management Studio will still tell you you have an old version running, etc. etc.)
  3. Now you can re-attach your original databases one by one using SQL Server Management Studio (RClick + Attach…)
  4. Done.

Blazor WebAssembly with gRPC-Web code-first approach

Do you like WCF-like approach and need to cover communication in between ASP.NET Core service and Blazor WebAssembly client? Use code-first with gRPC-Web! You can try the it right now by following a few simple steps (GitHub repo):

1. Blazor.Server – Prepare the ASP.NET Core host

Add NuGet packages:

  1. Grpc.AspNetCore.Web (prerelease)
  2. protobuf-net.Grpc.AspNetCore

Register CodeFirstGrpc() and GrpcWeb() services in Startup.cs ConfigureServices() method:

services.AddCodeFirstGrpc(config => { config.ResponseCompressionLevel = System.IO.Compression.CompressionLevel.Optimal; });

Add GrpcWeb middleware in between UseRouting() and UseEndpoints():

app.UseGrpcWeb(new GrpcWebOptions() { DefaultEnabled = true });

2. Blazor.Shared – Define the service contract (code-first)

Add System.ServiceModel.Primitives NuGet package.

Define the interface of your service:

public interface IMyService
	Task DoSomething(MyServiceRequest request);


public class MyServiceResult
	[DataMember(Order = 1)]
	public string NewText { get; set; }

	[DataMember(Order = 2)]
	public int NewValue { get; set; }

public class MyServiceRequest
	[DataMember(Order = 1)]
	public string Text { get; set; }

	[DataMember(Order = 2)]
	public int Value { get; set; }

3. Blazor.Server – Implement and publish the service

Implement your service:

public class MyService : IMyService
	public Task DoSomething(MyServiceRequest request)
		return Task.FromResult(new MyServiceResult()
			NewText = request.Text + " from server",
			NewValue = request.Value + 1

Publish the service in Startup.cs:

app.UseEndpoints(endpoints =>
	// ...

4. Blazor.Client (Blazor Web Assembly) – consume the service

Add NuGet packages:

  1. Grpc.Net.Client
  2. Grpc.Net.Client.Web (prerelease)
  3. protobuf-net.Grpc

Consume the service in your razor file:

var handler = new Grpc.Net.Client.Web.GrpcWebHandler(Grpc.Net.Client.Web.GrpcWebMode.GrpcWeb, new HttpClientHandler());
using (var channel = Grpc.Net.Client.GrpcChannel.ForAddress("https://localhost:44383/", new Grpc.Net.Client.GrpcChannelOptions() { HttpClient = new HttpClient(handler) }))
	var testFacade = channel.CreateGrpcService();
	this.result = await testFacade.DoSomething(request);

(You can move the plumbing to ConfigureServices() and use pure dependency injection in your razor files.)


  1. Steve Sanderson: Using gRPC-Web with Blazor WebAssembly
  2. Use gRPC in browser apps | Microsoft Docs
  3. protobuf-net.Grpc – Getting Started

Blazor Component Lifecycle Diagram

Blazor Component Lifecycle Diagram

Get familiar with basic Blazor (Razor) Component Lifestyle Methods:

  • SetParametersAsync
  • OnInitialized, OnInitializedAsync
  • OnParametersSet, OnParametersSetAsync
  • event handlers, event callbacks
  • BuildRenderTree
  • OnAfterPreRender, OnAfterPreRenderAsync
  • IDisposable.Dispose