更新:我最近在这方面得到了一些支持,所以我想我会让人们知道我在下面给出的建议并不是最好的。自从我最初开始在旧的无密钥数据库上做实体框架以来,我已经意识到你能做的最好的事情就是通过反向代码优先来做。有一些关于如何做到这一点的好文章。只需按照它们,然后当您想为其添加密钥时,使用数据注释来“伪造”密钥。
例如,假设我知道我的表 Orders,虽然它没有主键,但可以确保每个客户只有一个订单号。由于这是表格的前两列,我将代码优先类设置为如下所示:
[Key, Column(Order = 0)]
public Int32? OrderNumber { get; set; }
[Key, Column(Order = 1)]
public String Customer { get; set; }
通过这样做,您基本上是在伪造 EF 以相信存在一个由 OrderNumber 和 Customer 组成的集群键。这将允许您在无密钥表上进行插入、更新等操作。
如果您对逆向 Code First 不太熟悉,请去找一个关于 Entity Framework Code First 的好教程。然后在 Reverse Code First 上找到一个(即使用现有数据库执行 Code First)。然后回到这里,再次查看我的关键建议。 :)
原答案:
首先:正如其他人所说,最好的选择是向表中添加主键。句号。如果您能做到这一点,请不要继续阅读。
但如果你不能,或者只是讨厌自己,有一种方法可以在没有主键的情况下做到这一点。
就我而言,我使用的是遗留系统(最初是 AS400 上的平面文件,移植到 Access,然后移植到 T-SQL)。所以我必须想办法。这是我的解决方案。以下内容对我使用 Entity Framework 6.0(截至本文撰写时 NuGet 的最新版本)有效。
在解决方案资源管理器中右键单击您的 .edmx 文件。选择“打开方式...”,然后选择“XML(文本)编辑器”。我们将在这里手动编辑自动生成的代码。
寻找这样的一行:
<EntitySet Name="table_name" EntityType="MyModel.Store.table_name" store:Type="Tables" store:Schema="dbo" store:Name="table_nane">
从末尾删除store:Name="table_name"。
将store:Schema="whatever"更改为Schema="whatever"
查看该行下方并找到<DefiningQuery> 标记。它将有一个很大的选择语句。删除标签及其内容。
现在您的线路应该如下所示:
<EntitySet Name="table_name" EntityType="MyModel.Store.table_name" store:Type="Tables" Schema="dbo" />
我们还有一些东西要改变。浏览您的文件并找到以下内容:
<EntityType Name="table_name">
您可能会在附近看到一些注释文本,警告您它没有识别主键,因此已推断出键并且定义是只读表/视图。您可以保留或删除它。我删了。
下面是<Key> 标签。这就是 Entity Framework 将用来执行插入/更新/删除的内容。所以请确保您做对了。该标记中的属性(或多个属性)需要指示唯一可识别的行。例如,假设我知道我的表orders,虽然它没有主键,但可以确保每个客户只有一个订单号。
所以我的看起来像:
<EntityType Name="table_name">
<Key>
<PropertyRef Name="order_numbers" />
<PropertyRef Name="customer_name" />
</Key>
说真的,不要做错了。假设即使永远不应该有重复,两行以相同的订单号和客户名称进入我的系统。哎呀!这就是我不使用密钥的结果!所以我使用实体框架删除一个。因为我知道副本是今天发出的唯一订单,所以我这样做:
var duplicateOrder = myModel.orders.First(x => x.order_date == DateTime.Today);
myModel.orders.Remove(duplicateOrder);
你猜怎么着?我刚刚删除了副本和原件!那是因为我告诉 Entity Framework order_number/cutomer_name 是我的主键。因此,当我告诉它删除 duplicateOrder 时,它在后台执行的操作类似于:
DELETE FROM orders
WHERE order_number = (duplicateOrder's order number)
AND customer_name = (duplicateOrder's customer name)
有了这个警告……你现在应该可以开始了!