创建项目
我们使用Microsoft Visual Studio Community 2022 (64 位) - Current 版本 17.14.0
创建一个asp.net core 空
的项目。
解决方案名是Lesson。由于后续我们还会陆续创建其他类似项目,于是给项目命名为Lesson1。
为了方便后续访问,我们取消掉配置HTTPS
。
安装依赖
Microsoft.AspNetCore.OData
是一个用于在 ASP.NET Core 应用程序中实现 OData(Open Data Protocol)服务的官方库。我们可以在Package Manager Console(程序包管理控制台)中输入下面的指令以安装该库。
Install-Package Microsoft.AspNetCore.OData
编写代码
创建模型类
在项目中创建目录Models
,然后创建两个类:Customer类和Order类。
Order是一个数据模型。在业务上,它表示一个订单信息。Id是订单的唯一标识,所以该属性使用了required约束,表示它不可以为空;Amount是订单的花费。
namespace Lesson1.Models
{
public class Order
{
public required int Id { get; set; }
public decimal Amount { get; set; }
}
}
Customer也是一个数据模型。在业务上表示一个客户信息。除了唯一标识该客户的Id属性,还有姓名(Name)以及订单(Orders)。
订单表列属性Orders对应于EDM(Entiy Data Model)
中的关系(Relationship)
,是导航属性(Navigation properties)
。
namespace Lesson1.Models
{
using System.Collections.Generic;
public class Customer
{
public required int Id { get; set; }
public required string Name { get; set; }
public List<Order>? Orders { get; set; }
}
}
此时我们还不能分辨Customer和Order是Entity Type
还是Entity Set
。它们只是单纯定义了数据结构。
创建Controller
在项目中我们创建Controller目录,并创建一个CustomersController,让它继承于ODataController。
继承ODataController之后,CustomersController就可以自动支持 OData 的查询选项(如 $filter、$select、$orderby、$expand 等),无需手动解析这些参数。
using System.Reflection.Emit;
namespace Lesson1.Controllers
{
using Lesson1.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OData.Query;
using Microsoft.AspNetCore.OData.Routing.Controllers;
using System;
using System.Collections.Generic;
using System.Linq;
public class CustomersController : ODataController
{
private static Random random = new Random();
// 生成 3 个客户,每个客户有 2 个订单
private static readonly List<Customer> customers = [.. Enumerable.Range(1, 3).Select(idx => new Customer
{
Id = idx,
Name = $"Customer {idx}",
Orders = [.. Enumerable.Range(1, 2).Select(dx => new Order
{
Id = (idx - 1) * 2 + dx,
Amount = random.Next(1, 9) * 10
})]
})];
[EnableQuery]
public ActionResult<IEnumerable<Customer>> Get()
{
return Ok(customers);
}
[EnableQuery]
public ActionResult<Customer> Get([FromRoute] int key)
{
var item = customers.SingleOrDefault(d => d.Id.Equals(key));
if (item == null)
{
return NotFound();
}
return Ok(item);
}
}
}
上面代码中,我们创建了3个Customer Entity,它们的Id分别是1、2、3。它们的名称分别是 Customer 1
、Customer 2
、Customer 3
。
每个Customer Entity有两个Order Entity,它们的Id也有一定的规律:1、3、5、7、9、11。但是Amount 值是随机的。
后面两个查询函数Get的入参不同,对应的URL也不同。
- Get()方法对应于 /odata/Customers,支持OData 查询:如 $filter、$orderby 等。
- Get([FromRoute] int key)对应于处理 /odata/Customers({key}) ,key 参数自动从路由中获取。
上述查询函数都是被[EnableQuery]
修饰。 [EnableQuery]
是 ASP.NET Core OData 提供的一个特性(Attribute),用于在控制器的 Action 方法上启用 OData 查询功能。它允许客户端在请求 URL 中使用 OData 查询参数(如 $filter、$select、$orderby、$top、$skip 等),并自动解析和应用这些查询参数到返回的数据集合上,无需手动处理。
编写主函数
在主文件Program.cs中,我们填入下面的代码。
using Lesson1.Models;
using Microsoft.AspNetCore.OData;
using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder;
var builder = WebApplication.CreateBuilder(args);
// 提取 OData EDM 模型构建为方法,便于维护和扩展
static IEdmModel GetEdmModel()
{
var modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntityType<Order>();
modelBuilder.EntitySet<Customer>("Customers");
return modelBuilder.GetEdmModel();
}
// 添加 OData 服务和配置
builder.Services.AddControllers().AddOData(options =>
options.Select()
.Filter()
.OrderBy()
.Expand()
.Count()
.SetMaxTop(null)
.AddRouteComponents("odata", GetEdmModel())
);
var app = builder.Build();
app.UseRouting();
app.MapControllers();
app.Run();
ODataConventionModelBuilder的EntityType\<T\>()
方法用于将一个数据模型(本例中是Order类)注册为Entity Type
;EntitySet\<\>()
方法将一个数据模型(本例中是Customer类)注册为Entity Set
。
EntityType 只定义类型结构,不暴露为 OData 端点;EntitySet 定义集合,并将其暴露为 OData 端点(如 /odata/Customers),供客户端访问。
所以我们在后续编码时,对于每个要暴露的集合调用 EntitySet\<\>()
,而EntityType\<T\>()
只在需要单独定义类型时使用(如复杂类型(Complex Types)
、导航属性(Navigation properties)
等)。
代码地址
https://github.com/f304646673/odata/tree/main/csharp/Lesson/Lesson1