efcore: Cannot create a unique index / alternate key - and wrong error message is given

I am trying to avoid duplicate entries in a link table.

When I use either HasIndex or HasAlternateKey using a navigation property, not only does EF migrations disallow it, but it gives me a wrong error message (which has led me on a wild goose chase for most of the day).

Source code

Domain classes

public class Parent
{
	public Guid Id { get; private set; } = Guid.NewGuid();
	public virtual IEnumerable<Child> Children { get; private set; } = new List<Child>();
}

public class Child
{
	public Guid Id { get; private set; } = Guid.NewGuid();
	public virtual Parent Parent { get; set; } = null!;
	public string Name { get; set; } = "";
}

ApplicationDbContext

internal class ApplicationDbContext : DbContext
{
	public DbSet<Child> Children { get; set; } = null!;
	public DbSet<Parent> Parents { get; set; } = null!;


	protected override void OnConfiguring(DbContextOptionsBuilder options)
	{
		base.OnConfiguring(options);
		options.UseSqlServer(
			 connectionString: @"Server=.\SQLExpress;Database=StevenTCramerSmells;Trusted_Connection=Yes;",
			 x => x.EnableRetryOnFailure());
	}

	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
		base.OnModelCreating(modelBuilder);
		modelBuilder.Entity<Parent>(x =>
		{
			x.HasMany(x => x.Children).WithOne(x => x.Parent);
		});

		modelBuilder.Entity<Child>(x =>
		{
// See notes for why these lines are commented out.
//			x.Property(x => x.Parent)
//				.HasColumnName(nameof(Child.Parent))
//				.IsRequired()
//				.HasColumnType("uniqueidentifier");
			x.HasIndex(x => new { x.Parent, x.Name }).IsUnique();
		});
	}
}

csproj file

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

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.8">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="6.0.8" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.8" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.8">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

</Project>

The error message I see is

‘Parent’ cannot be used as a property on entity type ‘Child’ because it is configured as a navigation.

This has led me to believe my problem is the C# property Parent on the Child class - but it is nothing to do with that. It is the Parent shadow property that is being added by the index.

So my ticket has two parts

1: Could the message be changed to something like “Failed to add shadow property ‘Parent’ on entity type ‘Child’ when creating an index, because a shadow property named Parent has already been defined as a navigation property”

2: How do I ensure uniqueness in my link table by combining the Parent and OtherTable columns into a unique index?

Notes

Please note that I considered whether or not this issue is a duplicate of #11336 or not, but decided it wasn’t because of the following reasons.

A: This ticket asks for the error message to be changed to make it clear the problem is not with the Child.Parent property the application source code, but a shadow-property issue, and that the message should say it is the index/alternate key causing the problem (showing the index name would be fantastic)

B: I tried the solution proposed in that ticket and it did not solve the issue in this case. See the commented out lines in ApplicationDbContext above.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 15 (2 by maintainers)

Most upvoted comments

@GioviQ I’ve standardised on XId instead and it works, thank you!

I think the error message needs to be more helpful, though.