实体框架: 没有主键的表

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

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

表/视图 TABLE _ NAME 没有定义主键 并且无法推断出有效的主键 要使用该实体,您需要检查您的模式, 添加正确的键,并取消注释。

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

226379 次浏览

这个错误的意思正是它所说的

就算你能解决这个问题,相信我,你也不会想的。可能引入的令人困惑的 bug 的数量是惊人的和可怕的,更不用说你的性能可能会下降管道的事实。

不要绕过这个问题,修复您的数据模型。

编辑: 我已经看到很多人对这个问题投了反对票。我认为这很好,但是请记住 OP 要求映射一个没有主键的 桌子,而不是 风景。答案还是一样。从可管理性、数据完整性和性能的角度来看,围绕 EF 需要在表上进行 PK 是一个糟糕的主意。

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

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

但是,如果有一个,但它只是没有指定与数据库中的索引,你不能改变数据库(是的,我的工作在呆伯特的世界) ,你可以手动映射字段(s)为关键。

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

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

艾瑞克

我认为蒂利托解决了这个问题:

实体框架和 SQLServer 视图

下面我将引用他的话:

我们遇到了同样的问题,这就是解决办法:

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

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

应用这一点的一种简单方法是将视图的 select 语句封装在另一个 select 中。

例如:

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

10年4月26日17:00由蒂利托回答

  1. 更改 Table 结构并添加一个主列。更新模型
  2. 修改。在 XML 编辑器中添加 EDMX 文件,并尝试在此特定表的标记下添加一个 New Column (将不工作)
  3. 而不是创建一个新的主列退出表,我将 通过包含所有现有列(成功了)创建复合键

实体框架: 向实体模型添加不带主键的数据表。

该表只需要有一个不允许为空的列

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

这个方法有效

你不需要手动地图,即使你没有一个 PK。 您只需要告诉 EF,其中一个列是 index,而 index 列不能为空。

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

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

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

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

如果没有某种唯一键,重复记录可能(并将)出现在表中,这对 ORM 层和对数据的基本理解都是非常有问题的。具有重复记录的表可能是糟糕设计的一个症状。

至少,该表应该至少有一个标识列。添加自动生成的 ID 列在 SQLServer 中大约需要2分钟,在 Oracle 中需要5分钟。由于这一额外的努力,许多 很多问题将得以避免。

你可以设置一个组合键(类似于 VIEWS 在 EF 中的设置方式) ,并将键和列顺序应用到字段中,这样组合就是唯一的,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 字段确实包含 null,但是通过这三个字段的组合,所有行现在都是唯一的。

多年后编辑:)

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

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

作弊 EF。工作完美,没有人注意到... :)

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

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


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

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

组合键也可以用实体框架 FluentAPI 完成

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

这也许太迟了... 不过..。

如果一个表没有主键,那么为了使 EF 正常工作,需要分析的场景很少。规则是: EF 将使用带有主键的表/类。这就是它追踪的方式。

你的桌子 1. 记录是独一无二的: 独一无二是由一个外键列造成的: 2. 记录是独一无二的: 独一无二是由多列组合而成的。 3. 记录不是唯一的(在大多数情况下)。

对于场景 # 1和 # 2,您可以向 DbContext 模块 OnModelCreate 方法添加以下代码行: HasKey (x = > new { x.column _ a,x.column _ b }) ;//使记录唯一所需的列数。

对于场景 # 3,在研究了表之后,您仍然可以使用上面的解决方案(# 1 + # 2)(* 是什么使得所有记录都是唯一的)。如果必须包含所有列才能使所有记录唯一,那么可能需要向表中添加一个主键列。如果该表来自第三方供应商,那么通过克隆脚本任意添加主键列,将该表克隆到本地数据库(过夜或需要的时间)。

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

对于那些达到这个问题并正在使用实体框架核心的人来说,您不再需要向这些表添加 PK 或做任何变通方法。自 EF Core 2.1以来,我们有了一个新的特性 查询类型

查询类型必须用于:

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

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

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

更新 @ CodeNotfound的答案。

在 EF Core 3.0中,DbQuery<T>已经被废弃了,相反,你应该使用 无键实体类型,它应该做同样的事情。这些都是使用 ModelBuilderHasNoKey()方法配置的。在 DbContext 类中,执行以下操作

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


}

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

  • 永远不会跟踪 DbContext 中的更改,因此永远不会在数据库中插入、更新或删除。
  • 只支持导航映射功能的子集,特别是:
    • 他们可能永远不会成为一段关系的主要目标。
    • 它们可能没有导航到所有实体
    • 它们只能包含指向常规实体的引用导航属性。
    • 实体不能包含无键实体类型的导航属性。

这意味着

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

您不能以这种方式修改数据——无论您如何阅读。我们可以设想使用另一种方式(例如 ADO.NET,Dapper)来修改数据-这可能是一个解决方案,在你很少需要做非读操作的情况下,你仍然愿意坚持使用 EF Core 为您的大多数情况。

此外,如果您真的需要/想使用堆(无键)表——考虑放弃 EF 并使用其他方式与数据库通信。

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

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

参考文献: < a href = “ https://Learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/whatsnew # use-a-c-tribute-to-Indic- that-an-tity-has-no-key”rel = “ nofollow norefrer”> https://learn.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

我是通过解决这个问题来吸取教训的。简短的回答是 不要绕过它

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

我们有一个没有唯一 ID 列的表。 其他列应该创建一个组合键,但是随着时间的推移,数据有时在所有组合键列中没有值。

下面是一个使用.NET 实体框架的解决方案:

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

并更改 SQL 以选择以下内容:

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