Не могу правильно обновить данные сущности
Не могу правильно обновить сущность, постояннно выскакивает исключение:
System.InvalidOperationException: "The instance of entity type 'Inventory' cannot be tracked because another instance with the same key value for {'Inventid'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values."
Что делаю не так, тоже понять не могу, обычный метод Update() не работает!
var id = context.Inventories.Where(x => x.Inventid == Convert.ToInt32(dataGridView1[0, dataGridView1.CurrentRow.Index].Value.ToString()))
.Select(z => z.Inventid).FirstOrDefault();
var fil = context.Filials.Where(x => x.Filialname == cbxFilial.Text)
.Select(z => z.Filialid).FirstOrDefault();
var invttype = context.Inventtypes.Where(x => x.Inventname == cbxTypename.Text)
.Select(z => z.Inventtypeid).FirstOrDefault();
var cabinet = context.Cabinets.Where(x => x.Cabinetname == cbxCabnum.Text)
.Select(z => z.Cabinetid).FirstOrDefault();
var service = context.Services.Where(x => x.Servicename == cbxService.Text)
.Select(z => z.Serviceid).FirstOrDefault();
var status = context.Statuses.Where(x => x.Statusname == cbxStatus.Text)
.Select(z => z.Statusid).FirstOrDefault();
Inventory inv = new()
{
Inventid = id,
Inventname = tbxName.Text,
Serialnum = tbxSerialnum.Text,
Datepov = dtpDatepov.Value.Date.ToUniversalTime(),
Datenextpov = dtpDatenextpov.Value.Date.ToUniversalTime(),
Filialid = (int)fil,
Inventtype = invttype,
Cabinetid = cabinet,
Serviceid = service,
Statusid = status,
Yearofman = dtpYearOfMan.Value.Date.ToUniversalTime()
};
context.Entry(inv).State = EntityState.Modified;
await context.SaveChangesAsync();
Ответы (3 шт):
Да, надо приаттачить новый элемент.
Такое
var id = context.Inventories.Where(x => x.Inventid == Convert.ToInt32(dataGridView1[0, dataGridView1.CurrentRow.Index].Value.ToString())) .Select(z => z.Inventid).FirstOrDefault();везде заменить на такое
var id = context.Inventories.FirstOrDefault(x => x.Inventid == Convert.ToInt32(dataGridView1[0, dataGridView1.CurrentRow.Index].Value.ToString()))?.Inventid;
Должно быть как-то так:
var fil = context.Filials.FirstOrDefault(x => x.Filialname == cbxFilial.Text)?.Filialid;
var invttype = context.Inventtypes.FirstOrDefault(x => x.Inventname == cbxTypename.Text)?.Inventtypeid;
var cabinet = context.Cabinets.FirstOrDefault(x => x.Cabinetname == cbxCabnum.Text)?.Cabinetid;
var service = context.Services.FirstOrDefault(x => x.Servicename == cbxService.Text)?.Serviceid;
var status = context.Statuses.FirstOrDefault(x => x.Statusname == cbxStatus.Text)?.Statusid;
// тут проверить, что полученные значения не null
Inventory inv = new()
{
Inventname = tbxName.Text,
Serialnum = tbxSerialnum.Text,
Datepov = dtpDatepov.Value.Date.ToUniversalTime(),
Datenextpov = dtpDatenextpov.Value.Date.ToUniversalTime(),
Filialid = (int)fil,
Inventtype = (int)invttype,
Cabinetid = (int)cabinet,
Serviceid = (int)service,
Statusid = (int)status,
Yearofman = dtpYearOfMan.Value.Date.ToUniversalTime()
};
context.Inventories.Attach(inv);
await context.SaveChangesAsync();
P.S. Кстати, чтобы не заниматься такими безобразиями
var fil = context.Filials.FirstOrDefault(x => x.Filialname == cbxFilial.Text)?.Filialid;
, нужно в ComboBox'ы набивать пары значений (id, name):
public struct stIntStr {
public stIntStr(int i, string s) { Value = i; Name = s; }
public int Value { get; }
public string Name { get; }
}
cbxFilial.DisplayMember = "Name";
cbxFilial.ValueMember = "Value";
cbxFilial.DataSource = context.Filials.Select(s => new stIntStr(s.Filialid, s.Filialname)).ToList();
// а потом в new
Filialid = (int)cbxFilial.SelectedValue,
Если кому то это будет полезно, проблема решилась вот так:
var fil = context.Filials.FirstOrDefault(x => x.Filialname == cbxFilial.Text)?.Filialid;
var invttype = context.Inventtypes.FirstOrDefault(x => x.Inventname == cbxTypename.Text)?.Inventtypeid;
var cabinet = context.Cabinets.FirstOrDefault(x => x.Cabinetname == cbxCabnum.Text)?.Cabinetid;
var service = context.Services.FirstOrDefault(x => x.Servicename == cbxService.Text)?.Serviceid;
var status = context.Statuses.FirstOrDefault(x => x.Statusname == cbxStatus.Text)?.Statusid;
Inventory inv = new()
{
Inventname = tbxName.Text,
Serialnum = tbxSerialnum.Text,
Datepov = dtpDatepov.Value.Date.ToUniversalTime(),
Datenextpov = dtpDatenextpov.Value.Date.ToUniversalTime(),
Filialid = (int)fil,
Inventtype = (int)invttype,
Cabinetid = (int)cabinet,
Serviceid = (int)service,
Statusid = (int)status,
Yearofman = dtpYearOfMan.Value.Date.ToUniversalTime()
};
context.Inventories.Update(inv);
DeletingWithoutDialog();
await context.SaveChangesAsync();
Метод DeletingWithoutDialog() вылядит так:
public async void DeletingWithoutDialog()
{
InvDbContext db = new InvDbContext();
//получение ID прибора
var p = Convert.ToInt32(dataGridView1[0, dataGridView1.CurrentRow.Index].Value.ToString());
//Выбор по ID
Inventory? inv = db.Inventories.FirstOrDefault(c => c.Inventid == p);
if (inv != null)
{
db.Remove(inv);
await db.SaveChangesAsync();
Start();
}
}
Суть проблемы на самом деле в том, что EF отслеживает сущности и у вас получается две сущности с одним Id - та, которую EF прочитал из базы, и ваша новая сущность. Так делать нельзя. Правильно это решается одним из двух способов:
- Берёте из
EFстарую сущность с этимId, меняете ей поля на нужные значения и сохраняете её. - Добавляете в
EFновую сущность с другимId.
Просто определитесь, что же вы хотите: обновить имеющуюся сущность или добавить новую.