Thread Safety & Scoped Lifetime in Dependency Injection Containers

Thread Saftey & Scoped Lifetime in Dependency Injection Containers

If you’re using Scoped Lifetime in a Dependency Injection containers, beware! You may be run into race conditions due to a lack of thread safety.

I was doing a live stream on Domain Events over on my YouTube Channel where I was taking advantage of Scoped Lifetime. After the stream, I realized Scoped Lifetime and thread safety isn’t mentioned much and I’m not sure why? It could be because developers aren’t using multiple threads within the top-level request (HTTP request or service bus message invocation). Or it’s because developers are writing thread-safe code by default. I tend to think it’s the former.

Either way, I figured it would be worthwhile providing an example of using Scoped Lifetime and how you can run into issues if you aren’t writing thread-safe code.


Check out my YouTube channel where I created a video that accompanies this blog post.


I’m going to first create ClassC which will have a method NotThreadSafe where it will take a string as a parameter, set that to a private member, then return the length of that private member.

The NotThreadSafe() method on ClassC is not thread-safe because setting the private member and returning its length are two different operations. If it’s called concurrently by two different threads, it could return the length of the last thread that set the _state member. Ultimately this is a race condition.


To demonstrate this I’m going to create a couple more classes that illustrate this race condition in a way this is more similar to what you’d actually have in a project with dependencies.

ClassB will take a dependency on ClassC, and I’ll create a top-level ClassA which will take a dependency on both ClassB and ClassC.

Scoped Lifetime in Dependency Injection Containers

The thing to notice here is that ClassA uses a Task.WhenAll. I’m not immediately awaiting the result of the Task. Instead, I’m going to fire off both calls asynchronously and then wait for all of them to finish. What this means is that multiple threads could be used. This is ultimately what can cause the race condition.

Running this code as-is is so simplistic that you won’t likely hit the race condition. In order to make it more likely to hit, I’m going to add a delay.

Now to prove the race condition, here’s a little xUnit test.

This test fails because ClassB sets the _state member second, but finishes first. ClassA finishes last and returns _state.length which is now blank because ClassB has already finished.

Scoped Lifetime in Dependency Injection Containers

If you’re using Scoped Lifetime in a DI container, do you run into thread safety issues? Do you think about thread-safety or are you not concerned as you do not typically use multiple threads within a top-level request such as an ASP.NET Core HTTP request. Let me know in the comments or on Twitter.

Related Links

Enjoy this post? Subscribe!

Subscribe to our weekly Newsletter and stay tuned.

NuGet Package Alternatives when Migrating to .NET Core

NuGet Package Alternatives

Migrating from .NET Framework to .NET Core requires all of your NuGet Package dependencies to also be compliant with .NET Core. But what happens when you find a dependency that only targets .NET Framework? Here are a few NuGet package alternatives that I’ve run into during my own migration.

Migrating from .NET Framework to .NET Core

This post is in a blog series for migrating from .NET Framework to .NET Core. Here’ are some earlier post if you need to catch up:

Entity Framework

Probably the most common dependency most projects have is on Entity Framework. Luckily there are a few options in terms of support for .NET Core.

First, Entity Framework version 6.3 and higher target .NET Standard 2.1. Which means that you must migrate to .NET Core 3.0 or higher.

This is a bit of a problem if you want to migrate to .NET Core 2.1 as I’ve mentioned in my previous posts. This is more applicable if you’re migrating from ASP.NET on .NET Framework.

There is a second option which is to use Z.EntityFramework.Classic

Entity Framework Classic is an EF6 fork with performance enhancement, new features, and .NET Core support.

The community (free) version supports everything that’s in EF6 but targets .NET Standard 2.0. Which means you can run on .NET Core 2.1.

Machine Key

If you’re using System.Web.MachineKey and cannot for whatever reason move to the new ASP.NET Core Data Protection, you might want to look at AspNetCore.DataProtection.MachineKey

This package adds a new DataProtector that is configured using your existing Decryption & Validation Keys from your web.config. This allows you to Decrypt data that you might have already encrypted using MachineKey on .NET Framework. This package also supports .NET Standard 2.0


If you do any image processing on .NET Framework, you might be using the ImageProcessor library. Although not a direct replacement, the same author, James Jackson-South has created a new library ImageSharp that does work on .NET Core

These libraries are completely different. ImageSharp is not a drop in replacement for ImageProcessor but it’s by far the best image library that can run on .NET Core.


Another issue we ran into was PDF generation. For this I turned to IronPDF. This is a commercial product that requires a license. It does support .NET Standard 2.0 and works on Windows, MacOS, and Linux.

If you’re using PdfSharp, there is a  replacement package, PdfSharpCore.PdfSharp that targets .NET Standard.

NuGet Package Alternatives

Have you run into packages that don’t support .NET Core or .NET Standard?

If you have and/or have found any NuGet package alternatives, let me know in the comments or on Twitter. I’d like to keep this an evolving post with other recommendations.

Enjoy this post? Subscribe!

Subscribe to our weekly Newsletter and stay tuned.

Multi-Targeted NuGet Package Gotchas!

Multi-Targeted NuGet Package Gotchas!

In order to migrate your application from .NET Framework to .NET Core, one part of the migration is making sure all of your dependencies via NuGet packages will work on .NET Core. Most packages nowadays are multi-targeted. Meaning they target various versions of .NET Framework, .NET Core, and .NET Standard. Here are a few of multi-targeted NuGet package gotchas that I’ve discovered in my own migration.

Migrating from .NET Framework to .NET Core

This post is in a blog series for migrating from .NET Framework to .NET Core. Here’ are some earlier post if you need to catch up:


Check out my YouTube channel where I also cover this topic


Before we get into the gotchas, I want to just clarify that in your migration from .NET Framework to .NET Core, you need to look at all of your NuGet Packages that you depend on, and confirm they are targeting .NET Core or .NET Standard.

The simplest way to do this is to go to and do a search for your package. Here’s an example of Newtonsoft.Json

Under the dependencies section, it lists each platform it targets as well as the dependencies it needs when running under that platform. The thing to be on the look out for is .NET Standard 2.0 (or lower) or .NET Core. It should list .NET Framework as you’re already using it under that if you’re attempting to migrate.

This is what multi-targeting. This package is built for various targets and the NuGet package you download contains the assemblies for each of these targets. When you build your project, the appropriate assembly is used depending on which platform you’re running under.

Gotcha #1: Behavior

It would be fair to assume that the package you use would behave the same under .NET Framework or .NET Core. However, that’s not always the case. Library authors will use #if directives to have certain code only exists when built when targeting a certain platform. Because of this, there may be differences in behavior when you run the exact same code on .NET Framework or .NET Core.

An example of this that I ran into was with NodaTime. When serializing a ZonedDateTime object to JSON via JSON.NET, the output was different.

.NET Framework Output

.NET Core Output

Thankfully this issue was resolved in the 3.0 release of NodaTime. It now consistently uses the same serialization regardless of target. However, this still proves that there may be differences in behavior that you may not expect.

Gotcha #2: API Surface

Just because you’re using the same version of a NuGet package against .NET Framework and .NET Core does not mean that each target has the same API surface. Just like gotcha #1, this is also because of #if directives. Meaning, library authors may leave out APIs for certain targets.

An example of this is with the AWS SDK. Specifically the AWS S3 Package.

If you’re using this package with .NET Framework, there are non-Async APIs such as the GetObject method on the AmazonS3Client. However, once you’re targeting .NET Core it uses the .NET Standard target which does not contain any sync methods. Instead, they are all marked as internal. This means that you will not be able to even build your project when you target .NET Core.

.NET Framework
.NET Standard

Gotcha #3: Serializing BCL Types

This last gotcha isn’t so much with NuGet packages, but more so with JSON.NET and serializing standard types that are in the Bass Class Library.

This is very specific when you’re using JSON.NET and serializing/deserializing with TypeNameHandling.All. This setting adds type information to the JSON output from SerializeObject so that you can use it later when deserializing.

The problem is that BCL types live in different assemblies. In .NET Framework this is referred to as mscorlib. In .NET Core it’s System.Private.CoreLib.

When you serialize a List<string> here is the output in .NET Framework:

Output from .NET Core:

If you try and deserialize the .NET Core output when running on .NET Framework it will throw a JsonSerializationException.

This is something to be aware of if during your migration you’re communicating between services/applications that are mixed between .NET Framework and .NET Core.

Multi-Targeted NuGet Package Gotchas

If you’ve run int other gotchas during a migration, let me know in the comments or on Twitter.

Enjoy this post? Subscribe!

Subscribe to our weekly Newsletter and stay tuned.