Entity Framework Core:与PK不同数据类型的FK

9

运行时: netcoreapp2.0

ASP.Net Core: 2.0.1

EF Core: 2.0.1

Microsoft.EntityFrameworkCore.SqlServer: 2.0.1

我正在处理一个被许多服务使用的旧数据库,原始创建者将某个表的主键定义为 short 类型,而对该表的外键则定义为 int 类型。我是否有可能处理这种情况呢?

注意:目前不可行更改列类型。

考虑以下两个类:

public class Database {
  public short DatabaseId { get; set; }
  public Database { get; set; }
}

public class Client {
  public int ClientId { get; set; }
  public int DatabaseId { get; set; }
  public Database Database { get; set; }
}

我的第一次尝试:

public int DatabaseId { get; set; }
[ForeignKey("DatabaseId")] public Database Database { get; set; }

抛出(throws)
System.InvalidOperationException: The relationship from 'Client.Database' to 'Database.Client' with foreign key properties {'DatabaseId' : int} cannot target the primary key {'DatabaseId' : short} because it is not compatible. Configure a principal key or a set of compatible foreign key properties for this relationship.

现在使用流畅的API:

使用流畅的API现在:

modelBuilder.Entity<Client>(table => {    
    table.HasOne(p => p.Database)
        .WithOne(i => i.Client)
        .HasForeignKey<Client>(c => c.DatabaseId)
        .HasPrincipalKey<Database>(d => d.DatabaseID);
});

定义阴影属性失败

modelBuilder.Entity<Client>(table => {
    table.Property<int>("DatabaseId");

    table.HasOne(p => p.Database)
        .WithOne(i => i.Client)
        .HasForeignKey("DatabaseId")
        .HasPrincipalKey<Database>(d => d.DatabaseId);
});

抛出异常

System.InvalidOperationException: You are configuring a relationship between 'Client' and 'Database' but have specified a foreign key on 'DatabaseId'. The foreign key must be defined on a type that is part of the relationship.

好的,这次没有阴影属性:

modelBuilder.Entity<Client>(table => {

    table.HasOne(p => p.Database)
        .WithOne(i => i.Client)
        .HasForeignKey<Client>(c => c.DatabaseId)
        .HasPrincipalKey<Database>(d => d.SQLDatabaseID);
});

抛出异常。
System.InvalidOperationException: The types of the properties specified for the foreign key {'DatabaseId'} on entity type 'Client' do not match the types of the properties in the principal key {'DatabaseID'} on entity type 'Database'.

于是,我试着只改变实体类型,希望EF可以将int映射为int16。

public class Client {
  public int ClientId { get; set; }
  public short DatabaseId { get; set; } // <-- now a short
  public Database Database { get; set; }
}

抛出(throws)
System.InvalidOperationException: An exception occurred while reading a database value for property 'Client.DatabaseId'. The expected type was 'System.Int16' but the actual value was of type 'System.Int32'. ---> System.InvalidCastException: Unable to cast object of type 'System.Int32' to type 'System.Int16'.

我开始感觉这似乎是不可能的。

那么......如果您拥有id为“short.MaxValue + 1”的行,会发生什么?EF会如何映射它到“short”?您将如何在数据库中存储该值?您可以更改数据库吗? - Camilo Terevinto
该死,这真是个大问题,不确定他们在将 Id 设为短整型时在想什么。但如果无法更改架构,您可以在执行 Linq 查询时将标识符转换为 ToString(),虽然这种方法不太可维护。您也可以通过强制转换来显式地进行转换,例如 Convert.ToInt16(int.MaxValue)。 - Mohamoud Mohamed
我仍在努力弄清楚为什么你会使用SHORT。 - mvermef
问题不仅在于执行简单的查询(这是可以接受的)...当尝试跟踪导航属性时,例如_databases.Include(d => d.Client),问题就会出现。 EF无法跟踪导航属性。 - jaredcnance
我已经复制了你的尝试,参考我的下面答案的评论... - mvermef
这是EF目前的一个限制,他们计划通过值转换器来支持它(https://github.com/aspnet/EntityFrameworkCore/issues/11112#event-1501968692)。然而,已知存在一个问题阻止了它,并且应该在2.1 github.com/aspnet/EntityFrameworkCore/issues/11117中得到解决。一旦ValueConverters问题得到解决,我会更新答案。 - jaredcnance
2个回答

10

2
您可以使用以下代码定义外键,从而绕过由实体框架生成的错误。
[Column(TypeName = "int")]
public short DatabaseId { get; set; }

但是,一旦你运行迁移,你将会得到这个错误,而这个错误是由SQL服务器生成的。

列'Databases.DatabaseId'与外键'FK_Clients_Databases_DatabaseId'中引用列'Clients.DatabaseId'的数据类型不同。

这是完全有道理的。你所尝试实现的是不可能的,因为SQL服务器要求外键必须是相同的类型,否则无法建立外键关系。


我不关心迁移,因为我正在编写的应用程序不拥有数据库。然而,这仍然无法正常工作并抛出异常:System.InvalidOperationException: 在读取属性“Client.DatabaseId”的数据库值时发生异常。期望类型为“System.Int16”,但实际值的类型为“System.Int32”。---> System.InvalidCastException: 无法将类型为“System.Int32”的对象强制转换为类型“System.Int16”。我还尝试了另一种方式,并将其定义为int。 - jaredcnance
@jaredcnance 这与迁移无关,而与设计有关。你试图把一个方形木栓塞进一个圆形孔里。INT != SQL SERVER中的SHORT,SHORT = SMALLINT,SQL SERVER中的SMALLINT!= INT。截断!EF足够聪明,知道这一点...因此出现了错误,这就是EF Core在地板上打滚的原因。 - mvermef
我的关于迁移的评论是针对答案的。我知道迁移不是问题所在。我正在寻找解决这个问题的方法,因为我无法实际改变数据库。其他应用程序依赖于当前数据类型,如果我更改了数据库,它们将会出现故障。如果绝对不能这样做,那么好吧,我将不得不使用Dapper或者不使用导航属性。我的问题是EF Core是否存在解决方法。 - jaredcnance
@jaredcnance,你无法在物理上这样做,即使使用Dapper也不行。最终你会得到异常foreign key is not of same type。唯一可能的方法是删除表之间的关系并手动处理所有外键。如果删除关系,则连接将无法正常工作(你还将失去导航属性)。 - Mujahid Daud Khan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接