Обновление двух связанных объектов с использованием универсального шаблона репозитория и Entity Framework - PullRequest
0 голосов
/ 25 марта 2019

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

Я также использую отложенную загрузку, AutoMapper и сервисный уровень для изоляции домена.

public class DetalhesDoArquivoViewModel
{
    public DetalhesDoArquivoViewModel()
    {
        Id = Guid.NewGuid();
    }

    [Key]
    public Guid Id { get; set; }

    public string FileName { get; set; }

    public string Extension { get; set; }

    public Guid FormularioId { get; set; }

    public virtual FormularioDoUploadViewModel DescricaoDoUpload { get; set; }
}

public class FormularioDoUploadViewModel
{
    public FormularioDoUploadViewModel()
    {
        Id = Guid.NewGuid();
    }

    [Key]
    public Guid Id { get; set; }

    [Required(ErrorMessage = "Digite um nome")]
    [Display(Name = "Nome")]
    [MaxLength(100)]
    public string Nome { get; set; }

    [Required(ErrorMessage = "Entre com uma descrição")]
    [Display(Name = "Descrição")]
    [MaxLength(500)]
    public string Descricao { get; set; }

    public virtual IEnumerable<DetalhesDoArquivoViewModel> DetalhesDoArquivo { get; set; }
}

Репозиторий My Update

public virtual TEntity Atualizar(TEntity obj)
{
        var entry = Db.Entry(obj);
        Dbset.Attach(obj);
        entry.State = EntityState.Modified;

        SaveChanges();
        return obj;
}

Мой класс обслуживания:

public class UploadAppServices : BaseService, IUploadServices
{
    private readonly IFormularioUploadRepository _formularioUploadRepository;
    private readonly IDetalhesDoArquivoRepository _detalhesDoArquivoRepository;

     // Update
     public FormularioDoUploadViewModel Atualizar(FormularioDoUploadViewModel formularioDoUploadViewModel)
    {
        var form = Mapper.Map<FormularioUpload>(formularioDoUploadViewModel);
        _formularioUploadRepository.Atualizar(form);
        Commit();
        return formularioDoUploadViewModel;
    }

    //getById
    public FormularioDoUploadViewModel ObterPorId(Guid id)
    {
        return Mapper.Map<FormularioDoUploadViewModel>(_formularioUploadRepository.ObterPorId(id));
    }
}

Мой контроллер:

public class FormularioDoUploadController : BaseController
{
    private ApplicationDbContext db = new ApplicationDbContext();

    private IFormularioUploadRepository _formularioUploadRepository;
    private IUploadServices _uploadServices;

    public ActionResult Edit(Guid id)
    {         
        var formularioDoUploadViewModel = _uploadServices.ObterPorId(id);

        if (formularioDoUploadViewModel == null)
        {
            return HttpNotFound();
        }

        return View(formularioDoUploadViewModel);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit(FormularioDoUploadViewModel formularioDoUploadViewModel)
    {
        if (ModelState.IsValid)
        {
            for (int i = 0; i < Request.Files.Count; i++)
            {
                var file = Request.Files[i];

                if (file != null && file.ContentLength > 0)
                {
                    var fileName = Path.GetFileName(file.FileName);

                    DetalhesDoArquivoViewModel detalhesDoArquivo = new DetalhesDoArquivoViewModel()
                    {
                        FileName = fileName,
                        Extension = Path.GetExtension(fileName),
                        FormularioId = formularioDoUploadViewModel.Id,
                    };

                    var path = Path.Combine(Server.MapPath("~/App_Data/Upload/"), detalhesDoArquivo.Id + detalhesDoArquivo.Extension);
                    file.SaveAs(path);
                }

                // Update
                _uploadServices.Atualizar(formularioDoUploadViewModel);
                return RedirectToAction("Index");
            }
        }

        return View(formularioDoUploadViewModel);
    }

1 Ответ

0 голосов
/ 26 марта 2019

Automapper отлично подходит для сопоставления сущности с моделью представления, но я бы не использовал его для сопоставления модели представления с сущностью. Это может показаться удобным, но вы фактически безоговорочно доверяете данным, полученным от клиента, и перезаписываете данные своей базы данных. Это означает, что вы должны отправить клиенту 100% модели вашего домена сущности, раскрывая больше информации о структуре вашего домена, чем вам нужно, и затем принять эту расширенную модель домена, которая может содержать изменения, которые ваше клиентское приложение не собирается делать. (перехват сообщения на сервере в отладчике браузера и изменение значений в объекте, отправленном обратно на сервер)

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

  • Подтвердите, что текущий пользователь сеанса имеет право изменять записи, указанные в запросе на отправку.
  • Ограничить обновление конкретными значениями, указанными в запросе.
  • Подтвердите эти конкретные значения.
  • Отключите сеанс пользователя и сообщите администраторам, если что-либо из перечисленного нарушено.

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

Передавая сущности или просматривая модели, которые отображаются непосредственно на сущности для метода, предназначенного для обновления некоторых аспектов сущности, я могу:

  • Переназначить эту сущность кому-то еще.
  • Используйте запрос, чтобы попытаться присвоить себе другую случайную сущность.
  • Отрицать или иным образом изменять любые данные, записанные в этом объекте.

Не доверяйте ничему, полученному от клиента.

Эта проблема также связана с проблемой одновременного доступа, когда ваша система использует сценарий «последний выиграл». Между моментом, когда вы предоставили модель объекта / представления, и временем, когда вы отправили модель представления обратно на сервер, эти данные объекта могли измениться. Сопоставляя данные в новый класс сущностей, прикрепляя, помечая измененные и сохраняя, вы перезаписываете данные, не обращая внимания на то, были ли данные устаревшими.

Чтобы избежать проблемы, с которой вы сталкиваетесь, и проблем с безопасностью / устареванием, вы должны загрузить сущность из контекста при обновлении после вызова, проверить авторизацию для текущего пользователя, проверить номер строки # или временную метку, чтобы убедиться, что запись не устарела, проверьте обновленные данные, а затем, когда вы абсолютно уверены, что данные в вашей модели представления не представляют риска для вашей сущности, вы можете использовать .Map(source, detination) для автоматического копирования значений. Если вам необходимо обновить связанные сущности для моделей связанных представлений, то, если вы .Include() связывает эти связанные сущности при извлечении сущности из контекста, то вызов .Map() должен обрабатывать связанные данные.

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