Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HasKey changed functionality in 2.1.1 Service Release #12414

Closed
danjohnso opened this issue Jun 19, 2018 · 1 comment · Fixed by #17481
Closed

HasKey changed functionality in 2.1.1 Service Release #12414

danjohnso opened this issue Jun 19, 2018 · 1 comment · Fixed by #17481
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported punted-for-3.0 type-bug
Milestone

Comments

@danjohnso
Copy link

danjohnso commented Jun 19, 2018

When overriding calls to HasKey for an entity, the newest servicing release changes behavior and does not override previous calls to HasKey. The first call to HasKey is used to generate a Unique Constraint on the field, which looks to be broken as HasKey is supposed to be for defining a primary key (only one).

Using this example context in a brand new project:

public class Thing : Entity
{
	public Guid ParentId { get; set; }

	public string Name { get; set; }
	public string Code { get; set; }
}

public abstract class Entity
{
	public Guid Id { get; set; }

	public byte[] RowVersion { get; set; }
}

public static class EntityMapping
{
	public static EntityTypeBuilder<T> MapEntity<T>(this EntityTypeBuilder<T> entity, bool isAutoId = true) where T : Entity
	{
		entity.HasKey(x => x.Id);

		if (isAutoId)
		{
			entity.Property(x => x.Id).ValueGeneratedOnAdd().HasDefaultValueSql("NEWID()");
		}
		else
		{
			entity.Property(x => x.Id).ValueGeneratedNever();
		}

		entity.Property(x => x.RowVersion).IsRowVersion();

		return entity;
	}
}

public class TestContext : DbContext
{
	protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
	{
		optionsBuilder
			.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
	}

	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
		modelBuilder.Entity<Thing>().Map();
	}
}

internal static class ThingEntityExtensions
{
	public static EntityTypeBuilder<Thing> Map(this EntityTypeBuilder<Thing> entity)
	{
		entity.ToTable(nameof(Thing));
		entity.MapEntity(false);
		entity.HasKey(x => new { x.Id, x.ParentId });

		entity.Property(x => x.Name).IsRequired().HasMaxLength(250);

		return entity;
	}
}

The migration generated in 2.1.0 is:

public partial class Init : Migration
{
	protected override void Up(MigrationBuilder migrationBuilder)
	{
		migrationBuilder.CreateTable(
			name: "Thing",
			columns: table => new
			{
				Id = table.Column<Guid>(nullable: false),
				RowVersion = table.Column<byte[]>(rowVersion: true, nullable: true),
				ParentId = table.Column<Guid>(nullable: false),
				Name = table.Column<string>(maxLength: 250, nullable: false),
				Code = table.Column<string>(nullable: true)
			},
			constraints: table =>
			{
				table.PrimaryKey("PK_Thing", x => new { x.Id, x.ParentId });
			});
	}

	protected override void Down(MigrationBuilder migrationBuilder)
	{
		migrationBuilder.DropTable(
			name: "Thing");
	}
}

In 2.1.1, the migration adds a unique constraint like it is applying both calls to HasKey and not overwriting the earlier call like it did before.

public partial class Init : Migration
{
	protected override void Up(MigrationBuilder migrationBuilder)
	{
		migrationBuilder.CreateTable(
			name: "Thing",
			columns: table => new
			{
				Id = table.Column<Guid>(nullable: false),
				RowVersion = table.Column<byte[]>(rowVersion: true, nullable: true),
				ParentId = table.Column<Guid>(nullable: false),
				Name = table.Column<string>(maxLength: 250, nullable: false),
				Code = table.Column<string>(nullable: true)
			},
			constraints: table =>
			{
				table.PrimaryKey("PK_Thing", x => new { x.Id, x.ParentId });
				table.UniqueConstraint("AK_Thing_Id", x => x.Id);
			});
	}

	protected override void Down(MigrationBuilder migrationBuilder)
	{
		migrationBuilder.DropTable(
			name: "Thing");
	}
}

Further technical details

EF Core version: 2.1.1
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 10
IDE: Visual Studio 2017 15.7.4

@ajcvickers
Copy link
Member

ajcvickers commented Jun 21, 2018

@danjohnso Thanks for reporting this. The fix in 2.1.1 that causes this issue can be reverted by setting the app switch "Microsoft.EntityFrameworkCore.Issue12119" to true as documented here: https://msdn.microsoft.com/en-us/library/system.appcontext.setswitch(v=vs.110).aspx

@ajcvickers ajcvickers added this to the 2.2.0 milestone Jun 21, 2018
@ajcvickers ajcvickers modified the milestones: 2.2.0-preview2, 2.2.0 Sep 11, 2018
@ajcvickers ajcvickers modified the milestones: 2.2.0-preview3, 2.2.0, 3.0.0 Oct 15, 2018
@ajcvickers ajcvickers modified the milestones: 3.0.0, Backlog Jun 28, 2019
@AndriySvyryd AndriySvyryd modified the milestones: Backlog, 3.1.0 Aug 28, 2019
@AndriySvyryd AndriySvyryd added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Aug 28, 2019
@AndriySvyryd AndriySvyryd removed their assignment Aug 28, 2019
AndriySvyryd added a commit that referenced this issue Aug 28, 2019
Don't try to scaffold alternate keys for owned types

Fixes #15698
Fixes #13628
Fixes #12414
AndriySvyryd added a commit that referenced this issue Aug 29, 2019
Don't try to scaffold alternate keys for owned types

Fixes #15698
Fixes #13628
Fixes #12414
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported punted-for-3.0 type-bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants