From Java Spring Boot to ASP.NET Core – First Impressions

In this post, I want to reflect on my impressions and experiences from the first two weeks of coding in ASP.NET Core. It was actually just some work on my hobby project in my spare time but I did manage to ‘deliver’ the core functionality of the system I’m working on. With this, I’ve made a vertical cut through all the system layers and learned a bit about everything – implementing persistence with Entity Framework Core, writing web API in ASP.NET Core and building a web UI with Blazor.

In my impressions, I am looking from the perspective of a longtime Java Spring Boot developer with some experience in the .NET standard. I’m not gonna go very deep with this and don’t expect another Java vs .NET argument.

Let me first recount how did it go with data access layer.

Working with Entity Framework Core

I only had a very short experience using Entity Framework from the standard .NET. At Visma we’re using Dapper and in my early days at Jeppesen, before my team switched to Java, we used NHibernate and sometimes raw ADO.NET. On this project, I wanted to use EF Core to explore it and see if I prefer it over NHibernate.

In general, the developer experience of using Entity Framework Core is very similar to what EF6 offers. Some features are not yet implemented but until now I didn’t hit any roadblocks. On the other hand, there are some new features in EF Core, like alternate keys or mixed client/database evaluation, which are not even planned to be implemented in EF6, but has already proven useful to me. You can find a comprehensive comparison here.

So here’s a few nice things I noticed.

The Goodies

Generally, I follow the DDD approach and in my business layer, I have Entities. While it is still a popular design choice to separate the business model from the persistence model – I prefer having just one. I think this separation trend became popular back when Hibernate (and probably NHibernate) introduced annotations as an alternative to XML configuration. XML was a pain and I remember I was also happy to start using annotations. But to many, myself included, it seemed that persistence concern now pollutes my business layer. Thus separation. Fortunately, EF Core has Fluent Syntax allowing to do all the ORM plumbing outside of my business objects, which is cool.

Goodie number two. Built-in support for migrations – awesome. I’m a longtime fan of keeping database schema under version control. The concept of migrations was I think first introduced in Ruby on Rails. Also, Martin Fowler and Scott Alen both covered the topic in great detail, but to set it all up on your own is a lot of work. Now, I can’t say I did a lot of migrations in EF Core yet – merely generated the initial schema, so I’ll see later how well it actually works.

Another nice thing was how easy it was to set up the in-memory database for automated tests. No special configuration needed. Just a few lines of code in base class for database-backed tests.

Then, of course, there is LINQ. Its query expressions do require getting used to, but fluent syntax feels very much like programming with Java 8 streams.

That said, there was a trap or two I fell into on the way.

The Gotchas

The first problem I encountered was related to design-time development tools and caused by having DbContext code in a separate assembly from the web application serving the API. Since it’s the web app where I keep database configuration, I thought it would be a natural place for storing migrations. However the tools could not find my DbContext, which was, of course, available through reference to my data access library, so I found it weird. The solution – to create the context in design-time factory – was described on a separate documentation page, but I actually found it through some other blog.

I ended up putting migrations on the data access assembly. Here however I had no natural way of specifying configuration for the database, so I ended up hardcoding the connection string. For now. I will find a more elegant solution later.

Another small gotcha was that the CLI designer tools require the very latest version of Microsoft.EntityFrameworkCore.Design to even detect that the package is present. Otherwise you get error message that the package is not found at all.

Last thing I stumbled upon was related to my automated tests. They started showing weird and inconsistent results – a clear indication that the database state is affected by other tests. What happened was that I missed the tip from documentation to use a different database name for each test to ensure separation. Tests run in parallel so some of them clashed regardless of me cleaning the database on each test start. I fixed it not by using different database name, but by building a dedicated service provider for each test. Like this:

private static MyDbContext CreateInMemoryDbContext()
    var serviceProvider = new ServiceCollection()

    var builder = new DbContextOptionsBuilder<MyDbContext>();
    var options = builder.Options;
    var db = new MyDbContext(options);
    return db;

Writing web API in ASP.NET Core

I must say that in some aspects ASP.NET Core is strikingly similar to Java Spring Boot. Same conventions around configuration files and providers. The same approach to environments support. Possibility to run in a container or with an embedded HTTP server. All feels like home.

Generally writing an API in ASP.NET Core is a smooth ride. One feature I quite dig is smart model binding where the framework picks up whatever matches the target model class or properties from different parts of an HTTP request.

Another thing I find very elegant is app configuration with Add… extension methods and Options pattern.

Last part which remains to discuss is the client side.

Implementing web UI with Blazor

There are so many potential applications of running C# and netstandard2.0 on the browser, I won’t even try to list them here. I’ll just mention one I actually benefited from writing this tiny bit of web UI.

Shared model classes

Remember when I said I have just one model for business and persistence? Well, I still use the same classes in DTOs and view models! No rewriting same structures in javascript. How nice!

Now, I had to put those model classes into a separate assembly called ‘shared’, which is used by ‘model’ (where the server side logic goes) and ‘webapp‘ where the UI is. This is because ‘webapp‘ running Blazor needs to target netstandard2.0 and it cannot depend on library targeting netcoreapp of any version. One extra assembly is a low price to pay for a common data model so I’m quite satisfied with how it turned out.

At a moment of writing this post, Blazor is still an experimental framework of which preview release is available. I’m not even sure if it will be officially released with .NET Core 3.0 later this year. Many features and tool support are still missing and it does affect developer experience.

Had to use Visual Studio

At first, I tried developing the UI with Rider. I use it at work and used IntelliJ IDEA for years before. Unfortunately, the support for Blazor is tentatively considered for 2019.1 release. In 2018.3 Rider not only fails to run Blazor apps but also reports many false positive errors on .cshtml pages. I ended up installing Visual Studio 2019 preview and writing UI there.

Another problem is limited support for debugging. It is not yet possible to attach Visual Studio to the process of the browser. You can debug Blazor in the browser by creating a special tab with a custom shortcut on Chrome instance which needs to be started with debugging enabled. All a bit clunky process.

Chrome debugger of WASM has also a number of limitations listed here. For example, it will not stop at breakpoints inside asynchronous methods.

Anyway. Blazor is not yet released and is already pretty awesome. No point dwelling on what’s missing.

Parting words

Using ASP.NET Core feels not much different from Java SpringBoot. Cool features of C# language, very good documentation and one-stop-shop experience of .NET makes it all even better. Now I can’t wait for ASP.NET Core 3.0 and for when Blazor is finally unleashed to disrupt the world of frontend development.

2 Replies to “From Java Spring Boot to ASP.NET Core – First Impressions”

  1. i switched from c# senior(10+ years + architect) to java to get more money as for some reason java developers get more and i see that java have only one good thing remote debug and external library debug(in c# it’s not working properly everywhere, only with .net reflector), everything else is bad comparing to c#, if you check ORM, IOC, configuration and even compilation, everything is more complicated and made on hacks, last thing i found that JPA is making insert while i call select from code. Code is not my and i understand why it’s made so but still EF allow to do exactly what you want without any strange requests, which you might not expect. Of course i can learn how to use JPA properly to avoid such problems, but why not make JPA simplier to use just by not allowing to make things which are not possible to make without hacks, developer should white hacks by himself if he wants, but not expect that external library do some magic

  2. vova,
    I’ve been using Java for 20+ years and Spring and Spring Boot since they came out. I started with Hibernate and have moved to JPA. I am not sure what issues you are referring to. Let me know if i can be of help.
    If you are trying to do JPA/Java without Spring Boot, I can see why there would be issues. This guide shows you how to easily do ORM, IOC and configuration and compilation easily. And also dependency management.

    FYI, i have been doing C# on and off since it came out. I’ve used and NHibernate. C# is a great language. I keep checking to see if there is any thing like Spring Boot in .NET. I can’t find it. : (

    PonderingProgrammer, ASP.NET core is like SPring MVC and EF is like-ish Hibernate.
    Spring is much more than just MVC and Hibernate. Spring Boot is more than Spring and Spring is more anything I’ve seen on any platform.

Leave a Reply

Your email address will not be published. Required fields are marked *