23

J'essaie de créer un nouveau projet ASP.NET Core avec une API web "simple" utilisant OData et EntityFramework. J'ai précédemment utilisé OData avec des versions plus anciennes d'ASP.NET.Comment intégrer correctement OData avec ASP.net Core

J'ai configuré un contrôleur avec seulement une simple fonction get. J'ai réussi à le faire fonctionner avec les commandes OData basiques comme filtre et top, mais je ne peux pas faire fonctionner la commande expand. Je pense que c'est parce que je n'arrive pas à comprendre comment le configurer dans Startup.cs. J'ai essayé beaucoup de choses, y compris après quelques échantillons OData de Github:

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

Dans mon fichier de démarrage, j'essaie d'exclure certaines propriétés de la classe de service qui n'a pas d'effet du tout. Le problème peut donc résider dans la façon dont j'utilise l'interface IDataService. (Le ApplicationContext l'implémente comme dans les exemples)

Pour être clair, je crée une API web ASP.NET Core avec le .NET Framework complet et pas seulement le framework .Core. Mon code actuel est un mélange du meilleur/pire des deux échantillons et fonctionne dans le sens où je peux filtrer le WebAPI mais je ne peux pas l'obtenir pour développer ou masquer les propriétés.

Quelqu'un peut-il voir ce qui me manque et avoir un échantillon ASP.NET Odata qui fonctionne. Je suis nouveau à l'installation entière dans startup.cs? Je suppose que je cherche quelqu'un qui a fait ce travail.

Contrôleur

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

démarrage

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 dépendances

"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" 
+0

@l --''''''--------- '' '' '' '' '' '', essayez d'utiliser https://github.com/voronov- max/OdataToEntity witch a container comme client, expand, select et other – itikhomi

Répondre

-6

Vous devez hériter du contrôleur ODataController

2

J'ai un repo github qui génère automatiquement les contrôleurs ASP.NET de base OData à partir d'un premier modèle EF de code, en utilisant T4. Il utilise Microsoft.AspNetCore.OData.vNext 6.0.2-alpha-rtm. Ça pourrait être intéressant.

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

+0

J'ai essayé pendant plusieurs heures pour que cela fonctionne. Mais pour une raison quelconque, je ne pouvais pas le transformer et je recevais une erreur très étrange que je ne pouvais pas à Google. Auriez-vous quelques moments pour vous aider? –

5

j'ai réussi à le faire fonctionner, mais je ne pas utiliser le routage fourni OData parce que je avais besoin de plus granularité. Avec cette solution, vous pouvez créer votre propre API Web tout en permettant l'utilisation des paramètres de requête OData.

Notes:

  • J'utilisé paquet Nuget Microsoft.AspNetCore.OData.vNext, la version 6.0.2-alpha-rtm, ce qui nécessite .NET 4.6.1
  • Comme fas que je peux dire, OData vNext prenant en charge que OData v4 (donc pas v3)
  • OData vNext semble avoir été précipité, et est bourré de bugs.Par exemple, le paramètre de requête $orderby est cassé

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

Comment tester

Vous pouvez utiliser l'URI suivant: /api/Entity/all?$filter=contains(SomeText,'Test'). Si cela fonctionne correctement, vous ne devriez voir que les deux premières entités.

+0

c'est un petit bijou merci beaucoup! –

+0

a confirmé que cela fonctionne avec le noyau 2.0, aussi ce qui se passe quand nous avons plusieurs entités avec une relation entre eux? Serons-nous capables de faire de l'expansion et de toutes ces opérations? –

+0

Cette solution vous permet d'avoir plus de contrôle sur la façon dont vous exposez vos entités via OData, mais l'inconvénient est qu'elle ne génère pas automatiquement de routes. Donc non, vous ne serez pas en mesure d'étendre les entités enfants. –

4

J'ai aussi Microsoft.AspNetCore.OData.vNext, version 6.0.2-alpha-rtm travailler, mais j'ai utilisé le code suivant à la carte Edm modèle itinéraires:

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

avec services.AddOData()

Il est étrange mais il semble fonctionner avec .Net Core 1.1

+0

Cela ressemble à une approche intéressante qui pourrait vous aider si vous voulez juste appliquer OData dans des requêtes spécifiques. – bigtlb

+0

cela fonctionne-t-il avec le noyau 2? –

+0

Supporte 461 donc oui. – davidcarr