dotnet / dotnet/EntityFramework.Docs

Document that identity resolution does not apply to instances explicitly created with new

Open
#4,639 1 comment 0 reactions 0 assignees View on GitHub
area-query-related-data
Dominant language
Mermaid
Stars
1.7k
Forks
2k
Avg merge
7d 23h
Merged PRs (30d)
16

Description

If I use AsNoTracking with IdentityResolution in combination with a projection in where a reference navigation property is also projected, then IdentityResolution doesn't work: Multiple principal instances will be created for entities having the same primary key.
If the referenced navigation property is NOT projected it works as expected.

### Example
In my example (full working UnitTest see below) I've got an 'OrderPosition' containing a reference navigation 'Article'.
There're two order positions in database that each have a reference to the same (only) Article that exists.

This leads EFCore to create only one Article instance that is then referenced by both the positions (as expected):
```C#
var positions = ctx.OrderPosition.Select(
op => new OrderPosition() {
OrderPositionId = op.OrderPositionId,
Article = op.Article
})
.AsNoTrackingWithIdentityResolution()
.ToList();
```

But this leads EFCore to create TWO Article instances (what I think is wrong):
```C#
var positions = ctx.OrderPosition.Select(
op => new OrderPosition() {
OrderPositionId = op.OrderPositionId,
Article = new Article() {
ArticleId = op.ArticleId,
Description = op.Description
}
})
.AsNoTrackingWithIdentityResolution()
.ToList();
```

### The complete example (UnitTest)

```C#
using System.Linq;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Xunit;

namespace EFCore.Issue {
class MyContext(DbContextOptions options) : DbContext(options) {
public DbSet OrderPosition { get; set; }
public DbSet Article { get; set; }
}

class OrderPosition {
public int OrderPositionId { get; set; }
public string? Description { get; set; }
public int ArticleId { get; set; }
public Article? Article { get; set; }
}

class Article {
public int ArticleId { get; set; }
public string? Description { get; set; }
}

public class TestIdentityResolution {
private static DbContextOptions CreateOptions() where T : DbContext {
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = ":memory:" };
var connectionString = connectionStringBuilder.ToString();
var connection = new SqliteConnection(connectionString);
if (connection.State != System.Data.ConnectionState.Open) {
connection.Open();
}
var builder = new DbContextOptionsBuilder();
builder.UseSqlite(connection);
return builder.Options;
}

static MyContext CreateContext() {
var options = CreateOptions();
FillData(options);
return new MyContext(options);

static void FillData(DbContextOptions options) {
var dbContext = new MyContext(options);
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();
dbContext.Article.Add(new Article { ArticleId = 99, Description = "Some article" });
dbContext.OrderPosition.Add(new OrderPosition() {
OrderPositionId = 1,
Description = "Order pos 1",
ArticleId = 99
});
dbContext.OrderPosition.Add(new OrderPosition() {
OrderPositionId = 2,
Description = "Order pos 2",
ArticleId = 99
});
dbContext.SaveChanges();
}
}

[Fact]
public void Projection() {
var ctx = CreateContext();
var positions = ctx.OrderPosition.Select(
op => new OrderPosition() {
OrderPositionId = op.OrderPositionId,
Article = op.Article
})
.AsNoTrackingWithIdentityResolution()
.ToList();

var pos1Art = positions[0].Article;
var pos2Art = positions[1].Article;
Assert.Equal(pos1Art, pos2Art);
}

[Fact]
public void ProjectionAlsoReferenceProjected() {
var ctx = CreateContext();
var positions = ctx.OrderPosition.Select(
op => new OrderPosition() {
OrderPositionId = op.OrderPositionId,
Article = new Article() {
ArticleId = op.ArticleId,
Description = op.Description
}
})
.AsNoTrackingWithIdentityResolution()
.ToList();

var pos1Art = positions[0].Article;
var pos2Art = positions[1].Article;
Assert.Equal(pos1Art, pos2Art);
}
}
}

```

ProjectFile
```csproj


net8.0
enable
Nullable
false


net8.0-windows







all
runtime; build; native; contentfiles; analyzers; buildtransitive

```

### Used provider and versions

EF Core version: 8.0.1
Database provider: Microsoft.EntityFrameworkCore.Sqlite | Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 8.0
Operating system: Win 10.0.19044.3086]
IDE: Visual Studio 2022 17.8.4

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.