Ядро Entity Framework Помещает запрос только на частичное обновление, файлы обновляться не будут

#c# #asp.net-core #entity-framework-core

Вопрос:

Проблема: Частичное обновление данных редактирования не приведет к обновлению части файлов формы редактирования.

Я использую ASP.NET Ядро с ядром Entity Framework 5 с интерфейсом vue js. Я действительно получаю все данные с внешнего интерфейса, включая файлы, но когда он переходит к методу сохранения, он сохраняет только основные данные, а не файлы.

это мой запрос на размещение

     [HttpPut("{key:int}")]
    public async Task<IActionResult> PutAsset(int key,[FromForm] AssetPutDto assetPutDto)
    {
        IList<AssetFile> assetFiles = new List<AssetFile>();
        if (assetPutDto.Files == null)
            {
                assetFiles = null;
            }
            else
            {
                foreach (var file in assetPutDto.Files)
                {
                    if (file.Length <= 0) continue;
                    await using var ms = new MemoryStream();
                    await file.CopyToAsync(ms);
                    var fileBytes = ms.ToArray();

                    var thisFile = new AssetFile()
                    {
                        File = fileBytes,
                        Name = file.FileName,
                        MimeType = file.ContentType
                    };
                    assetFiles.Add(thisFile);
                }
            }

            assetPutDto.AssetFiles = assetFiles;
            var asset = _mapper.Map<Asset>(assetPutDto);
            asset.Id = key;
            
            _context.Entry(asset).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException dce)
            {
                if (!AssetExists(key))
                {
                    return NotFound();
                }
                else
                {
                    Console.WriteLine(dce.ToString());
                }
            }

        return NoContent();
    }
 

и это мой актив, входящий в

     public class AssetPutDto : AssetDto
    {
            /// <summary>
           /// Sets the files to Iformfile format to process it to the database
          /// </summary>
          public IList<IFormFile> Files { get; set; }
          // TODO: RetireDate
         // Gets and sets the AssetFiles property
         public IList<AssetFile> AssetFiles { get; set; }
    }
 

Результат, который я ищу, заключается в том, что я хочу обновить данные, включая файлы. На данный момент я просто озадачен тем, почему это работает не так, как должно.

любая помощь в правильном направлении будет признательна

Комментарии:

1. Вы уверены, что ваш объект _mapper отображает ваш assetPutDto.AssetFiles ? установите точку останова после _mapper. Вызов метода сопоставления и убедитесь, что ваши файлы активов правильно сопоставлены. Кроме того, если данные AssetFiles сохраняются в другой таблице, я не уверен, что ef поймет и заменит ранее сохраненные данные объединенной таблицы

2. @Патрик взглянул на картограф, и, похоже, он сопоставляет файлы, я имею в виду, что я получаю ресурс полностью до _context. Точка входа, но после этого ничего. Файлы активов действительно сохраняются в их собственной таблице, если ef core не понимает этого, как я могу заставить его обновить эту таблицу так же, как и таблицу активов ?

3. вы можете загрузить объект актива с его свойством навигации либо путем нетерпеливой, либо ленивой загрузки, затем вручную удалить и вставить новые файлы активов

4. не могли бы вы помочь с примером того, как я могу это сделать, я все еще новичок в ef core и все еще понимаю, что мне нужно делать … Спасибо

5. Я дам ответ, я просто проверяю ситуацию, чтобы убедиться, что я передаю правильную информацию, а не только то, как вы решаете эту проблему

Ответ №1:

Эф ядро произведения, представляющие свои данные в объекты, а как шоу-код не всегда является объект того же типа, представительство код записи в таблице будут знать в лицо, поэтому для того, чтобы лицо признать объект в качестве ссылки к записи ключ должен быть грамотно расположен, и вы «насильственно» говорит лицо, к которому данный объект был изменен, и теперь она должна обновлять его через accordling _context.Entry(asset).State = EntityState.Modified; но как вы делаете это существо лишь знание и отслеживание активов объекта и всех его отношений будут игнорироваться.

TL;DR

  1. заставьте EF загрузить отношения, прежде чем вносить в них изменения, сначала прикрепив объект к контексту с помощью _context.Attach(asset);
  2. загрузка предыдущей связи из базы данных с await _context.Entry(asset).Collection(d => d.Files).LoadAsync();
  3. внесение изменений в отношения (либо с помощью вашего автомата, либо вручную, предварительно сохранив предыдущую коллекцию и сбросив ее здесь
  4. обновление с помощью await _context.SaveChangedAsync()

Подробный

в вашем случае EF не обновляет свои отношения, потому что они не отслеживаются

в этом репозитории я провел несколько тестов, чтобы быть действительно уверенным, что это то, что происходит, и это действительно так.

Запустите миграции, а затем InitialSeed() сначала выполните метод для заполнения данных, которые мы будем тестировать

если вы запустите CaseReproduction() его, он воспроизведет то, что происходит в вашем коде, и предоставит консоли полезные журналы

 SET NOCOUNT ON;
UPDATE [Assets] SET [Name] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;
 

в этом журнале мы видим, что свойства объекта asset изменяются, но в нем ничего не говорится о assetfiles

но если мы заставим сущность распознать и загрузить ее связь до внесения изменений (методом запуска CaseCorrection() ), мы увидим, что сущность загружает и подтверждает asset связь с assetfiles ними и соответствующим образом обновляет их с помощью следующего журнала:

 SET NOCOUNT ON;
DELETE FROM [AssetFiles]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

DELETE FROM [AssetFiles]
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

DELETE FROM [AssetFiles]
WHERE [Id] = @p2;
SELECT @@ROWCOUNT;

DELETE FROM [AssetFiles]
WHERE [Id] = @p3;
SELECT @@ROWCOUNT;

DELETE FROM [AssetFiles]
WHERE [Id] = @p4;
SELECT @@ROWCOUNT;

INSERT INTO [AssetFiles] ([Id], [AssetId], [Name])
VALUES (@p5, @p6, @p7),
(@p8, @p9, @p10);
UPDATE [Assets] SET [Name] = @p11
WHERE [Id] = @p12;
SELECT @@ROWCOUNT;
 

PS: вы также можете загрузить свою сущность со всеми ее отношениями вместо того, чтобы создавать новый объект и присоединять его к своей базе данных с _context.Assets.Include(asset => asset.AssetFiles).FirstOrDefault(d=> d.Id == key); помощью, а затем изменять этот объект с помощью или без вашего автомата

Комментарии:

1. Спасибо вам за вашу помощь в этом,