2

Я нахожусь в центре рефакторинга проекта, над которым я работаю. В моих существующих контроллерах я использую шаблон репозитория, но я все еще выполнял слишком много строительных лесов, чем мне было удобно. Это и некоторые из моих контроллеров могли иметь 10+ репозиториев, переданных (через Ninject). Итак, я решил ввести сервисный уровень, где мое намерение состоит в том, чтобы иметь одну услугу на одного контроллера, и каждая служба будет вместо этого иметь несколько репозиториев, введенных в нее, и выполнять необходимую мне работу. Пока это отлично работает, но я сталкиваюсь с путаницей: как перенести проверку модели с контроллера и на уровень обслуживания?Как правильно отделить проверку модели от контроллера в службе?

Например, посмотрите этот Edit метод на моем OfficesController:

[HttpPost] 
public async Task<RedirectToRouteResult> Edit(
    short id, 
    FormCollection form, 
    [Bind(Prefix = "Office.Coordinates", Include = "Latitude,Longitude")] Coordinate[] coordinates) { 
    if (id > 0) { 
     Office office = await this.OfficesService.GetOfficeAsync(id); 

     if ((office != null) 
      && base.TryUpdateModel(office, "Office", new string[2] { 
       "Name", 
       "RegionId" 
      }, form) 
      && base.ModelState.IsValid) { 
      this.OfficesService.UpdateOfficeAsync(office, coordinates); 
     } 

     return base.RedirectToAction("Edit", new { 
      id = id 
     }); 
    } 

    return base.RedirectToAction("Default"); 
} 

Проблема с этим, по сравнению с методами управления является то, что я до сих пор захватить Office объект из базы данных, выполните обновить, подтвердить его, а затем сохранить его снова. В этом случае сложность возрастала, а не уменьшалась. Раньше я вызывал репозиторий в методе, теперь я вызываю службу, которая вызывает репозиторий для выполнения той же функции. До сих пор это увеличение сложности показало себя только в моих методах Edit, везде сложность значительно уменьшилась, и это то, что я хочу.

Итак, что было бы правильным способом проверки валидации, и теперь, когда я думаю об этом, логика обновления модели из контроллера и в службу? Рекомендации приветствуются!

Для справки, вот как структурирован мой проект:

  • данных: Содержит все мои модели классов
  • Data.Google.Maps: Содержит все классы мне нужна десериализация конкретного Kml
  • Data.Models: Содержит мои DbContext, конфигурацию, модели вида и частичные модели вида
  • Data.Repositories: Содержит все мои репозитории, которые общаются с DbContext. Поскольку EF является псевдорепозитором, он использует мои «репозитории» как более конкретный способ запроса данных. Пример: FindTechnicians() или FindActive() и т. Д.
  • Данные. Службы: Содержит все услуги, которые я буду использовать. Сервисы будут иметь один или несколько репозиториев, введенных в них, и выполнить всю логику, которую мне нужно сделать, прежде чем передать завершенную модель представления обратно в контроллер.
  • Идентификатор: Содержит мою реализацию ASP.NET Identity.
  • Web.Private: Содержит проект MVC.
+0

Какое количество зависимостей репозитория зависит от ваших услуг? Вы просто переместили проблему чрезмерной инъекции зависимостей или решили ее решить? – danludwig

+0

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

ответ

2

Вот 2 статьи вы должны прочитать, если вы еще не:

ответы на ваши проблемы являются FluentValidation.NET и украшения зависимость.

С его помощью вы могли бы сделать что-то вроде этого:

private readonly IExecuteCommands _commands; 

[HttpPost] 
public async Task<RedirectToRouteResult> Edit(short id, UpdateOffice command) { 

    // with FV.NET plugged in, if your command validator fails, 
    // ModelState will already be invalid 
    if (!ModelState.IsValid) return View(command); 

    await _commands.Execute(command); 
    return RedirectToAction(orWhateverYouDoAfterSuccess); 
} 

Команда является только простой DTO, как ViewModel. Может выглядеть примерно так:

public class UpdateOffice 
{ 
    public int OfficeId { get; set; } 
    public int RegionId { get; set; } 
    public string Name { get; set; } 
} 

... и магический валидатор:

public class ValidateUpdateOfficeCommand : AbstractValidator<UpdateOffice> 
{ 
    public ValidateUpdateOfficeCommand(DbContext dbContext) 
    { 
     RuleFor(x => x.OfficeId) 
      .MustFindOfficeById(dbContext); 

     RuleFor(x => x.RegionId) 
      .MustFindRegionById(dbContext); 

     RuleFor(x => x.Name) 
      .NotEmpty() 
      .Length(1, 200) 
      .MustBeUniqueOfficeName(dbContext, x => x.OfficeId); 
    } 
} 

Каждый из этих правил валидации будет работать, прежде чем ваш метод действия даже запускается на выполнение, при условии, что вы установили валидаторы для внедрения зависимостей и что вы используете поставщик проверки FV MVC. Если есть ошибка проверки, ModelState.IsValid будет ложным.

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

+0

Я прочитал первую статью ранее сегодня, не прочитал вторую статью, главным образом потому, что статьи Стива довольно продолжительны. Что касается FluentValidation.NET, на которую вы ссылаетесь, как это отличается от API EF Fluent и есть ли еще какая-то польза для него, если я использую API EF Fluent? – Gup3rSuR4c

+0

Свободный API EF предназначен для сопоставления реляционной схемы с концептуальной объектной моделью, а ее проверки минимальны. Вы можете указать необходимые поля и указать максимальную длину, но об этом. С приведенным выше, ваш валидатор делает чек, чтобы убедиться, что все ваши другие бизнес-правила не нарушаются. В SQL вам придется делать это с помощью триггеров, для которых EF не имеет свободного API. – danludwig

Смежные вопросы