linq2sql:无法添加具有已在使用的键的实体

问题描述 投票:0回答:12

我有一个 linq2sql 设置,其中对象从客户端发送(通过 flowinefx 进行弯曲)并将它们附加到新的数据上下文,如下所示:

我还有一个在整个会话过程中使用的“全局”数据上下文。

    public static void Update(Enquiry enquiry)
    {
        OffertaDataContext db = new OffertaDataContext();


        db.Enquiries.Attach(enquiry);
        db.Refresh(RefreshMode.KeepCurrentValues, enquiry);

        db.SubmitChanges();
    }

这种方法通常工作正常,但一段时间后我收到错误“无法添加具有已在使用的密钥的实体”。

c# linq-to-sql
12个回答
43
投票

我收到此错误,这是因为我忘记将数据库中的主键字段设置为“身份规范”(自动增量)。 当我改变这一点时,我很好。 嚯!


10
投票

我认为如果您将

Attach
一个实体连接到已加载的
DataContext
,就会发生此错误。

导致错误的代码与您在此处显示的代码完全相同?创建新的

OffertaDataContext
后,您是否在
Attach
之前查询任何内容?


5
投票

这可能不是你的问题(我不知道),但这是我的问题,当人们用谷歌搜索这个问题时,它可能会帮助其他人。 如果您没有使用内置的 Linq-to-SQL 设计器或 SQLMetal 工具来生成 Linq-to-SQL 类,或者如果您忘记将 ID 列设置为 IDENTITY,则您的列上可能缺少属性名为“IsDbGenerate”的属性。确保您的列属性如下所示:

<Column(Name:="ID", DbType:="Int NOT NULL IDENTITY", CanBeNull:=False, IsPrimaryKey:=True, IsDbGenerated:=True)>

5
投票

如果您一次插入多个实体,您可能只是尝试将重复的实体插入到当前数据上下文中。我知道这太简单了,但这只是发生在我自己身上。


3
投票

您是否尝试在使用 0 键创建 LinqEntities 的情况下一次性添加多个新对象?

过去,当我尝试将项目添加到页面上的表格中,然后当我尝试删除或更新这些项目时,多个项目的键值为 0。所以显然它不知道该怎么做按照我的要求...


2
投票

这就是我一直在做的来解决这个错误。基本上,您可以根据主键找到该行在数据库中的位置。如果它不存在,那么您将其插入。否则,您从数据库获取版本并更新所有必要的字段。

public static void Update(Enquiry enquiry)
{
    JobsDataContext db = new JobsDataContext();

    var enquiries = from e in db.Enquiries
                    where e.PKID == enquiry.PKID
                    select e;

    if (enquiries.Count() < 1)
    {
        db.Enquiries.InsertOnSubmit(enquiry);
    }
    else
    {
        Enquiry updateEnquiry = enquiries.Single();

        updateEnquiry.LengthMm = enquiry.LengthMm;
        updateEnquiry.ShippedQty = enquiry.ShippedQty;
        updateEnquiry.StatusCode = enquiry.StatusCode;
    }

    db.SubmitChanges();
}

如果您一直更新数据库架构,这可能会变得乏味,因为您必须返回此处更新代码。


1
投票

在从数据库中选择主键为“BOB”的行后,我遇到了这个问题。 然后我会用

dc.ExecuteCommand("TRUNCATE TABLE ShippingReport");
截断表格并执行
SubmitChanges()
,认为这会删除该字段,并且我可以使用相同的键插入另一个字段,但是在尝试插入时我收到了OP的错误。 只需在第一个 SubmitChanges 之后执行
dc = new DataContext();
即可为我修复它,因为该对象仍然存在于 DataContext 中,这基本上就是 bruno conde 的答案所说的。


0
投票

我有和Noah类似的方法,但是我使用存储过程来验证具有该PK的记录是否存在,这样实体就不会加载到上下文中,并且更新代码只涉及两行代码,不会将来当您从表中添加/删除字段时需要更改,只有当表的 PK 发生变化时才需要更改 SP:

bool existe = Convert.ToBoolean(dbc.spSConfigReportesPeriodicos(configReportesPeriodicos.CodigoCliente));

if (existe)
{
    dbc.ConfigReportesPeriodicos.Attach(configReportesPeriodicos);
    dbc.Refresh(RefreshMode.KeepCurrentValues, configReportesPeriodicos);
}
else
{
    dbc.ConfigReportesPeriodicos.InsertOnSubmit(configReportesPeriodicos);
}
dbc.SubmitChanges();

这是存储过程:

ALTER PROCEDURE dbo.spSConfigReportesPeriodicos
(
    @codigoCliente int
)
AS

IF EXISTS(SELECT 1 FROM dbo.ConfigReportesPeriodicos WHERE CodigoCliente = @codigoCliente)
    RETURN 1
ELSE
    RETURN 0

RETURN

0
投票

您不必进行此类检查来确定是否需要使用更新或插入 - 这是 Linq 要做的!

这是我正在从事的一个项目的示例(抱歉它是在 VB.Net 中:)),它演示了如何解决这个问题。

代码尚未优化且相当丑陋 - 但它已经说明了要点。您可以忽略从复选框列表中提取值的部分 - 这只是显示如何更新子实体。

这是 OnUpdating 方法,其中包含更新(这是截断的代码):

Protected Sub LinqDataSource22_Updating(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.LinqDataSourceUpdateEventArgs) Handles LinqDataSource22.Updating

            ' The main entity
            Dim updatedObject As FeedbackDraft = e.NewObject

            updatedObject.Modified = DateTime.Now
            updatedObject.ModifiedBy = UserHelper.GetCurrentUserName

            ' Example: Modify the updated object
            Dim aList As RadioButtonList = FeedbackFormView.FindControl("MyRadioButtonList")
            If aList IsNot Nothing AndAlso Not String.IsNullOrEmpty(aList.SelectedValue) Then
                updatedObject.aProperty = aList.SelectedValue
            End If


            ' Main context - for updating parent entity
            Using ctx As New CustomDataContext()

                ' Example: ... more modification of the main entity
                updatedObject.Status = "Draft"

                ' Deal with child items
                ' Secondary context - for checking against existing data in DB and removing items that have been unselected in the form
                Using ctx2 As New CustomDataContext()

                    ' We need to pull the record from the database to get the full constructed object graph
                    ' This method does a linq query to retrieve the FeedbackDraft object by ID
                    Dim originalObject As FeedbackDraft = GetOriginalFeedbackDraft(ctx2, updatedObject.FeedbackId)

                    ' ... truncated ...

                    ' Loop through CheckBoxList items and updated our entity graph
                    For Each li As ListItem In cbList.Items

                        ' ... code to work with ListItem truncated ...

                        Dim c As New ChildObject()
                        updatedObject.ChildObjects.Add(c)
                        ' Set the child collection to insert - this is using the main context
                        ctx.ChildObjects.InsertOnSubmit(c)


                        ' We can also delete things using the secondary context
                        Dim o as OtherChildObject()
                        o = GetOtherChildObjectById(updatedObject.FeedbackId)
                        ctx2.OtherChildObjects.DeleteOnSubmit(o)
                        ctx2.SubmitChanges()

                    Next
                End Using

                ' You can do further child object updates here...

                ' Now, attach main object for update
                ctx.PartnerFeedbackDrafts.Attach(updatedObject, e.OriginalObject)
                ctx.SubmitChanges()

            End Using

            e.Cancel = True

        End Sub

0
投票

即使您的 TEntity(此处为区域)ID 是标识符列,也请尝试此操作; 就这样,无需对 SP 或型号进行任何更改:

public void InitForm()
{
    bnsEntity.DataSource = CacheManagement.cachedAreas;
    newID = CacheManagement.cachedAreas.LastOrDefault().areaID + 1;
    grdEntity.DataSource = bnsEntity;
}

private void tsbNew_Click(object sender, EventArgs e)
{
    var newArea = new Area();
    newArea.areaID = (byte)newID++;
    dataContext.GetTable<Area>().InsertOnSubmit(newArea);
    bnsEntity.Add(newArea);
    grdEntity.MoveToNewRecord();
}

0
投票

就我而言,它发生在我检索了一个条目,然后尝试使用新条目更新该条目的情况。有人可能会说,这些事情确实发生了。 :p

public void UpdateEntry(Entity entity)
{
    var oldEntry = select ....
    var updatedEntity = new Entity{...}; // mix of entity and oldEntry

    _repository.Update<Entity>(updatedEntity);
}

成为

public void UpdateEntry(Entity entity)
{
    var oldEntry = select ....

    oldEntry.CreationDate = entity.CreationDate {...}

    _repository.Update<Entity>(oldEntry);
}

0
投票

使用 DuplicateKeyException 类添加 try/catch。

然后输出错误信息:

Debug.WriteLine("重复键 {0}", dke.Object);

感谢 Björn Lindqvist 在此发帖:

http://www.bjornlindqvist.se/blog/2012/12/the-cannot-add-entity-with-key-that-is

© www.soinside.com 2019 - 2024. All rights reserved.