C# – Wissenwertes zu IEnumerable und wie man es richtig nutzt

Peter Bucher hat einen interessanten Artikel rund um IEnumerable und wie und wann man es richtig nutzt veröffentlicht.
Sollte man mal gelesen haben und natürlich auch anwenden. Ich werd es auf jeden Fall!

Wann IEnumerable nutzen, wann ICollection und wieso überhaupt IList?

Gibt in dem Blog übrigens noch andere lesenswerte Beiträge!

Html.Textbox und Ajax.BeginForm

So langsam sammel ich meine Erfahrung mit ASP.NET MVC, gute sowie schlechte.
Da kommen wir mal zu meinem heutigen Problem. Ich habe ein Formular, welches ich per Ajax an den Server senden möchte, dieser überprüft die Daten und gibt bei Bedarf ein Zusammenfassung der Fehler aus, wobei das Formular ausgefüllt bleibt.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MyModel>" %>
<%= Html.ValidationSummary(); %>
<% using(Ajax.BeginForm("MyAction", new AjaxOptions { UpdateTargetId = "fromDiv" })) { %>
Benutzername: <%= Html.Textbox("username", Model.Username) %>
<input type="submit" value="Absenden" />
<% } %>

Hier mal als Beispiel. Das ganze wurde dann an den Server gesendet, validiert und bei Bedarf halt mit Fehlermeldung noch mal ausgegeben.

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult MyAction(string username) {
   if(this.UsernameExists(username)) {
      this.ModelState.AddModelError("errorUsername","Benutzername existiert schon!");
      MyModel user = new MyModel { Username = username + "1" };
      return PartialView("MyControl", user);
   }
   //...
}

So, angenommen wir geben nun einen Benutzername ein, welcher schon existiert. Dann sollte nun das Formular erscheinen, oben die ValidationSummery und als Benutzername vorgeblendet der von eben + 1. Möge man meinen, ist aber nicht so. Es erscheint zwar die ValidationSummary aber nicht die 1 hinter dem Namen.
Ich hab das gute Teil rauf und runter debugt, in div. Browsern, bei allen das selbe Ergebnis. Im Model steht der richtige Wert, er wird aber nicht angezeigt.
Eine Erklärung konnte ich nicht finden, aber eine Lösung.

Das Problem liegt an Html.Textbox(). Es scheint, als ob er das Objekt im Cache vorhält. Da werden ganz böse Erinnerungen an ViewState wach. Lösen konnte ich das Problem, in dem ich die Textbox manuell erstelle:

Benutzername: <input type="text" name="username" value="<%= Html.Encode(Model.Username) %/>" />

Aber auf so etwas muss man erstmal kommen. Löst ja eigentlich nicht das Problem sondern nur umgangen. Vielleicht find ich ja noch mal einen Beitrag dazu in der MSDN oder jemand hinterlässt mir einen schönen Link in den Kommentaren.

Linq Probleme mit ToString()

Ich finde Linq ja eine echt tolle Sache. Damit kann man schöne lesbare Statements formulieren, welche eigentlich sehr logisch sind.

Heute wollte ich anhand einer Datenbank eine <select>-Liste in einem ASP.NET MVC Projekt erstellen.
Dazu wollte ich die Html.DropDownList() verwenden, welche eine Liste des Typen „SelectListItem“ verlangt. Dies wollte ich schnell mit Linq erstellen:

var daten = from datensatz in this.datenSet select new SelectListItem { 
Value = datensatz.Id.ToString(),
Text = datensatz.Name
}

Das hat so nicht geklappt, Fehlermeldung lautete:

‚LINQ to Entities‘ erkennt die Methode ‚System.String ToString()‘ nicht, und diese Methode kann nicht in einen Speicherausdruck übersetzt werden.

Zudem wurde mir als fehlerauslösende Zeile die Html.DropDownList() angezeigt. Das Problem liegt an dem „datensatz.Id.ToString()“, obwohl es vom Visual Studio direkt vorgeschlagen wird. Auch andere casting Methoden ((string) vorschreiben, Convert.ToString()) waren nicht erfolgreich.
Per Google kam ich auf diesem Beitrag, welcher die Lösung enthielt. Ich schaufel die Daten vorher in ein Dictionary und gebe sie von dort aus, da das Dictionary die ToString() Methode kennt.

var foo = from datensatz in this.datenSet.ToDictionary(i => i.Id, i => i.Name)
          select new SelectListItem
                     {
                         Text = datensatz.Value,
                         Value = datensatz.Key.ToString()
                     };

Diese Lösung klappt hier nun ganz gut, da wir wirklich nur einen Key und ein Value haben. Spätestens bei drei Werten kann man nicht über ein Dictionary gehen. Da müssen die Daten dann vorher in eine „Dummy“-Klasse gepumpt werden, welche die Wertetypen so annimmt. Nicht die schönste Weg, aber ein besserer fällt mir spontan nicht ein.

ADO.NET Foreign Key = null selektieren

Ich habe zwei Tabellen erstellt, welche über einen Foreign Key miteinander verknüpft sind.
Nun habe ich über den Wizard ein „ADO.NET Entity Data Model“ erstellt und die beiden Tabellen ausgewählt. Richtig hat er die Relation zwischen Tabelle A und Tabelle B mit 0..1 –> * erkannt.

Nun wollte ich alle Einträge aus Tabelle B, welche keine Verknüpfung zu Tabelle A haben, sprich in der Spalte den Wert „NULL“ besitzen.
Leider ist es nicht möglich, die Spalte im Code anzusteuern.
Foreignkey1

Die Lösung ist hier eigentlich ganz einfach, meine erste Idee war auch richtig, hat aber nicht „geklappt“, da ich mir die falsche Variable ausgeben lassen habe.
Da durch die 0..1 –> * Beziehung im „AdressbuchUser“-Objekt automatisch ein (in dem Fall) „AdressbuchFirmen“-Objekt angelegt wird, welches die Daten aus der anderen Tabelle enthält, braucht man dies nur auf null zu prüfen, schon funktioniert die ganze Geschichte.

Foreignkey2

Weitere Informationen zu dem Thema findet ihr hier.

ASP.NET MVC unter IIS 5.1 / 6

Ein kleines Problem von ASP.NET MVC liegt darin, dass es nur unter dem IIS7 läuft. Für den IIS7 braucht man aber Windows Server 2008 R2 bzw. Windows 7. Was ist wenn man dafür aber eben mal keine Lizenz hat sondern auf XP ausweichen muss? Dann muss man sich mit einem Workaround behelfen.

Einige habe ich auf codeville.net gefunden.
Ausprobiert habe ich selber nur „Option 2“. Dazu änderte ich in der Global.asax die Default-MapRoute:

routes.MapRoute(
    "Default",                                                           // Route name
    "{controller}.aspx/{action}/{id}",                           // URL with parameters
    new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
);

Sofern im Projekt die Links über z.B. Html.ActionLink() erstellt werden, läuft das Projekt nun ohne weitere Probleme. Die Urls sehen nun so aus: http://localhost/Home.aspx/Index/123
Ist zwar nicht so der Hit, aber die einfachste Möglichkeit ohne viel gefrickel.

Aber etwas solltet ihr beachten. Sofern ihr die Authentifizierung nutzt, sollte ihr in der Web.config den Pfad zur Loginurl ändern:

<forms loginUrl="~/Account.aspx/LogOn" timeout="2880" />

Dann klappt es auch mit dem Nachbarn 🙂