ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于 ASP.NET Core 与 SQL Server JSON 函数构建 Product Catalog 应用:dotnet-jquery-bootstrap-app 实战解析

基于 ASP.NET Core 与 SQL Server JSON 函数构建 Product Catalog 应用:dotnet-jquery-bootstrap-app 实战解析 示例工程数据库教程后端【免费下载链接】sql-server-samplesAzure Data SQL Samples - Official Microsoft GitHub Repository containing code samples for SQL Server, Azure SQL, Azure Synapse, and Azure SQL Edge项目地址https://gitcode.com/gh_mirrors/sq/sql-server-samples点击查看免费下载导读本文围绕 sql-server-samples 仓库中samples/features/json/product-catalog/dotnet-jquery-bootstrap-app示例系统讲解如何利用 SQL Server 2016 及以上版本含 Azure SQL Database内置的 JSON 支持构建一个具备产品列表展示、新增、编辑、删除能力的 ASP.NET Core Web 应用。文章将覆盖从数据库脚本、REST API 到 JQuery/Bootstrap 前端的完整链路并深入剖析OPENJSON、JSON_VALUE、JSON_QUERY、JSON_MODIFY、FOR JSON等核心函数在真实业务中的用法帮助你掌握关系表 JSON 列混合建模与前后端 JSON 数据交换的完整方案。示例概览该示例是一个典型的 CRUD 应用前端使用 JQuery 与 Bootstrap 构建页面并用 JQuery DataTables 组件渲染产品表格服务端使用 ASP.NET Core Web API 暴露 REST 接口而数据层则直接调用 SQL Server 的 JSON 函数将数据库查询结果以 JSON 格式流式输出给前端页面。适用版本SQL Server 2016或更高版本、Azure SQL Database核心特性SQL Server 2016 / Azure SQL Database 的 JSON 函数编程语言C#、Html/JavaScript、Transact-SQL示例作者Jovan Popovic示例的核心价值在于演示关系型主数据 JSON 灵活字段的混合方案Product表既包含Name、Color、Price等强类型关系列也包含Data、Tags两个 JSON 列用于承载无法预先建模的灵活信息如制造产地、标签数组、自定义属性等。环境准备在运行示例之前需要准备以下环境软件前提SQL Server 2016或更高版本或一个 Azure SQL DatabaseVisual Studio 2015 Update 3或更高版本或安装 ASP.NET Core 1.0或更高版本的 Visual Studio Code 编辑器。Azure 前提拥有创建 Azure SQL Database 的权限。需要说明的是示例工程使用的是 ASP.NET Core 1.0 时代的project.json/xproj工程格式见 project.json依赖Microsoft.AspNetCore.Mvc、Microsoft.AspNetCore.Server.Kestrel、Belgrade.Sql.Client0.3.0 等 1.0.0 版本包。如果你使用更新的 .NET SDK建议在还原时留意依赖兼容性或参考同目录下 dotnet-rest-api 的无前端版本对比学习。数据库准备深入解析 setup.sql示例的运行前提是先建库、建表并加载种子数据。在 SQL Server Management StudioSSMS或 SQL Server Data Tools 中连接目标实例后执行 setup.sql 脚本该脚本依次完成以下工作。创建数据库与 Product 表USE master GO DROP DATABASE IF EXISTS ProductCatalog GO CREATE DATABASE ProductCatalog GO USE ProductCatalog GO CREATE TABLE Product ( ProductID int IDENTITY PRIMARY KEY, Name nvarchar(50) NOT NULL, Color nvarchar(15) NULL, Size nvarchar(5) NULL, Price money NOT NULL, Quantity int NULL, Data nvarchar(4000), Tags nvarchar(4000) )表结构的关键设计在于Data与Tags两列它们以nvarchar存储 JSON 文档Data用于存放任意嵌套对象如{Type:Part,MadeIn:China}Tags用于存放 JSON 数组如[promo,new]。这种设计允许应用在不改动表结构的情况下扩展产品属性。用 OPENJSON 批量导入种子数据脚本使用OPENJSON函数将一个包含 17 件产品的 JSON 数组一次性解析为关系行集插入表中DECLARE products NVARCHAR(MAX) N[{ProductID:15,Name:Adjustable Race,...}] INSERT INTO Product (ProductID, Name, Color, Size, Price, Quantity, Data, Tags) SELECT ProductID, Name, Color, Size, Price, Quantity, Data, Tags FROM OPENJSON (products) WITH( ProductID int, Name nvarchar(50), Color nvarchar(15), Size nvarchar(5), Price money, Quantity int, Data nvarchar(MAX) AS JSON, Tags nvarchar(MAX) AS JSON )这里有两个值得注意的细节OPENJSON配合WITH子句指定了输出列的类型映射如ProductID int、Price moneyJSON 中的字符串值会自动完成向目标类型的转换Data、Tags两列声明为AS JSON表示保留其原始 JSON 片段而不是被解析为标量值。种子数据中可以看到Tags字段在不同产品上存在/缺失[promo]、[new]或没有该键这正是 JSON 半结构化特性的体现。三个 JSON 存储过程示例在数据库端预置了三个存储过程分别对应前端的新增、更新与存在则更新否则插入三种写入语义。它们都接收 JSON 字符串作为参数再用OPENJSON解析后落库。InsertProductFromJson —— 插入并返回新 IDCREATE PROCEDURE dbo.InsertProductFromJson(ProductJson NVARCHAR(MAX)) AS BEGIN INSERT INTO dbo.Product(Name,Color,Size,Price,Quantity,Data,Tags) OUTPUT INSERTED.ProductID SELECT Name,Color,Size,Price,Quantity,Data,Tags FROM OPENJSON(ProductJson) WITH ( Name nvarchar(100) Nstrict $.Name, Color nvarchar(30), Size nvarchar(10), Price money Nstrict $.Price, Quantity int, Data nvarchar(max) AS JSON, Tags nvarchar(max) AS JSON) as json END注意Nstrict $.Name与Nstrict $.Price中的strict路径模式它要求 JSON 中必须存在Name与Price键否则会报错从而保证必填字段的完整性约束OUTPUT INSERTED.ProductID则让新增成功后可以立即拿到自增主键回传给前端。UpdateProductFromJson —— 按 ID 更新CREATE PROCEDURE dbo.UpdateProductFromJson(ProductID int, ProductJson NVARCHAR(MAX)) AS BEGIN UPDATE dbo.Product SET Name json.Name, Color json.Color, Size json.Size, Price json.Price, Quantity json.Quantity, Data ISNULL(json.Data, dbo.Product.Data), Tags ISNULL(json.Tags,dbo.Product.Tags) FROM OPENJSON(ProductJson) WITH ( Name nvarchar(100) Nstrict $.Name, Color nvarchar(30), Size nvarchar(10), Price money Nstrict $.Price, Quantity int, Data nvarchar(max) AS JSON, Tags nvarchar(max) AS JSON) as json WHERE dbo.Product.ProductID ProductID END这里ISNULL(json.Data, dbo.Product.Data)的写法实现了部分更新语义如果前端提交的 JSON 中没有Data键则保留原值避免整行覆盖。UpsertProductFromJson —— MERGE 原子化写入CREATE PROCEDURE dbo.UpsertProductFromJson(ProductID int, ProductJson NVARCHAR(MAX)) AS BEGIN MERGE INTO dbo.Product USING ( SELECT Name,Color,Size,Price,Quantity,Data,Tags FROM OPENJSON(ProductJson) WITH (...)) as json ON (dbo.Product.ProductID ProductID) WHEN MATCHED THEN UPDATE SET Name json.Name, ... WHEN NOT MATCHED THEN INSERT (Name,Color,Size,Price,Quantity,Data,Tags) VALUES (json.Name, json.Color, ...); END该过程利用MERGE将更新与插入合并为单条语句是应对 Web 端 PUT/PATCH 幂等写法的常用模式。运行示例从配置到启动配置连接字符串示例支持通过appsettings.json或环境相关的appsettings.development.json提供连接字符串。本地 SQL Server 的配置示例如下{ ConnectionStrings: { ProductCatalog: Server.;DatabaseProductCatalog;Integrated Securitytrue } }若数据库托管在 Azure则改为{ ConnectionStrings: { ProductCatalog: ServerSERVER.database.windows.net;DatabaseProductCatalog;User IdUSER;PasswordPASSWORD } }从 Startup.cs 的源码可以看出配置系统会依次加载appsettings.json、appsettings.{EnvironmentName}.json与环境变量最终通过Configuration[ConnectionStrings:ProductCatalog]读取连接串并注册为两个核心服务services.AddTransientIQueryPipe(_ new QueryPipe(new SqlConnection(ConnString))); services.AddTransientICommand(_ new Command(new SqlConnection(ConnString)));这里引入了Belgrade.Sql.Client库的IQueryPipe负责查询并把结果流式输出到Stream与ICommand负责执行非查询命令两者都以瞬态Transient生命周期注册。默认的 appsettings.json 只包含日志配置连接字符串需要你自行按上述格式添加。还原、构建与运行用 Visual Studio 2015 打开根目录下的ProductCatalog.xproj在项目右键菜单选择Restore Packages还原依赖或者在命令行应用根目录执行dotnet restore构建在 VS 中使用CtrlShiftB、右键 Build或命令行执行dotnet build运行在 VS 中按F5或CtrlF5或命令行执行dotnet run。应用启动后入口见 Program.cs 中的 Kestrel IISIntegration 宿主配置浏览器打开/index.html即可看到产品列表打开/index.html获取数据库中的全部产品点击Add按钮新增产品点击表格中的Edit按钮编辑产品点击表格中的Delete按钮删除产品。源码级解析JSON 数据如何从数据库流向浏览器REST API 层FOR JSON 流式输出ProductController.cs 暴露了/api/Product的完整 REST 端点全部数据交换都基于 JSON。其核心技巧是查询语句直接以FOR JSON生成 JSON再由IQueryPipe.Stream将结果字节流直接写入Response.Body完全绕过了在服务端手动组装 JSON 对象的开销。GET /api/Product —— 产品列表[HttpGet] public async Task Get() { await sqlQuery.Stream( select ProductID, Name, Color, Price, Quantity, JSON_VALUE(Data, $.MadeIn) as MadeIn, JSON_QUERY(Tags) as Tags from Product FOR JSON PATH, ROOT(data), Response.Body, EMPTY_PRODUCTS_ARRAY); }这条 SQL 同时使用了三个 JSON 函数JSON_VALUE(Data, $.MadeIn)从Data对象中提取MadeIn标量值作为列表的产地列JSON_QUERY(Tags)保留Tags数组的 JSON 片段若不加此包装FOR JSON PATH会把数组当字符串转义FOR JSON PATH, ROOT(data)则将结果行集格式化为{data:[...]}结构——这与前端 DataTables 默认的ajax数据格式完全吻合。第三个参数EMPTY_PRODUCTS_ARRAY{data:[]}是空结果时的兜底输出避免前端解析失败。GET /api/Product/{id} —— 单条产品[HttpGet({id})] public async Task Get(int id) { var cmd new SqlCommand( select ProductID, Name, Color, Price, Quantity from Product where ProductId id FOR JSON PATH, WITHOUT_ARRAY_WRAPPER); cmd.Parameters.AddWithValue(id, id); await sqlQuery.Stream(cmd, Response.Body, {}); }单条查询使用WITHOUT_ARRAY_WRAPPER输出单个 JSON 对象而非数组并通过AddWithValue参数化id防止 SQL 注入。Stream方法的重载支持直接传入SqlCommand。POST / PUT / PATCH / DELETE —— 写入操作[HttpPost] public async Task Post() { string product new StreamReader(Request.Body).ReadToEnd(); var cmd new SqlCommand(InsertProductFromJson); cmd.CommandType System.Data.CommandType.StoredProcedure; cmd.Parameters.AddWithValue(ProductJson, product); await sqlCmd.ExecuteNonQuery(cmd); }POST /api/Product→ 调用InsertProductFromJson存储过程PUT /api/Product/{id}→ 调用UpdateProductFromJsonPATCH /api/Product/{id}→ 调用UpsertProductFromJson注意该方法的[HttpPatch]特性但id无路由占位符是从查询串或表单传递DELETE /api/Product/{id}→ 执行内联delete Product where ProductId id。请求体中的 JSON 被原样读出并作为存储过程参数传入由数据库端OPENJSON完成解析实现了JSON 进、JSON 出的极简服务层。报表端点控制器还提供了三个报表端点将聚合查询结果直接格式化为 D3 图表所需的数据结构[HttpGet(Report1)] public async Task Report1() { await sqlQuery.Stream( select Color as [key], AVG( Price ) as value from Product group by Color FOR JSON PATH, Response.Body, []); }Report1输出[{key:Black,value:...}]形式供饼图使用Report2/Report3输出[{x:...,y:...}]形式供柱状图使用[key]/[value]与x/y的别名直接决定了 JSON 键名。前端层DataTables 与 Bootstrap Modalindex.html 页面按顺序引用了 Bootstrap 3.3.5、JQuery DataTables 及配套的jquery.dataTables.Bootstrap.js、表单序列化插件jquery.serializejson.js、提示组件toastr等本地资源全部托管在wwwroot/media下无需 CDN。页面主体是一个带idexample的表格列定义依次为Product、Color、Price、Quantity、Made in、Tags以及两个操作列Edit/Delete。products.js 是前端核心逻辑ROOT_API_URL /api/Product/作为所有 AJAX 请求的统一基址DataTables 通过ajax: ROOT_API_URL自动请求列表数据columns配置将后端返回的Name、Color、Price、Quantity、MadeIn、Tags字段映射到对应列其中Price声明sType: numeric以获得正确的数值排序Edit/Delete 按钮通过render函数渲染点击事件分别触发getProduct(id)与deleteProduct(id)getProduct用$.ajax拉取单条 JSON 后调用$modal.loadJSON(json)由jquery.html-template.js提供把 JSON 填充到 Bootstrap Modal 表单保存时$form.serializeJSON({ checkboxUncheckedValue: false, parseAll: true })将表单序列化为 JSON 对象再JSON.stringify后通过POST无 ID或PUT有 ID提交每次操作成功后通过toastr.success提示并调用$table.ajax.reload(null, false)局部刷新表格保留当前分页/排序状态。dashboard.html 则是一个基于 D3 v4 的数据可视化页面它通过$.getJSON请求Report1/Report2/Report3三个聚合端点分别用饼图Pie和柱状图BarChart源码见 pie.js、barChart.js展示按颜色分组的平均价格等统计信息展示了聚合查询结果直接作为图表数据源的 JSON 数据通路。SQL/JSON 功能演示demo.sql 全景解读示例附带的 demo.sql 是一份可以直接在 SSMS 中分步执行的 JSON 功能演示脚本覆盖了 SQL Server 2016 JSON 支持的绝大部分能力建议与 Web 应用对照学习。混合查询JSON 列与关系列联合使用select ProductID, Name, Color, Size, Price, Quantity, Data, Tags, JSON_VALUE(Data, $.MadeIn) as MadeIn from Product where JSON_VALUE(Data, $.Type) partJSON_VALUE可以在SELECT、WHERE、GROUP BY、HAVING、ORDER BY任意位置使用例如按 JSON 内部字段做聚合select JSON_VALUE(Data, $.Type) as Type, Color, AVG( cast(JSON_VALUE(Data, $.ManufacturingCost) as float) ) as Cost from Product group by JSON_VALUE(Data, $.Type), Color having JSON_VALUE(Data, $.Type) is not null order by JSON_VALUE(Data, $.Type)注意ManufacturingCost是 JSON 中的数值需要cast(... as float)后才能参与AVG聚合。为 JSON 字段建立索引纯JSON_VALUE表达式无法直接利用索引demo.sql 展示了官方推荐的计算列 索引方案alter table product add Type AS JSON_VALUE(Data, $.Type), ManufacturingCost AS cast(JSON_VALUE(Data, $.ManufacturingCost) as float) create index json_index on Product(Type, Color) include (ManufacturingCost)把JSON_VALUE表达式固化为持久化计算列再在计算列上创建索引后续对JSON_VALUE(Data, $.Type)的查询即可走索引扫描这是 JSON 列性能优化的关键手段。JSON_MODIFY局部更新 JSON 文档update Product set Data JSON_MODIFY(Data, $.ManufacturingCost, 10), Tags JSON_MODIFY(Tags, append $, new) where ProductID 16JSON_MODIFY支持三种路径模式strict路径必须存在、lax默认路径不存在则忽略、append向 JSON 数组追加元素如示例向Tags追加new。该函数可以在 T-SQL 层面完成对 JSON 字段的精准修改而无需读出整行再在应用层改写。OPENJSONJSON 与关系行集互转demo.sql 演示了OPENJSON的三种典型场景单个 JSON 对象转行OPENJSON(product) WITH (Name nvarchar(50), ...)将对象键映射为列JSON 数组转行集WITH子句支持$.Data.Type路径提取嵌套字段并用AS JSON保留子对象数组分析与聚合可以直接对OPENJSON结果做GROUP BY ... HAVING ... ORDER BY例如按颜色统计平均价格SELECT ISNULL(Color, N/A) Color, AVG(Price) AvgPrice, MIN(Quantity) MinQuantity FROM OPENJSON (products) WITH (Name nvarchar(50),Color nvarchar(15),Size nvarchar(5),Price money,Quantity int) group by Color having MIN(Quantity) 10 order by Color此外demo.sql 还展示了用CROSS APPLY OPENJSON(Tags)查找包含特定标签值的行select ProductID, Name, Color, Size, Price, Quantity, Tags from Product CROSS APPLY OPENJSON(Tags) where value promoCROSS APPLY OPENJSON会把每行的Tags数组展开为多行默认输出列名为key、value、type再在WHERE中按展开值过滤这是JSON 数组中找值的标准写法。FOR JSON查询结果格式化输出-- 未包装 JSON 列会被转义为字符串→ 演示错误场景 select Name,Color,Size,Price,Quantity,Data,Tags from Product FOR JSON PATH -- 正确写法JSON_QUERY 包装 JSON 列 select Name,Color,Size,Price,Quantity,JSON_QUERY(Data) as Data, JSON_QUERY(Tags) as Tags from Product FOR JSON PATH -- 单行导出为单个对象 select ... from Product where ProductID 17 FOR JSON PATH, WITHOUT_ARRAY_WRAPPERdemo.sql 特意先演示了未用JSON_QUERY包装导致 JSON 列被转义成字符串的错误写法再给出JSON_QUERY(Data)/JSON_QUERY(Tags)的正确版本——这正是 Web API 端能输出合法嵌套 JSON 的底层原因也与 ProductController.cs 中的查询逻辑一一对应。设计取舍与扩展建议示例作者在 README 的 Disclaimers 部分明确说明该示例刻意保持最小实现用于聚焦 JSON 功能演示而非作为通用 Web 架构的最佳实践范本。它没有引入 Repository 等分层模式只使用了 ASP.NET Core 内置的依赖注入且非强制要求。这意味着你可以按需把它改造为贴合自身架构的形式例如引入仓储层、服务层或 ORM示例展示了JSON 进、JSON 出的极简服务端模型若你的系统已经大量使用 JSON 数据交换这种数据库直接产出 JSON的模式可以有效减少服务端序列化代码与类型映射成本当 JSON 列参与过滤、聚合时务必参考 demo.sql 中的计算列 索引方案避免全表扫描带来的性能问题。延伸阅读同目录下还有不依赖前端 UI 的纯 REST API 版本dotnet-rest-api以及 Node.js 实现 nodejs-jquery-bootstrap-app可对比不同技术栈下相同的 JSON 数据通路JSON 功能的更多示例与配套说明见 samples/features/json本示例基于 MIT 许可证发布代码文件见 license.txt。赞分享示例工程数据库教程后端【免费下载链接】sql-server-samplesAzure Data SQL Samples - Official Microsoft GitHub Repository containing code samples for SQL Server, Azure SQL, Azure Synapse, and Azure SQL Edge项目地址https://gitcode.com/gh_mirrors/sq/sql-server-samples点击查看免费下载相关推荐Buzz 实时录制Live Recording完整指南从麦克风实时转录到系统音频捕获Buzz 实时录制Live Recording完整指南从麦克风实时转录到系统音频捕获 导读 实时录制是 Buzz 的核心功能之一它拾取麦克风或任意音频示例工程数据库教程后端使用 ASP.NET Core 与 SQL Server FOR JSON 构建 ReactJS 评论应用sql-server-samples 中的 JSON 前后端集成实战使用 ASP.NET Core 与 SQL Server FOR JSON 构建 ReactJS 评论应用sql server samples 中的 JSON示例工程数据库教程后端基于 ASP.NET Boilerplate 构建多租户 SaaS 应用EventCloud 实战ASP.NET Core EF Core Angular 5基于 ASP.NET Boilerplate 构建多租户 SaaS 应用EventCloud 实战ASP.NET Core EF Core Angu后端Web框架依赖注入认证鉴权上一篇Better-XCloud完整指南强力优化Xbox云游戏体验的实战教程下一篇GHelper硬件控制工具实战攻略华硕笔记本性能优化指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表