如何生成并返回数据库中存储的CSHTML视图?模型绑定及方案咨询
Custom HTML Helper Approach
Absolutely, a custom HTML helper is a perfect fit for this scenario—it lets you encapsulate the logic of fetching the DB-stored CSHTML, compiling it, and binding your model directly within your views. Here's a step-by-step implementation:
Step 1: Create the Helper Class
First, build a static extension class for HtmlHelper that handles fetching the template from your database and rendering it with your model:
using Microsoft.AspNetCore.Html; using Microsoft.AspNetCore.Mvc.Rendering; using RazorEngineCore; namespace YourAppNamespace.Helpers { public static class DynamicReportHelper { public static IHtmlString RenderDynamicReport(this HtmlHelper htmlHelper, string reportType, string userLocation, object model) { // 1. Fetch CSHTML from your database (replace with your stored procedure call) string cshtmlTemplate = FetchTemplateFromDb(reportType, userLocation); if (string.IsNullOrWhiteSpace(cshtmlTemplate)) { return new HtmlString("No report template available for this location/report type."); } // 2. Compile and render the Razor template with your model var razorEngine = htmlHelper.ViewContext.HttpContext.RequestServices.GetRequiredService<IRazorEngine>(); var compiledTemplate = razorEngine.Compile(cshtmlTemplate); var renderedHtml = compiledTemplate.Run(new ExecuteContext { Model = model }); return new HtmlString(renderedHtml); } // Replace this with your actual stored procedure logic (use Dapper/EF Core here) private static string FetchTemplateFromDb(string reportType, string userLocation) { // Example: Return a sample CSHTML template tied to the report/location return @" <div class=""report-container""> <h2>@Model.ReportName</h2> <p>Generated for location: @ViewBag.UserLocation</p> <table class=""table""> <thead> <tr> <th>ID</th> <th>Value</th> </tr> </thead> <tbody> @foreach(var item in Model.DataItems) { <tr> <td>@item.Id</td> <td>@item.Value</td> </tr> } </tbody> </table> </div>"; } } }
Step 2: Use the Helper in Your View
In your CSHTML view, reference the helper and pass in your parameters/model:
@model YourAppNamespace.Models.ReportModel @using YourAppNamespace.Helpers @{ ViewBag.UserLocation = User.Claims.First(c => c.Type == "Location").Value; } @Html.RenderDynamicReport("SalesSummary", ViewBag.UserLocation, Model)
Alternative Implementation Options
1. Custom IView for Controller-Side Rendering
If you prefer handling the dynamic view logic at the controller level, you can create a custom IView that loads content from the database:
using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.AspNetCore.Mvc.ViewFeatures; using RazorEngineCore; using System.IO; namespace YourAppNamespace.Views { public class DatabaseView : IView { private readonly string _cshtmlContent; public string Path => "DynamicDatabaseView"; public DatabaseView(string cshtmlContent) { _cshtmlContent = cshtmlContent; } public async Task RenderAsync(ViewContext context) { var razorEngine = context.HttpContext.RequestServices.GetRequiredService<IRazorEngine>(); var compiledTemplate = razorEngine.Compile(_cshtmlContent); var renderedHtml = compiledTemplate.Run(new ExecuteContext { Model = context.ViewData.Model, ViewBag = context.ViewBag }); await context.Writer.WriteAsync(renderedHtml); } } }
Then use it in your controller:
public async Task<IActionResult> GenerateReport(string reportType) { var userLocation = User.Claims.First(c => c.Type == "Location").Value; var cshtmlTemplate = FetchTemplateFromDb(reportType, userLocation); var reportModel = GetReportModel(reportType, userLocation); // Your model fetch logic return View(new DatabaseView(cshtmlTemplate), reportModel); }
2. View Component for Modular Reuse
For more modularity, wrap the logic in a View Component—this is great if you need to reuse the dynamic report rendering across multiple views:
using Microsoft.AspNetCore.Mvc; using RazorEngineCore; namespace YourAppNamespace.ViewComponents { public class DynamicReportViewComponent : ViewComponent { private readonly IRazorEngine _razorEngine; public DynamicReportViewComponent(IRazorEngine razorEngine) { _razorEngine = razorEngine; } public async Task<IViewComponentResult> InvokeAsync(string reportType, string userLocation, object model) { string cshtmlTemplate = FetchTemplateFromDb(reportType, userLocation); var renderedHtml = _razorEngine.Compile(cshtmlTemplate).Run(new ExecuteContext { Model = model }); return Content(renderedHtml); } private string FetchTemplateFromDb(string reportType, string userLocation) { // Your stored procedure call here return ""; } } }
Call it in your view like this:
@model YourAppNamespace.Models.ReportModel <vc:dynamic-report report-type="Inventory" user-location="@User.Claims.First(c => c.Type == ""Location"").Value" model="@Model" />
Key Considerations
- Security: Since you're executing raw CSHTML from the database, restrict access to template editing to trusted users only—malicious code in the template could execute on your server.
- Caching: Add caching (e.g.,
IMemoryCache) for frequently used templates to avoid repeated database calls and compilation overhead. - Type Safety: Ensure your DB templates reference the correct model type—store the model's fully qualified name alongside the template in the database to validate during rendering.
内容的提问来源于stack exchange,提问作者Mahesh Kudikala

