dotnet / dotnet/efcore

Feature request: reload entity with all dependencies

Open
#31,149 1 comment 0 reactions 0 assignees View on GitHub
area-change-tracking customer-reported
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

### What problem are you trying to solve?

My code:
```C#
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;

namespace EfCoreReloadEntity;
public class Program
{
public static async Task Main(params string[] args)
{
IHost host = Host.CreateDefaultBuilder()
.ConfigureServices(services =>
{
services.AddDbContext(builder =>
{
builder.UseNpgsql("Host=localhost;Port=7435;Database=testdb;Username=admin;Password=testpass")
.UseSnakeCaseNamingConvention();
});
})
.Build();

await LoadAndReloadEntity(host);
}

public static async Task LoadAndReloadEntity(IHost host)
{
using var scope = host.Services.CreateScope();
var provider = scope.ServiceProvider;
var context = provider.GetRequiredService();

Product product = context.Set().First(x => x.Id == 1);

await context.Database.ExecuteSqlRawAsync("update products set name = 'Product-2' where products.id = 1");
await context.Database.ExecuteSqlRawAsync("INSERT INTO items (id, product_id, size) VALUES (3, 1, 40);");

try
{
product.Name = "Product-3";
await context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
var entries = ex.Entries;
if (entries.Count != 1)
throw;

EntityEntry entry = entries[0];
if (entry.Entity != product)
throw;

await entry.ReloadAsync();
Product reloadedProduct = (Product)entry.Entity;
if (reloadedProduct != product)
throw;

//return 2 items but product contains 3 items after execution raw sql
Console.WriteLine(reloadedProduct.Items.Count);
}
}
}

public class Product
{
private readonly uint _version;

public required int Id { get; set; }

public required string Name { get; set; }
public List Items { get; set; }
}

public class ProductItem
{
public int Id { get; set; }
public int Size { get; set; }
public int ProductId { get; set; }
}

public class MyContext : DbContext
{
public DbSet Product { get; set; }
public DbSet Items { get; set; }

public MyContext(DbContextOptions options)
: base(options)
{
Database.EnsureDeleted();
Database.EnsureCreated();
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var product = new Product()
{
Id = 1,
Name = "Product-1",
Items = new List()
};

List items = new()
{
new ProductItem()
{
Id = 1,
Size = 30,
ProductId = 1
},
new ProductItem()
{
Id = 2,
Size = 35,
ProductId = 1
}
};

modelBuilder.Entity().HasData(product);

modelBuilder.Entity().HasData(items);

modelBuilder.Entity(builder =>
{
builder.ToTable("products");

builder
.Navigation(p => p.Items)
.AutoInclude();

builder
.Property(p => p.Name)
.HasColumnName("name");

builder
.Property("_version")
.IsRowVersion();
});
}
}
```

Product object is an aggregate. I'm using autoinclude but want to reload entire product with all dependencies. But EF Core doesn't let to reload it with dependencies. I know that I can remove object from context and load new Product. But it will be new object.
In this case I have to return new object to calling method.
Lazy loading also is not good for me because I'm using repository pattern and object can contains many dependencies.

### Describe the solution you'd like
I want to have a solution to reload entire aggregate with dependencies without creating new aggregate.
it might look like this:
```C#
await entry.ReloadAsync(loadDependencies: true);
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.