Я использую Entity Framework для заполнения элемента управления сеткой. Иногда, когда я делаю обновления, я получаю следующую ошибку:
Заявление об обновлении, вставке или удалении повлияло на неожиданное количество строк (0). Объекты могут быть изменены или удалены, поскольку объекты загружены. Обновить записи ObjectStateManager.
Я не могу понять, как воспроизвести это. Но это может иметь какое-то отношение к тому, как близко я делаю обновления. Кто-нибудь видел это или кто-нибудь знает, что означает сообщение об ошибке?
Изменить: К сожалению, я больше не могу воспроизвести проблему, с которой я столкнулся, потому что я отступил от этого проекта и не помню, нашел ли я в конце концов решение, если другой разработчик исправил его, или если я работал вокруг него. Поэтому я не могу принять никаких ответов.
Это побочный эффект функции, называемой оптимистичной concurrency.
Не 100% уверен, как включить или отключить его в Entity Framework, но в основном то, что он говорит вам, заключается в том, что между вами, когда вы извлекаете данные из базы данных, и когда вы сохранили изменения, кто-то другой изменил данные (что означало когда вы пошли, чтобы сохранить его, 0 строк действительно обновились). В терминах SQL их предложение update
query where
содержит исходное значение каждого поля в строке, и если 0 строк затронуты, он знает, что что-то пошло не так.
Идея заключается в том, что вы не закончите перезаписывать изменения, которые не было известно вашему приложению, - это в основном небольшая мера безопасности, которая была внесена .NET во все ваши обновления.
Если это согласовано, вероятность того, что это происходит в вашей собственной логике (EG: вы фактически обновляете данные самостоятельно в другом методе между выбором и обновлением), но это может быть просто условие гонки между двумя приложениями.
Я столкнулся с этим, и это было вызвано тем, что поле идентификатора объекта (ключа) не было установлено. Таким образом, когда контекст пошел на сохранение данных, он не смог найти идентификатор = 0. Обязательно поместите точку останова в свой оператор обновления и убедитесь, что идентификатор объекта был установлен.
От комментария Пола Беллоры
У меня была эта точная проблема, вызванная забыванием включить скрытый идентификатор ввод на странице редактирования .cshtml
Ничего себе, много ответов, но я получил эту ошибку, когда сделал что-то немного другое, о чем не упоминал нигде.
Короче говоря, если вы создадите новый объект и сообщите EF, что его изменение было изменено с помощью EntityState.Modified
, тогда он будет выкидывать эту ошибку, поскольку она еще не существует в базе данных. Вот мой код:
MyObject foo = new MyObject()
{
someAttribute = someValue
};
context.Entry(foo).State = EntityState.Modified;
context.SaveChanges();
Да, это кажется глупым, но оно возникло из-за того, что метод, о котором идет речь, имел foo
, переданный ему, который был создан ранее, теперь он передал ему только someValue
и сам создал foo
.
Простое исправление, просто измените EntityState.Modified
на EntityState.Added
или измените всю эту строку на:
context.MyObject.Add(foo);
Я столкнулся с такой же пугающей ошибкой...:) Тогда я понял, что забыл установить
@Html.HiddenFor(model => model.UserProfile.UserId)
для первичного ключа обновляемого объекта! Я стараюсь забыть простую, но очень важную вещь!
Кстати: HiddenFor
для ASP.NET MVC.
UserId
в форме, очень подверженной хакерам ... это должно быть заполнено впоследствии из HttpContext.Current.User.Identity.Name
Проверьте, не забыл ли вы атрибут "DataKeyNames" в GridView. это необходимо при изменении данных в GridView
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.datakeynames.aspx
Проблема вызвана одной из двух вещей: -
Concurrency Mode: Fixed
.., а Оптимистичный Concurrency не позволил сохранить данные. То есть. некоторые изменили данные строки между временем, когда вы получили данные сервера, и когда вы сохранили данные своего сервера.StoreGeneratedPattern = Computed
), и эта строка не существует.У меня была та же проблема, я выяснил, что это было вызвано RowVersion, которая была нулевой. Убедитесь, что Идентификатор и RowVersion не нуль.
для получения дополнительной информации см. этот учебник
У меня была такая же проблема, и @webtrifusion ответ помог найти решение.
Моя модель использовала атрибут Bind(Exclude)
в идентификаторе объекта, который вызывал значение идентификатора объекта в HttpPost.
namespace OrderUp.Models
{
[Bind(Exclude = "OrderID")]
public class Order
{
[ScaffoldColumn(false)]
public int OrderID { get; set; }
[ScaffoldColumn(false)]
public System.DateTime OrderDate { get; set; }
[Required(ErrorMessage = "Name is required")]
public string Username { get; set; }
}
}
Во время редактирования включается идентификатор или первичный ключ объекта как скрытое поле в представлении
т
@Html.HiddenFor(m => m.Id)
который решает проблему.
Кроме того, если ваша модель включает неиспользуемый элемент, включите это и отправьте это на контроллер
Вам нужно явно включить BoundField первичного ключа. Если вы не хотите, чтобы пользователь видел первичный ключ, вам нужно скрыть его с помощью css:
<asp:BoundField DataField="Id_primary_key" ItemStyle-CssClass="hidden"
HeaderStyle-CssClass="hidden" />
Где "hidden" - это класс в css, для которого установлено значение "none".
Я также столкнулся с этой ошибкой. Проблема, которая, как оказалось, была вызвана триггером на таблице, которую я пытался сохранить. В Trigger используется "INSTEAD OF INSERT", что означает, что в эту таблицу вставлены 0 строк, следовательно, ошибка. К счастью, в случае, когда функция триггера была неправильной, но я предполагаю, что это может быть действительная операция, которая должна каким-то образом обрабатываться в коде. Надеюсь, это поможет кому-то в один прекрасный день.
SELECT SCOPE_IDENTITY() as MyViewId
Я получил ту же ошибку, потому что часть PK была столбцом datetime, а вставляемая запись использовала DateTime.Now как значение для этого столбца. Структура Entity будет вставлять значение с точностью до миллисекунды, а затем искать значение, которое он только вставил, также с точностью до миллисекунды. Однако SqlServer округлил значение до второй точности, и, таким образом, инфраструктура сущности не смогла найти значение точности в миллисекундах.
Решением было усечение миллисекунд из DateTime.Now перед вставкой.
Date
со значением DateTime
@Html.HiddenFor(model => model.RowVersion)
Моя rowversion была нулевой, поэтому пришлось добавить это в представление который решил мою проблему
Просто убедитесь, что таблица и форма имеют первичный ключ и edmx.
я обнаружил, что любые ошибки во время обновления обычно происходили из-за:
- Нет первичного ключа в таблице
- Нет первичного ключа в Редактировании вида/формы (например, @Html.HiddenFor(m=>m.Id
)
У меня была та же проблема. В моем случае я пытался обновить первичный ключ, который не разрешен.
Я получил эту ошибку, когда я удалял некоторые строки в БД (в цикле) и добавлял новые в ту же таблицу.
Решения для меня заключались в том, чтобы динамически создавать новый контекст в каждой итерации цикла
Я начал получать эту ошибку после перехода от первой модели к кодовой. У меня есть несколько потоков, обновляющих базу данных, где некоторые могут обновлять одну и ту же строку. Я не знаю, почему у меня не было проблемы с использованием model-first, предположим, что он использует другой concurrency по умолчанию.
Чтобы обрабатывать его в одном месте, зная условия, при которых это могло произойти, я добавил следующую перегрузку в класс DbContext:
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
public class MyDbContext: DbContext {
...
public int SaveChanges(bool refreshOnConcurrencyException, RefreshMode refreshMode = RefreshMode.ClientWins) {
try {
return SaveChanges();
}
catch (DbUpdateConcurrencyException ex) {
foreach (DbEntityEntry entry in ex.Entries) {
if (refreshMode == RefreshMode.ClientWins)
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
else
entry.Reload();
}
return SaveChanges();
}
}
}
Затем называется SaveChanges(true)
, где это применимо.
Ну, у меня такая же проблема. Но это было связано с моей собственной ошибкой. Фактически я сохранял объект вместо его добавления. Итак, это был конфликт.
Одним из способов отладки этой проблемы в среде Sql Server является использование Sql Profiler, включенного в вашу копию SqlServer, или, если вы используете экспресс-версию, получите копию Express Profiler для свободного доступа из CodePlex по следующей ссылке ниже
Используя Sql Profiler, вы можете получить доступ ко всему, что отправляется EF в БД. В моем случае это составляло:
exec sp_executesql N'UPDATE [dbo].[Category]
SET [ParentID] = @0, [1048] = NULL, [1033] = @1, [MemberID] = @2, [AddedOn] = @3
WHERE ([CategoryID] = @4)
',N'@0 uniqueidentifier,@1 nvarchar(50),@2 uniqueidentifier,@3 datetime2(7),@4 uniqueidentifier',
@0='E060F2CA-433A-46A7-86BD-80CD165F5023',@1=N'I-Like-Noodles-Do-You',@2='EEDF2C83-2123-4B1C-BF8D-BE2D2FA26D09',
@3='2014-01-29 15:30:27.0435565',@4='3410FD1E-1C76-4D71-B08E-73849838F778'
go
Я скопировал это в окно запроса на Sql Server и выполнил его. Разумеется, хотя он и запущен, на этот запрос повлияло 0 записей, поэтому ошибка возвращается EF.
В моем случае проблема была вызвана CategoryID.
Не было идентификатора категории, идентифицированного идентификатором EF, отправленного в базу данных, поэтому пострадали 0 записей.
Это был не EF-недостаток, а скорее неправильный коллапс "??" выражение в диспетчере представлений, который посылал глупость до уровня данных.
Это также произойдет, если вы пытаетесь вставить в ситуацию с уникальным ограничением, т.е. если у вас может быть только один тип адреса для каждого работодателя, и вы пытаетесь вставить вторую часть того же типа с тем же работодателем, вы получите та же проблема.
ИЛИ
Это также может произойти, если все назначенные свойства объекта были назначены с теми же значениями, что и раньше.
using(var db = new MyContext())
{
var address = db.Addresses.FirstOrDefault(x => x.Id == Id);
address.StreetAddress = StreetAddress; // if you are assigning
address.City = City; // all of the same values
address.State = State; // as they are
address.ZipCode = ZipCode; // in the database
db.SaveChanges(); // Then this will throw that exception
}
Если вы пытаетесь создать сопоставление в вашем файле edmx в "функции Imports", это может привести к этой ошибке. Просто очистите поля для вставки, обновления и удаления, которые находятся в Картографических данных для данного объекта в вашем edmx, и он должен работать. Надеюсь, я дал понять.
Когда принятый ответ сказал ", это не приведет к перезаписыванию изменений, которые ваше приложение не знало, произошло", я был скептичен, потому что мой объект был недавно создан. Но потом оказывается, что к таблице добавлен INSTEAD OF UPDATE, INSERT- TRIGGER
, который обновлял вычисленный столбец той же таблицы.
Как только я изменил это на AFTER INSERT, UPDATE
, он работал нормально.
Ни один из вышеперечисленных ответов не полностью охватил мою ситуацию и решение проблемы.
Код, в котором ошибка была указана в контроллере MVC5:
if (ModelState.IsValid)
{
db.Entry(object).State = EntityState.Modified;
db.SaveChanges(); // line that threw exception
return RedirectToAction("Index");
}
Я получил это исключение, когда я сохранял объект в режиме редактирования. Причина, по которой это бросила, состояла в том, что, когда я вернулся, чтобы сохранить его, я изменил свойства, которые сформировали первичный ключ на объекте. Таким образом, установка его состояния в Modified не имела никакого смысла для EF - это была новая запись, а не ранее сохраненная.
Вы можете решить эту проблему либо A), чтобы изменить вызов сохранения, либо добавить объект, либо B) просто не изменяйте первичный ключ при редактировании. Я сделал B).
У вас была такая же проблема.
Я использую EF 6, Code First + Migrations. Проблема заключалась в том, что наш администратор баз данных создал ограничение на таблицу, которая запустила ошибку.
public void Save(object entity)
{
using (var transaction = Connection.BeginTransaction())
{
try
{
SaveChanges();
transaction.Commit();
}
catch (OptimisticConcurrencyException)
{
if (ObjectStateManager.GetObjectStateEntry(entity).State == EntityState.Deleted || ObjectStateManager.GetObjectStateEntry(entity).State == EntityState.Modified)
this.Refresh(RefreshMode.StoreWins, entity);
else if (ObjectStateManager.GetObjectStateEntry(entity).State == EntityState.Added)
Detach(entity);
AcceptAllChanges();
transaction.Commit();
}
}
}
Я столкнулся с этим с помощью Telerik RadGrid. У меня был первичный ключ как столбец с сеткой, который был установлен только для чтения. Это будет нормально работать, если столбец был отображен = "false", но readonly = "true" вызвало проблему. Я решил это, указав отображение столбца сетки = false и добавив отдельный столбец шаблона для отображения
<telerik:GridBoundColumn HeaderText="Shouldnt see" Display="false"
UniqueName="Id" DataField="Id">
</telerik:GridBoundColumn>
<telerik:GridTemplateColumn HeaderText="Id" UniqueName="IdDisplay">
<ItemTemplate>
<asp:Label ID="IDLabel" runat="server"
Text='<%# Eval("Id") %>'></asp:Label>
</ItemTemplate>
</telerik:GridTemplateColumn>
Это случилось со мной. Я запускал Aurora (AWS MySQL) и пытался добавлять записи в таблицу. Поле, отмеченное [Key] в модели, было сопоставлено с автоматически увеличивающимся полем в таблице... или так я думал. Он был установлен как первичный ключ, но он не был настроен на автоматическое увеличение. Поэтому, чтобы настроить автоматическое увеличение, я исправил свою проблему.
Это случилось со мной из-за несоответствия между datetime и datetime2. Как ни странно, он работал отлично до того, как тестер обнаружил проблему. Моя первая модель кода включала DateTime как часть первичного ключа:
[Key, Column(Order = 2)]
public DateTime PurchasedDate { get; set; } = (DateTime)SqlDateTime.MinValue;
Сгенерированный столбец является столбцом datetime. При вызове SaveChanges EF генерирует следующий SQL:
-- Region Parameters
DECLARE @0 Int = 2
DECLARE @1 Int = 25
DECLARE @2 Int = 141051
DECLARE @3 DateTime2 = '2017-07-27 15:16:09.2630000' --(will not equal a datetime value)
-- EndRegion
UPDATE [dbo].[OrganizationSurvey]
SET [OrganizationSurveyStatusId] = @0
WHERE ((([SurveyID] = @1) AND ([OrganizationID] = @2)) AND ([PurchasedDate] = @3))
Поскольку он пытался сопоставить столбец datetime с значением datetime2, он не возвращал результатов. Единственное решение, о котором я подумал, это изменить столбец на datetime2:
[Key, Column(Order = 2, TypeName = "DateTime2")]
public DateTime PurchasedDate { get; set; } = (DateTime)SqlDateTime.MinValue;
datetime
и datetime2
/ datetime
datetime2
. По существу, некоторые значения в миллисекундах будут соответствовать друг другу, другие - нет. То же самое случилось со мной, и я также переключился на DateTime2
.
У меня была проблема с идентификатором Mvc при регистрации нового пользователя, а не:
var result = await UserManager.CreateAsync(user);
Я делал:
var result = await UserManager.UpdateAsync(user);
мы забыли упоминать "enctype" размещение "многочастных" данных формы. Один из моих других сценариев, с которыми я сейчас сталкиваюсь...
Аналогичная проблема возникает при удалении элемента из таблицы (ParentTable), на который ссылались другие внешние ключи таблицы с правилом ON DELETE CASCADE (RefTable1, RefTable2). Проблема возникает из-за "ПОСЛЕ УДАЛЕНИЯ" триггера в одной из ссылочных таблиц (RefTable1). Этот триггер удалял связанную запись из ParentTable в результате, также была удалена запись RefTable2. Похоже, что Entity Framework, в то время как внутри-код был явно установлен для удаления записи ParentTable, удалял связанную запись из RefTable1, а затем записывал из RefTable2 после последней операции, это исключение было выбрано, потому что триггер уже удалил запись из ParentTable, которая в результате удалила запись RefTable2.
У меня есть это исключение, и я установил столбец id
в качестве автоматического увеличения в моем database table
, а затем он отлично работает
Для тех, кто использует AutoMapper Если вы обновляете сущность, которая имеет внешние ключи для другого объекта (или сущностей), убедитесь, что все внешние объекты имеют свой первичный ключ, установленный в базу данных, сгенерированную (или автоматически увеличивающуюся для MySQL).
Например:
public class BuyerEntity
{
[Key]
public int BuyerId{ get; set; }
public int Cash { get; set; }
public List<VehicleEntity> Vehicles { get; set; }
public List<PropertyEntity> Properties { get; set; }
}
Транспортные средства и свойства хранятся в других таблицах, кроме покупателей. Когда вы добавляете нового покупателя, AutoMapper и EF автоматически обновляют таблицы "Транспортные средства" и "Свойства", поэтому, если у вас нет автоматического инкремента, установленного в любой из этих таблиц (например, я этого не сделал), тогда вы увидите ошибку вопрос ОП.
В последнее время я пытаюсь обновить EF5 до примера EF6. Таблица примерного проекта имеет десятичные (5,2) столбцы типа. Миграция базы данных успешно завершена. Но исходное исключение семенного семени.
Модель:
public partial class Weather
{
...
public decimal TMax {get;set;}
public decimal TMin {get;set;}
...
}
Неверная конфигурация:
public partial class WeatherMap : EntityTypeConfiguration<Weather>
{
public WeatherMap()
{
...
this.Property(t => t.TMax).HasColumnName("TMax");
this.Property(t => t.TMin).HasColumnName("TMin");
...
}
}
Данные:
internal static Weather[] data = new Weather[365]
{
new Weather() {...,TMax = 3.30M,TMin = -12.00M,...},
new Weather() {...,TMax = 5.20M,TMin = -10.00M,...},
new Weather() {...,TMax = 3.20M,TMin = -8.00M,...},
new Weather() {...,TMax = 11.00M,TMin = -7.00M,...},
new Weather() {...,TMax = 9.00M,TMin = 0.00M,...},
};
Я нашел проблему, данные о семени имеют значения точности, но в конфигурации нет параметров точности и масштаба. TMax и TMin, определяемые десятичной (10,0) в таблице образцов.
Правильная конфигурация:
public partial class WeatherMap : EntityTypeConfiguration<Weather>
{
public WeatherMap()
{
...
this.Property(t => t.TMax).HasPrecision(5,2).HasColumnName("TMax");
this.Property(t => t.TMin).HasPrecision(5,2).HasColumnName("TMin");
...
}
}
Мой пример проекта выполняется с помощью: MySql 5.6.14, Devart.Data.MySql, MVC4,.Net 4.5.1, EF6.01
С уважением.
Это может произойти, если вы пытаетесь обновить запись с идентификатором, который не существует в базе данных.
У меня возникла эта проблема, когда я случайно попытался обновить объект вместо сохранения!
У меня был
if (IsNewSchema(model))
unitOfWork.SchemaRepository.Update(schema);
else
unitOfWork.SchemaRepository.Insert(schema);
когда я должен был
if (IsNewSchema(model))
unitOfWork.SchemaRepository.Insert(schema);
else
unitOfWork.SchemaRepository.Update(schema);
У меня также была эта ошибка. Существуют ситуации, когда объект может не знать о фактическом контексте базы данных, который вы используете, или Модель может отличаться. Для этого установите: EntityState.Modified; в EntityState.Added;
Для этого:
if (ModelState.IsValid)
{
context.Entry(yourModelReference).State = EntityState.Added;
context.SaveChanges();
}
Это гарантирует, что Entity знает, что вы используете или добавляете состояние, с которым вы работаете. В этот момент необходимо установить все правильные значения модели. Осторожно, чтобы не потерять никаких изменений, которые могли быть сделаны в фоновом режиме.
Надеюсь, что это поможет.
Я получил это исключение при присоединении объекта, которого не было в базе данных. Я предположил, что объект загружен из отдельного контекста, но если он впервые посетил сайт, объект был создан с нуля. У нас есть автоматически увеличивающиеся первичные ключи, поэтому я мог бы заменить
context.Users.Attach(orderer);
с
if (orderer.Id > 0) {
context.Users.Attach(orderer);
}
У меня была аналогичная проблема сегодня, я опишу ее здесь, так как это не совсем оптимистичная ошибка concurrency.
Я конвертирую старую систему в новую базу данных, и у нее есть несколько тысяч объектов, к которым я должен был script перейти в новую систему. Тем не менее, чтобы помочь с здравомыслием, я решил сохранить оригинальные уникальные идентификаторы и поэтому ввел это в новый объект, а затем попытался сохранить его.
Проблема заключалась в том, что я использовал MVC Scaffolding для создания базовых репозиториев, и у них есть шаблон в их методе UpdateOrInsert, который в основном проверяет, установлен ли атрибут Key перед тем, как он добавит новый объект или изменит его состояние изменено.
Поскольку Guid был установлен, он пытался изменить строку, которая на самом деле не существовала в базе данных.
Я надеюсь, что это поможет кому-то еще!
Я выброшу это на случай, если кто-то столкнется с этой проблемой при работе в параллельном цикле:
Parallel.ForEach(query, deet =>
{
MyContext ctx = new MyContext();
//do some stuff with this to identify something
if(something)
{
//Do stuff
ctx.MyObjects.Add(myObject);
ctx.SaveChanges() //this is where my error was being thrown
}
else
{
//same stuff, just an update rather than add
}
}
Я изменил его на следующее:
Parallel.ForEach(query, deet =>
{
MyContext ctxCheck = new MyContext();
//do some stuff with this to identify something
if(something)
{
MyContext ctxAdd = new MyContext();
//Do stuff
ctxAdd .MyObjects.Add(myObject);
ctxAdd .SaveChanges() //this is where my error was being thrown
}
else
{
MyContext ctxUpdate = new MyContext();
//same stuff, just an update rather than add
ctxUpdate.SaveChanges();
}
}
Не уверен, что это "Лучшая практика", но она исправила мою проблему, если каждая параллельная операция использует свой собственный контекст.
Получите эту ошибку при использовании SaveChanges (false), а затем позже SaveChanges() в том же контексте, в unitofwork, где несколько строк были удалены из двух таблиц (в контексте) (SaveChanges (False) был в одном из Затем в вызывающей функции вызывается SaveChanges(). Решение заключалось в том, чтобы удалить ненужные SaveChanges (false).
В нашем случае эта ошибка была вызвана маркировкой сущностей как измененной, когда ни один из их свойств "действительно" не изменился. Например, когда вы присваиваете такое же значение свойству, контекст может видеть это как обновление, в котором нет базы данных.
В основном мы запустили script, чтобы повторно заполнить одно свойство с помощью конкатенированных значений из других свойств. Для большого количества записей, которые не означали никаких изменений, но они помечены как измененные. DB вернула различное количество обновленных объектов, которые предположительно вызвали это исключение.
Мы решили это, проверив значение свойства и назначая только новое, если оно отличается.