Подтвердить что ты не робот

Entity Framework с сильно типизированным MVC

Я использую ASP.NET MVC и ADO.NET Entity Framework вместе.

Я хочу, чтобы мои представления и контроллеры были строго типизированы.

Но как я должен обрабатывать ассоциации сущностей?

Вот простой пример:

У человека есть один отдел. Департамент имеет ноль или более людей.

Entity Data Model of Person and Department entities

Мой контроллер передает экземпляр объекта Person и коллекцию всех объектов отдела в представление.

public class PersonController : Controller
{
    ...

    //
    // GET: /Person/Create

    public ActionResult Create()
    {
        Person Model = new Person();
        Model.Id = Guid.NewGuid();
        ViewData["Departments"] = db.Department;
        return View(Model);
    } 
    ...
}

В My View есть "Департамент" DropDownList со всеми отделами в качестве параметров.

<% using (Html.BeginForm()) {%>

    <fieldset>
        <legend>Fields</legend>
        <p>
            <label for="Id">Id:</label>
            <%= Html.TextBox("Id") %>
            <%= Html.ValidationMessage("Id", "*") %>
        </p>
        <p>
            <label for="Name">Name:</label>
            <%= Html.TextBox("Name") %>
            <%= Html.ValidationMessage("Name", "*") %>
        </p>
        <p>
            <label for="Department">Family:</label>
            <%= Html.DropDownList("Department", new SelectList((IEnumerable)ViewData["Departments"], "Id", "Name"))%>
            <%= Html.ValidationMessage("Department", "*")%>
        </p>
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>

<% } %>

Но объект Person, отправленный в контроллер, имеет no Department и не работает!

public class PersonController : Controller
{
    ...

    //
    // POST: /Person/Create

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create(Person Model)
    {
        try
        {
            db.AddToPerson(Model);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        catch
        {
            return View();
        }
    }
    ...
}

Почему выбранный отдел из DropDownList "Департамент" автоматически добавлен в модель Person?

Как использовать ADO.NET Entity Framework и ASP.NET MVC с сильно типизированными представлениями и контроллерами?

4b9b3361

Ответ 1

Я попытаюсь направить вас с стороны MVC, так как это проблема, я полагаю,

В методе Create() вы создаете объект Person и список департаментов из базы данных, тогда оба объекта передаются в представление. Вид берет данные из списка Департамента и использует его для отображения HTML-формы - используя только Идентификатор и Имя.

На следующем шаге форма отправляется на сервер в виде набора пар ключ-значение (стандартный POST). Механизм маршрутизации принимает запрошенный url из атрибута действия и решает его в PersonController.Create(Person Model). Аргументом этого метода является Person, поэтому вложение данных происходит, создает новый экземпляр класса Person и пытается сопоставить входящие данные со свойствами Person. В случае Департамента входное значение - это идентификатор отдела (потому что это то, что вы задали как элемент значения для DropDownList), в то время как отдел свойств класса Person, вероятно, относится к типу отдела. Это несоответствие, поэтому оно не может заполнить свойство, и оно остается пустым.

Как вы можете видеть, это не ограничение DropDownList, проблема в том, что вы не можете передавать все данные Департамента в DropDownList и обновлять его во время сохранения (например, с Person) из-за характера запроса POST, и поэтому DropDownList принимает только два значения из каждого Департамента (значение и имя).

Мое обычное решение: как правило, мои модели не являются теми же классами, что и мои бизнес-объекты, я делаю это, имея два свойства в модели: получить только свойство IEnumerable и другое свойство DepartmentId (get/set). Затем я использую его следующим образом:

<%= Html.DropDownList("DepartmentId", Model.Departments) %>

Затем в действии сохранения я беру Департамент из db, используя DepartmentId, назначаю его Person и сохраняю.

В вашем случае (модели, являющиеся бизнес-объектами), я бы, вероятно, не попытался автоматически привязать Департамент к модели Person, но просто возьму Id и сделаю это сам.

Это просто предположение (я не специалист по EF), но я думаю, что у вас может быть другая проблема: если db является полем на контроллере и он воссоздается по каждому запросу, это может быть ненужным накладные расходы. Я надеюсь, что он не откроет соединение db каждый раз, пожалуйста, проверьте его.

Надеюсь, что поможет

Ответ 2

Я использую ViewModel (проверьте учебник NerdDinner, ссылки внизу).

Во-первых, вам нужно подделать ограничение внешнего ключа, расширив вашу модель в частичном:

public partial class Person
{
  public int DepartmentId
  {
    get
    {
      if(this.DepartmentsReference.EntityKey == null) return 0;
      else 
        return (int)this.DepartmentsReference.EntityKey.EntityKeyValues[0].Value;
    }
    set
    {
      this.DepartmentsReference.EntityKey = 
          new EntityKey("YourEntities.DepartmentSet", "Id", value);
    }
  }
}

Во-вторых, создайте ViewModel:

public class PersonFormViewModel
{
  public SelectList Departments { get; set: }
  public Person Pers { get; set; }

  public PersonFormViewModel(Person p, List<Department> departments)
  {
    this.Pers = p;
    this.Departments = new SelectList(departments, "Id", "Name", p.DepartmentId);
  }
}

В-третьих, действие контроллера (сокращенный пример создания):

public ActionResult Create()
{
  YourEntities entities = new YourEntities();
  List<Department> departments = entities.DepartmentSet.ToList();
  PersonFormViewModel viewModel = 
    new PersonFormViewModel(new Person(), departments);
  return View(modelView);
}    

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude="Id")]Person personToCreate
{
  YourEntities entities = new YourEntities(); // Probably not instantiated here
  entities.AddToPersonSet(personToCreate);
  entities.SaveChanges();
  redirectToAction("Index");
}

В-четвертых, фрагмент представления:

<p>
  <label for="Name">Name:</label>
  <%= Html.TextBox("Name", Model.Pers.Name) %>
  <%= Html.ValidationMessage("Name", "*") %>
</p>
<p>
  <label for="DepartmentId">Family:</label>
  <%= Html.DropDownList("DepartmentId", Model.Departments)%>
  <%= Html.ValidationMessage("DepartmentId", "*")%>
</p>

Литература:

Ответ 3

Я не нашел, что готовый помощник DropDownList отлично работает с привязкой к модели. Мне всегда приходится извлекать значение DropDownList из ViewData и сопоставлять значение вручную в моем контроллере до того, как он внесет изменения в репозиторий или db.

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