web-dev-qa-db-ja.com

ODataをASP.net Coreと適切に統合する方法

ODataとEntityFrameworkを使用して、「単純な」Web APIで新しいASP.NET Coreプロジェクトを作成しようとしています。以前、ASP.NETの古いバージョンでODataを使用しました。

単純なget関数のみを使用してコントローラーをセットアップしました。基本的なODataコマンドをフィルターおよびtopとして機能させることはできましたが、expandコマンドを機能させることはできません。これは、Startup.csでどのように設定するのかよくわからないからだと思います。 Githubからいくつかのodataサンプルを含むなど、多くのことを試しました。

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

スタートアップファイルでは、Serviceクラスからいくつかのプロパティを除外しようとしましたが、まったく効果がありません。したがって、問題はIDataServiceインターフェイスの使用方法にある可能性があります。 (ApplicationContextはサンプルのように実装します)

明確にするために、.Coreフレームワークだけでなく、完全な.NET FrameworkでASP.NET Core Web APIを作成しています。私の現在のコードは、両方のサンプルの最良/最悪の組み合わせであり、WebAPIをフィルター処理できますが、プロパティを展開または非表示にできないという意味で機能します。

誰も私が見逃しているものを見ることができますか? 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"
}
27
Sli

また、_Microsoft.AspNetCore.OData.vNext_、_version 6.0.2-alpha-rtm_を取得しましたが、次のコードを使用してEdmモデルをルートにマップしました。

_services.AddOData();
// ...
app.UseMvc(routes =>
{
  ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
  modelBuilder.EntitySet<Product>("Products");
  IEdmModel model = modelBuilder.GetEdmModel();
  routes.MapODataRoute(
    prefix: "odata",
      model: model
  );
_

services.AddOData()とともに

奇妙ですが、.Net Core 1.1で動作するようです

6
Alex Buchatski

私は何とかそれを機能させましたが、提供されるODataルーティングを使用しなかったのは、より細かくする必要があるからです。このソリューションを使用すると、ODataクエリパラメーターの使用を許可しながら、独自のWeb APIを作成できます。

ノート:

  • 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; }
    }
}
_

Startup.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?}");
            });
        }
    }
}
_

Controller.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')。正常に機能する場合、最初の2つのエンティティのみが表示されます。

6
Métoule

T4を使用して、コードファーストEFモデルからASP.NET Core OData v4コントローラーを自動生成するgithubリポジトリがあります。 Microsoft.AspNetCore.OData.vNext 6.0.2-alpha-rtmを使用します。興味深いかもしれません。

https://github.com/afgbeveridge/AutoODataEF.Core

2
Aldous Zodiac

WEB APIサーバー側から:

最も簡単な使用方法は、直接の[EnableQuery]属性です。現在、最近の7.xパッケージでは、うまく機能しています。

以下のように、一般的な実装も簡単にできます。アイデアには一般的な方法があり、必要なエンティティ名に基づいて明確になります。クライアント側で使用するLinq2RestANCを使用すると、カスタムクエリパラメーターも簡単に渡すことができます。以下の例のように、Movies1とMovies2の2つのテーブルがある場合、$ expandとサブフィルター/サブプロセス条件を実行すると、クエリは直接dbにのみ適用されます。

[EnableQuery]
public IActionResult Get([FromQuery] string name)
{
        switch (name)
        {
            case "Movie2":
                return Ok(new List<ViewModel>{new ViewModel(Movies2=_db.Movies2)});
        }
        return Ok(new List<ViewModel>{new ViewModel(Movies1=_db.Movies1)});
 }

クライアント側の消費の場合-> ODATAサービスプロキシを使用しないでください。これはバグが多く、多くのエラーをスローします。 -> Simple.OData.Clientは良いです。ただし、expand内のネストされたクエリのサポートは遅れています。例えば/ Products?$ expand = Suppliers($ select = SupplierName; $ top = 1;)このような内部展開の場合、それ以上のフィルタリングはサポートされません。これはバグ#200として追跡されます

-> Linq2RestANCは美しい選択肢です。それもネイティブにネストされた展開をサポートしていませんが、ネイティブのIQueryProviderを継承することで実装されているため、完成したネストされたディープレベルの展開シナリオを変更およびテストするのに3〜4時間かかりました。 Expressionprocessor.csの「Expand」とParameterBuilder.csのGetFullUri()を少し変更して、機能させる必要があります。

0
user3585952

ODataチームでは現在、これはアルファ版のようです。 この問題による

0
bigtlb