Laden...

Profil + Binding

Erstellt von Andreas@Tricept vor 12 Jahren Letzter Beitrag vor 12 Jahren 3.236 Views
A
Andreas@Tricept Themenstarter:in
289 Beiträge seit 2006
vor 12 Jahren
Profil + Binding

Hallo Zusammen,

ich arbeite mit dem MVC 3 (Razor) Modell und habe mir ein CustomProfil erstellt. Dazu habe ich in der Webconfig folgendes ergänzt:

 <profile inherits="Kano.Models.KanoProfile"> 

Meine Klasse ist zunächst simpel gehalten:


    public class KanoProfile : ProfileBase
    {
        public string Nachname
        {
            get
            {
                return GetPropertyValue("Nachname") as string;
            }
            set
            {
                SetPropertyValue("Nachname", value);
            }
        }
     }

Ich versuche nun per Controller ein Profil zu erstellen und zu bearbeiten:
dazu verwendende ich folgende Controller-Methode:


        public ActionResult Index()
        {
            KanoProfile profile = (KanoProfile) KanoProfile.Create(User.Identity.Name);
            return View(profile);
        }

In meinem Formular kann ich das Profil bearbeiten. Mit dem Submit Button bekomme ich jedoch immer einen Fehler (Fehler weiter unten zu sehen).

Zu meiner Submit Funktion Index(KanoProfile profile) komme ich garnicht erst.
Ich habe bemerkt das es nicht an meinem CustomProfile hängt, denn wenn ich den DefaultProfile verwende bekomme ich die gleiche Meldung.
Wenn ich das Profil im Code bearbeite und speichere funktioniert alles anstandslos.
also z.B. so:


        public ActionResult Index()
        {
            KanoProfile profile = (KanoProfile) KanoProfile.Create(User.Identity.Name);
            profile.Nachname = "Schulz";
            profile.Save();
            return View(profile);
        }

Das Profil wird dann auch in die DB geschrieben und wird auch normal geladen. Warum klappt es denn mit dem Binding der View nicht?

Fehler:

Serverfehler in der Anwendung /.

Der Wert darf nicht NULL sein.
Parametername: username

Beschreibung: Unbehandelte Ausnahme beim Ausführen der aktuellen Webanforderung. Überprüfen Sie die Stapelüberwachung, um weitere Informationen über diesen Fehler anzuzeigen und festzustellen, wo der Fehler im Code verursacht wurde. 

Ausnahmedetails: System.ArgumentNullException: Der Wert darf nicht NULL sein.
Parametername: username

Quellfehler: 

Beim Ausführen der aktuellen Webanforderung wurde einen unbehandelte Ausnahme generiert. Informationen über den Ursprung und die Position der Ausnahme können mit der Ausnahmestapelüberwachung angezeigt werden.

Stapelüberwachung: 


[ArgumentNullException: Der Wert darf nicht NULL sein.
Parametername: username]
   System.Web.Util.SecUtility.CheckParameter(String& param, Boolean checkForNull, Boolean checkIfEmpty, Boolean checkForCommas, Int32 maxSize, String paramName) +2385917
   System.Web.Profile.SqlProfileProvider.FindProfilesByUserName(ProfileAuthenticationOption authenticationOption, String usernameToMatch, Int32 pageIndex, Int32 pageSize, Int32& totalRecords) +39
   System.Web.Profile.ProfileBase.RetrieveDates() +85
   System.Web.Profile.ProfileBase.get_LastActivityDate() +21

[TargetInvocationException: Der Eigenschaftenaccessor LastActivityDate für das Kano.Models.KanoProfile-Objekt hat folgende Ausnahme verursacht: Der Wert darf nicht NULL sein.
Parametername: username]
   System.ComponentModel.ReflectPropertyDescriptor.GetValue(Object component) +400
   System.Web.Mvc.<>c__DisplayClassb.<GetPropertyValueAccessor>b__a() +18
   System.Web.Mvc.ModelMetadata.get_Model() +19
   System.Web.Mvc.<Validate>d__1.MoveNext() +154
   System.Web.Mvc.<Validate>d__5.MoveNext() +318
   System.Web.Mvc.DefaultModelBinder.OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext) +139
   System.Web.Mvc.DefaultModelBinder.BindComplexElementalModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Object model) +66
   System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +1367
   System.Web.Mvc.DefaultModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +449
   System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) +317
   System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) +117
   System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +343
   System.Web.Mvc.Controller.ExecuteCore() +116
   System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +97
   System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +10
   System.Web.Mvc.<>c__DisplayClassb.<BeginProcessRequest>b__5() +37
   System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +21
   System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +12
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
   System.Web.Mvc.<>c__DisplayClasse.<EndProcessRequest>b__d() +50
   System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f) +7
   System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +22
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +60
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8969201
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +184
16.806 Beiträge seit 2008
vor 12 Jahren

Hallo,

Du hats irgendwo eine Null-Value (im Bereich von "userName"), das aber kein Null sein darf; scheinst aber eben dennoch drauf zuzugreifen.
Steht aber auch in der Fehlermeldung.. recht deutlich 😉

zB ist vielleicht bei Dir kein Vorname im Submit-Form angegeben; verlangst aber einen.

Könnte im Prinzip nichts anderes als [FAQ] NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt sein.
Ich bezweifel aber, dass es an dem Code Ausschnitt liegt, was Du hier zeigst.

A
Andreas@Tricept Themenstarter:in
289 Beiträge seit 2006
vor 12 Jahren

Ich wüsste nicht woran: Hier ist Code meiner View:



@model Kano.Models.KanoProfile


@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>KanoProfile</legend>

            @Html.EditorFor(model => model.Vorname)

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

Zu der Submit Methode kommt er wie gesagt nicht. Nach dem Click kommt es soft zur Meldung. Diese View ist automatisch generiert.

Mein Submit (Auch wenn er nicht erreicht wird):

        
        [HttpPost]
        public ActionResult Index(KanoProfile profile)
        {
            profile.Save();
            return View();
        }

16.806 Beiträge seit 2008
vor 12 Jahren
          
        [HttpPost]  
        public ActionResult Index(KanoProfile profile)  
        {  
            profile.Save();  
            return View();  
        }  
  

Was tut das? Warum Save() auf eine Entity? Wo ist hier der Context zur Datenbank? Nicht allem ernstes in Deinem Entity? Wo ist hier eine Validierung und dessen Fehlerbehandlung?
Wo ist überhaupt die Validierungsdefinition? NotNullable-Feld in der Datenbank, aber gar kein Wert eingetragen? Wie sieht die Entity-Definition überhaupt aus?Fragen über Fragen....

A
Andreas@Tricept Themenstarter:in
289 Beiträge seit 2006
vor 12 Jahren

Erstmal komme ich garnicht in das Save. Das Save ist eine Framework-funktion von ProfileBase. Die Profileigenschaften werden dann in die von VS2010 erstellte DB geschrieben und autom. wieder geladen.

Ich wollte das Profil nicht alleine erstellen, aber mir bleibt ja keine wahl oder?

16.806 Beiträge seit 2008
vor 12 Jahren

Okay. Hab übersehen, dass es hier noch das ProfileBase ist; das ist dann verständlich.

Nichts desto trotz musst Du ja bei Deinem ProfileProvider die zusätzlichen Properties definieren, zb mit <add name="Nachname" type="string" defaultValue="[null]" customProviderData="Nachname;nvarchar" />
beziehungsweise über die Definition von KanoProfile.
Und die Info fehlt hier noch; vielleicht ist die Definition Deiner Properties einfach falsch.

So wie ich verstanden habe kommst Du zum Anzeigen des Profils, aber wenn man speichern will kommt direkt eine Exception. Und vermutlich passt irgendwas nicht an Deiner Definition, sodass der PropertyBinder kracht.

Tipp: verwende immer ViewModels/SubmitModels, statt direkt irgendeine Entity an eine View zu senden oder zu empfangen.
View -> Anzeigen
Submit -> Empfangen

A
Andreas@Tricept Themenstarter:in
289 Beiträge seit 2006
vor 12 Jahren

Hallo, danke erstmal für die Antworten.

Ich habe kein Problem beim speichern des Profils.
So speichert er mir diesen Teil vollkommen korrekt ab:


KanoProfile profile = (KanoProfile) KanoProfile.Create(User.Identity.Name);
            profile.Nachname = "Schulz";
            profile.Save();

Sprich in der DB kommt ein entsprechender Eintrag mit meinem Wert. Wenn der Benutzer sich einloggt hat er in seinem Profil auch den Nachnamen "Schulz" eingetragen.
Wenn ich diesen an die View übergebe wird er sogar korrekt angezeigt.

ABER. Wenn ich in der View das supmit verwende komme ich sofort zu diesem Fehler.
Also das Binding mit meinem Objekt und der Oberfläche scheint irgendein Problem zu verursachen. Die Username Property ist schon befüllt bevor ich das Profil an die View übergebe....

deswegen weiss ich leider überhaupt nicht woran das liegen kann?

Der Fehler wird außerhalb meines Codings geworfen.
Aber wenn ich selbst das DefaultProfile verwende, also ein neues VS MVC 3 Projekt erstelle und der View das aktuelle Profil übergebe und submit betätige ohne etwas zu ändern, bekomme ich dort auch eine Fehlermeldung. Ist das wohl ein Fehler im Framework?

16.806 Beiträge seit 2008
vor 12 Jahren

Tipp: verwende immer ViewModels/SubmitModels, statt direkt irgendeine Entity an eine View zu senden oder zu empfangen.
View -> Anzeigen
Submit -> Empfangen

Mal ausprobiert?
Genau diese Art und Weise nutze ich bei meinem ProfilProvider und meinen Controller/Actions etc....
Es ist nur ein Tipp und eine allgemeine Empfehlung. Du musst Dich natürlich nicht dran halten..... 🤔

A
Andreas@Tricept Themenstarter:in
289 Beiträge seit 2006
vor 12 Jahren

Deinen Tipp verstehe ich leider nicht.

Ich habe in verschiedenen Dokus gelernt das man stark typisierte Views verwenden soll und per VS2010 Assistent werden diese dann auch automatisch generiert.
Also ich gebe ein typisiertes Objekt weiter, welches ich bearbeiten kann und mit dem Submit validiere ich und speicher es.

Wie meinst du das denn? Kannst du mir ein Codebeispiel geben?

16.806 Beiträge seit 2008
vor 12 Jahren

ViewModels sind in der Entwicklungswelt Klassen, die einfach nichts tun, außer genau die Daten anzuzeigen, die die View auch anzeigen soll.
Man sollte tunlichst vermeiden, dass Datenbank-Objekte in der View landen. Widerspricht sich mit dem Gedanken der Modularität und der Schichten-Trennung.

Wenn Du eine View hast, die nichts anderes anzeigt, außer die Straße, die Hausnummer, die PLZ und den Ort; dann erstellst Du Dir eine Klasse mit genau diesen vier Properties.
Kein Name, keine E-Mail Adresse, keine anderen Daten, die in der View sowieso nicht angezeigt / benötigt werden.

Dieses ViewModel erstellst Du im Controller und übergibst es (entweder leer (Add) oder vorgefüllt (Update) an die View.
Bei der Action, die beim Submit aufgerufen wird hast Du wiederum ein SubmitModel, das die tatsächlichen Eingaben repräsentiert.


public ActionResult Add()
{
	var viewModel = new PersonAdressViewModel();
	
	return View ("Add", viewModel);
}

public ActionResult Update(id userID)
{
	var viewModel = new PersonAdressViewModel();
	
	// Hier das Entity aus der Datenbank lesen, und an das ViewModel übergeben
	
	return View ("Update", viewModel);
}

[HttpPost]
public ActionResult Update(id userID, PersonAdressSubmitModel submitModel)
{
	// Die im Formularfeld ausgefüllten Daten sind nun im Submitmodel.
	// Entity aus der DB anhand der UserID laden
	// Die Daten aus dem SubitModel in die Entity übertragen
	// Entity auf Gültigkeit prüfen
	// Entitychanges in der DB speichern
}

Eine Trennung zwischen ViewModel und SubmitModel hat folgenden Grund:

Ein SubmitModel enthält nur die Werte, die tatsächlich eingetragen / ausgefüllt wurden.
Ein ViewModel hingegen könnte eine Collection für eine SelectBox enthalten; sodass zB eine Auswahl an Ländern in der View angezeigt werden kann und später wirklich auch nur das ausgewählte Land im SubmitModel landet.

A
Andreas@Tricept Themenstarter:in
289 Beiträge seit 2006
vor 12 Jahren

Ah vielen Dank ! Hört sich sehr sinnvoll an. Hast mir geholfen.

16.806 Beiträge seit 2008
vor 12 Jahren

Übrigens:

wenn ich mir so andere Beispiele anschaue, und auch meine Implementierung, dann sieht das alles so aus:

public virtual string FirstName 
{ 
        get { return base["FirstName"] as string; } 
        set { base["FirstName"] = value; } 
} 

Du hast aber:


         public string FirstName
         {
             get
             {
                 return GetPropertyValue("FirstName") as string;
             }
             set
             {
                 SetPropertyValue("FirstName", value);
             }
      }

.. vielleicht hat das auch irgendwelche sonstigen auswirkungen; der Rest scheint ja bei Dir zu gehen.