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

MVC 3 - Как это будет работать?

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

Я либо что-то пропустил, либо Microsoft действительно перепутала MVC. Я работал над проектами Java MVC, и они были чистыми и простыми. Это, однако, полный беспорядок IMO. Примеры онлайн, такие как NerdDinner и проекты, обсуждаемые в ASP.Net, слишком просты, поэтому они "просто" работают. Извините, если это звучит отрицательно, но это мой опыт.

У меня есть репозиторий и служба, которая ссылается на репозиторий. Контроллеры звонят по телефону.

Мой уровень данных НЕ является постоянным, поскольку классы были сгенерированы SQL-металом. Из-за этого у меня много ненужной функциональности. В идеале я бы хотел иметь POCO, но пока не нашел хорошего способа достичь этого.

* Обновление: Конечно, Microsoft ничего не испортила - я это сделал. Я не совсем понял инструменты, которые были в моем распоряжении. Главным недостатком в том, что я сделал, было то, что я выбрал неправильную технологию для сохранения моих сущностей. LINQ to SQL хорошо работает в приложениях с поддержкой состояния, поскольку контекст данных можно легко отслеживать. Однако это не относится к контексту без атак. Какой был бы правильный выбор? Код Entity Framework сначала или код работают очень хорошо, но что более важно, так это то, что это не имеет значения. MVC или приложения на передней панели не должны знать, как сохраняются данные. *

При создании entites я могу использовать привязку объектов:

[HttpPost]
public ActionResult Create(Customer c)
{
    // Persistance logic and return view
}    

Это отлично работает, MVC делает некоторые привязки за сценой, и все "весело".

Это не было "Веселой Доброй". Клиент был моделью домена, и, что еще хуже, он зависел от среды постоянства, потому что он был создан SQL metal. Теперь я буду создавать модель моего домена, которая не зависит от уровней хранения данных или уровня представления. Затем я бы создал модель представления из моей модели домена и использовал ее.

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

    [HttpPost]
    public ActionResult Create(Order o)
    {
        // Persistance logic and return view
    }

Чтобы сохранить заказ, мне нужен Клиент или, по крайней мере, CustomerId. CustomerId присутствовал в представлении, но к тому времени, когда он получил метод Create, он потерял CustomerId. Мне не нравится сидеть вокруг отладки кода MVC, так как я не смогу изменить его в среде хостинга в любом случае.

Хорошо, немного стонать здесь, извините. Теперь я создам модель представления, называемую NewOrder, или SaveOrder, или EditOrder в зависимости от того, чего я пытаюсь достичь. Эта модель представления будет содержать все свойства, которые меня интересуют. Автоматическая привязка из коробки, как следует из названия, свяжет переданные значения и ничего не будет потеряно. Если я хочу пользовательское поведение, тогда я могу реализовать свою собственную "привязку", и он выполнит эту работу.

Альтернативой является использование FormCollection:

[HttpPost]
public ActionResult Create(FormCollection collection)
{
   // Here I use the "magic" UpdateModel method which sometimes works and sometimes doesn't, at least for LINQ Entities.               
}

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

Автообязательность с моделями просмотра будет работать большую часть времени. Если это не так, вы можете переопределить его. Откуда вы знаете, что это всегда будет работать? Вы unit test и хорошо спать.

Другой подход, который я пробовал, заключается в использовании объектов ViewModel - wrapper с правилами проверки. Это звучит неплохо, за исключением того, что я не хочу добавлять аннотации к классам Entity. Этот подход отлично подходит для отображения данных, но что вы делаете, когда речь идет о записи данных?

[HttpPost]
public ActionResult Create(CustomViewWrapper submittedObject)
{
    // Here I'd have to manually iterate through fields in submittedObject, map it to my Entities, and then, eventually, submit it to the service/repository.
}    

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

Я что-то упустил или так работает Microsoft MVC3? Я не вижу, как это упрощает вещи, особенно в сравнении с Java MVC.

Прошу прощения, если это звучит отрицательно, но до сих пор это был мой опыт. Я ценю тот факт, что структура постоянно совершенствуется, внедряются такие методы, как UpdateModel, но где находится документация? Может быть, пора остановиться и немного подумать? Я предпочитаю, чтобы мой код был последовательным во всем, но с тем, что я видел до сих пор, я не уверен, что это правильный путь вперед.

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

4b9b3361

Ответ 1

Я не думаю, что ваша проблема связана с asp.net MVC, но со всеми частями, которые вы выбрали для совместного использования.

Вы хотите, чтобы это было грубо и просто?

Используйте POCOs со всех сторон и реализуйте репозиторий там, где он вам нужен.

Я не использовал Java MVC, но это заставило бы весь вопрос выглядеть не так, как заявка, если вы включите, как вы решили конкретную проблему там.

Позвольте прояснить некоторые заблуждения или, может быть, недопонимание:

  • Вы можете передавать сложные объекты через сообщение в представление. Но вы только хотите сделать это, если это имеет смысл, см. Следующую марку
  • Образец, который вы выбрали, вызывает некоторые тревоги. Принятие данных клиента или CustomerID для заказа и не проверка авторизации может быть большой дырой в безопасности. То же самое можно сказать о заказе, в зависимости от того, что вы принимаете/позволяете. Это огромный случай использования ViewModels, независимо от POCOs, LINQ, Asp.net MVC или Java MVC.
  • Вы можете передавать простые значения, которые не отображаются через сообщение в представлении. Это сделано со скрытыми полями (которые ASP.NET MVC поддерживает очень просто, чтобы использовать значение модели), и в некоторых сценариях он генерирует скрытые поля для вас.
  • Вы никоим образом не принуждаете использовать linq2sql с Asp.net MVC. Если вам не хватает того, как вы собираетесь его использовать, отодвиньтесь от него. Примечание. Мне нравится linq2sql, но как это связано с вашим представлением о том, что вы можете сделать с asp.net mvc, является странным.
  • "Я работал над проектами Java MVC, и они были чистыми и простыми". Работа над проектом - это не то же самое, что разрабатывать проект самостоятельно. Навыки дизайна влияют на то, что вы получаете от чего-либо. Не говоря уже о вашем случае, но просто хотел указать на это, учитывая отсутствие специфики того, чего вам не хватает в Java MVC.
  • "Мой уровень данных НЕ является постоянным, поскольку классы были созданы SQL-металлом. Из-за этого у меня много ненужных функций. В идеале я бы хотел иметь POCO, но я не нашел хорошего пути для достижения этого еще". Вы выбрали неправильную технологию, linq2sql не подходит для этого требования. Это не было проблемой в проектах, которые я использовал, но все разработано таким образом, что оно менее привязано к его особенностям, чем кажется. Тем не менее, просто переходите к чему-то другому. Кстати, вы должны были поделиться тем, что вы использовали с Java MVC.
  • "CustomerId присутствовал в представлении, но к тому времени, когда он получил метод Create, он потерял CustomerId". Если свойство находится в порядке, вы можете поспорить, что ваш код имеет ошибку. Теперь это был совершенно другой реальный вопрос, почему он не использует CustomerId/такой вопрос, который возникнет: ваш класс Customer, View, то, что вы передаете в ответы View..., будет включать, но не ограничивайтесь: проверяйте исходный HTML-код в браузере, чтобы узнать, какое значение вы действительно публикуете в источнике (вместо этого используйте скрипт, чтобы увидеть то же самое), убедитесь, что CustomerId действительно имеет значение, когда вы передаете его в представление.
  • Вы сказали: "magic" метод UpdateModel, который иногда работает, а иногда и не". Это не волшебство, вы можете видеть, что он делает и, конечно, найти информацию об этом. В информации, которую вы публикуете, что-то отсутствует, моя ставка не является необязательными полями или неправильными значениями для информации, которая анализируется... представления поддерживают добавление валидаций для этого. Без проверок этого может не хватить.
  • Вы сказали в комментарии: "После вызова UpdateModel я не могу явно указать CustomerId, мне нужно будет вернуть объект клиента, а затем назначить его на порядок, который кажется накладным как все, что я нужно CustomerId"... вы принимаете CustomerId, который является пользователем (даже если это скрытое поле), вы действительно хотите проверить этот ввод. Кроме того, вы противоречите себе, вы утверждаете, что просто нуждаетесь в CustomerId, но тогда вы говорите, что вам нужен полный объект клиента, связанный с порядком. Что это такое, если вы только привязываете CustomerId, вам все равно нужно получить его и назначить ему. Нет волшебства, кроме сцен...
  • Также в комментарии: "Обновить модель - это то, чего я сейчас полностью избегаю, так как не знаю, как она будет вести себя с объектами LINQ. В классе модели представления я создал конструктор, который преобразует объект LINQ в мою модель представления Это уменьшило количество кода в контроллере, но все равно не кажется правильным". Причина использования ViewModel (или EditModel) заключается не в том, что это linq2sql... это потому, что из-за многих других причин вы подвергаете воздействию модели, которая позволяет манипулировать тем, что вы действительно хотите разрешить пользователю изменять. Воздействие исходной модели, если у нее есть поля, которые пользователь не должен разрешать изменять, является реальной проблемой.

Ответ 2

1) В случае сохранения заказа и отсутствия наличия CustomerId. Если Order имеет на нем свойство CustomerId, и у вас есть строго типизированное представление, вы можете перенести это обратно на действие вашего контроллера, добавив

@Html.HiddenFor(model => model.CustomerId)

Для этого вам будет добавлено связующее устройство по умолчанию.

2) Что касается использования модели представления, я бы рекомендовал этот подход. Если вы используете что-то вроде AutoMapper, вы можете взять некоторую боль из избыточных картографических сценариев. Если вы используете что-то вроде Fluent Validation, вы можете легко отделить проверки.

Здесь хорошая ссылка на общий подход реализации ASP.NET MVC.

Ответ 3

Если ваше представление правильно определено, вы можете легко сделать это >

    [HttpPost]
    public ActionResult Create(Order o, int CustomerId)
    {
        //you got the id, life back to jolly good (hopefully)
        // Persistance logic and return view
    }

EDIT:

как упомянуто attadieni, по правильному виду, я имел в виду, что у вас есть что-то подобное внутри тега формы >

@Html.HiddenFor(model => model.CustomerId)

ASP.NET MVC автоматически привязывается к соответствующим параметрам.

Ответ 4

Мне не хватает проблемы.

У вас есть заказ контроллера с действием создания так же, как вы сказали:

public class OrderController()
{
    [HttpGet]
    public ViewResult Create()
    {
        var vm = new OrderCreateViewModel { 
            Customers = _customersService.All(),
            //An option, not the only solution; for simplicities sake
            CustomerId = *some value which you might already know*; 
            //If you know it set it, if you don't use another scheme.
        }                             
        return View(vm);    
    }

    [HttpPost]
    public ActionResult Create(OrderCreateViewModel model)
    {
        // Persistance logic and return view
    }  
}

Функция "Создать действие" возвращает обратно модель представления типа OrderCreateViewModel, которая выглядит так.

public class OrderCreateViewModel 
{
    // a whole bunch of order properties....
    public Cart OrderItems { get; set; }
    public int CustomerId { get; set; }

    // Different options
    public List<Customer> Customers { get; set; } // An option
    public string CustomerName { get; set; } // An option to use as a client side search
}

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

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

"Чтобы сохранить заказ, мне нужен Клиент или хотя бы CustomerId. CustomerId присутствовал в представлении, но к тому времени, когда он получил метод Create, он потерял CustomerId."

Что? Зачем? Если CustomerId был в представлении, установлен и отправлен обратно, он в модели для метода HttpPost Create, который именно там, где вам это нужно. Что значит потерять?

ViewModel сопоставляется с объектом модели типа order. Как было предложено, использование AutoMapper полезно...

[HttpPost]
public ActionResult Create(OrderCreateViewModel model)
{
    if(!ModelState.IsValid)
    {
      return View(model);
    }   

    // Persistance logic and return view

    var orderToCreate = new Order();

    //Build an AutoMapper map
    Mapper.CreateMap<OrderCreateViewModel, Order>();

    //Map View Model to object(s)
    Mapper.Map(model, orderToCreate);       

    //Other specialized mapping and logic

    _orderService.Create(orderToCreate);

    //Handle outcome. return view, spit out error, etc.
}  

Это не обязательно, вы можете сопоставить его вручную, но это упрощает процесс.

И ты настроен. Если вы не хотите использовать аннотации данных для проверки, хорошо, сделайте это на уровне сервиса, используйте свободную библиотеку проверки, упомянутую, независимо от того, что вы выберете. После того, как вы вызовете метод Create() вашего уровня обслуживания со всеми данными, вы хорошо пойдете. Где отключиться? Что нам не хватает?

Ответ ataddeini правильный, я просто пытаюсь показать немного больше кода. Upvote ataddeini

Ответ 5

Если идентификатор клиента уже находится в модели заказа (в этом примере), он должен быть доступен без расширения сигнатуры метода. Если вы просматриваете источник на визуализированном представлении, является ли идентификатор клиента правильно испускаемым в скрытом поле в форме? Используете ли вы атрибут [Bind] в классе модели Order и непреднамеренно не заполняете идентификатор клиента?

Ответ 6

Я бы подумал, что таблица Order будет включать поле CustomerID, если это так, единственная проблема заключается в том, что вы не включаете какой-либо элемент управления в представление, чтобы сохранить это значение, а затем теряетесь.

Попробуйте следовать этому примеру.

1) GET действие перед отправкой в ​​представление, скажем, вы назначаете идентификатор Customer в этой точке.

public ActionResult Create()
        {
            var o = new Order();
            o.CustomerID = User.Identity.Name; // or any other wher you store the customerID
            return View(o);
        }

2) View, если вы не используете какой-либо элемент управления для CustomerID, например текстовое поле, combobox и т.д., вы должны использовать скрытое поле для сохранения значения.

@using (Html.BeginForm())
    {
        @Html.HiddenFor(m => m.CustomerID)

        <label>Requested Date:</label>
        @Html.TextBoxFor(m => m.DateRequested)

        ...
    }

3) Наконец, действие POST, чтобы получить и сохранить порядок. Здесь, когда CustomerID хранился в скрытом значении, модель Binder автоматически поместит все значения формы в объект Order o, тогда вам просто нужно использовать методы CRUD и сохранить его.

[HttpPost]
public ActionResult Create(Order o)
        {
            return View();
        }

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

Для более сложных сценариев, где не все поля находятся в одной и той же модели, вам нужно использовать ViewModels. Например, для сценариев матер-детали вы создадите OrderViewModel, который имеет два свойства: Order o и IEnumerable <OrderDetail> od, но опять же вам понадобится явно использовать значения в представлении или использовать скрытые поля.

В последних выпусках теперь вы можете использовать классы POCO и Code-First, что делает все более простым и понятным. Возможно, вы захотите попробовать EF4 + CTP5.

Ответ 7

если вы используете сервисы (например, сервисный уровень, бизнес-фасад), чтобы обработать, скажем, OrderModel, вы можете извлечь интерфейс и получить свой ViewModel/DTO для его реализации, чтобы вы могли вернуть ViewModel/DTO к сервису.

Если вы используете репозитории для непосредственного управления данными (без слоя servie) в контроллере, вы можете сделать это старым старым способом загрузки объекта из репозитория, а затем сделать UpdateModel на нем.

[HttpPost]
public ActionResult Create(string customerCode, int customerId, Order order)
{
    var cust = _customerRepository.Get(customerId);
    cust.AddOrder(order);//this should carry the customerId to the order.CustomerId
}

Кроме того, URL-адреса могут немного помочь, если это имеет смысл, я имею в виду, что вы можете добавить идентификатор клиента в URL-адрес, чтобы создать порядок.

UpdateModel должен работать, если ваш FormCollection имеет значения для свойств, которые не являются nullable, и они пустые /null в FormCollection, тогда UpdateModel должен завершиться неудачей.