Übersichtwiederverwendbare MVC-Code: Werttypen in Linq Umgang mit Ausdrücken
ich ein MVC Satz schreibe, die einfache Suche liefern soll/hinzufügen/bearbeiten/löschen Funktionalität für ein Objekt Entity Framework. Es macht auch einige ausgefallene Dinge wie die Unterstützung von Dropdowns, die aus anderen Tabellen mit Attributen gefüllt werden.
Die Hauptaufgabe im Spiel ist ein vererbbarer generische Steuergerät (AdminController<T>
wo T
das EF-Objekt ist), um ein Array von AdminField<T>
, von denen jedes Element ein Feld darstellt/Objekt in T
enthält. AdminController<T>
kann ein Objekt AdminViewModel<T>
generieren. Unten ist ein Visio-Diagramm der Beziehungen zwischen diesen Objekten.
Das Problem
ist mein Problem in den Ansichten. Um Werte anzuzeigen, verwende ich die Html.EditorFor
/HiddenFor
/TextboxFor
Helfer. Eine hyper-vereinfachte Ansicht durchläuft einfach das Feld und zeigt ihre Werte an, so dass sie bearbeitet werden können, wenn sie bearbeitbare Felder sind. Das sieht aus wie:
@model AdminViewModel<ExampleEFObject>
@using (Html.BeginForm())
{
foreach (var f in Model.Fields)
{
var expression = Model.ExpressionFromField(f);
<label class="control-label col-md-3">@f.DisplayName</label>
@if (!f.Editable)
{
@Html.TextBoxFor(expression, new { @class = "form-control", @disabled = "disabled" })
}
else
{
@Html.EditorFor(expression, new { htmlAttributes = new { @class = "form-control" } })
}
}
<input type="submit" value="Save" class="btn btn-primary" />
}
Nun, das ist, was ich will es aussehen. Es funktioniert gut mit Feldern/Eigenschaften, die Referenztypen sind. Mit Werttypen, jedoch bekomme ich einen System.InvalidOperationException
Fehler auf der EditorFor
Zeile: „Vorlagen nur mit Feldzugriff verwendet werden können, Zugriff auf Eigenschaften, eindimensionaler Array-Index oder Einzelparameter benutzerdefinierte Indexer Ausdrücke.“
Dies liegt daran, dass die Methode ExpressionFromField
, die einen Ausdruck zurückgibt, der auf eine Instanz von T
im ViewModel zugreift, den Rückgabetyp Expression<Func<AdminViewModel<T>, object>>
hat. Da ein Objekttyp zurückgegeben wird, wird beim Generieren des Ausdrucks Boxing erzwungen, sodass anstelle von (z. B. t => t.Count
) meine Methode tatsächlich t => Convert(t.Count)
zurückgibt.
Aktuelle Behelfslösung
Meine aktuelle Problemumgehung ist ein zweites Verfahren als Expression<Func<AdminViewModel<T>, int>> IntExpressionFromField(AdminField<T> field)
definiert haben. Da es einen int
zurückgibt, wird das Boxing in dem Ausdruck nicht erzwungen. Allerdings macht es meine Ansichten extrem hässlich:
@model AdminViewModel<ExampleEFObject>
@using (Html.BeginForm())
{
foreach (var f in Model.Fields)
{
var expression = Model.ExpressionFromField(f);
var intExpression = Model.IntExpressionFromField(f);
<label class="control-label col-md-3">@f.DisplayName</label>
@if (!f.Editable)
{
if (intExpression == null)
{
@Html.TextBoxFor(expression, new { @class = "form-control", @disabled = "disabled" })
}
else
{
@Html.TextBoxFor(intExpression, new { @class = "form-control", @disabled = "disabled" })
}
}
else
{
if (intExpression == null)
{
@Html.EditorFor(expression, new { htmlAttributes = new { @class = "form-control" } })
}
else
{
@Html.EditorFor(intExpression, new { htmlAttributes = new { @class = "form-control" } })
}
}
}
<input type="submit" value="Save" class="btn btn-primary" />
}
Schlimmer noch, diese unterstützt nur 1 Werttyp (int
). Ich würde auch gern Dezimal, Lang und was auch immer sonst noch unterstützen - am besten, ohne für jede einzelne Methode eigene Methoden erstellen zu müssen, sowie die gesamte Logik, um sich gegen andere Typen abzusichern.
Hilfe!
Ich würde gerne eine elegante, einfache Möglichkeit finden, dieses Problem zu lösen. Wenn keiner existiert, denke ich, dass mein nächster Schritt wäre, die Html
Hilfsmethoden zu stoppen; Dies würde jedoch erfordern, dass die Konsistenz mit dem Rest der Anwendung gebrochen wird, und ich weiß nicht einmal, dass dies das Problem lösen würde.
Jede Hilfe wäre sehr willkommen!
Es ist insgesamt eine schlechte Übung, Ihr EF-Objekt an Ansicht übergeben. Sie müssten wahrscheinlich ein abstraktes FormViewModel erstellen, das Implementierungen einiger abstrakter FieldViewModels (mit Editorvorlagen, wie DatePickerViewModel, TextBoxViewModel usw.) enthält, und in Ihrem Controller müssen Sie dieses ViewModel umwandeln, um Operationen für Ihre Entity Framework-Entität zu aktualisieren in DTO, wenn Sie eine Service-Schicht haben. –
Es lohnt sich möglicherweise nicht, da es Ihnen nicht erlaubt, komplexe Layouts oder ausgefallene Benutzerinteraktion zu machen, also zu ViewModels und manuellem Code zu wechseln, um Entitäten zu aktualisieren, wenn Sie nicht so viele Entitäten haben –
@raderick: Danke für reagieren. Ich habe mein EF-Objekt nicht an die View übergeben, sondern nur ein generisches ViewModel. Aber wenn das eine schlechte Praxis ist, würde ich gerne verstehen, warum ich in Zukunft einen besseren Ansatz wählen kann. Der Grund, warum ich das tue, ist, weil es Dutzende von Objekttypen gibt, die nur einige grundlegende Wartungsfunktionen erfordern. Ich möchte vermeiden, Dutzende von identischen MVC-Sets zu haben. Der von Ihnen beschriebene Ansatz scheint unglaublich schwer und einschränkend zu sein, wie Sie schon sagten, aber ich werde es als Fallback-Option berücksichtigen. – Daniel