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>



Saturday, 10 August 2024

Calculating DateTime in .NET from Unix timestamp

This article will describe how to calculate datetime in .NET . A Unix timestamp is defined as : - The number of SECONDS that has passed SINCE Unix Epoch, a defined date set to January 1st, 1970 at UTC (Universal Time Coordinated). We can calculate a Unix timestamp for now time if we call the following method:

	DateTimeOffset.UtcNow.ToUnixTimeSeconds()

For example, as of 10th August 2024 at 21:41 this method gives: 1723318757 To calculate the DateTime value from a Unix Timestamp we can define the following extension method, since we will have an interesting issue in 2038 on 19th January 2038 at 03:14:08 UTC. At that time, an overflow will occur for systems using 32-bits integers, which is actually quite a lot of systems included 32-bits time stamps in database systems. A solution of this is to switch to 64-bits long, but this is as stated not an issue until 2038, which is still about 14 years into the future as of 2024.. We will create extension methods for both seconds and milliseconds because sometimes Unix timestamps are given in milliseconds, and we should also support both int and long. In the future, that is 2038, int cannot be used, long must be used. In addition, extension method on string is also added for convenience.


public static class DateTimeExtensions {

	public static DateTime? FromUnixTimeSeconds(this string unixTimestamp) =>
		long.TryParse(unixTimestamp, out long unixTimestampCasted) ? 
			(DateTime?) CalculateDateTimeFromUnixTimeStamp(unixTimestampCasted) : null;

	public static DateTime FromUnixTimeSeconds(this int unixTimestamp) =>
		CalculateDateTimeFromUnixTimeStamp(unixTimestamp);

	public static DateTime FromUnixTimeSeconds(this long unixTimestamp) =>
		CalculateDateTimeFromUnixTimeStamp(unixTimestamp);
		
	private static DateTime CalculateDateTimeFromUnixTimeStamp(long unixTimeStamp) =>
		new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(unixTimeStamp);

}


Here is how the method could be used in some examples:


    DateTime? dt = "1723318757".FromUnixTimeSeconds();
    DateTime? dt2 = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString().FromUnixTimeSeconds();
	
	Console.WriteLine(dt.Value);
	Console.WriteLine(dt.Value.Kind);
	Console.WriteLine(dt2);
    

The screenshot below shows how it can be calculated.








Let's next look at an extension method that converts a DateTime to Unix time stamp.


    public static long ToUnixTimeSeconds(this DateTime dt) =>
		CalculateUnixTimeSeconds(dt);

	public static long? ToUnixTimeSeconds(this DateTime? dt) =>
		dt.HasValue ? CalculateUnixTimeSeconds(dt.Value) : null;

	private static long CalculateUnixTimeSeconds(this DateTime dt) =>
		(long) dt.ToUniversalTime().Subtract(DateTime.UnixEpoch).TotalSeconds;



Usage example:

DateTime? dt4 = DateTime.UtcNow;
Console.WriteLine(dt4.ToUnixTimeSeconds());    

This gives the unix time stamp from current time. Please note that it also possible to cast a DateTime into a DateTimeOffset and then just use built in method ToUnitTimeSeconds


long unixTimestamp5 = ((DateTimeOffset)(dt4)).ToUnixTimeSeconds();
Console.WriteLine(unixTimestamp5);
    

In addition to the methods above, adding support for milliseconds should be fairly straightforward. Officially, Unix time stamps are measured in seconds. And when you convert from a Unix timestamp to a datetime, you will get UTC time. To get the local time you can do this:

DateTime? dt = "1723325214".FromUnixTimeSeconds();
Console.WriteLine(dt.Value.ToLocalTime()); 
    

Use the method ToLocalTime() on the datetime of Kind Utc, i.e. convert from UTC datetime to local datetime, taking your time zone into account. The entire extension method class then looks like this:

DateTimeExtensions.cs



public static class DateTimeExtensions {

	public static long ToUnixTimeSeconds(this DateTime dt) => 
		CalculateUnixTimeSeconds(dt);

	public static long? ToUnixTimeSeconds(this DateTime? dt) =>
		dt.HasValue ? CalculateUnixTimeSeconds(dt.Value) : null;

	private static long CalculateUnixTimeSeconds(this DateTime dt) =>
		(long) dt.ToUniversalTime().Subtract(DateTime.UnixEpoch).TotalSeconds;

	public static DateTime? FromUnixTimeSeconds(this string unixTimestamp) =>
		long.TryParse(unixTimestamp, out long unixTimestampCasted) ? 
			(DateTime?) CalculateDateTimeFromUnixTimeStamp(unixTimestampCasted) : null;

	public static DateTime FromUnixTimeSeconds(this int unixTimestamp) =>
		CalculateDateTimeFromUnixTimeStamp(unixTimestamp);

	public static DateTime FromUnixTimeSeconds(this long unixTimestamp) =>
		CalculateDateTimeFromUnixTimeStamp(unixTimestamp);
		
	private static DateTime CalculateDateTimeFromUnixTimeStamp(long unixTimeStamp) =>
		new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(unixTimeStamp);

}