Bug #73608 Cannot use unsigned integers as PKs / FKs in Entity Framework Code First
Submitted: 15 Aug 2014 22:25 Modified: 15 Aug 2014 22:30
Reporter: Fernando Gonzalez.Sanchez Email Updates:
Status: Won't fix Impact on me:
None 
Category:Connector / NET Severity:S3 (Non-critical)
Version:6.8.3 OS:Any
Assigned to: Fernando Gonzalez.Sanchez CPU Architecture:Any
Tags: entity-framework connector-net

[15 Aug 2014 22:25] Fernando Gonzalez.Sanchez
Description:
When trying to use Entity Framework 6 Code First with entities that have as Primary Keys / Foreign Keys unsigned values, an error occurs. It seems to work only for signed types for PKs / FKs.

This applies to all Connector/NET versions like 6.8.3, 6.9.2, etc.

How to repeat:
As sample:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Data.Entity; 
using System.ComponentModel.DataAnnotations; 
using System.ComponentModel.DataAnnotations.Schema; 
using MySql.Data.Entity; 
using System.Data.Entity.Migrations; 
using System.Data.Entity.Migrations.History; 
using System.Data.Common; 

namespace EF6UnitOfWorkPattern 
{ 
public class Program 
{ 
static void Main(string[] args) 
{ 
using (QuoteDBContext ctx = new QuoteDBContext()) 
{ 
ctx.Database.Delete(); 
ctx.Database.CreateIfNotExists(); 

var client = new Client(); 
ctx.Clients.Add(client); 
var quote = new Quote(); 
quote.Client = client; 
ctx.Quotes.Add(quote); 
ctx.SaveChanges(); 
} 
} 
} 

[DbConfigurationType(typeof(MySqlEFConfiguration))] 
public class QuoteDBContext : DbContext 
{ 
public DbSet<Quote> Quotes { get; set; } 
public DbSet<Client> Clients { get; set; } 

public QuoteDBContext() 
{ 
Database.SetInitializer<QuoteDBContext>(new QuoteDBInitialize()); 
Database.SetInitializer<QuoteDBContext>(new MigrateDatabaseToLatestVersion<QuoteDBContext, Configuration<QuoteDBContext>>()); 
} 

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
base.OnModelCreating(modelBuilder); 
modelBuilder.Configurations.AddFromAssembly(System.Reflection.Assembly.GetExecutingAssembly()); 
} 
} 

public class QuoteDBInitialize : DropCreateDatabaseReallyAlways<QuoteDBContext> 
{ 
protected override void Seed(QuoteDBContext ctx) 
{ 
base.Seed(ctx); 
} 
} 

/// <summary> 
/// This initializer really drops the database, not just once per AppDomain (like the DropCreateDatabaseAlways). 
/// </summary> 
/// <typeparam name="TContext"></typeparam> 
public class DropCreateDatabaseReallyAlways<TContext> : IDatabaseInitializer<TContext> where TContext : DbContext 
{ 
public void InitializeDatabase(TContext context) 
{ 
context.Database.Delete(); 
context.Database.CreateIfNotExists(); 
this.Seed(context); 
context.SaveChanges(); 
} 

protected virtual void Seed(TContext context) 
{ 
} 
} 

public class MyHistoryContext : MySqlHistoryContext 
{ 
public MyHistoryContext(DbConnection existingConnection, string defaultSchema) 
: base(existingConnection, defaultSchema) 
{ 
} 

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
base.OnModelCreating(modelBuilder); 

modelBuilder.Entity<HistoryRow>().ToTable("__MySqlMigrations"); 
modelBuilder.Entity<HistoryRow>().Property(h => h.MigrationId).HasColumnName("_MigrationId"); 
modelBuilder.Entity<HistoryRow>().Property(h => h.ContextKey).HasColumnName("_ContextKey"); 
modelBuilder.Entity<HistoryRow>().Property(h => h.Model).HasColumnName("_Model"); 
modelBuilder.Entity<HistoryRow>().Property(h => h.ProductVersion).HasColumnName("_ProductVersion"); 
} 
} 

public class Configuration<TContext> : DbMigrationsConfiguration<TContext> where TContext : DbContext 
{ 
public Configuration() 
{ 
CodeGenerator = new MySqlMigrationCodeGenerator(); 
AutomaticMigrationsEnabled = true; 
SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator()); 
SetHistoryContextFactory("MySql.Data.MySqlClient", (existingConnection, defaultSchema) => new MyHistoryContext(existingConnection, defaultSchema)); 
} 
} 

public class Client 
{ 
[Key] 
public long Id { get; set; } 
} 

public class Quote 
{ 
[Column("Id")] 
[Key] 
public long Id { get; set; } 

[Column("Client")] 
[ForeignKey("Client")] 
public long ClientId { get; set; } 

public virtual Client Client { get; set; } 
} 
}

And  as App.config

<?xml version="1.0" encoding="utf-8"?> 
<configuration> 
<configSections> 
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> 
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> 
</configSections> 
<startup> 
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> 
</startup> 
<entityFramework codeConfigurationType="MySql.Data.Entity.MySqlEFConfiguration, MySql.Data.Entity.EF6"> 
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> 
<parameters> 
<parameter value="v11.0" /> 
</parameters> 
</defaultConnectionFactory> 
<providers> 
<provider invariantName="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6" /> 
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> 
</providers> 
</entityFramework> 
<connectionStrings> 
<add name="QuoteDBContext" connectionString="***" providerName="MySql.Data.MySqlClient" /> 
</connectionStrings> 
</configuration>

An error with this Stacktrace happens:

EF6UnitOfWorkPatternEF6CodeFirstMySqlConsoleApp.Client: : EntityType 'Client' has no key defined. Define the key for this EntityType.
EF6UnitOfWorkPatternEF6CodeFirstMySqlConsoleApp.Quote: : EntityType 'Quote' has no key defined. Define the key for this EntityType.
Clients: EntityType: EntitySet 'Clients' is based on type 'Client' that has no keys defined.
Quotes: EntityType: EntitySet 'Quotes' is based on type 'Quote' that has no keys defined.

   at System.Data.Entity.Core.Metadata.Edm.EdmModel.Validate()
   at System.Data.Entity.DbModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo)
   at System.Data.Entity.DbModelBuilder.Build(DbConnection providerConnection)
   at System.Data.Entity.Internal.LazyInternalContext.CreateModel(LazyInternalContext internalContext)
   at System.Data.Entity.Internal.RetryLazy`2.GetValue(TInput input)
   at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
   at System.Data.Entity.Internal.InternalContext.CreateObjectContextForDdlOps()

   at System.Data.Entity.Database.Delete()
   at EF6UnitOfWorkPatternEF6CodeFirstMySqlConsoleApp.Program.Main(String[] args) in c:\Users\Fito\Documents\Visual Studio 2013\Projects\EF6CodeFirstMySqlConsol
eApp\EF6CodeFirstMySqlConsoleApp\Program.cs:line 23
Press any key to continue . . .

If the types are change from ulong to long, it works fine.
[15 Aug 2014 22:30] Fernando Gonzalez.Sanchez
This won't be fixed, unsigned types are not supported as PK / FKs in Entity Framework Infrastructure.

Possible reasons are, they are not supported in MSSQL (exactly same error occurs in SQL Server) and the unsigned types are not CLS Compliant.

For more details, it seems the problem is on 
System.Data.Entity.DbModelBuilder.MapTypes which eventually calls
System.Data.Entity.ModelConfiguration.Conventions.KeyAttributeConvention.ApplyPropertyConfiguration, which checks the property is a valid PrimitiveType and only CLR compliant types are there.

And ProviderManifest.xml does not allow to add mappings for unsigned .NET types.
[15 Aug 2014 22:34] Fernando Gonzalez.Sanchez
As a follow up, a discussion was opened at https://entityframework.codeplex.com/discussions/561342