This is the archived version of Roland Weigelt's weblog that ran from 2003 to 2023 at weblogs.asp.net

Contents tagged with ASP.NET Core

  • Tiny Difference, Big Consequences Reloaded: SignalR in .NET Core 3.1 vs. .NET 5

    In a past blog post I wrote about a surprising change I encountered during the migration of a .NET Core 2.2 application to .NET Core 3.0. I have just migrated that same application to .NET 5 and guess what, I stumbled across another “tiny difference with big consequences”.

    The situation: I have a WPF desktop client that communicates with an ASP.NET Core server that in turn communicates with a small TypeScript browser application. I use Web API for accessing files and meta data, and SignalR to call remote functions.

    After the migration I ran the application and things were working fine – until a certain point, where I received the following exception in a call of SignalR’s HubConnection.InvokeCoreAsync() (actual function name replaced with “doSomething”):

    Microsoft.AspNetCore.SignalR.HubException
       HResult=0x80131500
       Message=Failed to invoke 'doSomething' due to an error on the server.
       Source=System.Private.CoreLib

    On the server side, my own code was not even called.

    It took me a while to notice that one of the parameters of the function had the type of a class with a specific constellation of constructors:

    • A public constructor with parameters
    • A private default constructor.

    It turned out while SignalR in .NET Core 3.1 has no problem (de)serializing an instance of a class with a private default constructor, SignalR in .NET 5 does. So simply removing that constructor fixed the problem. In terms forcing the usage of the other constructor when explicitly creating objects of that type, nothing changed; I did not need a private default constructor for that.

    P.S. Do not ask me why I had a private default constructor in the first place. But the project is a couple of years old now, and things like this do happen.

  • JSON Serialization in .NET Core 3: Tiny Difference, Big Consequences

    Recently, I migrated a web API from .NET Core 2.2 to version 3.0 (following the documentation by Microsoft). After that, the API worked fine without changes to the (WPF) client or the controller code on the server – except for one function that looked a bit like this (simplified naming, obviously):

    [HttpPost]
    public IActionResult SetThings([FromBody] Thing[] things)
    {
        ...
    }

    The Thing class has an Items property of type List<Item>, the Item class has a SubItems property of type List<SubItem>.

    What I didn’t expect was that after the migration, all SubItems lists were empty, while the Items lists contained, well, items.

    But it worked before! I didn’t change anything!

    In fact, I didn’t touch my code, but something else changed: ASP.NET Core no longer uses Json.NET by NewtonSoft. Instead, JSON serialization is done by classes in the new System.Text.Json namespace.

    The Repro

    Here’s a simple .NET Core 3.0 console application for comparing .NET Core 2.2 and 3.0.

    The program creates an object hierarchy, serializes it using the two different serializers, deserializes the resulting JSON and compares the results (data structure classes not shown yet for story-telling purposes):

    class Program
    {
        private static Item CreateItem()
        {
            var item = new Item();
            item.SubItems.Add(new SubItem());
            item.SubItems.Add(new SubItem());
            item.SubItems.Add(new SubItem());
            item.SubItems.Add(new SubItem());
            return item;
        }
        static void Main(string[] args)
        {
            var original = new Thing();
            original.Items.Add(CreateItem());
            original.Items.Add(CreateItem());
    
    
            var json = System.Text.Json.JsonSerializer.Serialize(original);
            var json2 = Newtonsoft.Json.JsonConvert.SerializeObject(original);
            Console.WriteLine($"JSON is equal: {String.Equals(json, json2, StringComparison.Ordinal)}");
            Console.WriteLine();
    
            var instance1 = System.Text.Json.JsonSerializer.Deserialize<Thing>(json);
            var instance2 = Newtonsoft.Json.JsonConvert.DeserializeObject<Thing>(json);
    
            Console.WriteLine($".Items.Count: {instance1.Items.Count} (System.Text.Json)");
            Console.WriteLine($".Items.Count: {instance2.Items.Count} (Json.NET)");
            Console.WriteLine();
            Console.WriteLine($".Items[0].SubItems.Count: {instance1.Items[0].SubItems.Count} (System.Text.Json)");
            Console.WriteLine($".Items[0].SubItems.Count: {instance2.Items[0].SubItems.Count} (Json.NET)");
        }
    }

    The program writes the following output to the console:

    JSON is equal: True
    
    .Items.Count: 2 (System.Text.Json)
    .Items.Count: 2 (Json.NET)
    
    .Items[0].SubItems.Count: 0 (System.Text.Json)
    .Items[0].SubItems.Count: 4 (Json.NET)
    

    As described, the sub-items are missing after deserializing with System.Text.Json.

    The Cause

    Now let’s take a look at the classes for the data structures:

    public class Thing
    {
        public Thing()
        {
            Items=new List<Item>();
        }
    
        public List<Item> Items { get; set; }
    }
    
    public class Item
    {
        public Item()
        {
            SubItems = new List<SubItem>();
        }
    
        public List<SubItem> SubItems { get; }
    }
    
    public class SubItem
    {
    }

    There’s a small difference between the two list properties:

    • The Items property of class Thing has a getter and a setter.
    • The Subitems property of class Item only has a getter.

    (I don’t even remember why one list-type property does have a setter and the other does not)

    Apparently, Json.NET determines that while it cannot set the SubItems property directly, it can add items to the list (because the property is not null).

    The new deserialization in .NET Core 3.0, on the other hand, does not touch a property it cannot set.

    I don’t see this as a case of “right or wrong”. The different behaviors are simply the result of different philosophies:

    • Json.NET favors “it just works.”
    • System.Text.Json works along the principle “if the property does not have a setter, there is probably a reason for that.”

    The Takeways

    1. Replacing any non-trivial library “A” with another library “B” comes with a risk.
    2. Details. It’s always the details.
    3. Consistency in your code increases the chances of consistent behavior when something goes wrong.
  • WPF with Web API and SignalR – Why and How I Use It

    Preface: This blog post is less about going deep into the technical aspects of ASP.NET Core Web API and SignalR. Instead it’s a beginner’s story about why and how I started using ASP.NET Core. Maybe it contains bits of inspiration here and there for others in a similar situation.

    The why

    Years ago, I wrote a WPF application that manages media files, organizes them in playlists and displays the media on a second screen. Which is a bit of an understatement, because that “second screen” is in fact mapped onto the LED modules of a perimeter advertising system in a Basketball arena for 6000 people.

    Unfortunately, video playback in WPF has both performance and reliability problems, and a rewrite in UWP didn’t go as planned (as mentioned in previous blog posts). Experiments with HTML video playback, on the other hand, went really well.

    At this point, I decided against simply replacing the display part of the old application with a hosted browser control. Because my plans for the future (e.g. synchronizing the advertising system with other LED screens in the arena) already involved networking, I planned the new software to have three separate parts right from the beginning:

    1. A server process written in C#/ASP.NET Core for managing files and playlists. For quick results, I chose to do this as a command line program using Kestrel (with the option to move to a Windows service later).
    2. A (non-interactive) media playback display written in TypeScript/HTML, without any framework. By using a browser in kiosk mode, I didn’t have to write an actual “display application” – but I still have the option to do this at a later time.
    3. A C#/WPF application for creating and editing playlists, starting and stopping media playback, which I call the cockpit. I chose WPF because I had a lot of existing code and custom controls from the old application that I could reuse.

    The how: Communication between server, cockpit and display

    For communication, I use Web API and SignalR.

    Not having much prior experience, I started with the “Use ASP.NET Core SignalR with TypeScript and Webpack” sample and read the accompanying documentation. Then I added app.UseMvc() in Startup.Configure and services.AddMvc in Startup.ConfigureServices to enable Web API. I mention this detail because that was a positive surprise for me. When learning new technologies, I sometimes was in situations where I had created an example project A and struggled to incorporate parts of a separate example project B.

    For quick tests of the Web API controllers, PostMan turned out to be a valuable tool.

    Before working on “the real thing”, I read up on best practices on the web and tried to follow them to my best knowledge.

    Web API

    I use Web API to

    • create, read, update or delete (“CRUD”) lists of “things”
    • create, read, update or delete a single “thing”

    In my case, the “things” are both meta data (playlists, information about a media file, etc.) as well as the actual media files.

    Side note: I wrote about my experiences serving video files in my post “ASP.Net Core: Slow Start of File (Video) Download in Internet Explorer 11 and Edge”.

    SignalR

    While I use Web API to deal with “things”, I use SignalR for “actions”:

    • I want something to happen
    • I want to be notified when something happens.

    Currently the server distinguishes between “display” and “cockpit” roles for the communication. In the future, it’s likely I will have more than one “cockpit” (e.g. a “remote control” on a mobile device) – and more roles when the application grows beyond simple media playback only one display. Using the SignalR feature of groups, the clients of the server receive only those SignalR messages they are interested in as part of their role(s).

    Example

    When I select a video file in the “cockpit”, I want the “display” to preload the video. This means:

    • The cockpit tells the server via SignalR that a video file with a specific ID should be be preloaded in the display.
    • The server tells* the display (again, via SignalR) that the video file should be preloaded.
    • The display creates an HTML video tag and sets the source to a Web API URL that serves the media file.
    • When the video tag has been created and added to the browser DOM, the display tells the server to – in turn – tell the cockpit that a video with the specified is ready to be played.

    *) When I write “the server tells X”, this actually means that the server sends a message to all connections in group “X”.

    The cockpit

    In my WPF applications, I use the model-view-view model (MVVM) and the application service pattern.

    Using application services, view models can “do stuff” in an abstracted fashion. For example, when the code in a view model requires a confirmation from the user. In this case, I don’t want to open a WPF dialog box directly from a view model. Instead, my code tells a “user interaction service” to get a confirmation. The view model does not see the application service directly, only an interface. This means that the view model does not know (and does not care) whether the response really comes from a dialog shown to the user or some unit test code (that may just confirm everything).

    Application services can also offer events that view models can subscribe to, so the view models are notified when something interesting happens.

    Back to Web API and SignalR: I don’t let any view model use a SignalR connection or call a Web API URL directly. Instead I hide the communication completely behind the abstraction of an application service:

    • Web API calls and outgoing SignalR communication are encapsulated using async methods.
    • Incoming SignalR communication triggers events that view models (and other application services) can subscribe to.

    Minor pitfall: When a SignalR hub method is invoked and the handler is called in the WPF program, that code does not run on the UI thread. Getting around this threading issue (using the Post() method of SynchronizationContext.Current) is a good example of an implementation detail that an application service can encapsulate. The application service makes sure that the offered event is raised on the UI thread, and if a view model subscribes to this event, things “just work”.

  • Looking Back at 2018

    The end of the year is typically a time to look back – here’s my personal list of programs and technologies that played an important role for me in 2018.

    Visual Studio Code: It keeps getting better and better

    This editor-becoming-an-IDE needs no introduction. I have included it in this list because 2018 was the year where I started doing meaningful work beyond editing HTML/[S]CSS files in Visual Studio Code.

    For my purposes, it does not replace “classic” Visual Studio, but complements it. It’s not uncommon for me to debug ASP.NET Core and TypeScript code in Visual Studio Code as well as WPF code in Visual Studio 2018 at the same time (in a project where SignalR is used for Desktop Client <-> Server <-> Web Client communication).

    I’m extremely impressed by the ecosystem of extensions (e.g. Live Sass Compiler, Live Server) as well as the ongoing development with its monthly releases.

    Linea Sketch on iPad Pro: So good, it’s almost worth buying the hardware for

    I love Linea Sketch, which is an iPad app for (as the name implies) sketching. When I tried out various graphics applications for my 2018 iPad Pro, I was looking for something that would allow me to quickly draw UI sketches and illustrations for presentations without much overhead. Linea Sketch caught my attention because of the combination of simplicity and power. Two highlight features of the app are “ZipLine” and “ZipShape”, which let you quickly draw straight lines, circles and polygons without breaking your flow of work with the pen.

    Take a look at the Linea Sketch website to learn more about this fantastic app.

    UWP: I wanted to like it, but what a disappointment…

    I already wrote a bit about my frustration with the Universal Windows Platform (UWP) in the introduction to my previous blog post.

    Obviously, each new platform comes with a learning curve. If you have a clear vision of what you want to develop and are learning how to do it along the way, you are bound to take longer than expected. What happened with UWP, though, was that I encountered problems that too often became roadblocks for hours or sometimes days. If you get silent crashes or low-level exceptions with an HRESULT that basically says, “something went wrong”, the only thing you can do is to comment out code you have just written up to a point where the program is working again. That reminded me very much of the early Silverlight days (version 2 and 3), Back then, error messages only became helpful in later versions (I remember version 4 and 5 as being good enough).

    Web searches showed me that I wasn’t alone. Every so often, a question on a forum would made me go “yeah, I’d like to know an answer to that, too”. Unfortunately, often only reply would clearly indicate that the original post was only scanned for keywords, not actually read, let alone understood. Obviously, this is not a specific UWP problem. It’s just that other technologies seem to have reached a critical mass of users, so that e.g. on StackOverflow, additional comments and answers appear quicker and in larger numbers.

    The final straw for me were silent crashes of the Windows App Cert Kit, which had worked on my machine earlier. Searching for a solution, I came across Microsoft forum threads reporting WACK problems that went on for months, with no real solution in sight.

    I don’t know whether the WACK problem has been solved now, or whether UWP in general has become friendlier in terms of developer experience. But frankly, I have stopped caring. After all, we’re talking about programming as a hobby. Maybe I’ll revisit UWP in a few years – but for now, I have moved on.

    And by the way: I actually do like UWP APIs. They make e.g. creating a thumbnail of a media file or determining the length of a video file super-easy. But I can use these APIs from a WPF program as well, so that’s exactly what I’m doing now.

    ASP.NET Core (Web API, SignalR): A positive surprise

    While working with UWP was hard work for every step along the path, ASP.NET Core was clearly the opposite experience for me. Whenever I ran into a problem, I found a sample or some forum or StackOverflow post that helped me find a solution. And unlike with UWP, where I came from a WPF and Silverlight background, I didn’t have much prior knowledge.

    To put things in perspective:

    • I started with version 2.1, i.e. I surely skipped a lot of the problems of the 1.x days.
    • I found a great starting point with the SignalR with TypeScript and WebPack sample.
    • I use ASP.NET for non-UI, server-related functionality that is limited in scope.

    So, your mileage may vary. For me, the positive initial experience was a great motivation to learn more about the platform and to overcome the small problems that are simply a natural part of software development.

  • ASP.Net Core: Slow Start of File (Video) Download in Internet Explorer 11 and Edge

    While experimenting with the <video> tag on an HTML page with a web API endpoint as the source (ASP.NET Core 2.1.1), I noticed that the (not auto-playing) videos

    • appeared immediately in Chrome and Firefox,
    • but took a few seconds in Internet Explorer and Edge to show up on the page.

    This is what the code in my controller looked like:

    [HttpGet("{id}")] // api/video/example
    public IActionResult GetVideoByName(string id)
    {
    	// ...
    
    	var path = Path.Combine(@"D:\Example\Videos", id + ".mp4");
    	return File(System.IO.File.OpenRead(path), "video/mp4");
    }

    Searching the web for the obvious keywords (IE11, ASP.NET Core, file, stream, slow, download, etc.) yielded lots of results for all kinds of problems. In the end I found out that I needed to enable something called “range processing” (pardon my ignorance, I’m more of a UI guy than a networking expert).

    Fortunately, ASP.NET Core 2.1 offers a (new) overload for the File() function with a boolean parameter aptly named enabledRangeProcessing. So the solution is to set that parameter to true:

    	return File(System.IO.File.OpenRead(path), "video/mp4", true);