ASP.NET Core MVC:数据库优先模式下从SQL Server函数填充模型
ASP.NET Core MVC 数据库优先 CRUD 实战方案
1. 让DbContext识别数据库视图/表值函数
方式1:Scaffold时直接生成(推荐)
在Package Manager Console执行以下命令,一次性生成包含视图、函数的模型和DbContext:
Scaffold-DbContext "Server=你的服务器名;Database=你的数据库名;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Views -Functions -Force
-Views:生成视图对应的实体类与DbSet-Functions:生成表值函数的映射逻辑-Force:覆盖已存在的模型文件(适用于更新数据库结构后重新生成)
方式2:手动添加到现有DbContext
若已有DbContext,可手动添加视图的DbSet:
public DbSet<ProductView> ProductViews { get; set; }
对于表值函数,手动添加映射方法:
[DbFunction("GetFilteredProducts", Schema = "dbo")] public IQueryable<Product> GetFilteredProducts(int categoryId) { var param = new SqlParameter("@CategoryId", categoryId); return Set<Product>().FromSqlRaw("SELECT * FROM dbo.GetFilteredProducts(@CategoryId)", param); }
2. 用视图/表值函数填充模型实现CRUD
从视图读取数据(列表页)
直接通过DbContext查询视图获取展示数据:
public IActionResult Index() { var productList = _context.ProductViews.ToList(); return View(productList); }
调用表值函数获取筛选数据
根据业务参数调用表值函数,返回筛选后的模型集合:
public IActionResult Filter(int categoryId) { var filteredProducts = _context.GetFilteredProducts(categoryId).ToList(); return View("Index", filteredProducts); }
3. 下拉列表:显示标题存储主键
Controller中准备下拉数据源
在Create/Edit页面加载时,将关联表的键值对存入ViewBag:
public IActionResult Create() { // 绑定分类表的Id(存储值)和Name(显示值) ViewBag.CategoryId = new SelectList(_context.Categories, "Id", "Name"); return View(); }
View中渲染下拉列表
用Html.DropDownListFor绑定模型的外键字段,实现“显示标题、提交主键”:
<div class="form-group"> <label asp-for="CategoryId" class="control-label"></label> @Html.DropDownListFor(m => m.CategoryId, ViewBag.CategoryId as SelectList, "请选择分类", new { @class = "form-control" }) <span asp-validation-for="CategoryId" class="text-danger"></span> </div>
4. 编辑时调用存储过程/函数
调用存储过程执行编辑操作
在Post请求的Edit方法中,通过ExecuteSqlRaw调用存储过程完成数据更新:
[HttpPost] [ValidateAntiForgeryToken] public IActionResult Edit(int id, [Bind("Id,Name,Price,CategoryId")] Product product) { if (id != product.Id) return NotFound(); if (!ModelState.IsValid) { ViewBag.CategoryId = new SelectList(_context.Categories, "Id", "Name", product.CategoryId); return View(product); } try { var parameters = new[] { new SqlParameter("@Id", product.Id), new SqlParameter("@Name", product.Name), new SqlParameter("@Price", product.Price), new SqlParameter("@CategoryId", product.CategoryId) }; _context.Database.ExecuteSqlRaw("EXEC dbo.UpdateProduct @Id, @Name, @Price, @CategoryId", parameters); } catch (DbUpdateException) { if (!ProductExists(product.Id)) return NotFound(); throw; } return RedirectToAction(nameof(Index)); }
调用函数验证编辑权限
编辑前调用自定义函数检查数据合法性:
public IActionResult Edit(int id) { var canEdit = _context.Set<bool>().FromSqlRaw("SELECT dbo.CanEditProduct(@Id)", new SqlParameter("@Id", id)).FirstOrDefault(); if (!canEdit) return Forbid(); var product = _context.Products.Find(id); ViewBag.CategoryId = new SelectList(_context.Categories, "Id", "Name", product.CategoryId); return View(product); }
注意事项
- 视图默认只读,编辑操作建议直接针对原表,视图仅用于数据展示
- 表值函数、存储过程的参数需与数据库定义完全匹配,避免参数不匹配报错
- 确保DbContext的连接字符串拥有访问视图、函数、存储过程的权限
内容的提问来源于stack exchange,提问作者PKa
相关产品推荐
相关产品推荐

