web-dev-qa-db-de.com

So integrieren Sie OData ordnungsgemäß in ASP.net Core

Ich versuche, ein neues ASP.NET Core-Projekt mit einer "einfachen" Web-API mit OData und EntityFramework zu erstellen. Ich habe OData zuvor mit älteren Versionen von ASP.NET verwendet.

Ich habe einen Controller mit nur einer einfachen Get-Funktion eingerichtet. Ich habe es geschafft, es mit den grundlegenden OData-Befehlen als Filter und nach oben zu bringen, aber ich kann den Erweiterungsbefehl nicht ausführen. Ich denke es liegt daran, dass ich nicht ganz herausfinden kann, wie ich es in Startup.cs einrichten kann. Ich habe viele Dinge ausprobiert, darunter einige Odata-Samples von Github:

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

In meiner Startdatei versuche ich, einige Eigenschaften aus der Service-Klasse auszuschließen, was keinerlei Auswirkungen hat. Das Problem liegt also möglicherweise in der Art, wie ich die IDataService-Schnittstelle verwende. (Der ApplicationContext implementiert es wie in den Beispielen)

Um klar zu sein, ich erstelle ein ASP.NET Core-Web-API mit dem vollständigen .NET Framework und nicht nur dem .Core-Framework. Mein aktueller Code ist eine Mischung aus den besten/schlechtesten der beiden Beispiele und funktioniert in dem Sinne, dass ich die WebAPI filtern kann, die Eigenschaften jedoch nicht erweitern oder ausblenden kann.

Kann jeder sehen, was mir fehlt, und ein funktionierendes ASP.NET Odata-Beispiel haben. Ich bin neu im Setup von startup.cs? Ich denke, ich suche jemanden, der diese Arbeit gemacht hat.

Controller

[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;
    }
}

Anlaufen

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 Abhängigkeiten

  "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"
}
25
Sli

Ich habe auch Microsoft.AspNetCore.OData.vNext, version 6.0.2-alpha-rtm zur Arbeit, aber ich habe den folgenden Code verwendet, um Edm model Routen zuzuordnen:

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

zusammen mit services.AddOData()

Es ist seltsam, aber es scheint mit .Net Core 1.1 zu funktionieren

6
Alex Buchatski

Ich habe es geschafft, damit es funktioniert, aber ich habe das bereitgestellte OData-Routing nicht verwendet, weil ich mehr Granularität brauchte. Mit dieser Lösung können Sie Ihre eigene Web-API erstellen und gleichzeitig die Verwendung von OData-Abfrageparametern zulassen.

Anmerkungen: 

  • Ich habe das Nuget-Paket Microsoft.AspNetCore.OData.vNext, Version 6.0.2-alpha-rtm, verwendet, für das .NET 4.6.1 erforderlich ist 
  • Soweit ich das beurteilen kann, unterstützt OData vNext nur OData v4 (also keine v3).
  • OData vNext scheint überstürzt zu sein und ist voller Fehler. Beispielsweise ist der Abfrageparameter $orderby defekt

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>();
        }
    }
}

So testen Sie

Sie können den folgenden URI verwenden: /api/Entity/all?$filter=contains(SomeText,'Test'). Wenn es richtig funktioniert, sollten Sie nur die ersten beiden Entitäten sehen.

6
Métoule

Ich habe ein Github-Repo, das automatisch ASP.NET Core OData v4-Controller aus einem ersten EF-Code-Modell mithilfe von T4 generiert. Es verwendet Microsoft.AspNetCore.OData.vNext 6.0.2-alpha-rtm. Könnte von Interesse sein.

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

2
Aldous Zodiac

Von der WEB API Server Seite: 

Die einfachste Methode ist das direkte Attribut [EnableQuery]. Jetzt mit dem 7.x Pacakge funktioniert es super.

Sie können auch leicht ein generisches Impl. Wie unten erhalten. Idee ist eine gemeinsame Methode und wird anhand des von Ihnen benötigten Entitätsnamens unterschieden. Mit Linq2RestANC für die clientseitige Verwendung können Sie auch Ihre benutzerdefinierten Abfrageparameter problemlos übergeben. Wenn Sie, wie im Beispiel unten, über zwei Tabellen Movies1 und Movies2 verfügen, werden die Abfragen nur auf Ihre Datenbankbibliothek angewendet, wenn Sie darin $ expand- und sub-filter/subprozess-Bedingungen ausführen.

[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)});
 }

Für den clientseitigen Verbrauch- --> Verwenden Sie keinen ODATA Service-Proxy. Es ist fehlerhaft und wirft viele Fehler ab--> Simple.OData.Client ist gut. Unterstützung für verschachtelte Abfragen innerhalb von expand . /Products?$expand=Suppliers($select=SupplierName;$top=1;)Für eine solche innere Erweiterung wird keine weitere Filterung unterstützt. Das wird als Fehler Nr. 200 verfolgt

-> Linq2RestANC ist eine schöne Wahl. Auch das nativ unterstützte verschachtelte Erweiterungen nicht, wird jedoch durch die Übernahme des nativen IQueryProvider implementiert. Daher dauerte es nur 3 bis 4 Stunden, um die abgeschlossenen Erweiterungsszenarien auf verschachtelter Ebene zu ändern und zu testen. Sie müssen bei Expressionprocessor.cs "Expand" und ParameterBuilder.cs GetFullUri () ein wenig ändern, damit es funktioniert.

0
user3585952

Sieht aus, als wäre dies derzeit im Alpha-Bereich mit dem OData-Team . laut dieser Ausgabe

0
bigtlb