如何正确地将 OData 与 ASP.net Core 集成

2024-02-04

我正在尝试使用 OData 和 EntityFramework 创建一个具有“简单”Web api 的新 ASP.NET Core 项目。我以前曾将 OData 与旧版本的 ASP.NET 一起使用。

我已经设置了一个仅具有简单获取功能的控制器。我已经设法让它使用基本的 OData 命令作为过滤器和顶部,但我无法让扩展命令工作。我认为这是因为我不太清楚如何在 Startup.cs 中设置它。我尝试了很多事情,包括遵循 Github 上的一些 odata 示例:

https://github.com/OData/WebApi/tree/vNext/vNext/samples/ODataSample.Web https://github.com/OData/WebApi/tree/vNext/vNext/samples/ODataSample.Web https://github.com/bigfont/WebApi/tree/master/vNext/samples/ODataSample.Web https://github.com/bigfont/WebApi/tree/master/vNext/samples/ODataSample.Web

在我的启动文件中,我尝试从 Service 类中排除一些属性,但这些属性根本没有效果。所以问题可能出在我使用 IDataService 接口的方式上。 (ApplicationContext 像示例中一样实现它)

需要明确的是,我正在使用完整的 .NET Framework 而不仅仅是 .Core 框架创建 ASP.NET Core Web api。我当前的代码是两个示例中最好/最差的组合,并且在我可以过滤 WebAPI 但无法使其扩展或隐藏属性的意义上工作。

任何人都可以看到我缺少什么 og 有一个可用的 ASP.NET Odata 示例。我对startup.cs 中的整个设置不熟悉?我想我正在寻找能够完成这项工作的人。

控制器

[EnableQuery]
[Route("odata/Services")]
public class ServicesController : Controller
{
    private IGenericRepository<Service> _serviceRepo;
    private IUnitOfWork _unitOfWork;

    public ServicesController(IGenericRepository<Service> serviceRepo, IUnitOfWork unitOfWork)
    {
        _serviceRepo = serviceRepo;
        _unitOfWork = unitOfWork;
    }

    [HttpGet]
    public IQueryable<Service> Get()
    {
        var services = _serviceRepo.AsQueryable();
        return services;
    }
}

startup

using Core.DomainModel;
using Core.DomainServices;
using Infrastructure.DataAccess;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.OData.Extensions;

namespace Web
{
public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();

        if (env.IsDevelopment())
        {
            // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
            builder.AddApplicationInsightsSettings(developerMode: true);
        }
        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddApplicationInsightsTelemetry(Configuration);
        services.AddMvc().AddWebApiConventions();

        services.AddSingleton<ApplicationContext>(_ => ApplicationContext.Create());

        services.AddSingleton<IDataService, ApplicationContext>();

        services.AddOData<IDataService>(builder =>
        {
            //builder.EnableLowerCamelCase();
            var service = builder.EntitySet<Service>("Services");
            service.EntityType.RemoveProperty(x => x.CategoryId);
            service.EntityType.RemoveProperty(x => x.PreRequisiteses);
        });


        services.AddSingleton<IGenericRepository<Service>, GenericRepository<Service>>();
        services.AddSingleton<IUnitOfWork, UnitOfWork>();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        //ODataConventionModelBuilder builder = new ODataConventionModelBuilder();

        app.UseApplicationInsightsRequestTelemetry();

        //var builder = new ODataConventionModelBuilder(app.ApplicationServices.GetRequiredService<AssembliesResolver>());
        //var serviceCtrl = nameof(ServicesController).Replace("Controller", string.Empty);
        //var service = builder.EntitySet<Service>(serviceCtrl);
        //service.EntityType.RemoveProperty(x => x.CategoryId);

        app.UseOData("odata");

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseApplicationInsightsExceptionTelemetry();

        app.UseStaticFiles();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

Project.json 依赖项

  "dependencies": {
    "Microsoft.ApplicationInsights.AspNetCore": "1.0.2",
    "Microsoft.AspNet.Identity.EntityFramework": "2.2.1",
    "Microsoft.AspNetCore.Diagnostics": "1.0.0",
    "Microsoft.AspNetCore.Identity": "1.0.0",
    "Microsoft.AspNetCore.Mvc": "1.0.1",
    "Microsoft.AspNetCore.Razor.Tools": {
      "version": "1.0.0-preview2-final",
      "type": "build"
    },
    "Microsoft.AspNetCore.Routing": "1.0.1",
    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.1",
    "Microsoft.AspNetCore.StaticFiles": "1.0.0",
    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
    "Microsoft.Extensions.Configuration.Json": "1.0.0",
    "Microsoft.Extensions.Logging": "1.0.0",
    "Microsoft.Extensions.Logging.Console": "1.0.0",
    "Microsoft.Extensions.Logging.Debug": "1.0.0",
    "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
    "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0",
    "Microsoft.AspNetCore.OData": "1.0.0-rtm-00015",
    "dnx-clr-win-x86": "1.0.0-rc1-update2",
    "Microsoft.OData.Core": "7.0.0",
    "Microsoft.OData.Edm": "7.0.0",
    "Microsoft.Spatial": "7.0.0"
}

我设法让它工作,但我没有使用提供的 OData 路由,因为我需要更精细的粒度。通过此解决方案,您可以创建自己的 Web API,同时仍然允许使用 OData 查询参数。

Notes:

  • 我使用了 Nuget 包Microsoft.AspNetCore.OData.vNext, 版本6.0.2-alpha-rtm,需要 .NET 4.6.1
  • 据我所知,OData vNext 仅支持 OData v4(因此不支持 v3)
  • OData vNext 似乎很仓促,并且充满了错误。例如,$orderby查询参数损坏

MyEntity.cs

namespace WebApplication1
{
    public class MyEntity
    {
        // you'll need a key 
        public int EntityID { get; set; }
        public string SomeText { get; set; }
    }
}

启动.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.OData;
using Microsoft.AspNetCore.OData.Abstracts;
using Microsoft.AspNetCore.OData.Builder;
using Microsoft.AspNetCore.OData.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;

namespace WebApplication1
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();

            /* ODATA part */
            services.AddOData();
            // the line below is used so that we the EdmModel is computed only once
            // we're not using the ODataOptions.ModelManager because it doesn't seemed plugged in
            services.AddSingleton<IODataModelManger, ODataModelManager>(DefineEdmModel);
        }

        private static ODataModelManager DefineEdmModel(IServiceProvider services)
        {
            var modelManager = new ODataModelManager();

            // you can add all the entities you need
            var builder = new ODataConventionModelBuilder();
            builder.EntitySet<MyEntity>(nameof(MyEntity));
            builder.EntityType<MyEntity>().HasKey(ai => ai.EntityID); // the call to HasKey is mandatory
            modelManager.AddModel(nameof(WebApplication1), builder.GetEdmModel());

            return modelManager;
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

控制器.cs

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OData;
using Microsoft.AspNetCore.OData.Abstracts;
using Microsoft.AspNetCore.OData.Query;
using System.Linq;

namespace WebApplication1.Controllers
{
    [Produces("application/json")]
    [Route("api/Entity")]
    public class ApiController : Controller
    {
        // note how you can use whatever endpoint
        [HttpGet("all")]
        public IQueryable<MyEntity> Get()
        {
            // plug your entities source (database or whatever)
            var entities = new[] {
                new MyEntity{ EntityID = 1, SomeText = "Test 1" },
                new MyEntity{ EntityID = 2, SomeText = "Test 2" },
                new MyEntity{ EntityID = 3, SomeText = "Another texts" },
            }.AsQueryable();

            var modelManager = (IODataModelManger)HttpContext.RequestServices.GetService(typeof(IODataModelManger));
            var model = modelManager.GetModel(nameof(WebApplication1));
            var queryContext = new ODataQueryContext(model, typeof(MyEntity), null);
            var queryOptions = new ODataQueryOptions(queryContext, HttpContext.Request);

            return queryOptions
                .ApplyTo(entities, new ODataQuerySettings
                {
                    HandleNullPropagation = HandleNullPropagationOption.True
                })
                .Cast<MyEntity>();
        }
    }
}

如何测试

您可以使用以下 URI:/api/Entity/all?$filter=contains(SomeText,'Test')。如果工作正常,您应该只能看到前两个实体。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何正确地将 OData 与 ASP.net Core 集成 的相关文章

  • WPF DataGrid 多选

    我读过几篇关于这个主题的文章 但很多都是来自 VS 或框架的早期版本 我想做的是从 dataGrid 中选择多行并将这些行返回到绑定的可观察集合中 我尝试创建一个属性 类型 并将其添加到可观察集合中 它适用于单个记录 但代码永远不会触发多个
  • 在模板类中声明模板友元类时出现编译器错误

    我一直在尝试实现我自己的链表类以用于教学目的 我在迭代器声明中指定了 List 类作为友元 但它似乎无法编译 这些是我使用过的 3 个类的接口 Node h define null Node
  • 调用 McAfee 病毒扫描引擎

    我收到客户的请求 要求使用他们服务器上的 McAfee 病毒扫描将病毒扫描集成到应用程序中 我做了一些调查 发现 McScan32 dll 是主要的扫描引擎 它导出各种看起来有用的函数 我还发现提到了 McAfee Scan Engine
  • STL 迭代器:前缀增量更快? [复制]

    这个问题在这里已经有答案了 可能的重复 C 中的预增量比后增量快 正确吗 如果是 为什么呢 https stackoverflow com questions 2020184 preincrement faster than postinc
  • C# 异步等待澄清?

    我读了here http blog stephencleary com 2012 02 async and await html that 等待检查等待的看看它是否有already完全的 如果 可等待已经完成 那么该方法将继续 运行 同步
  • 通过引用传递 [C++]、[Qt]

    我写了这样的东西 class Storage public Storage QString key const int value const void add item QString int private QMap
  • 传递给函数时多维数组的指针类型是什么? [复制]

    这个问题在这里已经有答案了 我在大学课堂上学习了 C 语言和指针 除了多维数组和指针之间的相似性之外 我认为我已经很好地掌握了这个概念 我认为由于所有数组 甚至多维 都存储在连续内存中 因此您可以安全地将其转换为int 假设给定的数组是in
  • 如何使从 C# 调用的 C(P/invoke)代码“线程安全”

    我有一些简单的 C 代码 它使用单个全局变量 显然这不是线程安全的 所以当我使用 P invoke 从 C 中的多个线程调用它时 事情就搞砸了 如何为每个线程单独导入此函数 或使其线程安全 我尝试声明变量 declspec thread 但
  • 需要帮助优化算法 - 两百万以下所有素数的总和

    我正在尝试做一个欧拉计划 http projecteuler net问题 我正在寻找 2 000 000 以下所有素数的总和 这就是我所拥有的 int main int argc char argv unsigned long int su
  • 使用 C# 中的 CsvHelper 将不同文化的 csv 解析为十进制

    C 中 CsvHelper 解析小数的问题 我创建了一个从 byte 而不是文件获取 csv 文件的类 并且它工作正常 public static List
  • 两个静态变量同名(两个不同的文件),并在任何其他文件中 extern 其中一个

    在一个文件中将变量声明为 static 并在另一个文件中进行 extern 声明 我认为这会在链接时出现错误 因为 extern 变量不会在任何对象中看到 因为在其他文件中声明的变量带有限定符 static 但不知何故 链接器 瑞萨 没有显
  • 如何定义一个可结构化绑定的对象的概念?

    我想定义一个concept可以检测类型是否T can be 结构化绑定 or not template
  • 两个类可以使用 C++ 互相查看吗?

    所以我有一个 A 类 我想在其中调用一些 B 类函数 所以我包括 b h 但是 在 B 类中 我想调用 A 类函数 如果我包含 a h 它最终会陷入无限循环 对吗 我能做什么呢 仅将成员函数声明放在头文件 h 中 并将成员函数定义放在实现文
  • 如何在当前 Visual Studio 主机内的 Visual Studio 扩展中调试使用 Roslyn 编译的代码?

    我有一个 Visual Studio 扩展 它使用 Roslyn 获取当前打开的解决方案中的项目 编译它并从中运行方法 程序员可以修改该项目 我已从当前 VisualStudioWorkspace 成功编译了 Visual Studio 扩
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • C 函数 time() 如何处理秒的小数部分?

    The time 函数将返回自 1970 年以来的秒数 我想知道它如何对返回的秒数进行舍入 例如 对于100 4s 它会返回100还是101 有明确的定义吗 ISO C标准没有说太多 它只说time 回报 该实现对当前日历时间的最佳近似 结
  • 相当于Linux中的导入库

    在 Windows C 中 当您想要链接 DLL 时 您必须提供导入库 但是在 GNU 构建系统中 当您想要链接 so 文件 相当于 dll 时 您就不需要链接 为什么是这样 是否有等效的 Windows 导入库 注意 我不会谈论在 Win
  • C++ 中的参考文献

    我偶尔会在 StackOverflow 上看到代码 询问一些涉及函数的重载歧义 例如 void foo int param 我的问题是 为什么会出现这种情况 或者更确切地说 你什么时候会有 对参考的参考 这与普通的旧参考有何不同 我从未在现
  • 在OpenGL中,我可以在坐标(5, 5)处精确地绘制一个像素吗?

    我所说的 5 5 正是指第五行第五列 我发现使用屏幕坐标来绘制东西非常困难 OpenGL 中的所有坐标都是相对的 通常范围从 1 0 到 1 0 为什么阻止程序员使用屏幕坐标 窗口坐标如此严重 最简单的方法可能是通过以下方式设置投影以匹配渲
  • Mono 应用程序在非阻塞套接字发送时冻结

    我在 debian 9 上的 mono 下运行一个服务器应用程序 大约有 1000 2000 个客户端连接 并且应用程序经常冻结 CPU 使用率达到 100 我执行 kill QUIT pid 来获取线程堆栈转储 但它总是卡在这个位置

随机推荐