Linq2sql: невозможно добавить объект с уже существующим ключом
У меня есть установка linq2sql, где объекты отправляются с клиентской стороны (flex через flourinefx) и присоединяют их к новому datacontext, показанному ниже:
У меня также есть "глобальный" datacontext, который используется на протяжении всего сеанса.
public static void Update(Enquiry enquiry)
{
OffertaDataContext db = new OffertaDataContext();
db.Enquiries.Attach(enquiry);
db.Refresh(RefreshMode.KeepCurrentValues, enquiry);
db.SubmitChanges();
}
Этот подход обычно работает нормально, но через некоторое время я получаю сообщение об ошибке "Невозможно добавить объект с ключом, который уже используется".
Ответы
Ответ 1
Я думаю, что эта ошибка возникает, если вы Attach
сущность к DataContext
, которая уже была загружена.
Код, вызывающий ошибку, точно так же, как вы здесь показываете? После создания нового OffertaDataContext
вы запрашиваете что-либо перед Attach
?
Ответ 2
Я получал эту ошибку, и это произошло потому, что я забыл установить в поле "Основной ключ" базу данных "Спецификация идентификатора" (автоинкремент). Когда я изменил это, я был хорош. Doh!
Ответ 3
Возможно, это не ваша проблема (я не могу сказать), но она была моей, и, как люди google, это могло помочь кому-то другому. Если вы не используете встроенный конструктор Linq-to-SQL или материал SQLMetal для генерации ваших классов Linq-to-SQL, или если вы забыли сделать свой столбец ID IDENTITY, вы можете потерять свойство в своей колонке атрибут "IsDbGenerated". Убедитесь, что атрибут столбца выглядит примерно так:
<Column(Name:="ID", DbType:="Int NOT NULL IDENTITY", CanBeNull:=False, IsPrimaryKey:=True, IsDbGenerated:=True)>
Ответ 4
Вы пытаетесь добавить несколько новых объектов в один хит, где LinqEntities создаются с помощью клавиши 0?
У меня была эта проблема в прошлом, когда я пытался добавить элементы в таблицу на моей странице, а затем, когда я попытался удалить или обновить эти элементы, у кратных будет ключ 0. Так что очевидно, что он не знал, что делать с моими запросами...
Ответ 5
Если вы вставляете сразу несколько объектов, возможно, вы просто пытаетесь вставить дублирующийся объект в текущий datacontext. Я знаю, что это слишком просто, но это случилось с самим собой.
Ответ 6
Это то, что я делал, чтобы обойти эту ошибку. В основном вы находите, где эта строка находится в базе данных на основе первичного ключа. Если он не существует, вы вставляете его. В противном случае вы получите версию из базы данных и обновите все необходимые поля.
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();
}
Это может стать утомительным, если вы постоянно обновляете свою схему базы данных, так как вам нужно вернуться сюда, чтобы обновить код.
Ответ 7
попробуйте это, даже если ваш идентификатор TEntity (здесь Area) - столбец Identifier;
Только это, без каких-либо изменений в вашей 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();
}
Ответ 8
У меня есть аналогичный подход к Noah's, но я использую хранимую процедуру, чтобы проверить, существует ли запись с этим PK, таким образом, объект не загружается в контексте, а код обновления включает только две строки кода и не потребует изменений в будущем при добавлении/удалении полей из таблицы, потребуется изменить SP только в случае изменения PK таблицы:
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
Ответ 9
Вам не нужно делать такую проверку, чтобы увидеть, нужно ли вам использовать Updates или Inserts - для Linq для выполнения!
Вот пример из проекта, над которым я работаю (извините его в VB.Net:)), который демонстрирует, как это решить.
Код еще не оптимизирован и довольно уродлив, но он имеет смысл. Вы можете игнорировать бит, где он извлекает значения из checkboxlist - это просто показывает, как вы можете обновлять дочерние сущности.
Здесь используется метод 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
Ответ 10
У меня возникла эта проблема после выбора строки из базы данных с помощью первичного ключа, например "BOB". Затем я усекаю таблицу с помощью dc.ExecuteCommand("TRUNCATE TABLE ShippingReport");
и делаю SubmitChanges()
, думая, что это избавится от поля, и я смогу вставить другой с тем же ключом, но при попытке вставить ошибку при ошибке. Просто нужно было сделать dc = new DataContext();
после первого SubmitChanges и это исправило его для меня, поскольку этот объект все еще существовал в DataContext, что в основном говорит ответ bruno conde.
Ответ 11
В моем случае это произошло в ситуации, когда я получил запись, а затем попытался обновить запись новой записью. Мозг может сказать, что все это происходит.: Р
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);
}