本文属于 OData 系列文章
ABP vNext 是一个重新设计的,面向微服务的框架,提供了一些非常有用的特性,包括分页查询等但是它并不能原生支持 OData,我们需要自行实现。
side by side 方式,由于 ABP vNext 官方没有对应的设计,所以你依然需要自己编写控制器。
原理
ABP vNext 有自动生成 Controller 的机制,我们的 ApplicationService,它会帮我们自动生成对应的终结点,并对外提供服务。这个过程是由 ABP 控制的,我们能修改的内容非常有限。
// TodoAppHttpApiHostModule.cs
private void ConfigureConventionalControllers(
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(TodoAppApplicationModule.Assembly, options =>
{
//// 移除自动生成的控制器
//options.ControllerTypes.Remove(typeof(TodoAppController;
//options.RootPath = "abp";
//// 添加手动实现的控制器
//options.ControllerTypes.Add(typeof(TodoAppImplController;
};
};
}
它依照惯例生成,我们可以对类型进行增加或者减少,但是不能控制生成的行为。而 OData 的控制器,需要我们从 ODataController
继承,或者使用 [EnableQuery]
,因此我们需要阻止自动生成控制器,自己实现对应的逻辑。
实现
首先禁用自动生成控制器功能。
//TodoAppService.cs
[RemoteService(false]
public class TodoAppService : ApplicationService, ITodoAppService
{
private readonly IRepository<TodoItem, Guid> _todoItemRepository;
public TodoAppService(IRepository<TodoItem, Guid> todoItemRepository
{
_todoItemRepository = todoItemRepository;
}
public async Task<IQueryable<TodoItemDto>> GetListAsync(
{
var items = await _todoItemRepository.GetQueryableAsync(;
return items
.Select(item => new TodoItemDto
{
Id = item.Id,
Text = item.Text
}
}
}
将应用服务打上 [RemoteService(false]
标记,服务就不会自动生成控制器。接下来在 HttpApi
项目中新建一个与服务同名的控制器,由于 ABP 项目自动生成了控制器抽象类模板,我们继承并实现它即可。
/* Inherit your controllers from this class.
*/
public abstract class TodoAppController : AbpControllerBase
{
protected TodoAppController(
{
LocalizationResource = typeof(TodoAppResource;
}
}
public class TodoController : TodoAppController
{
private readonly ITodoAppService todoAppService;
public TodoController(ITodoAppService todoAppService
:base(
{
this.todoAppService = todoAppService;
}
[EnableQuery]
public async Task<IEnumerable<TodoItemDto>> GetAsync(
{
var result = await todoAppService.GetListAsync(;
return result.AsQueryable(;
}
}
注入对应的服务,实现自己的逻辑,如果需要利用 EF Core 的查询功能,请使用 IQueryable
传递数据到控制器层。然后配置 OData :
//TodoAppHttpApiHostModule.cs
private IEdmModel GetEdmModels(
{
var builder = new ODataConventionModelBuilder(;
var device = builder.EntitySet<TodoItemDto>("Todo".EntityType.HasKey(w => w.Id;
return builder.GetEdmModel(;
}
记得在 PreConfigureServices
中加上调用。
//TodoAppHttpApiHostModule.cs
public override void PreConfigureServices(ServiceConfigurationContext context
{
PreConfigure<OpenIddictBuilder>(builder =>
{
builder.AddValidation(options =>
{
options.AddAudiences("TodoApp";
options.UseLocalServer(;
options.UseAspNetCore(;
};
};
// 加上这个调用
PreConfigure<IMvcBuilder>(builder =>
{
builder.AddOData(opt =>
{
opt.RouteOptions.EnablePropertyNameCaseInsensitive = true;
opt.RouteOptions.EnableQualifiedOperationCall = false;
opt.Expand(.Filter(.Count(.OrderBy(.Filter(.SetMaxTop(30;
opt.AddRouteComponents("api/app/", GetEdmModels(;
};
};
}
运行后,大功告成:
总结
本文实现了 OData 在 ABP vNext 中的使用。请注意,本方案只是一个 Demo,应用到生产前请自行评估风险,期待 ABP 团队在未来正式支持 OData
吧。本文的完整代码在 github,运行前可能需要先执行数据库初始化。