Реализация ViewModel в ASP.NET MVC. Является ли этот код лучшей практикой?

Я только начал использовать ViewModels. Можете ли вы, ребята, проверить этот код, чтобы увидеть, следую ли я лучшей практике? Есть что-нибудь необычное? Вы бы сделали проверку по-другому?

Извините, если код длинный (в нем так много частей). Я пытался сделать это как можно более простым для понимания.

Спасибо!

модель

  public class CustomerModel
  {
    [Required(ErrorMessage="Primer nombre!")]
    public string FirstName { get; set; }

    [Required(ErrorMessage="Segundo nombre!")]
    public string LastName { get; set; }

    [Required(ErrorMessage="Edad")]
    public int? Age { get; set; }

    public string State { get; set; }
    public string CountryID { get; set; }

    [Required(ErrorMessage="Phone Number")]
    public string PhoneNumber { get; set; }
  }

ViewModel

  public class CustomerViewModel
  {
    public CustomerModel Customer { get; set; }

    public string Phone1a { get; set; }
    public string Phone1b { get; set; }
    public string Phone1c { get; set; }
  }

контроллер

    public ActionResult Index()
    {
      CustomerViewModel Customer = new CustomerViewModel()
      {
        Customer = new CustomerModel(),
      };


      return View(Customer);
    }


    [HttpPost]
    public ActionResult Index(CustomerViewModel c)
    {

      //ModelState.Add("Customer.PhoneNumber", ModelState["Phone1a"]);

      // Let's manually bind the phone number fields to the PhoneNumber properties in
      // Customer object. 
      c.Customer.PhoneNumber = c.Phone1a + c.Phone1b + c.Phone1c;

      // Let's check that it's not empty and that it's a valid phone number (logic not listed here)
      if (!String.IsNullOrEmpty(c.Customer.PhoneNumber))
      {
        // Let's remove the fact that there was an error! 
        ModelState["Customer.PhoneNumber"].Errors.Clear();
      } // Else keep the error there. 

      if (ModelState.IsValid)
      {
        Response.Write("<H1 style'background-color:white;color:black'>VALIDATED</H1>");
      }
      return View("Index", c);
    }

  }

Посмотреть

@model MVVM1.Models.CustomerViewModel

@using (Html.BeginForm("Index", "Detail"))
{  
  <table border="1" cellpadding="1" cellspacing="1">
    <tr>
      <td>@Html.LabelFor(m => m.Customer.FirstName)</td>
      <td>
        @Html.TextBoxFor(m => m.Customer.FirstName)
        @Html.ValidationMessageFor(m => m.Customer.FirstName)
      </td>
    </tr>
    <tr>
      <td>@Html.LabelFor(m => m.Customer.LastName)</td>
      <td>
        @Html.TextBoxFor(m => m.Customer.LastName)
        @Html.ValidationMessageFor(m => m.Customer.LastName)
      </td>
    </tr>
    <tr>
      <td>@Html.LabelFor(m => m.Customer.Age)</td>
      <td>
        @Html.TextBoxFor(m => m.Customer.Age)
        @Html.ValidationMessageFor(m => m.Customer.Age)
      </td>
    </tr>

    <tr>
      <td>@Html.LabelFor(m => m.Customer.PhoneNumber)</td>
      <td width="350">
        @Html.TextBoxFor(m => m.Phone1a, new { size="4", maxlength="3" })
        @Html.TextBoxFor(m => m.Phone1b)
        @Html.TextBoxFor(m => m.Phone1c)
        <div>
        @Html.ValidationMessageFor(m => m.Customer.PhoneNumber)
        </div>
      </td>
    </tr>
    <tr>
      <td></td>
      <td>
        <input type="submit" value="Submit" /></td>
    </tr>
  </table>
}

3 ответа

Решение

Одна вещь, которая выскакивает у меня, это:

  if (ModelState.IsValid) 
  { 
    Response.Write("<H1 style'background-color:white;color:black'>VALIDATED</H1>"); 
  } 
  return View("Index", c); 

Помните, что модели представлений хороши для передачи данных на ваш контроллер и обратно на вашу модель. Я рекомендую вам добавить свойство IsValid к вашей модели представления, а затем установить для него значение true вместо вызова Response.Write. Затем просто добавьте это в начало вашего частичного представления:

@if (Model.IsValid)
{
    <H1 style'background-color:white;color:black'>VALIDATED</H1>
}

Вы также можете перейти к ModelState, но некоторые утверждают, что это не лучшая практика. Однако, если вы не хотите добавлять свойство в вашу модель для чего-то, что вы можете просто увидеть в своем представлении, вы можете просто сделать это:

@if (ViewData.ModelState.IsValid)

Еще одна неприятная вещь - атрибуты проверки MVC обычно используются для проверки в пользовательском интерфейсе. Эта проверка может быть повторно использована в других областях, но в некоторых случаях является неоптимальной. Кроме того, вы не всегда сможете изменить модель своего домена. Поэтому, чтобы сохранить всю проверку пользовательского интерфейса в одном месте, я обычно оборачиваю свои доменные модели в мои модели представлений, чтобы вы получили что-то вроде этого:

public class CustomerViewModel                      
{                      
    public CustomerModel Customer { get; set; }

    [Required(ErrorMessage="Primer nombre!")]                        
    public string FirstName
    {
        get { return Customer.FirstName; } 
        set { Customer.FirstName = value; }
    }
...

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

Я сам знакомлюсь с MVC, но вчера я исследовал эту же тему и пришел к выводу, что не следует напрямую включать объект модели в ViewModel. Поэтому я понимаю, что было бы плохой практикой включать вашу CustomerModel непосредственно в CustomerViewModel.

Вместо этого вы хотите перечислить все свойства из CustomerModel, которые вы хотите включить в вашу ViewModel. Затем вы хотите либо вручную сопоставить данные из CustomerModel с CustomerViewModel, либо использовать такой инструмент, как AutoMapper, который автоматически делает это с помощью строки кода, подобной этой, внутри вашего метода действия:

public ViewResult Example()
{
    // Populate/retrieve yourCustomer here
    Customer yourCustomer = new CustomerModel();

    var model = Mapper.Map<CustomerModel, CustomerViewModel>(yourCustomer);

    return View(model);
}

В этом случае Mapper.Map вернет CustomerViewModel, который вы можете передать в View.

Вам также нужно будет включить в метод Application_Start следующее:

Mapper.CreateMap<CustomerModel, CustomerViewModel>();

Вообще я нашел AutoMapper довольно простым для работы. Это происходит автоматически, когда имена полей совпадают, если они не совпадают или у вас есть вложенный объект, вы можете указать эти сопоставления в строке CreateMap. Поэтому, если ваша CustomerModel использует объект Address вместо отдельных свойств, вы должны сделать это:

Mapper.CreateMap<CustomerModel, CustomerViewModel>()
    .ForMember(dest => dest.StreetAddress, opt => opt.MapFrom(src => src.Address.Street));

Пожалуйста, кто-нибудь поправит меня, если я ошибаюсь, так как я просто разбираюсь в MVC.

Я бы сказал, что ваша реализация ViewModel довольно стандартная. Вы используете ViewModel, чтобы действовать как промежуточный объект между вашим View и вашей моделью домена. Что является хорошей практикой.

Единственное, что меня утомит, так это то, как вы обрабатываете ошибки модели, а также ваша ViewModel должна иметь некоторые атрибуты. Например, вы можете использовать RegularExpressionAttribute Учебный класс:

  public class CustomerViewModel
  {
    public CustomerModel Customer { get; set; }

    [RegularExpression(@"^\d{3}$")]
    public string Phone1a { get; set; }
    [RegularExpression(@"^\d{3}$")]
    public string Phone1b { get; set; }
    [RegularExpression(@"^\d{4}$")]
    public string Phone1c { get; set; }
  }
Другие вопросы по тегам