How we got here
When I first started writing Damselfly back in 2019, Blazor WebAssembly (WASM) was a relatively new tech. At that point, Microsoft hadn’t got it to a mature enough state where they’d approve it for production use, and there were a lot of people who weren’t sure if it was going to be finished/supported at all. The concept for Damselfly is a server-based app for browsing photos that reside on the same host, with a small number of users (single-digits, in most cases) – and all typically accessed over the LAN; i.e., it was an ‘intranet’ app, perfectly suited for Blazor Server.
The Pros of Blazor Server
The development model of Blazor server is much easier. Firstly, there’s no browser-based client code so no tricky debugging scenarios. All of the code is running in-process, which means that you can do anything from anywhere. I tried to architect Damselfly with a reasonably well-structured approach – i.e., UI code that references services which encapsulate the core logic and functionality, and the DB access is managed from within those functional services. Occasionally, though, I’d take the
hacky tactical approach and throw a bit of direct DB access into the @code section of a UI control (yes, DBContects and EFCore linq statements directly dropped into a button-click handler – yuck). In general though, the approach is relatively straightforward, and key functions such as server-to-UI notifications are trivial to write in Blazor server.
For example, to ensure that the UI updates dynamically when the IndexingService detects a new file/folder on disk, the indexing service implements a FileWatcher, and conflates updates to the folders and files. The service exposes an OnFolderChanged event handler, and the UI code can subscribe to those events by @injecting the service into the component, and subscribing to those events.
This meant it was super-easy to make the UI dynamic and responsive, with push-style updates to the status bar, folder list, images, etc.
The Cons of Blazor Server
One of the downsides of Blazor server, though, is that everything has to go via the server if it runs in code. So for example, something as simple as the selection model – clicking an image and having it highlight to show it’s selected – involves a round-trip to the server. The mouse click is processed, the image state set to ‘selected’, and then a Blazor StateHasChanged update triggers the model state change to be pushed to the UI where it can be rendered. If the server running Damselfly is decent, and the network connection fast, this round-trip is usually unnoticeable – the UI is fast and responsive. However, the caveat to that is that if the server is under load, or Damselfly itself is working on processing CPU-heavy tasks (image recognition processing, etc) then the lag starts to build. If two or more users are interacting with the system, it can exacerbate the issue. Because each round-trip happens sequentially for each client, you can end up with the scenario where, under load, something as simple as clicking an image and waiting for its selection highlight to be rendered can take a few seconds. This can quickly make the user interaction slow, laggy and irritating.
The server ’round trip’ issue becomes even more prevalent for some of the functionality that I really want to add to Damselfly – i.e., basic image editing. It would be great if the user could apply some simple adjustments to the image (brightness, contrast, saturation, crops, etc) by adjusting them in the GUI, and then Damselfly would store the list of changes and apply them before rendering the image. The user could then export a high-res copy of the altered image, for use in their website, or other target audience – and all using non-destructive editing, which would preserve the integrity of the original source image/asset. However, doing that sort of processing on the server would require sending large volumes of image data back and forward to/from the server, which could quickly get slow and unwieldy – particularly with a rich UI paradigm such as a slider to adjust brightness. A better solution would be to do that image processing client-side, and act directly on the thumbnail preview that’s already available locally in the browser. This is where Blazor WASM comes in.
- Splitting services / server client, and interfaces
- Init properties and EFCore
- Client and server image caching
- User Management and Identity
- Consider changing client side memory-cache to use local storage? And pre-load image thumbs so they’re there for offline?
- Identity, brock from Duende, MSFT providing templates for non-identity, Chris Sainty, jwt management
- Running both, Link to dynamic switch post
Controversial / Reddit
- Splitting servicesInterfacesUser management
- Styling the progress bar
- AOT – not working on Android (issue) / Mobile and remote debugging, build times
- Blazor WASM cyclic DI (Issue)
- Other stuff
- System.Drawing and .Net 7