Rockford Lhotka

 Monday, September 2, 2019

I'm excited about two things in our industry right now: containers (specifically Kubernetes) and WebAssembly (specifically Blazor and Uno).

CSLA .NET version 4.11 got a lot of new and exciting features to support container and Kubernetes scenarios, and there are some more coming in CSLA version 5 as well.

But over the past few days I've been building a new Csla.Blazor package to provide some basic UI support when using CSLA 5 with Blazor. Specifically client-side Blazor, which is the really exciting part, though this should all work fine on server-side Blazor as well.

Application Context Manager

Part of this is a context manager that helps simplify configuration and context management within the Blazor client environment. Specifically:

  1. The HttpProxy is set to use text-based serialization, because Blazor (wasm) doesn't currently support passing binary data via HttpClient
  2. The User property is maintained in a static, just like in all other smart client scenarios

Configuring a Blazor Client App

Blazor relies on the standard Startup class like ASP.NET Core for configuring a client app. CSLA supports this model via an AddCsla method and the fluent CslaConfiguration system. As a result, basic configuration looks like this:

using Csla;
using Csla.Configuration;
using Microsoft.AspNetCore.Components.Builder;
using Microsoft.Extensions.DependencyInjection;

namespace BlazorExample.Client
{
  public class Startup
  {
    public void ConfigureServices(IServiceCollection services)
    {
      services.AddCsla();
      services.AddTransient(typeof(IDataPortal<>), typeof(DataPortal<>));
      services.AddTransient(typeof(Csla.Blazor.ViewModel<>), typeof(Csla.Blazor.ViewModel<>));
    }

    public void Configure(IComponentsApplicationBuilder app)
    {
      app.AddComponent<App>("app");

      CslaConfiguration.Configure().
        ContextManager(typeof(Csla.Blazor.ApplicationContextManager)).
        DataPortal().
          DefaultProxy(typeof(Csla.DataPortalClient.HttpProxy), "/api/DataPortal");
    }
  }
}

Blazor defaults to providing HttpClient as a service, and this code adds mappings for IDataPortal<T> and ViewModel<T>.

Notice that it also configures the app to use the ApplicationContextManager designed to support Blazor.

The HttpProxy data portal channel will gain access to the environment's HttpClient via dependency injection.

Data Portal Server Needs to Use Text

As noted above, the HttpClient implementation currently used by .NET in wasm can't transfer binary data. As a result both client and server need to be configured to use text-based data transfer (basically Base64 encoded binary data). This is automatic on the Blazor client, but the data portal server controller needs to use text as well. Here's the controller code from the server's endpoint:

  [Route("api/[controller]")]
  [ApiController]
  public class DataPortalController : Csla.Server.Hosts.HttpPortalController
  {
    public DataPortalController()
    {
      UseTextSerialization = true;
    }
  }

If your data portal needs to support Blazor and non-wasm clients, you'll need two controllers, one for wasm clients and one for everything else.

ViewModel Type

The new Csla.Blazor.ViewModel type provides basic support for creating a Razor Page that binds to a business domain object via the viewmodel.

As with all the previous XAML-based viewmodel types, this one exposes the domain object via a Model property, because the CSLA-based domain object already fully supports data binding. It would be a waste of code (to write, debug, test, and maintain) to duplicate all the properties from a CSLA-based domain class in a viewmodel.

Also, like the previous XAML-based viewmodel types, this one supports some basic verbs/operations that are likely to be triggered by the UI. Specifically the create/fetch and save operations.

Finally, the viewmodel type exposes a set of metastate methods designed to allow the Razor Page to easily understand and bind to the state of the business object. For example, is the object currently saveable? What are the information/warning/error validation messages for a given property? Is a property currently running any async business rules?

You can use all these metastate values to create a rich UI, much like in XAML, with no code. The Blazor data binding model, combined with the new ViewModel typically provide everything necessary.

To use this type in a page, make sure to add it to the services in Startup as shown earlier. Then inject it into the page:

@inject Csla.Blazor.ViewModel<PersonEdit> vm

And in the @code block call the RefreshAsync method:

@code {
  protected override async Task OnInitializedAsync()
  {
    await vm.RefreshAsync();
  }
}

The RefreshAsync method has various default behaviors around how it knows to fetch or create an instance of the business domain type:

  1. If the domain type is read-only it always does a fetch
  2. If the domain type is editable and no criteria parameters are provided it does a create
  3. If the domain type is editable and criteria is provided it does a fetch

You can override this behavior, but these defaults work well in many cases.

BlazorExample Sample

You can look at the Samples/BlazorExample sample to see how this comes together. That sample is the basic Blazor start template, plus the ability to add/edit person objects, and get a list of people in the "database" (a mock in-memory data store).

Monday, September 2, 2019 10:41:13 PM (Central Standard Time, UTC-06:00)  #    Disclaimer
 Wednesday, August 21, 2019

The new .NET Core 3 release includes support for the gRPC protocol. This is an efficient binary protocol for making network calls, and so is something that CSLA .NET should obviously support.

CSLA already has an extensible channel-based model for network communication via the data portal. Over the years there have been numerous channels, including:

  • .NET Remoting (obsolete)
  • asmx services (obsolete)
  • WCF (of limited value in modern .NET)
  • Http

I'm sure there have been others as well. The current recommended channel is via Http (using the HttpProxy type), as it best supports performance, routing, and various other features native to HTTP and to the data portal channel implementation.

CSLA .NET version 5.0.0 will include a new gRPC channel. Like all the other channels, this is a drop-in replacement for your existing channel.

⚠ This requires CSLA .NET version 5.0.0-R19082107 or higher

Client Configuration

On the client it requires a new NuGet package reference and a configuration change.

  1. Reference the new Csla.Channels.Grpc NuGet package
  2. On app startup, configure the data portal as shown here:
      CslaConfiguration.Configure().
        DataPortal().DefaultProxy(typeof(Csla.Channels.Grpc.GrpcProxy), "https://localhost:5001");

This configures the data portal to use the new GrpcProxy and provides the URL to the service endpoint. Obviously you need to provide a valid URL.

Server Configuration

On the server it requires a new NuGet package reference and a bit of code in Startup.cs to set up the service endpoint.

⚠ This requires an ASP.NET Core 3.0 project.

  1. Reference the new Csla.Channels.Grpc NuGet package
  2. In the ConfigureServices method you must configure gRPC: services.AddGrpc();
  3. In the Configure method add the data portal endpoint:
      app.UseRouting();

      app.UseEndpoints(endpoints =>
      {
        endpoints.MapGrpcService<Csla.Channels.Grpc.GrpcPortal>();
      });

General Notes

As usual, both client and server need to reference the same business library assembly, which should be a .NET Standard 2.0 library that references the Csla NuGet package. This assembly contains implementations of all your business domain classes based on the CSLA .NET base classes.

The gRPC data portal channel uses MobileFormatter to serialize and deserialize all object graphs, and so your business classes need to use modern CSLA coding conventions so they work with that serializer.

All the version and routing features added to the Http data portal channel in CSLA version 4.9.0 are also supported in this new gRPC channel, allowing it to take full advantage of container orchestration environments such as Kubernetes.

Also, as with the Http channel, the GrpcProxy and GrpcPortal types have virtual methods you can optionally override to implement compression on the data stream, and (on the client) to support advanced configuration scenarios when creating the underlying HttpClient and gRPC client objects.

Wednesday, August 21, 2019 1:37:26 PM (Central Standard Time, UTC-06:00)  #    Disclaimer