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,页面不存在

谢谢!

我尝试过的其他事情包括:

我的天啊。 (害羞地)解决了。 不是 Framework、Nuget、Controller base class、routeprefix、routes 或任何光荣的东西,而是……我。 我唯一没有看的地方是模型本身,它将 Id 定义为 Guid。 Action 正在使用 int,将其转换为 Guid。 ASP.Core 找不到它,因为它是基于模型(而不是控制器)构建路由,因此忽略了操作,因为它作为 int != Guid 对其基于约定的路由构建没有意义。呃。

如果您想知道我到底为什么使用 int...那是因为当我播种 Db 时我想要一个 Guid Key,但出于测试目的我想要一些记录具有我可以参考的特定 ID回到,我很懒,不想输入完整的 Guid。

回想起来,这是一个愚蠢的想法......:-(

但感谢您的关注!感谢您花费的时间。