我有一个现有的数据库,我想用它构建一个使用 EF4.0
的新应用程序
有些表没有定义主键,因此当我创建新的实体数据模型时,我会收到以下消息:
表/视图 TABLE_NAME 没有定义主键,并且无法推断出有效的主键。此表/视图已被排除。要使用实体,您需要查看架构、添加正确的键并取消注释。
如果我想使用它们并修改数据,我必须在这些表中添加一个 PK,还是有一种解决方法让我不必这样做?
我认为这是由 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 回答
错误的意思正是它所说的。
即使您可以解决此问题,相信我,您也不想这样做。可能引入的令人困惑的错误数量令人震惊和可怕,更不用说你的表现可能会下降的事实。
不要解决这个问题。修复您的数据模型。
编辑:我看到很多人都反对这个问题。我想这很好,但请记住,OP 询问了映射没有主键的表,而不是视图。答案还是一样的。从可管理性、数据完整性和性能的角度来看,解决 EF 对表进行 PK 的需要是一个坏主意。
一些人评论说他们无法修复底层数据模型,因为它们正在映射到第三方应用程序。这不是一个好主意,因为模型可以从你下面改变。可以说,在这种情况下,您可能希望映射到一个视图,这又不是 OP 所要求的。
如果我想使用它们并修改数据,我必须在这些表中添加一个 PK,还是有一种解决方法让我不必这样做?
对于那些遇到此问题并正在使用 Entity Framework Core 的人,您不再需要向这些表添加 PK 或执行任何解决方法。从 EF Core 2.1 开始,我们有了一个新功能 Query Types
查询类型必须用于:
作为即席 FromSql() 查询的返回类型。映射到数据库视图。映射到没有定义主键的表。映射到模型中定义的查询。
因此,在您的 DbContext 中,只需添加以下 DbQuery<T>
类型的属性,而不是像下面的 DbSet<T>
。假设您的表名是 MyTable
:
public DbQuery<MyTable> MyTables { get; set; }
复合键也可以使用 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>()
链调用,用于那些不是自然或复合的非常特殊的键,但可以依赖它是独一无二的(通常,无论如何。)
在我的情况下,我必须将一个实体映射到一个没有主键的视图。此外,我不允许修改此视图。幸运的是,这个视图有一列是唯一的字符串。我的解决方案是将此列标记为主键:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[StringLength(255)]
public string UserSID { get; set; }
被骗的EF。完美运行,没有人注意到... :)
UserID
列..!
Itentity
功能。基本上,如果我是正确的,它仍然会为您创建 UserID
列作为 PK,但是当您创建新记录时它不会像默认情况下那样自动增加 UserID
。此外,您仍然需要在 UserID
中保留不同的值。
EF 不需要数据库的主键。如果是这样,您将无法将实体绑定到视图。
您可以修改 SSDL(和 CSDL)以将唯一字段指定为主键。如果你没有一个独特的领域,那么我相信你已经被淹没了。但是你真的应该有一个独特的领域(和一个PK),否则你以后会遇到问题。
埃里克
拥有无用的身份密钥有时毫无意义。我发现如果没有使用ID,为什么要添加它?但是,Entity 对此并不宽容,因此最好添加一个 ID 字段。即使在不使用的情况下,也比处理Entity关于丢失身份密钥的不断错误要好。
这个解决方案有效
即使没有PK,也不需要手动映射。您只需要告诉 EF 您的列之一是索引并且索引列不可为空。
为此,您可以使用 isNull 函数向视图中添加行号,如下所示
select
ISNULL(ROW_NUMBER() OVER (ORDER BY xxx), - 9999) AS id
from a
ISNULL(id, number)
是这里的关键点,因为它告诉 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; }
同样,这只是一种解决方法。真正的解决方案是修复数据模型。
这可能会迟到回复......但是......
如果表没有主键,则需要分析的场景很少,以使 EF 正常工作。规则是:EF 将使用具有主键的表/类。这就是它的跟踪方式...
说,你的表 1. 记录是唯一的:唯一性是由单个外键列构成的: 2. 记录是唯一的:唯一性是由多个列的组合构成的。 3. 记录不是唯一的(大部分*)。
对于场景 #1 和 #2,您可以将以下行添加到 DbContext 模块的 OnModelCreating 方法:modelBuilder.Entity().HasKey(x => new { x.column_a, x.column_b }); // 使记录唯一所需的列数。
对于场景#3,您仍然可以在研究表格后使用上述解决方案(#1 + #2)(*无论如何使所有记录都是唯一的)。如果您必须包含所有列以使所有记录唯一,那么您可能需要向表中添加主键列。如果此表来自第 3 方供应商,则将此表克隆到您的本地数据库(过夜或您需要的多次),并通过您的克隆脚本任意添加主键列。
如果你真的没有PK,以上答案都是正确的。
但是,如果有一个,但只是没有在数据库中指定索引,并且您无法更改数据库(是的,我在 Dilbert 的世界中工作),您可以手动将字段映射为键。
更新 @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 并使用另一种方式与您的数据库通信。
在 EF Core 5.0 中,您还可以在实体级别定义它。
[Keyless]
public class Address
{
public string Street { get; set; }
public string City { get; set; }
public int Zip { get; set; }
}
更改表结构并添加主列。更新模型 在 XML 编辑器中修改 .EDMX 文件并尝试在此特定表的标记下添加一个新列(将不起作用)而不是为退出表创建一个新的主列,我将通过涉及所有现有的来创建一个复合键列(工作)
Entity Framework: Adding DataTable with no Primary Key to Entity Model.
您可以设置复合键(类似于在 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 字段确实包含空值,但结合这三行,所有行现在都是唯一的。
多年后编辑这个:)
从实际的角度来看,每个表——甚至是像仓库表这样的非规范化表——都应该有一个主键。或者,如果做不到这一点,它至少应该有一个唯一的、不可为空的索引。
如果没有某种唯一键,重复记录可以(并且将会)出现在表中,这对于 ORM 层和数据的基本理解都是非常有问题的。具有重复记录的表可能是设计不良的征兆。
至少,表应该至少有一个标识列。在 SQL Server 中添加自动生成的 ID 列大约需要 2 分钟,在 Oracle 中大约需要 5 分钟。对于那额外的努力,很多很多问题都将被避免。
我通过解决它吸取了教训。简短的回答是不要解决它。
我使用 EF6 读取没有 PK 但有复合键的表。具有相同复合键的多行将具有完全相同的记录。基本上只读取了一行,但用于填充所有行。由于有数百万条记录,并且仅发生在相对少量的记录中,因此很难找到问题。
我们有一个没有唯一 ID 列的表。预计其他列会创建复合键,但随着时间的推移,数据有时在所有复合键列中都没有值。
这是使用 .NET Entity Framework 的解决方案:
[Key]
[Column(Order = 1)]
public Guid FakeId { get; set; }
public ... other columns
并更改 SQL 以选择此:
SELECT NEWID() as FakeId, ... other columns
该表只需要有一列不允许空值