Sunday 8 September 2024

Using lazy loading in Entity Framework Core 8

This article will show some code how you can opt in something called lazy loading in EF. This means you do not load in all the related data for an entity until you need the data. Lets look at a simple entity called Customer. We will add to navigational properties, that is related entities. Without eager loading enabled automatically or lazy loading enabled automatically, EF Core 8 will not populated these navigational properties, which is pointing to the related entities. The fields will be null without active measure on the loading part. Let's inspect how to lazy load such navigational properties.

Customer.cs




 public class Customer {
 
  // more code.. 
 
  public Customer()
  {
      AddressCustomers = new HashSet<AddressCustomer>();
  }
  
  // more code .. 
 
  private Customer(ILazyLoader lazyLoader)
  {
    LazyLoader = lazyLoader;
  }

  public CustomerRank CustomerRank { get; set; }

  public virtual ICollection<AddressCustomer> AddressCustomers { get; set; }
  
 }
  
  
 
 
 
First off, the ILazyLoader service is from Microsoft.EntityFrameworkCore.Infrastructure. It is injected inside the entity, preferably using a private constructor of the entity. Now you can set up lazy loading a for a navigational property like this :



 public CustomerRank CustomerRank
 {
     get => LazyLoader.Load(this, ref _customerRank);
     set => _customerRank = value;
 }
  
  
 
 
 
If it feels a bit unclean to mix entity code with behavioral code since we inject a service into our domain models or entities, you can use the Fluent api instead while setting up the DbContext.



  modelBuilder.Entity<Customer>()
      .Navigation(e => e.AddressCustomers)
     .AutoInclude();

  modelBuilder.Entity<Customer>(entity =>
  {
      entity.HasKey(e => e.Id);
     entity.Navigation(e => e.CustomerRank).AutoInclude();
  });


 
 
 
If automatically lazy loading the data (the data will be loaded upon access of the navigational property) seems a bit little flexible, one can also set up loading manually wherever in the application code using the methods Entry and either Reference or Collection and then the Load method.



var customer = _dbContext.Customers.First();

_dbContext
    .Entry(customer)
    .Reference(c => c.CustomerRank)
    .Load();


_dbContext
    .Entry(customer)
    .Collection(c => c.AddressCustomers)
    .Load();


Once more, note that the data is still lazy loaded, their content will only be loaded when you access the particular navigational property pointing to the related data. Also note that if you debug in say VS 2022, data might look like they are automatically loaded, but this is because the debugger loads the contents if it can and will even do so for lazy loaded navigational fields. If you instead make in your application code a programmatic access to this navigational property and output the data you will see the data also being loaded, but this happens once it is programatic access. For example if we made the private field _customerRank public (as we should not do to protect our domain model's data) you can see this while debugging :


//changed a field in Customer.cs to become public for external access :
//  public CustomerRank _customerRank;

Console.WriteLine(customer._customerRank);
Console.WriteLine(customer.CustomerRank);

// considering this set up 

  public CustomerRank CustomerRank
  {
      get => LazyLoader.Load(this, ref _customerRank);
      set => _customerRank = value;
  }



The field _customerRank is initially null, it is when we access the property CustomerRank which I set to be AutoInclude i.e. lazy loaded I see that data is loaded.

Saturday 7 September 2024

Using implicit index access in object initializers using the hat operator in C# 13

This article presents a small sample how to use implicit index access in object initializers in C# 13, using the 'hat' operator. This is a very specialized functionality, but it can become handing when you intialize objects and their collection fields and properties. For example, creating an object with an array and just doing some few adjustments on the collection becomes a bit more accessible. Let's see some code what this new feature in C# 13 is :


var nums = new[] { 6, 9, 4, 20 };

var lastElement = nums[^1]; //last element via index 

Console.WriteLine(lastElement);

var greeter = new Greeter { 
    Message = { 
        [^1] = '!'
    },
     SomeNums = 
     {
         [^1] = 5,
         [^2] = 4,
         [^3] = 3,
         [^4] = 2,
         [^5] = 1
     }
};

Console.WriteLine(greeter);

Console.ReadKey();

class Greeter
{
    public char[] Message { get; set; } = "Hello?".ToCharArray();

    public int[] SomeNums = new int[5];

    public override string ToString()
    {
        return $"Message = {string.Join(',', Message)}, SomeNums = {string.Join(',', SomeNums)}";
    }
}


This outputs the following :


20
Message = H,e,l,l,o,!, SomeNums = 1,2,3,4,5


We initialize an instance of the class Greeter into the variable greeter and set the fields / properties in the initializer. We already inited the property Message to "Hello?" and at the same type, in the object initalizer, alter the last letter where we use the hat operator using : [^1] = '!' , which results into the string "Hello!". Here we initalized an object in an easy way and at the same time was able to alter the contents of collections of the object, altering in the example the last char of a char array. We also set the int array one element at a time using again the hat operator here. We could of course not use the hat operator here, which will access the nth last element in a collection. Make note that the hat operator is 1-based, so the last element is ^1, the second last element is [^2]. Note that to use C#, you have to download the .NET 9 SDK preview from this url : https://dotnet.microsoft.com/en-us/download/dotnet/9.0 Make note that you select the .NET 9 SDK for the platform you are using. Most .NET developers on Windows can download x64 SDK for example. After installing the SDK , go to Tools => Options in VS 2022 and choose Environment : Preview features. Check the option : 'Use previews of .NET SDK'. Restart VS 2022 after doing this. You can now test out C# 13 and .NET 9 and for example create a console application for .NET projects (not .NET Framework). The .csproj in my sample code looks like this, make note of the TargetFramework, set it to net9.0. Also set the LangVersion to Preview.



<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
	 <LangVersion>Preview</LangVersion>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>