Выбирающие данные в классе ASP.NET MVC ViewModel?

Я лично думаю дефис, поскольку разделитель слов делает для максимальной удобочитаемости и typability в целом, таким образом, я рекомендую это, где возможный (в некоторых контекстах, дефис не может использоваться, такой как в идентификаторах на большинстве языков). Одна важная вещь принять во внимание состоит в том, что схема, которую Вы выбираете, будет иметь влияние на потребовать оператор, который пользователи будут использовать с Вашим lib, и Вы хотите к , стараются не иметь различное имя драгоценного камня, чем название библиотеки .

Плохой
# gem install my_cool_lib
require 'my-cool-lib'

# gem install MyCoolLib
require 'my_cool_lib'
Хороший
# gem install my_cool_lib
require 'my_cool_lib'

# gem install my-cool-lib
require 'my-cool-lib'

, К сожалению, маленькое небольшое количество библиотек нарушает это простое правило удобства использования. Не будьте одной из тех библиотек.:)

13
задан tereško 25 February 2013 в 15:38
поделиться

6 ответов

Я столкнулся с той же проблемой. Я начал создавать классы для каждого действия, когда код стал слишком большим для методов действия. Да, у вас будет некоторый поиск данных в классах, а другой - в методах контроллера. Альтернативой является получение всех данных в классах, но половина классов, которые вам действительно не понадобятся, будут созданы для обеспечения согласованности или будут извлекать все данные в методах контроллера, но, опять же, некоторые из этих методов будут быть слишком сложным и его нужно было абстрагировать на классы ... так что выбирайте яд. Я бы предпочел иметь небольшую непоследовательность и иметь правильное решение для работы.

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

Были случаи, когда я ' Мы поместили методы преобразования во ViewModel. Например, мне нужно преобразовать ViewModel в соответствующую сущность или мне нужно загрузить ViewModel с данными из Entity.

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

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

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

Например, мне нужно преобразовать ViewModel в соответствующий объект, или мне нужно загрузить ViewModel с данными из Entity.

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

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

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

Например, мне нужно преобразовать ViewModel в соответствующую сущность или мне нужно загрузить ViewModel с данными из Entity.

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

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

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

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

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

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

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

7
ответ дан 2 December 2019 в 00:31
поделиться

There's more than that ;-) You can fetch in model binder or action filter. For the second option, check Jimmy Bogard's blog somewhere around here. I personally do it in model binders. I use ViewModel like this: My custom ASP.NET MVC entity binding: is it a good solution?. It is processed by my custom model binder:

public object BindModel(ControllerContext c, BindingContext b)
{
   var id = b.ValueProvider[b.ModelName]; // don't remember exact syntax
   var repository = ServiceLocator.GetInstance(GetRepositoryType(b.ModelType));
   var obj = repository.Get(id);
   if (obj == null)
     b.ModelState.AddModelError(b.ModelName, "Not found in database");
   return obj;
}

public ActionResult Action(EntityViewModel<Order> order)
{
   if (!ModelState.IsValid)
      ...;
}

You can also see an example of model binder doing repository access in S#arp Architecture.

As for static data in view models, I'm still exploring approaches. For example, you can have your view models remember the entities instead of lists, and

public class MyViewModel { public MyViewModel(Order order, IEmployeesSvc _svc) { }

  public IList<Employee> GetEmployeesList()
  {
      return _svc.GetEmployeesFor(order.Number);
  }

}

You decide how you inject _svc into ViewModel, but it's basically the same as you do for controller. Just beware that ViewModel is also created by MVC via parameterless constructor, so you either use ServiceLocator or extend MVC for ViewModel creation - for example, inside your custom model binder. Or you can use Jimmy Bogard's approach with AutoMapper which does also support IoC containers.

The common approach here is that whenever I see repetative code, I look to eliminate it. 100 controller actions doing domain-viewmodel marshalling plus repository lookup is a bad case. Single model binder doing it in generic way is a good one.

3
ответ дан 2 December 2019 в 00:31
поделиться

Вот еще одно решение: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/06/29/how-we-do-mvc-view-models .aspx

Основные моменты:

  1. Отображение выполняется посредником - в данном случае это AutoMapper, но это может быть ваш собственный класс (хотя это больше для кода). Это позволяет сконцентрировать и Domain, и ViewModel на логике домена / представления. Посредник (сопоставитель) будет содержать (в основном автоматическую) логику сопоставления, включая внедренные сервисы.
  2. Сопоставление применяется автоматически, все, что вы делаете, это указываете фильтру действий типы источника / назначения - очень чисто.
  3. (Кажется, важно для вас) AutoMapper поддерживает вложенные сопоставления / типы, поэтому вы можете объединить вашу ViewModel с несколькими независимыми моделями представления, чтобы ваш "экранный DTO" не был беспорядочным.

Как в этой модели:

public class WholeViewModel
{
   public Part1ViewModel ModelPart1 { get; set; }
   public Part2ViewModel ModelPart2 { get; set; }
}

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

Если вам не нужен AutoMapper, у вас есть интерфейсы IViewModelMapper, и тогда ваш контейнер IoC поможет вашему фильтру действий найти подходящие

container.Resolve(typeof(IViewModelMapper<>).MakeGenericType(mysourcetype, mydesttype))

, а также предоставит любые требуемые внешние службы для этого сопоставителя (это возможно и с AutoMapper). Но, конечно, AutoMapper может выполнять рекурсию, и в любом случае зачем писать дополнительный AutoMapper; -)

а затем ваш контейнер IoC поможет вашему фильтру действий найти подходящий

container.Resolve(typeof(IViewModelMapper<>).MakeGenericType(mysourcetype, mydesttype))

, и он также предоставит все необходимые внешние службы для этого сопоставителя (это также возможно с AutoMapper). Но, конечно, AutoMapper может выполнять рекурсию, и в любом случае зачем писать дополнительный AutoMapper; -)

и затем ваш контейнер IoC поможет вашему фильтру действий найти подходящий

container.Resolve(typeof(IViewModelMapper<>).MakeGenericType(mysourcetype, mydesttype))

, и он также предоставит все необходимые внешние службы для этого сопоставителя (это возможно и с AutoMapper). Но, конечно, AutoMapper может выполнять рекурсию, и в любом случае зачем писать дополнительный AutoMapper; -)

1
ответ дан 2 December 2019 в 00:31
поделиться

I wouldn't be fetching data from the database in your ViewModel. The ViewModel exists to promote separation of concerns (between your View and your Model). Tangling up persistance logic in there kind of defeats the purpose.

Luckily, the ASP.NET MVC framework gives us more integration points, specifically the ModelBinder.

I've got an implementation of a generic ModelBinder pulling information from the service layer at:-

http://www.iaingalloway.com/going-further-a-generic-servicelayer-modelbinder

It doesn't use a ViewModel, but that's easily fixed. It's by no means the only implementation. For a real-world project, you're probably better off with a less generic, more customised solution.

If you're diligent, your GET methods don't even need to know that the service layer exists.

The solution probably looks something like:-

Controller action method:-

public ActionResult Details(MyTypeIndexViewModel model)
{
  if( ModelState.IsValid )
  {
    return View(model);
  }
  else
  {
    // Handle the case where the ModelState is invalid
    // usually because they've requested MyType/Details/x
    // and there's no matching MyType in the repository
    // e.g. return RedirectToAction("Index")
  }
}

ModelBinder:-

public object BindModel
(
  ControllerContext controllerContext,
  BindingContext bindingContext
)
{
  // Get the Primary Key from the requestValueProvider.
  // e.g. bindingContext.ValueProvider["id"]
  int id = ...;

  // Get an instance of your service layer via your
  // favourite dependancy injection framework.
  // Or grab the controller's copy e.g.
  // (controllerContext.Controller as MyController).Service
  IMyTypeService service = ...;

  MyType myType = service.GetMyTypeById(id)

  if (myType == null)
  {
    // handle the case where the PK has no matching MyType in the repository
    // e.g. bindingContext.ModelState.AddModelError(...)
  }


  MyTypeIndexViewModel model = new MyTypeIndexViewModel(myType);

  // If you've got more repository calls to make
  // (e.g. populating extra fields on the model)
  // you can do that here.

  return model;
}

ViewModel:-

public class MyTypeIndexViewModel
{
  public MyTypeIndexViewModel(MyType source)
  {
    // Bind all the properties of the ViewModel in here, or better
    // inherit from e.g. MyTypeViewModel, bind all the properties
    // shared between views in there and chain up base(source)
  }
}

Build your service layer, and register your ModelBinder as normal.

3
ответ дан 2 December 2019 в 00:31
поделиться

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

Большой объем кода в контроллере для таких вещей, как получение данных, не считается лучшей практикой. Основная ответственность контроллера - «контролировать» поток приложения.

0
ответ дан 2 December 2019 в 00:31
поделиться

Отправить это поздно ... Награда почти закончилась. Но ...

Другой картограф, на который стоит обратить внимание, - это Automapper: http://www.codeplex.com/AutoMapper

И обзор того, как его использовать: http: //www.lostechies .com / blogs / jimmy_bogard / archive / 2009/01/22 / automapper-the-object-object-mapper.aspx

Мне действительно нравится его синтаксис.

// place this somewhere in your globals, or base controller constructor
Mapper.CreateMap<Employee, EmployeeViewModel>();

Теперь в вашем контроллере я бы использовал несколько моделей просмотра. Это принудительно применяет DRY, позволяя повторно использовать эти модели представления в другом месте вашего приложения. Я бы не стал привязывать их всех к одной модели просмотра. Я бы сделал рефакторинг примерно так:

public class EmployeeController()
{
  private IEmployeeService _empSvc;
  private ISpouseService _peopleSvc;

  public EmployeeController(
      IEmployeeService empSvc, ISpouseService peopleSvc)
  {
    // D.I. hard at work! Auto-wiring up our services.  :)
    _empSvc = empSvc;
    _peopleSvc = peopleSvc;

    // setup all ViewModels here that the controller would use
    Mapper.CreateMap<Employee, EmployeeViewModel>();
    Mapper.CreateMap<Spouse, SpouseViewModel>();
  }

  public ActionResult Employee(int empNum)
  {
    // really should have some validation here that reaches into the domain
    //

    var employeeViewModel = 
        Mapper.Map<Employee, EmployeeViewModel>(
          _empSvc.FetchEmployee(empNum)
        );

    var spouseViewModel =
        Mapper.Map<Spouses, SpousesViewModel>(
          _peopleSvc.FetchSpouseByEmployeeID(empNum)
        );

    employeeViewModel.SpouseViewModel = spouseViewModel;

    return View(employeeViewModel);    
  }

  [AcceptVerbs(HttpVerbs.Post)]
  public ActionResult Employee(int id, FormCollection values)    
  {
    try
    {
      // always post to an ID, which is the employeeID
      var employee = _empSvc.FetchEmployee(id);

      // and bind using the built-in UpdateModel helpers.
      // this will throw an exception if someone is posting something
      // they shouldn't be posting. :)
      UpdateModel(employee);

      // save employee here

      this.RedirectToAction(c => c.Index());
    }
    catch
    {
      // check your domain model for any errors.
      // check for any other type of exception.  
      // fail back to the employee screen
      RedirectToAction(c => c.Employee(id));
    }
  } 
}

Обычно я стараюсь избегать сохранения нескольких сущностей в действии контроллера. Вместо этого я бы реорганизовал объект домена сотрудника, чтобы иметь методы AddSpouse () и SaveSpouse (), которые будут принимать объект Spouse. Эта концепция известна как AggregateRoots, она контролирует все зависимости от корня, которым является объект Employee (). Но это только я.

0
ответ дан 2 December 2019 в 00:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: