Monday, 31 July 2023

Loading references (navigational properties) for an item in Azure Cosmos DB

The extensions methods shown here can be applied in general in Entity Framework Core. In Azure Cosmos DB, if you use the FindAsync method for example, you will not load the references of the item automatically. Instead, you must explicitly go via Entry method and then to LoadAsync on each Reference. A Reference is also called a navigational property in EF. Let's first consider this code finding some Trip data item (data is stored in json format in Azure Cosmos DB since it is schema less and non-relational db or a 'document DB') and related data inside the Driver, Address. The POCO for Trip looks like this:


#region Info and license

#endregion

namespace TransportApp.Domain
{
  public class Trip
  {
    public string TripId { get; set; }
    public DateTime BeginUtc { get; set; }
    public DateTime? EndUtc { get; set; }
    public short PassengerCount { get; set; }

    public string DriverId { get; set; }
    public Driver Driver { get; set; }
    public string VehicleId { get; set; }
    public Vehicle Vehicle { get; set; }
    public string FromAddressId { get; set; }
    public Address FromAddress { get; set; }
    public string ToAddressId { get; set; }
    public Address ToAddress { get; set; }
  }
}




We create a db context for Azure Cosmos DB like this :


 private readonly IDbContextFactory _contextFactory;

 public SomeService(IDbContextFactory contextFactory)
 {
  _contextFactory = contextFactory; 
  
  //..
  
 }
 
 public async Task RunSomeDemoCode(){
    using var context = await contextFactory.CreateDbContextAsync();
 
 }
 	


Once we have our context object we can get data from Azure Cosmos DB.

 
 public async Task RunSomeDemoCode(){
    using var context = await contextFactory.CreateDbContextAsync();
    
    var trip1 = await otherContext.Trips.FindAsync($"{nameof(Trip)}-1");
    await otherContext.LoadEntityWithAllReferences(trip1!);
 
 }
 	


Note that we probably want to check that trip1 object is not null here. We want to get the relations also here. This is not automatically loaded in Azure Cosmos DB ! We can load the relations or navigational properties using the following extensions methods listed below. If you use the method accepting an EntityEntry, you must use the Entry method first, like shown in the other method.


 public static class EntityEntryExtensions
    {
        public static async Task LoadAllReferences<T>(this EntityEntry<T> entry) where T : class
        {
            foreach (var reference in entry.References)
            {
                await reference.LoadAsync();
            }
        }
    }

    public static class EntityExtensions
    {
        public static async Task LoadEntityWithAllReferences<T>(this DbContext dbContext, T dataItem) where T : class
        {
            if (dataItem == null)
            {
                return;
            }
            var entity = dbContext.Entry(dataItem!);
            foreach (var reference in entity.References)
            {
                await reference.LoadAsync();
            }
        }
    }



I have added two screenshots here, first before calling the method LoadEntityWithAllReferences, and afterwards.
Before: 

After:



As we can see from the screen shots, the references has been loaded and now you avoid to manually have to load one and one reference property / navigation property. Note - about some of the sample code - it came from a Pluralsight Course. I have looked more into the extension methods here myself.


/*
  This demo application accompanies Pluralsight course 'Using EF Core 6 with Azure Cosmos DB', 
  by Jurgen Kevelaers. See https://pluralsight.pxf.io/efcore6-cosmos.

  MIT License

  Copyright (c) 2022 Jurgen Kevelaers

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/


No comments:

Post a Comment