Странная аномалия с: Прикрепление объекта типа 'X' не удалось, потому что другой объект того же типа уже имеет то же значение первичного ключа - PullRequest
0 голосов
/ 31 октября 2019

У меня была похожая проблема с этим вопросом , при обновлении записи с использованием EF6.

Я действительно думал, что взломал всю вещь обновления, но теперь приходится почти идентичные функции обновленияв том, что я думаю, был идентичным образом. Один работает, другой нет. Я исправил тот, который не работает, используя комментарий Джейми в приведенном выше вопросе, но я хотел бы понять, если функция, которая работает, на самом деле не должна, и поэтому заняла время, и я должен больше походить на 'фиксированныйИли, почему «исправленный» не сработал? Я даже переместил их в один и тот же контроллер, чтобы контекст базы данных (БД) был гарантирован одинаковым. Я что-то пропустил, и они совсем не идентичны (функционально)?

Это также может помочь некоторым другим, которые борются с этим, как я.

Функция, которая работает (вырубается) это:

        [HttpPost]
    [Route("UpdateAddBusinessService")]
    public async Task<IHttpActionResult> UpdateAddBusinessService(BusinessServiceDTO servicetoupdateoradd)
    {

        ... pre check stuff...

        try
        {
            if (servicetoupdateoradd.Id != null) // This is an existing service to be updated - if Is Null then create new
            {
                BusinessService businessService = await db.BusinessServices.FindAsync(servicetoupdateoradd.Id);
                if (businessService != null)
                {
                    Mapper.Map(servicetoupdateoradd, businessService);
                    db.Entry(businessService).State = EntityState.Modified;
                    await db.SaveChangesAsync();
                    return Ok("Service Updated");
                }
                else

Функция, которая не работает:

        [HttpPost]
    [Route("UpdateImage")]
    public async Task<IHttpActionResult> UpdateImage(ImageDTO imageDTO)
    {
        ... pre check stuff ... 
        try
        {
            // First find the image
            // Image imagetoupdate = await db.Images.FindAsync(imageDTO.Id);  <<-This FAILS.
            Image imagetoupdate = db.Images.AsNoTracking().Single(x => x.Id == imageDTO.Id);  <<- This WORKS
            if (imagetoupdate != null)
            {
                imagetoupdate = Mapper.Map<ImageDTO, Image>(imageDTO); // Move the stuff over..
                db.Entry(imagetoupdate).State = EntityState.Modified;
                await db.SaveChangesAsync();
                return Ok();
            }

Я подумал (как вы не сомневаетесь), если моя функция Mapper что-то делает, но яПодозреваю, что нет (не копая слишком глубоко, но я думаю, что это может быть), мои функции Mapper.Config для двух DTO очень похожи:

            cfg.CreateMap<Image, ImageDTO>();
            cfg.CreateMap<ImageDTO, Image>();

и:

            cfg.CreateMap<BusinessService, BusinessServiceDTO>();
            cfg.CreateMap<BusinessServiceDTO, BusinessService>();

IМне бы очень хотелось понять «правильный» способ сделать это, чтобы он меня больше не кусал. Заранее спасибо.

РЕДАКТИРОВАТЬ: Меня спросили (вполне разумно), что «предварительная проверка» что-то делает для извлечения данных, но нет, но есть тонкоеразница, которую я мог пропустить ... Это из функции BusinessService, которая работает:

        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        string userid = User.Identity.GetUserId(); //Check user is valid
        if (servicetoupdateoradd.UserId != userid)
        {
            var message = "User Id Not found - Contact support";
            HttpResponseMessage err = new HttpResponseMessage() { StatusCode = HttpStatusCode.ExpectationFailed, ReasonPhrase = message };
            return ResponseMessage(err);
        }

Это из функции UpdateImage, которая не работала:

        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        string userid = User.Identity.GetUserId();
        SGGUser user = db.Users.Find(userid); // Use this find and not UserManager becuase its a different context and buggers up the file save
        if (user == null)
        {
            var message = "User Id Not found - Contact support";
            HttpResponseMessage err = new HttpResponseMessage() { StatusCode = HttpStatusCode.ExpectationFailed, ReasonPhrase = message };
            return ResponseMessage(err);
        }

Iвидите, что в этом, хотя я не получаю соответствующие данные, я использую контекст 'db' .. может ли это быть ?? Объект Image содержит ссылку на пользователя, так что, может быть, это немного волшебства в фоновом коде?

Извинения, я просто не хотел слишком загромождать вопрос ...

1 Ответ

3 голосов
/ 31 октября 2019

Эта строка:

Mapper.Map(servicetoupdateoradd, businessService);

и эта строка:

imagetoupdate = Mapper.Map<ImageDTO, Image>(imageDTO); // Move the stuff over..

выглядят одинаково, но делают две разные вещи.

В первой строке Automapper сообщитскопируйте значения из первого объекта во вторую ссылку на объект, используя правила отображения. Вторая строка скажет Automapper сделать совершенно новую ссылку на сущность с сопоставленными значениями из предоставленного объекта и вернуть его.

Так что в первом случае ссылка на сущность сохраняется в той, что DbContextзнает о. Ссылка была загружена из DbContext и должна отслеживать изменения, поэтому вам даже не нужно устанавливать ее состояние объекта. Во втором случае Automapper создает совершенно новую ссылку и назначает ее поверх исходной ссылки. EF рассматривает это как совершенно новый экземпляр и пытается присоединить его, в результате чего он жалуется, потому что контекст уже загрузил эту сущность, вы просто перезаписали ссылку.

Это должно работать, если вы измените второй экземпляр на:

Mapper.Map(imageDTO, imagetoupdate);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...