我有一个 linq2sql 设置,其中对象从客户端发送(通过 flowinefx 进行弯曲)并将它们附加到新的数据上下文,如下所示:
我还有一个在整个会话过程中使用的“全局”数据上下文。
public static void Update(Enquiry enquiry)
{
OffertaDataContext db = new OffertaDataContext();
db.Enquiries.Attach(enquiry);
db.Refresh(RefreshMode.KeepCurrentValues, enquiry);
db.SubmitChanges();
}
这种方法通常工作正常,但一段时间后我收到错误“无法添加具有已在使用的密钥的实体”。
我收到此错误,这是因为我忘记将数据库中的主键字段设置为“身份规范”(自动增量)。 当我改变这一点时,我很好。 嚯!
我认为如果您将
Attach
一个实体连接到已加载的DataContext
,就会发生此错误。
导致错误的代码与您在此处显示的代码完全相同?创建新的
OffertaDataContext
后,您是否在 Attach
之前查询任何内容?
这可能不是你的问题(我不知道),但这是我的问题,当人们用谷歌搜索这个问题时,它可能会帮助其他人。 如果您没有使用内置的 Linq-to-SQL 设计器或 SQLMetal 工具来生成 Linq-to-SQL 类,或者如果您忘记将 ID 列设置为 IDENTITY,则您的列上可能缺少属性名为“IsDbGenerate”的属性。确保您的列属性如下所示:
<Column(Name:="ID", DbType:="Int NOT NULL IDENTITY", CanBeNull:=False, IsPrimaryKey:=True, IsDbGenerated:=True)>
如果您一次插入多个实体,您可能只是尝试将重复的实体插入到当前数据上下文中。我知道这太简单了,但这只是发生在我自己身上。
您是否尝试在使用 0 键创建 LinqEntities 的情况下一次性添加多个新对象?
过去,当我尝试将项目添加到页面上的表格中,然后当我尝试删除或更新这些项目时,多个项目的键值为 0。所以显然它不知道该怎么做按照我的要求...
这就是我一直在做的来解决这个错误。基本上,您可以根据主键找到该行在数据库中的位置。如果它不存在,那么您将其插入。否则,您从数据库获取版本并更新所有必要的字段。
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();
}
如果您一直更新数据库架构,这可能会变得乏味,因为您必须返回此处更新代码。
在从数据库中选择主键为“BOB”的行后,我遇到了这个问题。 然后我会用
dc.ExecuteCommand("TRUNCATE TABLE ShippingReport");
截断表格并执行 SubmitChanges()
,认为这会删除该字段,并且我可以使用相同的键插入另一个字段,但是在尝试插入时我收到了OP的错误。 只需在第一个 SubmitChanges 之后执行 dc = new DataContext();
即可为我修复它,因为该对象仍然存在于 DataContext 中,这基本上就是 bruno conde 的答案所说的。
我有和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
您不必进行此类检查来确定是否需要使用更新或插入 - 这是 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
即使您的 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();
}
就我而言,它发生在我检索了一个条目,然后尝试使用新条目更新该条目的情况。有人可能会说,这些事情确实发生了。 :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);
}
使用 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