无法更新EntitySet'InstanceObjectName',因为它具有DefiningQuery,并且元素中不存在支持当前操作的元素
答案 0 :(得分:25)
确保您的表有主键!
答案 1 :(得分:4)
实体框架不知道给定视图是否可更新,因此它添加了元素以防止框架尝试针对不可更新的视图生成查询。
如果您的视图是可更新的,您只需从.edmx的StorageModel部分中的视图的EntitySet定义中删除该元素,并且正常的更新处理将与任何其他表一样工作。
如果您的视图不可更新,则必须通过“修改函数映射”自行提供更新逻辑。修改函数映射调用.edmx的StorageModel部分中定义的函数。该函数可能包含数据库中存储过程的名称和参数,或者您可以使用“定义命令”直接在.edmx的StorageModel部分的函数定义中编写插入,更新或删除语句。
在这两个选项中,如果您的视图是可更新的(听起来可能是这样),最简单的当然是删除设计者插入的视图。
答案 2 :(得分:3)
更新:我最近得到了一些赞成票,所以我想我会让人们知道我给出的建议不是最好的。由于我最初开始在旧的无钥匙数据库上做实体框架,我已经意识到你可以做的最好的事情是通过反向代码优先做到这一点。关于如何做到这一点,有一些很好的文章。只需按照它们,然后当您想要添加密钥时,使用数据注释来“伪造”密钥。
例如,假设我知道我的表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>
标记。这是实体框架将用于执行插入/更新/删除的内容。所以请你确实这样做。该标记中的属性(或属性)需要指示唯一可识别的行。例如,假设我知道我的表orders
,虽然它没有主键,但确保每个客户只有一个订单号。
所以我看起来像:
<EntityType Name="table_name">
<Key>
<PropertyRef Name="order_numbers" />
<PropertyRef Name="customer_name" />
</Key>
说真的,不要做错了。让我们说即使不应该有重复,不知何故,两行进入我的系统,具有相同的订单号和客户名称。 Whooops!这就是我没有使用钥匙的原因!所以我使用Entity Framework删除一个。因为我知道副本是今天唯一的订单,所以我这样做:
var duplicateOrder = myModel.orders.First(x => x.order_date == DateTime.Today);
myModel.orders.Remove(duplicateOrder);
猜猜是什么?我刚刚删除了副本和原始版本!那是因为我告诉实体框架,order_number / cutomer_name是我的主键。所以当我告诉它删除duplicateOrder时,它在后台做的是:
DELETE FROM orders
WHERE order_number = (duplicateOrder's order number)
AND customer_name = (duplicateOrder's customer name)
有了这个警告......你现在应该好好去!