Frage

Jetzt habe ich einige Fragen wie diese gesehen, aber es ist nicht genau das, was ich fragen möchte, also entschuldige ich mich für all diese schreienden Duplikat :).

Ich habe ASP.NET MVC kaum berührt, aber soweit ich weiß, gibt es keinen ViewState/ControlState ... gut. Meine Frage ist also, was ist die Alternative, um den Zustand einer Kontrolle zu behalten? Gehen wir zurück zu Old School ASP, wo wir simulieren können, was ASP.NET ViewState/ControlState tut, indem wir versteckte Formulareingaben mit dem Zustand des Kontrolls oder mit MVC erstellen, nehmen wir einfach immer an Ajax und behalten alle staatlichen Kunden auf und machen Ajax Anrufe zum Update?

Diese Frage hat einige Antworten, Aufrechterhaltung des ViewState in ASP.NET MVC?, aber nicht genau das, wonach ich in einer Antwort suche.

Update: Danke für alle Antworten bisher. Nur um zu klären, wonach ich nicht suche und wonach ich suche:

Nicht auf der Suche nach:

  • Sitzungslösung
  • Cookie -Lösung
  • Ich suche nicht, Webformen in MVC nachzuahmen

Was ich bin/suchte:

  • Eine Methode, die den Zustand nur nach dem Nachback behält, wenn Daten nicht auf eine Kontrolle erholt werden. Denken Sie an Webformen mit dem Szenario, nur ein Gitter auf der Ladeseite der Anfangsseite zu binden, dh die Daten, die bei Bedarf nur bei Bedarf wiederhergestellt sind. Wie ich bereits erwähnte, versuche ich nicht, Webformen nachzuahmen und frage mich nur, was Mechanismen MVC bietet.
War es hilfreich?

Lösung

Die Konvention ist bereits erhältlich, ohne zu viele Reifen zu springen. Der Trick besteht darin, die Textboxwerte zu verkabeln, die auf dem Modell basieren, das Sie in die Ansicht übergeben.

[AcceptVerbs(HttpVerbs.Get)]   
public ActionResult CreatePost()
{
  return View();
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CreatePost(FormCollection formCollection)
{
  try
  {
    // do your logic here

    // maybe u want to stop and return the form
    return View(formCollection);
  }
  catch 
  {
    // this will pass the collection back to the ViewEngine
    return View(formCollection);
  }
}

Als nächstes passiert die ViewEngine die FormCollection und stimmt mit den Tasten in der Sammlung mit den ID -Namen/-werten überein, die Sie in Ihrer Ansicht mithilfe der HTML -Helfer haben. Zum Beispiel:

<div id="content">

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

  Enter the Post Title: <%= Html.TextBox("Title", Model["Title"], 50) %><br />
  Enter the Post Body: <%= Html.TextArea("Body", Model["Body"]) %><br />

  <%= Html.SubmitButton() %>

  <% } %>

</div>

Beachten Sie, dass die Textbox und die Texte die IDs von Titel und Körper haben? Beachten Sie nun, wie ich die Werte aus dem Modellobjekt der Ansicht einstelle? Da Sie in einer Formcollection bestanden haben (und die Ansicht so festlegen sollten, dass sie mit einer Formcollection stark eingegeben werden), können Sie jetzt darauf zugreifen. Oder, ohne stark zu typisieren, können Sie einfach ViewData ["Titel"] verwenden (glaube ich).

Poof Dein magischer Sehstat. Dieses Konzept wird über Konfiguration bezeichnet.

Jetzt befindet sich der obige Code in seiner einfachsten und rohen Form unter Verwendung von Formcollection. Die Dinge werden interessant, wenn Sie ViewModels anstelle der FormCollection verwenden. Sie können beginnen, Ihre eigene Validierung Ihrer Modelle/ViewModels hinzuzufügen und die Controller automatisch die benutzerdefinierten Validierungsfehler aufzusprudeln. Das ist allerdings eine Antwort für einen anderen Tag.

Ich würde vorschlagen, ein PostformViewModel anstelle des Postobjekts zu verwenden, jedoch zu jedem His-Eigentum. In beiden Fällen erhalten Sie durch ein Objekt für die Aktionsmethode jetzt eine IsValid () -Methode, die Sie aufrufen können.

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CreatePost(Post post)
{

  // errors should already be in the collection here
  if (false == ModelState.IsValid())
    return View(post);

  try
  {
    // do your logic here

    // maybe u want to stop and return the form
    return View(post);
  }
  catch 
  {
    // this will pass the collection back to the ViewEngine
    return View(post);
  }
}

Und Ihre stark typische Sicht müsste optimiert werden:

<div id="content">

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

  Enter the Post Title: <%= Html.TextBox("Title", Model.Title, 50) %><br />
  Enter the Post Body: <%= Html.TextArea("Body", Model.Body) %><br />

  <%= Html.SubmitButton() %>

  <% } %>

</div>

Sie können es noch einen Schritt weiter gehen und die Fehler in der Ansicht direkt aus dem ModelState, den Sie im Controller festlegen, anzeigen.

<div id="content">

  <%= Html.ValidationSummary() %>

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

  Enter the Post Title: 
    <%= Html.TextBox("Title", Model.Title, 50) %>
    <%= Html.ValidationMessage("Title") %><br />

  Enter the Post Body: 
    <%= Html.TextArea("Body", Model.Body) %>
    <%= Html.ValidationMessage("Body") %><br />

  <%= Html.SubmitButton() %>

  <% } %>

</div>

Interessant mit diesem Ansatz ist, dass Sie feststellen werden, dass ich weder die Validierungszusammenfassung noch die individuellen Validierungsnachrichten in der Ansicht festlegt. Ich übe gerne DDD -Konzepte, was bedeutet, dass meine Validierungsmeldungen (und Zusammenfassungen) in meiner Domäne kontrolliert werden und in Form einer Sammlung übergeben werden. Dann schaue ich durch die Sammlung (falls Fehler vorhanden) und füge sie zur aktuellen Modellstate.adderrors -Sammlung hinzu. Der Rest ist automatisch, wenn Sie die Ansicht (Post) zurückgeben.

Viele Kongress sind aus. Ein paar Bücher, die ich den Mustern sehr detaillierter empfehlen kann, sind:

Und in dieser Reihenfolge deckt die ersten Rohmuttern und Schrauben des gesamten MVC -Gerüsts ab. Letzteres deckt fortschrittliche Techniken außerhalb des Microsoft Official RELM ab, mit mehreren externen Werkzeugen, um Ihr Leben zu erleichtern (Castle Windsor, MOQ usw.).

Andere Tipps

Die Ansicht soll im MVC -Muster dumm sein und nur zeigt, was der Controller ihm gibt (offensichtlich haben wir dort oft eine Logik, aber die Prämisse ist, dass sie nicht sein kann). Daher sind die Steuerelemente nicht verantwortlich Ihr Zustand wird jedes Mal vom Controller kommen.

Ich kann Steven Sandersons Buch Pro ASP.NET MVC von Apress genug empfehlen, um sich mit diesem Muster und dieser Implementierung auseinanderzusetzen.

In Webformen werden Steuerwerte im ViewState beibehalten, sodass Sie (theoretisch) nicht mit jedem Postback neu initialisieren müssen. Die Werte werden (wieder theoretisch) vom Rahmen gepflegt.

Wenn Sie in ASP.NET MVC dem Paradigma folgen, müssen Sie den Zustand in Formularelementen nicht aufrechterhalten. Die Formularelementwerte sind im Post verfügbar, in dem Ihr Controller auf sie reagieren kann (Validierung, Datenbankaktualisierungen usw.). Für alle Formularelemente, die nach dem Verarbeiten der Post angezeigt werden, sind Sie (der Entwickler) für die Initialisierung verantwortlich - das Framework tut dies nicht automatisch für Sie.

Trotzdem habe ich über einen Mechanismus namens Tempdata gelesen, der es Ihrem Controller ermöglicht, Daten nach einer Weiterleitung an einen anderen Controller weiterzuleiten. Es ist tatsächlich eine Sitzungsvariable (oder Cookie, wenn Sie sie als solche konfigurieren), wird jedoch nach der nächsten Anfrage automatisch aufgeräumt.

Die Antwort hängt wirklich von den Arten von Kontrollen ab, für die Sie den Zustand beibehalten möchten. Für grundlegende HTML -Steuerelemente ist es sehr einfach, den Zustand mit Ihren Modellen zu pflegen. Dazu müssen Sie eine stark typisierte Ansicht erstellen.

Wenn wir also ein Benutzermodell mit den Eigenschaften hatten: Benutzername, Vollname, E -Mail, können wir in der Ansicht Folgendes tun:

<%= Html.ValidationSummary() %>

<% using (Html.BeginForm()) { %>
  <fieldset>
    <legend>User details</legend>
    <%= Html.AntiForgeryToken() %>

    <p>
      <label for="Username">Username:</label>
      <%= Html.Textbox("Username", Model.Username, "*") %>
    </p>
    <p>
      <label for="FullName">FullName:</label>
      <%= Html.Textbox("FullName", Model.FullName, "*") %>
    </p>
    <p>
      <label for="Email">Email:</label>
      <%= Html.Textbox("Email", Model.Email, "*") %>
    </p>
    <p>
       <input type+"submit" value="Save user" />
    </p>
  </fieldset>
<% } %>

Wir würden dann zwei Controller -Aktionen haben, die diese Ansicht anzeigen, eine für GET und eine für Post:

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult User()
{
  return View(new User())
}

[AcceptVerbs(HttpVerbs.Post)]
[ValidateAntiForgeryToken]
public ActionResult User([Bind(Include = "Username,FullName,Email")]User user)
{
   if (!ModelState.IsValid()) return View(user);

   try
   {
     user.save()
     // return the view again or redirect the user to another page
   }
   catch(Exception e)
   {
     ViewData["Message"] = e.Message;
     return View(user)
   }
}

Suchst du danach? Oder möchten Sie den Zustand der Modelle beibehalten, die nicht in Form von Anfragen angezeigt werden?

Das Wichtigste ist, dass Ihr Code für die Dauer der Anforderung und zum Ende auf dem Server ausgeführt wird. Die einzigen Informationen, die Sie zwischen Ihren Anforderungen übergeben können, sind grundlegende HTML -Formulardaten, URL -Parameter und Sitzungsinformationen.

Wie andere Leute erwähnt haben, würde ich Steve Sandersans Pro ASP.NET -MVC -Framework nur empfehlen, um das MVC -Framework zu verzeichnen.

  • Versteckte Felder wie:

    <% using (Html.BeginForm<SomeController>(c=>c.SomeAction(null))) {%>
      <%= Html.Hidden("SomeField", Model.SomeField)%>
      <%= Html.Hidden("AnotherField", Model.AnotherField)%>
    
  • Festlegen des spezifischen Modells und keine expliziten Felder (gibt Ihnen versteckte Felder). Im folgenden Beispiel wird das Modell vom Controller mit Werten gefüllt, die aus dem letzten Beitrag empfangen werden. Dadurch ermöglicht dies eine NO -JS -Option auf der Seite, die basierend auf einem Status filtern kann:

    Some Filter: <% using( Html.BeginForm<SomeController>(
            c => c.SomeAction(model.SomeField, model.AnotherField, model.YetAnotherField, null, model.SomeOtherField)
            )) { %>
                <%= Html.DropDownList("status", Model.StatusSelectList)%>
                <input type="submit" value="Filter" class="button" />
                <% } %>
    
  • Verwenden Sie Erweiterungsmethoden, um Felder zu erstellen. Wenn Sie nur möchten, dass die Felder mit veröffentlichten Werten gefüllt werden, wenn Sie fehlgeschlagene Validierungsnachrichten auf dem eingereichten Formular angezeigt werden
  • Auf ASP.NET MVC 2 haben sie eine Möglichkeit eingeführt, eine Instanz in einem versteckten Feld zu speichern ... codiert + (ich glaube) signiert
  • Tempdata Wenn alles der oben genannten nicht tut (durchläuft die Sitzung - auf die nächste Anfrage gereinigt)
  • Wie Sie erwähnt haben, befindet sich der Status bei der Verwendung von AJAX bereits in den zuvor geladenen Feldern in der Client -Site. Wenn Sie einen vollständigen Beitrag ausführen müssen, aktualisieren Sie jedes Feld, das Sie möglicherweise mit Ihrem JS benötigen.

Die oben genannten sind alle unterschiedlichen unabhängigen Optionen, um es zu erreichen, die in verschiedenen Szenarien verwendet werden können. Es gibt weitere Optionen, die ich nicht erwähnt habe, um IE Cookies, Session, Speichern in DB (wie für einen wiederauflösbaren Multi -Stufen -Assistenten), Parameter, die an eine Aktion übergeben wurden. Es gibt keinen einzigen Mechanismus, um sie alle zu regieren, und es sollte nicht sein.

Der beste Weg, dies zu tun, besteht meiner Meinung nach darin, Ihr ursprüngliches Modell auf ein verstecktes Feld zu serialisieren, es dann zu deserialisieren und das Modell auf der Post zu aktualisieren. Dies ist etwas ähnlich mit dem ViewState -Ansatz, nur Sie müssen ihn selbst implementieren. Ich benutze das:

Zuerst brauche ich einige Methoden, die die Dinge erleichtern:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using LuvDaSun.Extensions;
using System.Web.UI;

namespace LuvDaSun.Web.Mvc
{
    public static class HtmlHelperExtensions
    {
        static LosFormatter _losFormatter = new LosFormatter();
        public static string Serialize(this HtmlHelper helper, object objectInstance)
        {
            var sb = new StringBuilder();
            using (var writer = new System.IO.StringWriter(sb))
            {
                _losFormatter.Serialize(writer, objectInstance);
            }
            return sb.ToString();
        }


    }

    [AttributeUsage(AttributeTargets.Parameter)]
    public class DeserializeAttribute : CustomModelBinderAttribute
    {
        public override IModelBinder GetBinder()
        {
            return new DeserializeModelBinder();
        }
    }

    public class DeserializeModelBinder : IModelBinder
    {
        static LosFormatter _losFormatter = new LosFormatter();

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType.IsArray)
            {
                var type = bindingContext.ModelType.GetElementType();
                var serializedObjects = (string[])bindingContext.ValueProvider.GetValue(bindingContext.ModelName).ConvertTo(typeof(string[]));
                var deserializedObjects = Array.CreateInstance(bindingContext.ModelType.GetElementType(), serializedObjects.Length);

                for (var index = 0; index < serializedObjects.Length; index++)
                {
                    var serializedObject = serializedObjects[index];
                    var deserializedObject = _losFormatter.Deserialize(serializedObject);

                    deserializedObjects.SetValue(deserializedObject, index);
                }

                return deserializedObjects;
            }
            else
            {
                var serializedObject = (string)bindingContext.ValueProvider.GetValue(bindingContext.ModelName).ConvertTo(typeof(string));
                var deserializedObject = _losFormatter.Deserialize(serializedObject);

                return deserializedObject;
            }
        }
    }

}

Dann habe ich in meinem Controller so etwas (um ein Produkt zu aktualisieren)

    public ActionResult Update(string productKey)
    {
        var model = _shopping.RetrieveProduct(productKey);

        return View(model);
    }
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Update([Deserialize]Shopping.IProduct _model, FormCollection collection)
    {
        UpdateModel(model);

        model.Save();

        return RedirectAfterPost();
    }

und ich brauche ein verstecktes Feld, das das serialisierte Objekt in der Form hält:

    <% 
        using (Html.BeginRouteForm("Product", FormMethod.Post, new { id = UniqueID, }))
        {
    %>
<%= Html.Hidden("Model", Html.Serialize(Model)) %>
    <h1>
        Product bewerken</h1>
    <p>
        <label for="<%=UniqueID %>_Name">
            Naam:</label>
        <input id="<%=UniqueID %>_Name" name="Name" type="text" value="<%= Html.AttributeEncode(Model.Name) %>"
            class="required" />
        <br />
    </p>
    <p>
        Omschrijving:<br />
        <textarea id="<%= UniqueID %>_Description" name="Description" cols="40" rows="8"><%= Html.Encode(Model.Description) %></textarea>
        <br />
    </p>
    <p>
        <label for="<%=UniqueID %>_Price">
            Prijs:</label>
        <input id="<%= UniqueID %>_Price" name="Price" type="text" value="<%= Model.Price.ToString("0.00") %>"
            class="required" />
        <br />
    </p>
    <ul class="Commands">
        <li><a href="" class="ClosePopup">Annuleren</a></li>
        <li>
            <input type="submit" value="Opslaan" /></li>
    </ul>
    <% 
        } 
    %>

    <script type="text/javascript">

        jQuery('#<%= UniqueID %>').validate();

    </script>

Wie Sie sehen können, wird dem Formular ein verstecktes Feld (Modell) hinzugefügt. Es enthält die Serialisierungsinformationen für das ursprüngliche Objekt. Wenn das Formular veröffentlicht wird, wird das versteckte Feld ebenfalls veröffentlicht (natürlich) und der Inhalt wird vom benutzerdefinierten Modellbinder zum ursprünglichen Objekt, das dann vom Controller aktualisiert und gespeichert wird, deserialisiert.

Beachten Sie, dass das von Ihnen serialisierende Objekt mit dem serialisierbaren Attribut dekoriert werden muss oder über einen typeconverter verfügen, der das Objekt in eine Zeichenfolge umwandeln kann.

Die Losformatter (Serialisierung des begrenzten Objekts) wird vom ViewState in Webformen verwendet. Es bietet auch Verschlüsselung der Serialisierungsdaten.

begrüßt ...

Ajax Calls ist das, was wir tun. Wenn Sie im Allgemeinen über Netze sprechen, schauen Sie sich an JQGRID und wie sie die AJAX -Implementierung empfehlen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top