我使用实体框架1与。net 3.5。

我做的事情很简单,就像这样:

var roomDetails = context.Rooms.ToList();

foreach (var room in roomDetails)
{        
   room.LastUpdated = DateTime.Now;
}

当我尝试这样做时,我得到这个错误:

 context.SaveChanges();

我得到了错误:

无法更新EntitySet -因为它有一个DefiningQuery,并且<ModificationFunctionMapping>元素中没有<UpdateFunction>元素来支持当前操作。

我在上下文上做了很多更新,没有任何问题,只有当我试图更新这个特定的实体时。

我所有的搜索结果都是一样的,我要更新的实体上没有声明主键。但是,唉,我有一个主键声明…


当前回答

设置主键,然后保存表和刷新,然后去模型。edmx删除表,重新获取。

其他回答

更新:最近我得到了一些赞,所以我想让人们知道我下面给出的建议并不是最好的。自从我最初开始在旧的无键数据库上做实体框架,我已经意识到,到目前为止,你能做的最好的事情是先反向编码。有一些关于如何做到这一点的好文章。只需遵循它们,然后当您想要向它添加一个键时,使用数据注释来“伪造”键。

例如,假设我知道我的Orders表,虽然它没有主键,但可以保证每个客户只有一个订单号。由于这是表上的前两列,我将代码第一个类设置为如下所示:

    [Key, Column(Order = 0)]
    public Int32? OrderNumber { get; set; }

    [Key, Column(Order = 1)]
    public String Customer { get; set; }

通过这样做,您基本上伪造了EF,使其相信存在一个由OrderNumber和Customer组成的聚集键。这将允许您在无键表上进行插入、更新等操作。

如果你不太熟悉反向代码优先,去找一个关于实体框架代码优先的好教程。然后在“反向编码优先”(即使用现有数据库进行“编码优先”)上找到一个。然后回到这里,再看看我的主要建议。:)

最初的回答:

首先:正如其他人所说,最好的选择是向表添加一个主键。句号。如果你能做到这一点,就不要再读下去了。

但如果你做不到,或者讨厌自己,也有一种不用主键的方法。

在我的例子中,我使用的是遗留系统(最初是AS400上的平面文件移植到Access,然后移植到T-SQL)。所以我必须想办法。这就是我的解。我使用Entity Framework 6.0(在撰写本文时,NuGet的最新版本)完成了以下工作。

Right-click on your .edmx file in the Solution Explorer. Choose "Open With..." and then select "XML (Text) Editor". We're going to be hand-editing the auto-generated code here. Look for a line like this: <EntitySet Name="table_name" EntityType="MyModel.Store.table_name" store:Type="Tables" store:Schema="dbo" store:Name="table_nane"> Remove store:Name="table_name" from the end. Change store:Schema="whatever" to Schema="whatever" Look below that line and find the <DefiningQuery> tag. It will have a big ol' select statement in it. Remove the tag and it's contents. Now your line should look something like this: <EntitySet Name="table_name" EntityType="MyModel.Store.table_name" store:Type="Tables" Schema="dbo" /> We have something else to change. Go through your file and find this: <EntityType Name="table_name"> Nearby you'll probably see some commented text warning you that it didn't have a primary key identified, so the key has been inferred and the definition is a read-only table/view. You can leave it or delete it. I deleted it. Below is the <Key> tag. This is what Entity Framework is going to use to do insert/update/deletes. SO MAKE SURE YOU DO THIS RIGHT. The property (or properties) in that tag need to indicate a uniquely identifiable row. For instance, let's say I know my table orders, while it doesn't have a primary key, is assured to only ever have one order number per customer.

我的是这样的:

<EntityType Name="table_name">
              <Key>
                <PropertyRef Name="order_numbers" />
                <PropertyRef Name="customer_name" />
              </Key>

说真的,别做错了。让我们假设,尽管不应该有重复的数据,但由于某种原因,有两行数据进入了我的系统,它们具有相同的订单号和客户名称。Whooops !这就是我不用钥匙的后果!所以我使用实体框架删除一个。因为我知道副本是今天唯一的订单,我这样做:

var duplicateOrder = myModel.orders.First(x => x.order_date == DateTime.Today);
myModel.orders.Remove(duplicateOrder);

你猜怎么着?我刚刚删除了副本和原件!这是因为我告诉实体框架order_number/ customer_name是我的主键。所以当我告诉它删除duplicateOrder时,它在后台做的事情是这样的:

DELETE FROM orders
WHERE order_number = (duplicateOrder's order number)
AND customer_name = (duplicateOrder's customer name)

有了这个警告……您现在应该可以开始了!

I found the original answer of updating the .edmx file work best in my situation. I just wasn't too happy about altering the model every time it was updated from the database. That's why I wrote an additional Text Template file, that is automaticaly invoked when after the model has changed - just like the entities are newly generated. I post it here in this comment. To make it work, make sure you name it like {model name}.something.tt, and store it in the same folder as your .edmx folder. I named it {model name}.NonPkTables.tt. It does not generate a file on its own due to the invalid file extension definition in the second line. Feel free to use.

<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ output extension="/" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Data" #>
<#@ assembly name="System.Windows.Forms" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq"#>
<#@ assembly name="%VS120COMNTOOLS%..\IDE\EntityFramework.dll" #>
<#@ assembly name="%VS120COMNTOOLS%..\IDE\Microsoft.Data.Entity.Design.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Windows.Forms" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Xml.Linq" #>
<#@ import namespace="System.Globalization" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Data.Entity.Core.Metadata.Edm" #>
<#@ import namespace="System.Data.Entity.Core.Mapping" #>
<#@ import namespace="System.CodeDom" #>
<#@ import namespace="System.CodeDom.Compiler" #>
<#@ import namespace="Microsoft.CSharp"#>
<#@ import namespace="System.Text"#>
<#@ import namespace="System.Diagnostics" #>

<#
    string modelFileName= this.Host.TemplateFile.Split('.')[0] + ".edmx";
    string edmxPath = this.Host.ResolvePath( modelFileName );

    // MessageBox.Show( this.Host.TemplateFile + " applied." );
    var modelDoc = XDocument.Load(edmxPath);
    var root = modelDoc.Root;
    XNamespace nsEdmx = @"http://schemas.microsoft.com/ado/2009/11/edmx";
    XNamespace ns = @"http://schemas.microsoft.com/ado/2009/11/edm/ssdl";

    var runtime = root.Elements(nsEdmx + "Runtime").First();
    var storageModels = runtime.Elements(nsEdmx + "StorageModels").First();
    XNamespace nsStore = @"http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator";

    var schema = storageModels.Elements(ns + "Schema").First();
    XNamespace nsCustomAnnotation = @"http://schemas.microsoft.com/ado/2013/11/edm/customannotation";

    var entityTypes = schema.Nodes().OfType<XComment>().Where(c => c.Value.Contains("warning 6002: The table/view"));
    bool changed = false;

    foreach (var node in entityTypes)
    {
        var element = node.ElementsAfterSelf().First();
        string entityName = element.Attribute("Name").Value;

        // Find EntitySet in EntityContainer.
        var entityContainer = schema.Elements(ns + "EntityContainer").First();
        var entitySet = entityContainer.Elements(ns + "EntitySet").First(s => s.Attribute("Name").Value == entityName);

        // Change "store:Schema" attribute to "Schema" attribute.
        var attribute = entitySet.Attribute(nsStore + "Schema");

        if (attribute != null)
        {
            string schemaName = entitySet.Attribute(nsStore + "Schema").Value;
            entitySet.Attribute(nsStore + "Schema").Remove();
            entitySet.Add(new XAttribute("Schema", schemaName));
            changed |= true;
        }

        // Remove the DefiningQuery element.
        var definingQuery = entitySet.Element(ns + "DefiningQuery");

        if (definingQuery != null)
        {
            definingQuery.Remove();
            changed |= true;        
            Debug.WriteLine(string.Format("Removed defining query of EntitySet {0}.", entityName));
        }
    }

    if (changed)
        modelDoc.Save(edmxPath);
#>

这不是一个新的答案,但将帮助那些不知道如何设置表的主键的人。在一个新的查询中使用它并运行。这将把UniqueID列设置为主键。

USE [YourDatabaseName]
GO

Alter table  [dbo].[YourTableNname]
Add Constraint PK_YourTableName_UniqueID Primary Key Clustered (UniqueID);
GO

在我的情况下,忘记定义表的主键。因此,像图中所示的那样分配,并从。edmx文件的“Update model from Database”中刷新你的表。 希望能有所帮助!!

只需向表中添加一个主键。就是这样。问题解决了。

ALTER TABLE <TABLE_NAME>
ADD CONSTRAINT <CONSTRAINT_NAME> PRIMARY KEY(<COLUMN_NAME>)