快速搭建WebAPI(Odata+Code-First)附Odata条件查询表~
Odata是什么?
开放数据协议(Open Data Protocol,缩写OData)是一种描述如何创建和访问Restful服务的OASIS标准。该标准由微软发起,前三个版本1.0、2.0、3.0都是微软开放标准,遵循 [1] 。第四个版本4.0于2014年3月17日在OASIS投票通过成为开放工业标准 [2] 。
为什么需要OData?
OData是一个协议,一个标准。所以这个问题等同于为什么我们需要协议。类比TCP协议就可以理解一般。假设你开发的组件必须要和某个第三方组件通信,如果第三方组件不支持TCP而只支持其内部开发的一个私有协议,你就肯定头大了,你必须在你的组件里单独为其实现这个私有协议。如果大家都支持TCP协议,不就省事了么。这就是标准协议的作用:协议和标准用于制定一个统一通用的规则。 我们只需要按照这个协议或标准生产组件,那么这个组件就可以方便的和其他组件集成/协作。而无须根据其他组件的私有标准定制化组件。
如何使用OData?
Odata目前已经不怎么更新了,目前最高的5.7已经停留在2015年8月21 远古时代,然后我们还需要在WebApiConfig.cs中配置Odata
1 2 3 4 5 | ODataModelBuilder builder = new ODataConventionModelBuilder(); builder.EntitySet<TRoles>( "TRoles" ); builder.EntitySet<TUsers>( "TUsers" ); builder.EntitySet<TUsersRoles>( "TUsersRoles" ); config.Routes.MapODataServiceRoute( "odata" , "odata" , builder.GetEdmModel()); |
其中builder.EntitySet<TRoles>("TRoles"); 需要注意的是EntitySet这个方法是对你的实体类进行配置,其底层是要获取的类型放到它的配置项里。
Code-First 方式 create DataBase
Context.cs
1 2 3 4 5 6 7 8 | public Model1() : base ( "name=CodeFirstDemo" ) { } public DbSet<TUsers> Users { get ; set ; } public DbSet<TRoles> Roles { get ; set ; } public DbSet<TUsersRoles> UsersRoles { get ; set ; } |
其它的三个类就不写上来了,不想占用博客园的资源,也不想写没有意义的!
创建webAPIController
创建完的代码展示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | using System; using System.Collections.Generic; using System.Data; using System.Data.Entity; using System.Data.Entity.Infrastructure; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using System.Web.Http.ModelBinding; using System.Web.Http.OData; using System.Web.Http.OData.Routing; using heheda.Models; namespace heheda.Controllers { /* The WebApiConfig class may require additional changes to add a route for this controller. Merge these statements into the Register method of the WebApiConfig class as applicable. Note that OData URLs are case sensitive. using System.Web.Http.OData.Builder; using System.Web.Http.OData.Extensions; using heheda.Models; ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); builder.EntitySet<TRoles>("TRoles"); builder.EntitySet<TUsersRoles>("UsersRoles"); config.Routes.MapODataServiceRoute("odata", "odata", builder.GetEdmModel()); */ public class TRolesController : ODataController { private Model1 db = new Model1(); // GET: odata/TRoles [EnableQuery] public IQueryable<TRoles> GetTRoles() { return db.Roles; } // GET: odata/TRoles(5) [EnableQuery] public SingleResult<TRoles> GetTRoles([FromODataUri] int key) { return SingleResult.Create(db.Roles.Where(tRoles => tRoles.Id == key)); } // PUT: odata/TRoles(5) public IHttpActionResult Put([FromODataUri] int key, Delta<TRoles> patch) { Validate(patch.GetEntity()); if (!ModelState.IsValid) { return BadRequest(ModelState); } TRoles tRoles = db.Roles.Find(key); if (tRoles == null ) { return NotFound(); } patch.Put(tRoles); try { db.SaveChanges(); } catch (DbUpdateConcurrencyException) { if (!TRolesExists(key)) { return NotFound(); } else { throw ; } } return Updated(tRoles); } // POST: odata/TRoles public IHttpActionResult Post(TRoles tRoles) { if (!ModelState.IsValid) { return BadRequest(ModelState); } db.Roles.Add(tRoles); db.SaveChanges(); return Created(tRoles); } // PATCH: odata/TRoles(5) [AcceptVerbs( "PATCH" , "MERGE" )] public IHttpActionResult Patch([FromODataUri] int key, Delta<TRoles> patch) { Validate(patch.GetEntity()); if (!ModelState.IsValid) { return BadRequest(ModelState); } TRoles tRoles = db.Roles.Find(key); if (tRoles == null ) { return NotFound(); } patch.Patch(tRoles); try { db.SaveChanges(); } catch (DbUpdateConcurrencyException) { if (!TRolesExists(key)) { return NotFound(); } else { throw ; } } return Updated(tRoles); } // DELETE: odata/TRoles(5) public IHttpActionResult Delete([FromODataUri] int key) { TRoles tRoles = db.Roles.Find(key); if (tRoles == null ) { return NotFound(); } db.Roles.Remove(tRoles); db.SaveChanges(); return StatusCode(HttpStatusCode.NoContent); } // GET: odata/TRoles(5)/TRolesUsersList [EnableQuery] public IQueryable<TUsersRoles> GetTRolesUsersList([FromODataUri] int key) { return db.Roles.Where(m => m.Id == key).SelectMany(m => m.TRolesUsersList); } protected override void Dispose( bool disposing) { if (disposing) { db.Dispose(); } base .Dispose(disposing); } private bool TRolesExists( int key) { return db.Roles.Count(e => e.Id == key) > 0; } } } |
在之后我们就可以在前端中去通过这个协议规则去过滤我们的数据了!
Odata筛选数据规则表
操作 |
URL |
说明 |
$filter | http://localhost:8090/api/Meetings?$filter=ProductName eq 'Tofu' | 根据表达式的状态返回结果(返回ProductName 等于Tofu的Products) |
$orderby | http://localhost:8090/api/Meetings?$orderby=ProductName | 根据结果排序(根据ProductName列排序) |
$skip | http://localhost:8090/api/Meetings?$skip=10 | 越过结果中的n条数据,常用于分页 |
$top | http://localhost:8090/api/Meetings?$top=10 | 返回结果中的前n条记录,常用于分页 |
$select | http://localhost:8090/api/Meetings?$filter=ProductName eq 'Tofu'&$select=ProductName,UnitPrice | 选择需要返回的属性 |
$expand | http://localhost:8090/api/Meetings?$expand=Supplier | 返回Products中包含的导航属性(关联属性)Supplier |
$inlinecount | http://localhost:8090/api/Meetings?$inlinecount=allpages | 向服务器获取符合条件的资源总数(分页的total值) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异