#c# #asp.net-mvc #entity-framework #repository
#c# #asp.net-mvc #entity-framework #репозиторий
Вопрос:
Я использую общий репозиторий и 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; }
}
Мой репозиторий обновлений
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 amp;amp; 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:
Automapper отлично подходит для сопоставления объекта с view-model, но я бы не стал использовать его для сопоставления из view-model в entity. Это может показаться удобным, но фактически вы безоговорочно доверяете данным, полученным от клиента, и перезаписываете данные своей базы данных. Это означает, что вы должны отправить клиенту 100% вашей модели домена entity, раскрыв больше о вашей структуре домена, чем вам нужно, а затем принять эту расширенную модель домена, которая может содержать изменения, которые ваше клиентское приложение не намерено вносить. (перехват сообщения на сервер в отладчике браузера и изменение значений в объекте, отправленном обратно на сервер)
Действия отправки должны быть закодированы в:
- Убедитесь, что у текущего пользователя сеанса есть разрешение на изменение записи (записей), идентифицированных в запросе отправки.
- Ограничьте обновление конкретными значениями, указанными в запросе.
- Проверьте эти конкретные значения.
- Отключите сеанс пользователя и уведомите администраторов, если что-либо из вышеперечисленного нарушено.
В некоторых случаях, таких как добавление нового объекта, полезная нагрузка фактически будет представлять собой законченный объект и, возможно, некоторые связанные детали. Это все еще необходимо проверить на соответствие известному состоянию данных. В других случаях, когда вы предоставляете действие, которое обновляет объект, отправленная обратно модель должна содержать просто идентификатор обновляемого объекта и конкретные значения, которые клиенту разрешено обновлять. (не весь, измененный объект)
Передавая объекты или модели просмотра, которые сопоставляются непосредственно с объектами для метода, предназначенного для обновления некоторых аспектов объекта, я могу:
- Повторно назначьте эту сущность кому-то другому.
- Используйте запрос, чтобы попытаться назначить мне другой случайный объект.
- Отрицание или иное изменение любых данных, записанных в этом объекте.
Не доверяйте ничему, полученному от клиента.
Эта проблема также представляет собой проблему параллельного доступа, когда ваша система использует сценарий «последний в wins». Между предоставлением модели объекта / представления и отправкой модели представления обратно на сервер данные объекта могли измениться. Сопоставляя данные с новым классом сущностей, прикрепляя, помечая как измененные и сохраняя, вы перезаписываете данные без какого-либо рассмотрения того, были ли данные устаревшими.
Чтобы избежать проблемы, которую вы видите, и проблем с безопасностью / устареванием, вы должны загрузить объект из контекста при вызове Update post, подтвердить авторизацию для текущего пользователя, проверить номер версии строки или временную метку, чтобы убедиться, что запись не устарела, подтвердить обновленные данные, затем, как только вы будете абсолютно уверены, что данные в вашей модели представления не представляют риска для вашей сущности, вы можете использовать automapper .Map(source, detination)
для копирования значений. Если вам нужно обновить связанные объекты в соответствии с моделями связанных представлений, то до тех пор, пока вы .Include()
используете эти связанные объекты при извлечении объекта из контекста, .Map()
вызов должен обрабатывать связанные данные.