2012-03-01 3 views
2

Со следующим простым действием ...ASP.NET MVC Добавлено потерянный интерфейс модели Значения свойств и ModelState имеют ошибки. Ограничение привязки модели? Вопрос о сериализации?

[HttpPost] 
public PartialViewResult DoSomething(AnimalInfo animalInfo) 
{ 
    // animalInfo.AnimalKey SHOULD == DogKey { Id = 1, Name = "Dog" } 
    // BUT animalInfo.AnimalKey == null 

    return Something(); 
} 

Проводка к этому действию в порядке, и большинство свойств animalInfo были доступны, для объекта я создал сам, за исключением. Я предполагал, что это проблема с сериализацией, поэтому я добавил некоторую базовую сериализацию в мой класс, но я все равно получаю null для объекта AnimalKey (что определенно не является нулевым во время рендеринга).

Вот как я определяю AnimalInfo:

[DataContract] 
public class AnimalInfo : IAnimalInfo 
{ 
    [DataMember] 
    public IAnimalKey AnimalKey { get; set; } 

    public string Name { get; set; } 
} 

[DataContract] 
public class DogKey : IAnimalKey 
{ 
    public DogKey(int id){ DogId = id; } 

    [DataMember] 
    public int DogId { get; set; } 
} 

И я отправляю к действию с целью, как это ...

<% var currentAnimal = new AnimalInfo { AnimalKey = new DogKey(1), Name = "Dog" }; %> 
<% using (Html.BeginForm("DoSomething", "Controller", currentAnimal.AnimalInfo)) 
{ 
    %><button type="submit">post</button><% 
} %> 

Но к тому времени мое действие выполняется, AnimalKey имеет станет нулевым, а Name - «Собака». Взгляд в ModelState показывает то же самое. Это похоже на проблему с сериализацией. Это так? Если это так, DataContract и DataMember недостаточно подходят для обработки этого метода?


UPDATE

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

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Namespace.IAnimalInfo>" %> 

<% using (Html.BeginForm("DoSomething", "OM", Model)) 
{ 
    Html.HiddenFor(m => m.AnimalKey); 
    Html.HiddenFor(m => m.Name); 
    %><button type="submit">revert</button><% 
} %> 
  • Do MVC HtmlHelpers не сериализации сложных объектов на всех?

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

  • Или даже написать собственный сериализатор для строки для скрытого, а затем десериализовать его в обратном пути?


PROBABAL ПРИЧИНА

После немного более бесполезный Я почти уверен, что это связано с IAnimalKey быть интерфейсом. Когда я добавил другое свойство DogKey к моей модели, значение возвращается. Таким образом, кажется, что он потерял свои знания о том, какой тип объекта был на этом посту или вообще не поддерживает интерфейсы.


DIRTY FIX

Смотрите мой ответ

+1

Я не думаю, что вы можете передать богатый объект в «BeginForm». Объект используется для создания «RouteValueCollection», который представляет собой просто плоский словарь, а не полное дерево объектов. – bhamlin

+0

Если вы посмотрите на HTML, сгенерированный Html.HiddenFor (m => m.AnimalKey) ;, как он выглядит: как называется сгенерированное скрытое поле и каково его значение? – Rune

+0

Я просто сталкиваюсь с подобной ситуацией ... независимо от того, что я пытаюсь использовать свойство 'ConcurrencyCheck' типа' byte [] ', не будет добавлено в' ModelState'. Угадай, что? Он объявляется в интерфейсе и затем объявляется на каждом объекте «POCO». Мне нужно создать 'ViewModels'. Другого пути нет. Я думаю, что «ViewModel» - это путь. Больше кода на пути ...: D В любом случае вы сделали какой-то тяжелый рабочий код! Congrats ... –

ответ

2

Haven» t получил какие-либо ответы на конкретную проблему, возможно, потому, что он не поддерживается ASP.NET MVC, хотя я также не смог найти подтверждение на MSDN. Так вот мое грязное исправление из-за отсутствия лучшего ответа. AnimalInfo в абстрактный класс и реализовал то, что я назвал методами Serialize и Deserialize, которые преобразуют каждый AnimalKey в строку, которая может быть использована для воссоздания AnimalKey позднее.

Это работает, потому что в моем новом классе AnimalInfo, который я использую в качестве моей модели MVC, у меня есть свойство для SerializedKey, которое на Get пытается сериализовать AnimalKey. Использование HtmlHelper для хранения этого свойства приведет к тому, что Get будет выполнять сериализацию, сохраняя продукт в визуализированном представлении. Метод Set попытается выполнить десериализацию предоставленной строки в AnimialKey. Поэтому, когда возникает действие Post, MVC пытается установить SerializedAnimalKey, и, делая это, вызывает Deserialize и воссоздает объект AnimalKey со всем, что я намеревался сериализовать.

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

public abstract class AnimalKey 
{ 
    public abstract AnimalType AnimalType { get; } 
    public abstract string Serialize(); 

    public static AnimalKey Deserialize(string serializedKey) 
    { 
     var split = serializedKey.Split('|'); 
     switch ((AnimalType)Convert.ToInt32(split[0])) 
     { 
      case AnimalType.Dog: 
       var dogKey = new DogKey(); 
       var dogProperties = split[1].Split(','); 
       dogKey.DogId = Convert.ToInt32(dogProperties[0]); 
       return dogKey; 
      // TODO: Other key implementations 
     } 
    } 
} 

public class AnimalInfo : IAnimalInfo 
{ 
    public AnimalKey AnimalKey { get; set; } 

    public string SerializedAnimalKey 
    { 
     get { return AnimalKey != null 
      ? AnimalKey.Serialize() 
      : string.Empty; 
     } 
     set { AnimalKey = String.IsNullOrEmpty(value) ? null : AnimalKey.Deserialize(value); } 
    }  
} 

public class DogKey : AnimalKey 
{ 
    [DataMember] 
    public int DogId { get; set; } 

    public override AnimalType AnimalType 
    { 
     get { return AnimalType.Dog; } 
    } 

    public override string Serialize() 
    { 
     var serializeBuilder = new StringBuilder(); 
     serializeBuilder.AppendFormat("{0}|", (int)AnimalType); 
     serializeBuilder.AppendFormat("{0},", DogId); 

     return serializeBuilder.ToString(); 
    } 
} 
+0

Хорошо, что вы смогли его решить.Интересное решение. – Tx3

+0

Он раскрывает другие потенциальные проблемы по треку, но он выполняет свою работу для этого относительно простого случая. – Arkiliknam

0

вы не можете получить вещи сделано при использовании

<% using (Html.BeginForm("DoSomething", "Controller", currentAnimal.AnimalInfo)) 

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

Вы можете попробовать это

@using (Html.BeginForm()) 
{ 
    @Html.TextBoxFor(o => Model.Name); 
    @Html.TextBoxFor(o => Model.AnimalKey.DogId); 
    <button type="submit">post</button> 
} 

Значение будет проходить на сервер почтовых данных и он работает.

+0

Проблема в том, что мой AnimalKey имеет IAnimalKey, и я не знаю, содержит ли он DogId или CatId, поэтому явным образом пишу HtmlHelpers для них. Это может быть ограничение, о котором я раньше не знал. – Arkiliknam

+0

Возможно ли включить ID в IAnimalKey или, возможно, также свойство «Тип»? Я думаю, это упростит ситуацию. – Teddy

1

Помните, что @Html.Hidden и @Html.HiddenFor просто помощники, чтобы создать этот

<input type="hidden" value="yourValue" name="yourName" /> 

Скрытое поле очень полезно для строк, чисел, Guid и т.д. Размещение сложного объекта на нем будет, вероятно, сделать ToString операцию на нем. Вы можете использовать панель инструментов разработчика (обычно F12), чтобы проверить, что содержит input.

Должен признать, что я обычно делал такие вещи с использованием JavaScript (с jQuery). Поскольку пользовательские привязки имеют тенденцию становиться сложными. Что я делаю, так это то, что у меня есть модель представления, которую я визуализирую на экране, и на отправке я соберу необходимую информацию и сделаю $.ajax или $.post.

Возможно, кто-то еще может дать вам идеи о том, как реализовать пользовательскую привязку.

+0

Как вы уже упоминали, я думаю, что HtmlHelper, скорее всего, просто вызывал ToString на моем объекте ... поэтому кажется, что только самые простые типы значений поддерживаются из коробки. Я внедрил собственный Serialize для вывода строки и использовал ее для хранения и восстановления моей информации. – Arkiliknam

+0

@Arkiliknam интересное решение. Я наткнулся на многие проблемы с привязкой и, наконец, сдался и перешел на связь JSON между клиентом (jQuery) и сервером (ASP.NET MVC). – Tx3

+0

ASP.NET MVC Model Binding - это фантастика, когда она работает, но иногда это может быть больно, когда вы пытаетесь сделать что-то немного другое. Я также отправился на путь jQuery и JSON много времени, чтобы справиться с этими проблемами, но предпочитаю сохранять вещи .NET, когда это возможно. – Arkiliknam

0

Я думаю, что у вас есть основное недоразумение модели MVC.

Что вы описываете, десериализируя экземпляр и сохраняя на стороне клиента, это способ веб-форм делать вещи. Экземпляры сериализуются в ViewState, а при возврате страницы данные экземпляра регидратируются с использованием ViewSource.

Что-то вроде этого:

public AnimalInfo animalInfo 
{ 
    get 
    { 
     return ViewState("AnimalInfo") 
    } 
    set (AnimalInfo value) 
    { 
     ViewState("AnimalInfo") = value 
    } 
} 

Что здесь происходит (в мире веб-форм) является то, что экземпляр сериализации в ViewState в set и десериализации из ViewState в get.

Этот механизм не существует в MVC.

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

Во-вторых, концепция PostBack также не существует в MVC. Как только страница отправляется клиенту, вот и все.

Что MVC действительно делает это использовать оригинальный веб-методу POST и GET.

Передача информации осуществляется одним из двух способов: через querystring (GET) или через форму (POST).

Все, что говорится, вот некоторые из способов MVC делать то, что вы хотите.

Если это форма редактирования/обновления, вы можете использовать Html.EditorForModel. Это создаст поле формы для каждого свойства вашего ViewModel. Это не только так, но если вы правильно подключите свой контроллер ([HttpPost] public PartialViewResult DoSomething(AnimalInfo animalInfo)), MVC сканирует POSTED-форму и восстановит экземпляр для вас из значений поля формы и назначит новый экземпляр переменной animalInfo.

Если это не форма редактирования/обновления, зачем вам нужен контроллер для регидратации экземпляра?

Есть также способы гидрат только определенные свойства, например, как @HiTeddy предложил (@Html.TextBoxFor(o => Model.Name);). Это потребует некоторых дополнительных проводов на контроллере, чтобы сообщить ему, где для получения значений свойств из.

+0

Я не уверен, что вы поняли вопрос. У меня есть модель и понимаю, что это такое. Я пытаюсь опубликовать модель с одним свойством, которое имеет тип IAnimalKey. Когда я делаю сообщение, используя форму, например, только это свойство становится нулевым. Однако он будет работать, если я поменю мой IAnimalKey на конкретный класс DogKey. Но я не хотел этого делать, поскольку у меня много классов IAnimalKey. – Arkiliknam

+0

И если мне нужен POST для моего контроллера, его нужно будет сериализовать в визуализированное представление, чтобы сделать это, в большинстве случаев достаточно простого .ToString(), иначе все знания модели будут потерял. – Arkiliknam