Ho voluto qualcosa di simile a questo per un lungo periodo di tempo - un sacco di pagine che scriviamo può essere gettato insieme da una Jr Dev se non dovevano scrivere un sacco di domande; e, comunque, è sempre la stessa domanda di base boilerplate ogni volta - perché dovrebbero doversene scrivere per ogni Controller, quando la maggior parte del loro lavoro è di accontentarsi del contenuto? Uso C# quindi non devo occuparmi della gestione della memoria, perché un codificatore HTML deve occuparsi dei dettagli della query?
Esiste un trucco che è possibile utilizzare per ordinare implicitamente il caricamento asincratico dei dati nella vista. Innanzitutto, si definisce una classe che esprime quali dati si desidera. Quindi, in cima a ciascuna vista, crea un'istanza di quella classe. Tornando al controller, è possibile cercare la vista che si sta per usare, aprirla, quindi compilare la classe. È quindi possibile utilizzarlo per ottenere i dati necessari per la visualizzazione, asincrona, nel controller come applica MVC. Infine, passalo con un ViewModel alla vista come prescrive MVC e, attraverso alcuni trucchi, hai una vista che dichiara quali dati verrà utilizzato.
Ecco uno StoryController. Jr Devs scrivere storie come semplici file .cshtml senza dover conoscere ciò che un controller, database o LINQ è:
public class StoryController : BaseController
{
[OutputCache(Duration=CacheDuration.Days1)]
// /story/(id)
public async Task<ActionResult> Id(string id = null)
{
string storyFilename = id;
// Get the View - story file
if (storyFilename == null || storyFilename.Contains('.'))
return Redirect("/"); // Disallow ../ for example
string path = App.O.AppRoot + App.HomeViews + @"story\" + storyFilename + ".cshtml";
if (!System.IO.File.Exists(path))
return Redirect("/");
return View(storyFilename);
Tutto questo non fa per ora è andare a prendere il file Visualizza sulla base del URL, permettendo qualcosa come WebForms (tranne all'interno di MVC e usando Razor). Ma vogliamo mostrare alcuni dati - nel nostro caso, persone e progetti che si accumulano nel database - con alcuni ViewModels e Partials standard. Definiamo come e compilalo.(Si noti che ConservX sembra essere lo spazio dei nomi di progetto principale nel mio caso.)
public async Task<ActionResult> Id(string id = null)
{
string storyFilename = id;
// 1) Get the View - story file
if (storyFilename == null || storyFilename.Contains('.'))
return Redirect("/"); // Disallow ../ for example
string path = App.O.AppRoot + App.HomeViews + @"story\" + storyFilename + ".cshtml";
if (!System.IO.File.Exists(path))
return Redirect("/");
// 2) It exists - begin parsing it for StoryDataIds
var lines = await FileHelper.ReadLinesUntilAsync(path, line => line.Contains("@section"));
// 3) Is there a line that says "new StoryDataIds"?
int i = 0;
int l = lines.Count;
for (; i < l && !lines[i].Contains("var dataIds = new StoryDataIds"); i++)
{}
if (i == l) // No StoryDataIds defined, just pass an empty StoryViewModel
return View(storyFilename, new StoryViewModel());
// https://stackoverflow.com/questions/1361965/compile-simple-string
// https://msdn.microsoft.com/en-us/library/system.codedom.codecompileunit.aspx
// https://msdn.microsoft.com/en-us/library/system.codedom.compiler.codedomprovider(v=vs.110).aspx
string className = "__StoryData_" + storyFilename;
string code = String.Join(" ",
(new[] {
"using ConservX.Areas.Home.ViewModels.Storying;",
"public class " + className + " { public static StoryDataIds Get() {"
}).Concat(
lines.Skip(i).TakeWhile(line => !line.Contains("};"))
).Concat(
new[] { "}; return dataIds; } }" }
));
var refs = AppDomain.CurrentDomain.GetAssemblies();
var refFiles = refs.Where(a => !a.IsDynamic).Select(a => a.Location).ToArray();
var cSharp = (new Microsoft.CSharp.CSharpCodeProvider()).CreateCompiler();
var compileParams = new System.CodeDom.Compiler.CompilerParameters(refFiles);
compileParams.GenerateInMemory = true;
compileParams.GenerateExecutable = false;
var compilerResult = cSharp.CompileAssemblyFromSource(compileParams, code);
var asm = compilerResult.CompiledAssembly;
var tempType = asm.GetType(className);
var ids = (StoryDataIds)tempType.GetMethod("Get").Invoke(null, null);
using (var db... // Fetch the relevant data here
var vm = new StoryViewModel();
return View(storyFilename, vm);
}
Questa è la maggior parte del lavoro. Ora Jr Devs può semplicemente dichiarare i dati di cui hanno bisogno in questo modo:
@using ConservX.Areas.Home.ViewModels.Storying
@model StoryViewModel
@{
var dataIds = new StoryDataIds
{
ProjectIds = new[] { 4 }
};
string title = "Story Title";
ViewBag.Title = title;
Layout = "~/Areas/Home/Views/Shared/_Main.cshtml";
}
@section css {
...
Sembra qualcosa che si farebbe nel controller o più avanti nello stack nel codice back-end. –