Current Limitations of Entity Framework Core

Introduction

Although EF Core seems to be the most popular ORM in the .NET world in these days – and I for sure won’t contradict it –, there are still some functionality missing, specially if we compare it with other ORMs that also exist, NHibernate coming obviously to my mind. This post is a reflection about that, it is in no way a comparison with other ORMs, in particular, NHibernate, it’s more a wish list for EF Core. I still follow and love NHibernate, and it’s still, in many ways, way ahead of EF Core, but that’s not what I’ll be writing about.

Primary Key Generation

EF Core currently only supports the following primary key generation strategies:

  • Identity/autoincrement (on databases that support it)
  • GUID/uniqueidentifier (on databases that support it)
  • Sequence-based (on databases that support it)
  • HiLo (SQL Server only)
  • Manually assigned

For GUIDs, EF Core automatically generates, on the client-side, a new value before inserting, which is nice.

HiLo is a great pattern for generating primary keys, but, unfortunately, the way Microsoft implemented it, resorting to database sequences, makes it database-specific – currently, SQL Server only. If it were implemented with just standard DML commands, it could be made database-agnostic, for the benefit of all.

I dare say that EF Core is slightly biased towards SQL Server and, even more, that most SQL Server folks are used to having IDENTITY as their default primary key generation strategy. While this is fine, it does prevent batch insertions, because it requires obtaining the generated primary key after each inser (SELECT SCOPE_IDENTITY()). If we have a client-generated primary key, batch insertion is possible, which can be a great benefit.

I’d like to have a truly extensible primary key generation mechanism on EF Core. Maybe something for the future.

Collection Types

Currently, EF Core only supports one type of collection. It can be lazy loaded, in which case, you need to declare it as ICollection<T> or IList<T>, or not, but that is it. You can’t have collections of primitive types without using a value converter. Plus, you don’t have semantic collections such as sets or bags, or indexed collections such as lists or maps.

Table Inheritance Patterns

There are three canonical table inheritance patterns:

Currently, TPH and TPT are already supported, but TPC is not. There are plans to introduce it in the future (https://github.com/dotnet/efcore/issues/3170)

Expression Columns

Currently there’s no way to map a column to a SQL expression, which could be very handly. True, we can have a whole entity coming as the result of a view, or a raw SQL, but this is a different thing. Of course, that property would not be persisted.

Expressions for CUD Operations

Another useful operation, the ability to use raw SQL – which could even be a stored procedure call – for Create, Update, and Delete (CUD) operations. This is also currently scheduled by the team for a future release (https://github.com/dotnet/efcore/issues/245).

Strongly Typed DML Operations

One thing that would be nice and already exists on other ORMs is the ability to batch perform DML operations from LINQ queries, for example, UPDATEs and DELETEs. Take this hypothetical strongly typed delete on top of a LINQ query, for example:

var deletedPosts = ctx.Posts.Where(p => p.Timestamp.Date < DateTime.Today.AddDays(-365)).Delete();

Much better than writing SQL as hardcoded strings, don’t you think? Yes, I do know that there are third-party extensions out there that already do this, but it would be nice to have this built-in. It’s being tracked for future implementation (https://github.com/dotnet/efcore/issues/795).

Lifecycle Events

Some lifecycle events – such as EF 6’s ObjectMaterialized and SavingChanges – are conspicuously missing. This can be useful, for example, to do something on an entity after it is loaded (hydrated) from the database. Fortunately, there are plans to introduce them (https://github.com/dotnet/efcore/issues/15911).

Conclusion

As a I said, these are just some thoughts on nice-haves for EF Core. Some of them are already on the EF Core team’s list, and some are not. There are many more, see the whole plan for v7.0 here and do submit your own suggestions here. As usual, I’d love to hear your thoughts on this, so let’s have them!

Using Generated Methods Instead of Reflection

Introduction

It is a common thing to say that reflection is slow.You will find tons of posts saying this, and I generally tend to agree to them, although in most cases we generally don’t need to care that much – in fact, so many libraries and frameworks that we rely on daily depend on it!

When we talk about reflection, what is it that we talk about, normally? I’d say:

  • Getting an instance’s type, or base type, or implemented interfaces
  • Getting a list of properties, or fields
  • Setting values for properties, or fields
  • Finding methods
  • Invoking methods

Some things we can do to speed up the process include caching the PropertyInfo and FieldInfo objects, so that there is no need to retrieve them again and again from the declaring Type, but when it comes to getting and setting actual values, no caching can help here.

One thing that we can do, though, is generate code for actually accessing the properties/fields for us, in a strongly typed manner, without using reflection. This incurs in a startup cost, because of the overhead associated with the code generation, but after that it becomes much faster than the reflection-based code. Without more ado, let’s jump to the code.

First, some base class with just a single method for representing a simple setter operation:

public abstract class Setter {    

public abstract void Set(object obj, string propertyName, object value);

}

Simple, right? We take a target object (obj), a property (propertyName) and some value, and we’re good.

Reflection

The simplest, reflection-based implementation could look like this (ReflectionSetter):

public sealed class ReflectionSetter : Setter

{

     public override void Set(object obj, string propertyName, object value)

     {

         ArgumentNullException.ThrowIfNull(obj);

         ArgumentNullException.ThrowIfNull(propertyName);

         var property = obj.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.SetProperty);

         if (property != null)

         {

             property.SetValue(obj, value, null);

         }

         else

         {

             throw new InvalidOperationException(“Property not found.”);

         }

     } }

As you can see, nothing special here, the code does what you might expect: only public, instance properties with both a setter and a getter are looked for, and if not found, an exception is thrown.

Reflection With Caching

Another, slightly more elaborared version, which caches the PropertyInfo objects, but which requires that all types be initialized prior to being used (CachedReflectionSetter):

public sealed class CachedReflectionSetter : Setter

{

     private readonly Dictionary<Type, Dictionary<string, PropertyInfo>> _properties = new Dictionary<Type, Dictionary<string, PropertyInfo>>();

     public void Initialize(Type type)

     {

         ArgumentNullException.ThrowIfNull(type);

         this._properties[type] = new Dictionary<string, PropertyInfo>();

         foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.SetProperty))

         {

             this._properties[type][prop.Name] = prop;

         }

     }

     public override void Set(object obj, string propertyName, object value)

     {

         ArgumentNullException.ThrowIfNull(obj);

         ArgumentNullException.ThrowIfNull(propertyName);

         var property = this.GetPropertyFor(obj.GetType(), propertyName);

         if (property != null)

         {

             property.SetValue(obj, value, null);

         }

         else

         {

             throw new InvalidOperationException(“Property not found.”);

         }

     }

     private PropertyInfo? GetPropertyFor(Type type, string propertyName)

     {

         if (this._properties.TryGetValue(type, out var properties))

         {

             if (properties.TryGetValue(propertyName, out var prop))

             {

                 return prop;

             }

         }

         return null;

     } }

The only difference between the two is that this one does not retrieve the PropertyInfo objects on the fly from the passed instance, instead it does so from a dictionary. But one must not forget to call its Initialize method with all the Types that we want to use it with, otherwise, it will not know its properties.

Compiled Lambda

Now things get a little more complext, as we want to get away from reflection – at leat at the runtime level, when we are setting the value for the property, which is something that can potentially happen a lot of times during the lifefime of our application. So, enter the third iteration (CompiledSetter):

public sealed class CompiledSetter : Setter {

     private readonly Dictionary<Type, Dictionary<string, Delegate>> _properties = new Dictionary<Type, Dictionary<string, Delegate>>();

     public void Initialize(Type type)

     {

         ArgumentNullException.ThrowIfNull(type);

         this._properties[type] = new Dictionary<string, Delegate>();

         foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.SetProperty))

         {

            this.GenerateSetterFor(type, prop);

         }

     }

     public override void Set(object obj, string propertyName, object value)

     {

         ArgumentNullException.ThrowIfNull(obj);

         ArgumentNullException.ThrowIfNull(propertyName);

         var action = this.GetActionFor(obj.GetType(), propertyName);

         if (action is Action<object, object> act)

         {

                act(obj, value);

         }

         else

         {

             throw new InvalidOperationException(“Property not found.”);

         }

     }

     private void GenerateSetterFor(Type type, PropertyInfo property)

     {

         var propertyName = property.Name;

         var propertyType = property.PropertyType;

         var parmExpression = Expression.Parameter(typeof(object), “it”);

         var castExpression = Expression.Convert(parmExpression, type);

         var propertyExpression = Expression.Property(castExpression, propertyName);

         var valueExpression = Expression.Parameter(typeof(object), propertyName);

         var operationExpression = Expression.Assign(propertyExpression, Expression.Convert(valueExpression, propertyType));

         var lambdaExpression = Expression.Lambda(typeof(Action<,>).MakeGenericType(typeof(object), typeof(object)), operationExpression, parmExpression, valueExpression);

         var action = lambdaExpression.Compile();

         this._properties[type][propertyName] = action;

     }

     private Delegate? GetActionFor(Type type, string propertyName)

     {

         if (this._properties.TryGetValue(type, out var properties))

         {

             if (properties.TryGetValue(propertyName, out var action))

             {

                 return action;

             }

         }

         return null;

     } }

The approach here is to generate an expression for a lambda that accesses the property that we want to access and then compile it. The problem is that it would normally produce something like this:

Action<MyEntity> (it) => it.MyProperty = MyValue;

Which is not what we want, essentially, because with this we cannot call it dynamically – we do not know, at runtime, the MyEntity type or the type of MyProperty! What we want, and always have, is object, so, instead, we want to go with something along these lines:

Action<object, object> (it, prop) => ((MyEntity) it).MyProperty = (MyPropertyType)prop;

This way, we can always call the generated lambda with the target object and we

And it works like a charm! Winking smile

Performance Tests

I did a few tests with BenchmarkDotNet:

public class Entity

{

public int Id { get; set; }

public string Name { get; set; }

}

public class Test {     static CompiledSetter compiledSetter = new CompiledSetter();     static ReflectionSetter reflectionSetter = new ReflectionSetter();     static CachedReflectionSetter cachedReflectionSetter = new CachedReflectionSetter();     static Entity[]? entities = null;     [GlobalSetup]     public static void Setup()     {         compiledSetter.Initialize(typeof(Entity));         cachedReflectionSetter.Initialize(typeof(Entity));         entities = Enumerable.Range(0, 200).Select(x => new Entity()).ToArray();     }     private static void Common(Setter setter)     {         for (var i = 0; i < entities?.Length; i++)         {             setter.Set(entities[i], nameof(Entity.Id), i);             setter.Set(entities[i], nameof(Entity.Name), i.ToString());         }     }     [Benchmark]     public static void TestCompiled() => Common(compiledSetter);     [Benchmark]     public static void TestReflection() => Common(reflectionSetter);     [Benchmark]     public static void TestCachedReflection() => Common(cachedReflectionSetter); }

Essentially, the tests generated a few (200) entities with two properties, the setters that required initialization were initialized, and then the properties were set in a loop.

The results I got were:

results

As you can see, the compiled version (CompiledSetter) outperformed the other two by a great margin. There’s also a small benefit in caching the PropertyInfo, as CachedReflectionSetter version arrived second on the results.

Conclusion

It is clear that reflection is indeed slow and a few things can definitely be done about it. I showed here setting property values, but the same logic can be done for getting them, and I’m happy to provide the code to anyone interested. If you wish to discuss any aspects of this, or have any questions, just give me a shout. As always, hope you find this useful!

Changing Schema Dynamically in EF Core

Sometimes it may be necessary to change the schema for some entities based upon some criteria. This may be because of multitenancy or because you want to test something and don’t want to pollute the main schema. Here is a possible solution, going directly to the annotations that EF Core uses.

protected override void OnModelCreating(ModelBuilder modelBuilder) {

    base.OnModelCreating(modelBuilder);

//apply any IEntityTypeConfiguration<TEntity> configuration from this assembly

    modelBuilder.ApplyConfigurationsFromAssembly(typeof(DatabaseContext).Assembly);

    var model = modelBuilder.Model;

//possibly filter entities by some criteria?

    foreach (var entity in model.GetEntityTypes())

    {

        entity.RemoveAnnotation(“Relational:Schema”);

        entity.AddAnnotation(“Relational:Schema”, “FooBar”);

    } }

You need to override the OnModelCreating method, which is called by the infractructure when the model is being built by EF Core, and you let it do its job, and you then iterate through the found entities to add an annotation for “Relational:Schema”. And that’s it.

Posting AJAX Requests to ASP.NET Core MVC

Introduction

In the past, I’ve had trouble doing something that is apparently simple: invoking a simple action method in a controller using AJAX. Although it is indeed simple, when using jQuery, it may require some attention, hence this post.

In this example I will show how to do something simple: how to invoke an action that just adds two integers and returns the result using jQuery AJAX. This can be extended to any other kind of data, mind you, it is not tied to simple data types.

Prerequisites

We will be using .NET Core (or .NET 5/6, doesn’t really matter for the purpose of this sample) and jQuery 3.x, although it should work the same with 2.x. There is no special setup required other than adding controllers and views to dependency injection (or full MVC, if you prefer).

Controller Side

The controller part is simple, but we have two options:

  1. We can have multiple parameters
  2. Or we can have a single parameter of a data type that holds all of the data that we wish to send

Generally, option #2 is more extensible, for we can add more parameters easily, without changing the action method’s signature, but I will show here how it works with both options.

Multiple Parameters

Our action method will look like this:

[HttpPost]
public IActionResult Add(int a, int b)
{
    return Json(a + b);
}

And that’s it, pretty simple, the [HttpPost] attribute, as you probably know, is an action attribute that says that this action can only be called as a result of an HTTP POST.

Let’s see now what the single parameter version would look like.

Single Parameter

public record AddRequest(int A, int B);

[HttpPost]
public IActionResult Add([FromBody] AddRequest req)
{
    return Json(req.A + req.B);
}

Here we see a few more things:

  1. A record declaration for a type that contains two properties, A and B; I could have made it a regular class, but records are so much more fun! Winking smile You will notice that these two properties, although cased differently, are the same as the parameters on the previous example
  2. The parameter is decorated with a [FromBody] attribute. This one instructs ASP.NET Core MVC to provide the value for the parameter from the body of the request. Its usage could be skipped if we instead decorated the whole controller class with an [ApiController] attribute, but that is beyond the scope of this post

Client Side

On the client side, we also need to match the decision we made on the controller side, regarding how we defined the parameters.

Multiple Parameters

The JavaScript version goes like this:

function calculate() {     

const data = {  a: 1,         b: 2     };
$.ajax({      url: '/Home/Add',         type: 'POST',         data: data,         success: function(result, status, xhr) { /*hooray!*/ },         error: function(xhr, status, error) { /*houston, we have a problem!*/ }     }); 

}

Single Parameter

And the single one is just two small changes:

function calculate() {
     const data = {         a: 1,         b: 2     };
$.ajax({         url: '/Home/Add',         type: 'POST',         data: JSON.stringify(data),         contentType: 'application/json',         success: function(result, status, xhr) { /*hooray!*/ },         error: function(xhr, status, error) { /*houston, we have a problem!*/ }       }); }

As you can see, the difference is that here you need to explicitly serialize the data to JSON using the built-in JSON.stringify function and then also explicitly provide the content type application/json.

Pitfalls

There are a few possible pitfalls:

  1. You may get HTTP 400 Bad Request when you try to POST to your action method. This is likely because of Anti Cross Site Scripting Forgery (XSRF) protection, which is enabled by default in ASP.NET Core MVC. Please refer to my other post here, or, TL; DR, apply an [IgnoreAntiforgeryToken] to the action method
  2. When using the single parameter option, you may be getting a null value in the action method as the parameter value. This is likely the result of being unable to deserialize the contents that you are sending. Please ensure that you are allowing case-insensitive JSON serialization (the default) and that the parameters are well defined. See also here

Conclusion

AJAX has been around for quite a while and is a fun and safe option, but it has its tricks. Hope you find this post helpful!

ASP.NET Core Pitfalls – Null Models in Post Requests

Sometimes, when AJAX posting to a controller, you may get a null model. Why?

Let’s exclude the obvious – you didn’t send a null payload. The most typical reason is, the serializer could not deserialize the payload into the target type, and it just silently sets it to null, but no exception is thrown. Take this example:

public class Data

{

    public string X { get; set; }

    public int Y { get; set; }

}

If you try to send something totally unrelated, such as this:

<script>

     function process() {
         $.ajax({
             url: '/Home/Process',
             headers: {
                 'Content-Type': 'application/json',
             },
             data: { 'foo': 'bar' },
             type: 'POST',
             success: function (data) {

             },
             complete: function () {
             },
             error: function (error) {
             }
         });
     }

</script>

It should come as no surprise that the content you send is unrelated to the model you are expecting in your action method, therefore there is a mismatch, but you may be surprised to get a null value instead of an exception.

The code responsible for binding the request to the .NET class is the input formatter that can be specified in the AddMvc extension method:

services.AddMvc(options =>

{
    options.InputFormatters.Clear();
    options.InputFormatters.Add(new MyInputFormatter());
}):

In ASP.NET Core 5, the only included one is SystemTextJsonInputFormatter, which uses the System.Text.Json API for parsing the request as JSON and deserializing it into a .NET class. You can add as much input formatters as you want, the first one that can process the request is used and all the others are discarded, so the order matters.

ASP.NET Core Pitfalls – AJAX Requests and XSRF

When using Anti Cross Site Scripting Forgery (XSRF) protection in your application, which is on by default, you may be surprised when you try to AJAX submit to a controller and you get a HTTP 400 Bad Request: this may be happening because the framework is blocking your request due to XSRF.

Imagine this scenario: you have a global AutoValidateAntiforgeryTokenAttribute or ValidateAntiForgeryTokenAttribute filter applied to your site, controller, or action method:

services.AddMvc(options =>

{ options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()); });

In this case, the framework is expecting a header field with a name of “RequestVerificationToken” (this is the default, but can be configured) with a valid token value. If does not receive it, or receives an invalid value, then it returns HTTP 400.

You can sort this out by adding the header manually to the AJAX request. If using jQuery, you have at least two options, using ajaxSetup or ajax. Let’s see an example using ajax:

@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf

<script>

function sendRequest() { $.ajax({      url: ‘/Home/Process’,         headers: {          ‘@Xsrf.GetAndStoreTokens(Context).HeaderName’: ‘@Xsrf.GetAndStoreTokens(Context).RequestToken’         },         data: { },         type: ‘POST’,         success: function (data) {         },         complete: function () {         },         error: function (error) {         }     }); }

</script>

This code should go in a .cshtml file. As you can see, I am injecting an IAntiforgery instance, this is registered automatically by ASP.NET Core. The header that I am sending is whatever is configured, and can be changed in ConfigureServices:

services.AddAntiforgery(options =>

{ options.HeaderName = “x-header-name”; });

Another option, of course, is to add an IgnoreAntiforgeryTokenAttribute to the controller action method for which you do not want the validation to occur:

[HttpPost] [IgnoreAntiforgeryToken] public IActionResult Process() {

//do something return Json(true); }

Any way, this will allow the code to execute.

As always, hope you find this useful!

ASP.NET Core Pitfalls – Async File Uploads

When performing file upload(s) to an asynchronous action, it may happen that the uploaded file(s) may not be accessible, resulting in an ObjectDisposedException. This is because the uploaded files are saved to the filesystem temporarily by ASP.NET Core and then removed, after the request has been processed, which is generally what we want. If we issue an asynchronous task with an uploaded file and don’t wait for it to finish, it may happen that it occurs outside of the request’s scope, meaning, the uploaded file is already gone.

One way to address this is to have code like this:

[HttpPost]
public async Task<IActionResult> Upload(IFormFileCollection files)
{
    var f = files.ToArray();
    Array.ForEach(f, async (file) =>
    {
        using var stream = file.OpenReadStream();
        using var newStream = new MemoryStream();
        await stream.CopyToAsync(newStream);
        await ProcessFile(newStream);
    });
    return RedirectLocal("UploadSuccess");
}

For precaution, we make a copy of the original stream and use this copy instead in the upload processing code. I have successfully done this without the copy – which, of course, results in more memory usage, but occasionally I got errors as well. This way, I never had any problems.

Inline Images with ASP.NET Core

The most common way to show an image in an HTML page is to use the <img> tag to load an external resource. Another option is to use a URL that is a Base64 encoded version of the image. There are some aspects worth considering:

  1. Using this approach the HTML will become larger, because it will also contain the image as a Base64 encoded string
  2. There is no need to load external resources together with the HTML page, which may make the loading faster
  3. The images can be cached together with the page
  4. Of course, if the Base64 string is hardcoded in the page, to change the image, we must change the page

I am a big fan of ASP.NET Core’s tag helpers, and I already wrote about them. This time, I propose a tag helper for rendering a Base64 image from a local file!

Here is the code

[HtmlTargetElement("inline-img")]
public class InlineImage : TagHelper
{
private readonly IWebHostEnvironment _environment
    public InlineImage(IWebHostEnvironment environment)
    {
        this._environment = environment;
}

[HtmlAttributeName("id")]
public string Id { get; set; }

[HtmlAttributeName("src")]
    public string Src { get; set; }

    [HtmlAttributeName("style")]
    public string Style { get; set; } 

   [HtmlAttributeName("class")]
    public string Class { get; set; }

    [HtmlAttributeName("width")]
    public string Width { get; set; } 

   [HtmlAttributeName("height")] 
   public string Height { get; set; } 

    [HtmlAttributeName("title")]
    public string Title { get; set; }

    [HtmlAttributeName("alt")] 
   public string Alt { get; set; } 

    public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output) 
   {
     if (!string.IsNullOrWhiteSpace(this.Src)) 
        {
          string absoluteSrc;

            if (this.Src.StartsWith("~/"))
            {
                absoluteSrc = this.Src.Replace("~/", this._environment.WebRootPath);
            }
            else 
            {
                var src = this.Src.StartsWith("/") ? this.Src.Substring(1) : this.Src;
                absoluteSrc = Path.Combine(this._environment.WebRootPath, src);
            }

            var img = File.ReadAllBytes(absoluteSrc); 
            var ext = Path.GetExtension(absoluteSrc).Substring(1);
            var urlEncoded = $"data:image/{ext};base64, {Convert.ToBase64String(img)}";

            output.TagName = "img";      
      output.TagMode = TagMode.SelfClosing;     
        output.Attributes.Add("src", urlEncoded);

if (!string.IsNullOrWhiteSpace(this.Id)) 
            {            
     output.Attributes.Add("id", this.Id);
            }

            if (!string.IsNullOrWhiteSpace(this.Style)) 
            { 
                output.Attributes.Add("style", this.Style); 
            }   

          if (!string.IsNullOrWhiteSpace(this.Class)) 
            {  
               output.Attributes.Add("class", this.Class); 
            } 

            if (!string.IsNullOrWhiteSpace(this.Width)) 
            { 
                output.Attributes.Add("width", this.Width); 
            }

if (!string.IsNullOrWhiteSpace(this.Height)) 
            { 
                output.Attributes.Add("Height", this.Height); 
            } 

            if (!string.IsNullOrWhiteSpace(this.Title)) 
            { 
                output.Attributes.Add("title", this.Title); 
            } 

            if (!string.IsNullOrWhiteSpace(this.Alt)) 
            { 
                output.Attributes.Add("alt", this.Alt); 
            } 
        } 

        return base.ProcessAsync(context, output); 
    }
}

First, this code has absolutely no error checking, you can add it as an exercise!

This class inherits from the TagHelper base class. It is declared as being triggered by an <inline-img> tag, and it requires a property Source to be passed and then checks to see if its relative to the virtual path (starts with ~/), in which case, it replaces it by the actual path. Otherwise, it considers it relative to the root folder.

There are a few properties that sometimes come in handy when dealing with images: Id, Style, Class, Width, Height, Alt and Title, all optional. Feel free to add others.

To use this, you need to register this tag helper, possibly on the _ViewImports.cshtml file:

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, MyWebApplication

Just replace MyWebApplication by the full name of your assembly. After this, it’s just a matter of declaring an <inline-img> element on a Razor view:

<inline-img src="my.logo.png" />

And, presto, you will end up with something like this:

<img src="data:image/png;base64, iVBORw0KGgoAAAANSUhEU…………." />

Hope you enjoy this trick! Winking smile

Interfaces and Inversion of Control

The way I see it, there are three reasons for using an Inversion of Control (IoC) / Dependency Injection (DI) container:

  1. To decouple the actual implementation from a base class/interface (the contract), so that we can change the implementation when needed
  2. To allow the implementation itself to have services injected into it when it is built
  3. To control the lifetime of a service, for example, singleton, web request scoped, transient, etc

For #1, one common approach is to create an interface for every service that we are registering in the IoC/DI – this is actually why we call it Inversion of Control: we don’t control it, the container does. Nothing against it, but it may not be necessary: if for the service we are registering there will never, ever, be a different implementation (and we know that this may happen, like, for infrastructure code), there isn’t really a need for registering a service under an umbrella interface (or base class). Of course, this may bite us later, so we need to be really careful about it. The way I sometimes avoid interfaces is because I really don’t need them, and I end up saving some extra code and files.

The second reason is straightforward: we declare, on the implementation’s constructor, the services that we will be taking as dependencies, so that we can keep a reference to them for later usage.

Finally, reason #3, for me, its, for example, what makes Singleton an anti-pattern. Using an IoC container we can control the lifetime and have a service iinjected into our classes, transparently, without caring if there is one or more than one instances of it.

ASP.NET Core Pitfalls – Dependency Injection Lifetime Validation

As you can imagine, it doesn’t make much sense to have a service that is registered as singleton to depend on another service that is registered as scoped, because the singleton instantiation will only happen once. Take this example:

public interface IScopedService { }

public interface ISingletonService { }

public class SingletonService : ISingletonService

{

public SingletonService(IScopedService svc) { }

}

And this registration:

services.AddSingleton<ISingletonService, SingletonService>();

services.AddScoped<IScopedService, ScopedService>();

The actual implementation does not matter here. What is important is that you will only see this problem when you try to instantiate the ISingletonService, normally through constructor injection.

One way to prevent this problem is, of course, to take care with the services registration. Another one is to have ASP.NET Core validate the registrations for us, when building the service provider. This can be achieved on bootstrap, before the Startup class is instantiated, when the application starts:

public static IHostBuilder CreateHostBuilder(string[] args) =>

Host.CreateDefaultBuilder(args)

.UseDefaultServiceProvider((context, options) =>

{

options.ValidateScopes = context.HostingEnvironment.IsDevelopment();

options.ValidateOnBuild = true;

})

.ConfigureWebHostDefaults(webBuilder =>

{

webBuilder.UseStartup<Startup>();

});

Notice the ValidateScopes and ValidateOnBuild properties. The first will prevent a singleton service from taking a scoped service dependency, and the second actually does the validation upon startup. To be clear, one can be used without the other.

When ValidateScopes is enabled, you won’t be able to inject a singleton service that takes as its dependency a scoped one, the application will crash; with ValidateOnBuild you actually specify when it will crash: if set to true, it will crash at startup, otherwise, it will only crash when the injection occurs.

If you really need to instantiate a scoped service from a singleton, you need to use code such as this:

IServiceScopeFactory scopedFactory = … //obtained from dependency injection

using (var scope = scopedFactory.CreateScope())

{

var scopedService = scope.ServiceProvider.GetRequiredService<IScopedService>();

//if IScopedService implements IDisposable, it will be disposed at the end of the scope

}

Note that this will not validate open generic types, as stated in the documentation for ValidateOnBuild.

Finally, using the ApplicationServices collection of IApplicationBuilder, in the Configure method, you can only retrieve transient or singleton services, not scoped: the reason for this is that when Configure executes there is not yet a scope. This will fail:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

{

var scoped = app.ApplicationServices.GetRequiredService<IScopedService>();

//rest goes here

}

Interestingly, you can inject a scoped service as a parameter to Configure, e.g., this will not crash:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IScopedService svs)

{

//rest goes here

}