ASP.Core ODataControllers 无法识别 "get by key" 操作
ASP.Core ODataControllers not recognizing "get by key" operation
我正在研究如何在 ASP.Core 中使用 OData。
我创建了一个 BooksController
,它是 ODataController
的子类,我在其中定义了两个操作:Get()
和 Get(int id)
.
/odata/books
解析到第一个操作,但 /odata/books(1)
没有找到第二个操作。
一旦定义了模型,就可以找到以下控制器:
[ODataRoutePrefix("Books")]
public class BooksController : ODataController
{
private BookStoreContext _db;
public BooksController(BookStoreContext context)
{
_db = context;
}
[ODataRoute]
[EnableQuery]
public IActionResult Get()
{
return Ok(_db.Books);
}
[EnableQuery]
[ODataRoute("({key})")]
public IActionResult Get([FromODataUri] int key)
{
return Ok(_db.Books.FirstOrDefault(c => c.Id == key.ToGuid()));
}
}
该网站对所有路线都有默认的约定规则(见下文)。
但我认为这没有用,因为 BooksController
装饰有 [ODataRoutePrefix("Books")]
并且动作带有 [ODataRoute]
(和 [EnableQuery]
)——我认为这是基于属性的路由,优先(这是一个正确的假设吗?)。
我的 dto 模型是使用 Reflection 注册的...),但关键部分是 Startup 调用 UseMvc(...)
并定义路由,最终在此处调用:
private void CreateODataRoutes(IRouteBuilder routeBuilder)
{
// register the convention routes for MVC first...
routeBuilder.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
...
// then do the OData stuff...
routeBuilder.Count().Expand().Filter()
.MaxTop(100).OrderBy().Select();
// Use method further down the page
// to create a Build Model by reflection, using
// all OData Model definitions (ie, classes that implement
// IAllModulesOdataModelBuilderConfiguration)
var oDataConventionModelBuilder = BuildODataModelUsingReflectionAcrossAllAssemblies();
// Use the modelBuilder as the basis of defining routes:
RegisterRoutesToODataController(routeBuilder, oDataConventionModelBuilder);
}
其中 BuildODataModelUsingReflectionAcrossAllAssemblies
使用反射来查找各个模型定义,每个模型定义都非常简单,仅定义它们的 id(其余部分依赖于约定)。
请注意,我没有按照惯例定义操作(见下文)。
public class BookODataModelBuilderConfigurationBase<T> : IAllModulesOdataModelBuilderConfiguration
where T : class, IHasGuidId, new()
{
public virtual void Apply(ODataModelBuilder builder ...)
{
var _controllerName = this.GetControllerNameByConvention(typeof(Book));
var entity = builder.EntitySet<T>(this._controllerName).EntityType;
entity.HasKey(x => x.Id);
//Note...no Actions defined, as planning to rely on default conventions (routing by Verb to method starting with Get...)
}
}
创建模型时,注册如下;
private void RegisterRoutesToODataController(IRouteBuilder routeBuilder,
ODataConventionModelBuilder oDataConventionModelBuilder)
{
string routePrefix = $"{App.Modules.Core.Shared.Constants.ModuleSpecific.Module.AssemblyNamePrefix}.";
// Build the Edm model used to parse commands:
var edmModel = oDataConventionModelBuilder.GetEdmModel();
// Register the Odata paths
routeBuilder.MapODataServiceRoute(
routeName: $"{routePrefix}odataDefault",
routePrefix: "odata",
edmModel,
pathHandler:new DefaultODataPathHandler(),
// By convention? So that Get verb goes to Get action, etc.
routingConventions: ODataRoutingConventions.CreateDefault()
);
}
当路径为/odata/book(1)
时returnsHTTP ERROR 404
,页面不存在
谢谢!
我尝试过的其他事情包括:
- 注释掉 SwaggerAPI 的配置
- 删除了关键参数上的
[FromODataUri]
(有必要吗?)
- Added/removed
[ODataRoute("({key})")]
- 在 plural/singular
中将控制器注册为 BooksController
- 将操作的名称更改为
GetBook
,然后再次更改为 Get
- Added/removed
ODataRoutePrefix
- 在注册默认约定路由之前注册 OData 路由(认为应该一直如此,对吧?)。
- ...所有这些开始看起来更像是绝望而不是编码:-( ...
- 还在找。感谢您的指导。
我的天啊。 (害羞地)解决了。
不是 Framework、Nuget、Controller base class、routeprefix、routes 或任何光荣的东西,而是……我。
我唯一没有看的地方是模型本身,它将 Id 定义为 Guid。 Action 正在使用 int,将其转换为 Guid。
ASP.Core 找不到它,因为它是基于模型(而不是控制器)构建路由,因此忽略了操作,因为它作为 int != Guid 对其基于约定的路由构建没有意义。呃。
如果您想知道我到底为什么使用 int...那是因为当我播种 Db 时我想要一个 Guid Key,但出于测试目的我想要一些记录具有我可以参考的特定 ID回到,我很懒,不想输入完整的 Guid。
回想起来,这是一个愚蠢的想法......:-(
但感谢您的关注!感谢您花费的时间。
我正在研究如何在 ASP.Core 中使用 OData。
我创建了一个 BooksController
,它是 ODataController
的子类,我在其中定义了两个操作:Get()
和 Get(int id)
.
/odata/books
解析到第一个操作,但 /odata/books(1)
没有找到第二个操作。
一旦定义了模型,就可以找到以下控制器:
[ODataRoutePrefix("Books")]
public class BooksController : ODataController
{
private BookStoreContext _db;
public BooksController(BookStoreContext context)
{
_db = context;
}
[ODataRoute]
[EnableQuery]
public IActionResult Get()
{
return Ok(_db.Books);
}
[EnableQuery]
[ODataRoute("({key})")]
public IActionResult Get([FromODataUri] int key)
{
return Ok(_db.Books.FirstOrDefault(c => c.Id == key.ToGuid()));
}
}
该网站对所有路线都有默认的约定规则(见下文)。
但我认为这没有用,因为 BooksController
装饰有 [ODataRoutePrefix("Books")]
并且动作带有 [ODataRoute]
(和 [EnableQuery]
)——我认为这是基于属性的路由,优先(这是一个正确的假设吗?)。
我的 dto 模型是使用 Reflection 注册的...),但关键部分是 Startup 调用 UseMvc(...)
并定义路由,最终在此处调用:
private void CreateODataRoutes(IRouteBuilder routeBuilder)
{
// register the convention routes for MVC first...
routeBuilder.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
...
// then do the OData stuff...
routeBuilder.Count().Expand().Filter()
.MaxTop(100).OrderBy().Select();
// Use method further down the page
// to create a Build Model by reflection, using
// all OData Model definitions (ie, classes that implement
// IAllModulesOdataModelBuilderConfiguration)
var oDataConventionModelBuilder = BuildODataModelUsingReflectionAcrossAllAssemblies();
// Use the modelBuilder as the basis of defining routes:
RegisterRoutesToODataController(routeBuilder, oDataConventionModelBuilder);
}
其中 BuildODataModelUsingReflectionAcrossAllAssemblies
使用反射来查找各个模型定义,每个模型定义都非常简单,仅定义它们的 id(其余部分依赖于约定)。
请注意,我没有按照惯例定义操作(见下文)。
public class BookODataModelBuilderConfigurationBase<T> : IAllModulesOdataModelBuilderConfiguration
where T : class, IHasGuidId, new()
{
public virtual void Apply(ODataModelBuilder builder ...)
{
var _controllerName = this.GetControllerNameByConvention(typeof(Book));
var entity = builder.EntitySet<T>(this._controllerName).EntityType;
entity.HasKey(x => x.Id);
//Note...no Actions defined, as planning to rely on default conventions (routing by Verb to method starting with Get...)
}
}
创建模型时,注册如下;
private void RegisterRoutesToODataController(IRouteBuilder routeBuilder,
ODataConventionModelBuilder oDataConventionModelBuilder)
{
string routePrefix = $"{App.Modules.Core.Shared.Constants.ModuleSpecific.Module.AssemblyNamePrefix}.";
// Build the Edm model used to parse commands:
var edmModel = oDataConventionModelBuilder.GetEdmModel();
// Register the Odata paths
routeBuilder.MapODataServiceRoute(
routeName: $"{routePrefix}odataDefault",
routePrefix: "odata",
edmModel,
pathHandler:new DefaultODataPathHandler(),
// By convention? So that Get verb goes to Get action, etc.
routingConventions: ODataRoutingConventions.CreateDefault()
);
}
当路径为/odata/book(1)
时returnsHTTP ERROR 404
,页面不存在
谢谢!
我尝试过的其他事情包括:
- 注释掉 SwaggerAPI 的配置
- 删除了关键参数上的
[FromODataUri]
(有必要吗?) - Added/removed
[ODataRoute("({key})")]
- 在 plural/singular 中将控制器注册为
- 将操作的名称更改为
GetBook
,然后再次更改为Get
- Added/removed
ODataRoutePrefix
- 在注册默认约定路由之前注册 OData 路由(认为应该一直如此,对吧?)。
- ...所有这些开始看起来更像是绝望而不是编码:-( ...
- 还在找。感谢您的指导。
BooksController
我的天啊。 (害羞地)解决了。 不是 Framework、Nuget、Controller base class、routeprefix、routes 或任何光荣的东西,而是……我。 我唯一没有看的地方是模型本身,它将 Id 定义为 Guid。 Action 正在使用 int,将其转换为 Guid。 ASP.Core 找不到它,因为它是基于模型(而不是控制器)构建路由,因此忽略了操作,因为它作为 int != Guid 对其基于约定的路由构建没有意义。呃。
如果您想知道我到底为什么使用 int...那是因为当我播种 Db 时我想要一个 Guid Key,但出于测试目的我想要一些记录具有我可以参考的特定 ID回到,我很懒,不想输入完整的 Guid。
回想起来,这是一个愚蠢的想法......:-(
但感谢您的关注!感谢您花费的时间。