ChatGPT解决这个技术问题 Extra ChatGPT

实体框架:没有主键的表

我有一个现有的数据库,我想用它构建一个使用 EF4.0 的新应用程序

有些表没有定义主键,因此当我创建新的实体数据模型时,我会收到以下消息:

表/视图 TABLE_NAME 没有定义主键,并且无法推断出有效的主键。此表/视图已被排除。要使用实体,您需要查看架构、添加正确的键并取消注释。

如果我想使用它们并修改数据,我必须在这些表中添加一个 PK,还是有一种解决方法让我不必这样做?

引用 Joe Celko 的话:如果它没有主键,它就不是表。为什么有人会创建一个没有主键的“常规”表?只需添加那些PK!你会需要它们——而不是迟早......
如果它是一个视图,请看这个案例stackoverflow.com/a/10302066/413032
并非每个表都需要主键是完全正确的。不经常有用,但有效。混淆 EF 是一个很好的理由,而不是需要太多。 ;-)。
想象一下,我无法修改公司的数据库结构,并且它是由不会更改表结构的人创建的,这种情况是可能的。
这正是我们所处的位置。我们必须使用没有主键的第三方 Oracle 数据库。

C
Community

我认为这是由 Tillito 解决的:

Entity Framework and SQL Server View

我将在下面引用他的条目:

我们遇到了同样的问题,这是解决方案:

要强制实体框架使用列作为主键,请使用 ISNULL。

要强制实体框架不使用列作为主键,请使用 NULLIF。

一种简单的应用方法是将视图的选择语句包装在另一个选择中。

例子:

SELECT
  ISNULL(MyPrimaryID,-999) MyPrimaryID,
  NULLIF(AnotherProperty,'') AnotherProperty
  FROM ( ... ) AS temp

由 Tillito 于 2010 年 4 月 26 日 17:00 回答


+1这是正确的答案,在一个完美的世界中,进入并修改所有遗留数据库以具有参照完整性会很棒,但实际上这并不总是可能的。
我不会推荐这个。特别是 ISNULL 部分。如果 EF 检测到两个 PK 相同,它可能不会呈现唯一记录,而是返回一个共享对象。这以前发生在我身上。
@Todd - 如果 MyPrimaryID 是 NOT NULL 列,这怎么可能发生?
@JoeCool,仅仅因为它不是 NULL 并不意味着它是唯一的。我赞成“这个解决方案有效......”,因为无论它在什么环境中使用,你都可以确保它的独特性。虽然现在想想,如果一条记录被删除,那将有效地改变后面记录的“PK”。
-1,因为这没有回答如何配置实体框架/C# 代码来处理如何映射到缺少标识种子的表。某些第 3 方软件(出于某种原因)是以这种方式编写的。
v
vivek nuna

错误的意思正是它所说的。

即使您可以解决此问题,相信我,您也不想这样做。可能引入的令人困惑的错误数量令人震惊和可怕,更不用说你的表现可能会下降的事实。

不要解决这个问题。修复您的数据模型。

编辑:我看到很多人都反对这个问题。我想这很好,但请记住,OP 询问了映射没有主键的表,而不是视图。答案还是一样的。从可管理性、数据完整性和性能的角度来看,解决 EF 对表进行 PK 的需要是一个坏主意。

一些人评论说他们无法修复底层数据模型,因为它们正在映射到第三方应用程序。这不是一个好主意,因为模型可以从你下面改变。可以说,在这种情况下,您可能希望映射到一个视图,这又不是 OP 所要求的。


同意常见的场景,但在像 LOG 表这样的罕见场景中,您只需要尽快插入记录。在检查唯一性和索引发生时,拥有 PK 可能是一个问题。此外,如果您的 PK 是 IDENTITY,那么将生成的值返回给 EF 是另一个问题。使用 GUID 代替?生成时间和索引/排序是另一个问题!...所以在一些关键的 OLTP 场景(如日志记录)中没有 PK 是一个重点,并且没有任何积极意义!
@MahmoudMoravej:首先,不要混淆集群索引和主键的想法。他们不是一回事。您可以在 IDENTITY 列上具有聚集索引的表上进行非常高性能的插入。如果遇到索引维护问题,则应正确分区表。离开没有聚集索引的表也意味着您无法有效地对其进行碎片整理以在删除后回收空间。如果它没有索引,我很同情那个试图查询你的日志表的可怜人。
“修复你的数据模型”不是一个真正的答案。有时我们不得不忍受我们没有创造也无法改变的不太理想的情况。而且,正如@Colin 所说,有一种方法可以完全按照 OP 的要求去做。
更改代码以满足 EF 本身就是一种解决方法。并非所有表都需要主键,也不应强制要求。例如,您有一个主题,它有 0 个或多个关键字。关键字表可以有一个父主题 id 和一个对应的关键字。说我需要改造我的数据库,因为 EF 强迫我是跛脚的。
这应该被否决,因为它没有回答问题。我们经常需要使用无法更改的第三方数据库。
C
CodeNotFound

如果我想使用它们并修改数据,我必须在这些表中添加一个 PK,还是有一种解决方法让我不必这样做?

对于那些遇到此问题并正在使用 Entity Framework Core 的人,您不再需要向这些表添加 PK 或执行任何解决方法。从 EF Core 2.1 开始,我们有了一个新功能 Query Types

查询类型必须用于:

作为即席 FromSql() 查询的返回类型。映射到数据库视图。映射到没有定义主键的表。映射到模型中定义的查询。

因此,在您的 DbContext 中,只需添加以下 DbQuery<T> 类型的属性,而不是像下面的 DbSet<T>。假设您的表名是 MyTable

public DbQuery<MyTable> MyTables { get; set; }

如果您使用的是 EF Core,则为最佳答案!
A
Adrian Garcia

复合键也可以使用 Entity Framework Fluent API 完成

public class MyModelConfiguration : EntityTypeConfiguration<MyModel>
{
     public MyModelConfiguration()
     {
        ToTable("MY_MODEL_TABLE");
        HasKey(x => new { x.SourceId, x.StartDate, x.EndDate, x.GmsDate });
        ...
     }
}

在这种“手动映射”的情况下,我发现指定自定义键是有效的;此外,如果您没有复合键的好处(如本答案所示),您可以使用 .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None) 标记一个 modelBuilder.Entity<T>() 链调用,用于那些不是自然或复合的非常特殊的键,但可以依赖它是独一无二的(通常,无论如何。)
B
Boris Lipschitz

在我的情况下,我必须将一个实体映射到一个没有主键的视图。此外,我不允许修改此视图。幸运的是,这个视图有一列是唯一的字符串。我的解决方案是将此列标记为主键:

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[StringLength(255)]
public string UserSID { get; set; }

被骗的EF。完美运行,没有人注意到... :)


no.. 如果您使用 Code First 方法,它将创建 UserID 列..!
你没有欺骗EF。您刚刚命令关闭 Itentity 功能。基本上,如果我是正确的,它仍然会为您创建 UserID 列作为 PK,但是当您创建新记录时它不会像默认情况下那样自动增加 UserID。此外,您仍然需要在 UserID 中保留不同的值。
@Celdor UserSID 是一个字符串,它永远不会“自动增加”。如果它是一个整数标识列,那么数据库将在插入时增加它,而不是实体框架。
E
Erick T

EF 不需要数据库的主键。如果是这样,您将无法将实体绑定到视图。

您可以修改 SSDL(和 CSDL)以将唯一字段指定为主键。如果你没有一个独特的领域,那么我相信你已经被淹没了。但是你真的应该有一个独特的领域(和一个PK),否则你以后会遇到问题。

埃里克


这避免了 ISNULL hack。但根据情况,可能需要其他答案 - 例如,我感觉 EF 中的 PK 不支持某些数据类型。
I
IyaTaisho

拥有无用的身份密钥有时毫无意义。我发现如果没有使用ID,为什么要添加它?但是,Entity 对此并不宽容,因此最好添加一个 ID 字段。即使在不使用的情况下,也比处理Entity关于丢失身份密钥的不断错误要好。


m
marc_s

这个解决方案有效

即使没有PK,也不需要手动映射。您只需要告诉 EF 您的列之一是索引并且索引列不可为空。

为此,您可以使用 isNull 函数向视图中添加行号,如下所示

select 
    ISNULL(ROW_NUMBER() OVER (ORDER BY xxx), - 9999) AS id
from a

ISNULL(id, number) 是这里的关键点,因为它告诉 EF 该列可以是主键


我不会建议 ISNULL 部分。如果 EF 检测到两个相同的 PK,它可能不会呈现唯一记录,而是返回一个共享对象。这以前发生在我身上。
您必须使用 isnull,否则 EF 不会相信它不可为空。
D
Developer

这只是对@Erick T 答案的补充。如果没有具有唯一值的单列,解决方法是使用复合键,如下所示:

[Key]
[Column("LAST_NAME", Order = 1)]
public string LastName { get; set; }

[Key]
[Column("FIRST_NAME", Order = 2)]
public string FirstName { get; set; }

同样,这只是一种解决方法。真正的解决方案是修复数据模型。


S
Sam Saarian

这可能会迟到回复......但是......

如果表没有主键,则需要分析的场景很少,以使 EF 正常工作。规则是:EF 将使用具有主键的表/类。这就是它的跟踪方式...

说,你的表 1. 记录是唯一的:唯一性是由单个外键列构成的: 2. 记录是唯一的:唯一性是由多个列的组合构成的。 3. 记录不是唯一的(大部分*)。

对于场景 #1 和 #2,您可以将以下行添加到 DbContext 模块的 OnModelCreating 方法:modelBuilder.Entity().HasKey(x => new { x.column_a, x.column_b }); // 使记录唯一所需的列数。

对于场景#3,您仍然可以在研究表格后使用上述解决方案(#1 + #2)(*无论如何使所有记录都是唯一的)。如果您必须包含所有列以使所有记录唯一,那么您可能需要向表中添加主键列。如果此表来自第 3 方供应商,则将此表克隆到您的本地数据库(过夜或您需要的多次),并通过您的克隆脚本任意添加主键列。


P
Poker Villain

如果你真的没有PK,以上答案都是正确的。

但是,如果有一个,但只是没有在数据库中指定索引,并且您无法更改数据库(是的,我在 Dilbert 的世界中工作),您可以手动将字段映射为键。


P
Peter Lindsten

更新 @CodeNotFound 的答案。

在 EF Core 3.0 中,DbQuery<T> 已被弃用,而您应该使用 Keyless entity types,它据说可以做同样的事情。这些是使用 ModelBuilder HasNoKey() 方法配置的。在您的 DbContext 类中,执行此操作

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<YourEntityType>(eb =>
        {
            eb.HasNoKey();
        });

}

但是有一些限制,特别是:

永远不会跟踪 DbContext 中的更改,因此永远不会在数据库上插入、更新或删除。仅支持导航映射功能的子集,具体而言:它们可能永远不会充当关系的主要端。它们可能没有对拥有实体的导航它们只能包含指向常规实体的参考导航属性。实体不能包含无键实体类型的导航属性。

这意味着对于问题

如果我想使用它们并修改数据,我必须在这些表中添加一个 PK,还是有一种解决方法让我不必这样做?

您不能以这种方式修改数据 - 但是您可以阅读。可以设想使用另一种方式(例如 ADO.NET、Dapper)来修改数据 - 这可能是您很少需要执行非读取操作并且仍然希望在大多数情况下坚持使用 EF Core 的情况下的解决方案。

此外,如果您确实需要/想要使用堆(无密钥)表 - 考虑放弃 EF 并使用另一种方式与您的数据库通信。


v
vivek nuna

在 EF Core 5.0 中,您还可以在实体级别定义它。

[Keyless]
public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public int Zip { get; set; }
}

参考: https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/whatsnew#use-a-c-attribute-to-indicate-that-an-entity-has-no-key


R
Ralph Willgoss

更改表结构并添加主列。更新模型 在 XML 编辑器中修改 .EDMX 文件并尝试在此特定表的标记下添加一个新列(将不起作用)而不是为退出表创建一个新的主列,我将通过涉及所有现有的来创建一个复合键列(工作)

Entity Framework: Adding DataTable with no Primary Key to Entity Model.


我尝试了 EF 4.0 的复合键方法,但没有成功。
这种方法对我来说非常有效,“有时”使用遗留系统可能会很痛苦......
A
Azerax

您可以设置复合键(类似于在 EF 中完成 VIEWS 的方式),并将键和列顺序应用于字段,以便组合是唯一的,EF 不需要 PK(仅在进行插入时有用,更新或删除操作)开始,这是最近实现的一个示例:

[Key]
[Column(Order = 0)]
public int NdfID { get; set; }

[Key]
[Column(Order = 1)]
public int? UserID { get; set; }

[Key]
[Column(Order = 2)]
public int ParentID { get; set; }

在此示例中,我的 userid 字段确实包含空值,但结合这三行,所有行现在都是唯一的。

多年后编辑这个:)


A
Ash8087

从实际的角度来看,每个表——甚至是像仓库表这样的非规范化表——都应该有一个主键。或者,如果做不到这一点,它至少应该有一个唯一的、不可为空的索引。

如果没有某种唯一键,重复记录可以(并且将会)出现在表中,这对于 ORM 层和数据的基本理解都是非常有问题的。具有重复记录的表可能是设计不良的征兆。

至少,表应该至少有一个标识列。在 SQL Server 中添加自动生成的 ID 列大约需要 2 分钟,在 Oracle 中大约需要 5 分钟。对于那额外的努力,很多很多问题都将被避免。


我的应用程序位于数据仓库设置中(使用 Oracle),您说服我完成 5 分钟来添加索引。它实际上只需要 5 分钟(如果您需要 look it up 或修改 ETL,则需要更多时间)。
C
Cheng

我通过解决它吸取了教训。简短的回答是不要解决它。

我使用 EF6 读取没有 PK 但有复合键的表。具有相同复合键的多行将具有完全相同的记录。基本上只读取了一行,但用于填充所有行。由于有数百万条记录,并且仅发生在相对少量的记录中,因此很难找到问题。


J
Joe Kahl

我们有一个没有唯一 ID 列的表。预计其他列会创建复合键,但随着时间的推移,数据有时在所有复合键列中都没有值。

这是使用 .NET Entity Framework 的解决方案:

[Key]
[Column(Order = 1)]
public Guid FakeId { get; set; }
public ... other columns

并更改 SQL 以选择此:

SELECT NEWID() as FakeId, ... other columns

m
mister9

该表只需要有一列不允许空值


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅