23

Я пытаюсь создать новый проект ASP.NET Core с помощью «простого» веб-api с использованием OData и EntityFramework. Ранее я использовал OData со старыми версиями ASP.NET.Как правильно интегрировать OData с ASP.net Core

Я установил контроллер только с простой функцией get. Мне удалось заставить его работать с основными командами OData как с фильтром, так и сверху, но я не могу заставить команду expand работать. Я думаю, это потому, что я не могу понять, как настроить его в Startup.cs. Я пробовал много вещей, в том числе после некоторых OData образцов из Github:

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

В моем файле запуска я стараюсь, чтобы исключить некоторые свойства из класса обслуживания, который не имеет никакого эффекта. Таким образом, проблема может заключаться в том, как я использую интерфейс IDataService. (ApplicationContext реализует его, как в примерах)

Чтобы быть ясным, я создаю ASP.NET Core web api с полной платформой .NET Framework и не только. Мой текущий код представляет собой сочетание лучшего/худшего из обоих образцов и работает в том смысле, что я могу фильтровать WebAPI, но не могу заставить его расширять или скрывать свойства.

Может ли кто-нибудь увидеть, что у меня отсутствует, у вас есть рабочий образец 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; 
    } 
} 

запуска

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

@l - '' '' '' '' '', Попробуйте использовать https://github.com/voronov- maxim/OdataToEntity witch имеет контейнер в качестве клиента, расширяется, выбирает и другие – itikhomi

ответ

-6

Вы должны наследовать контроллер от ODataController

2

У меня есть GitHub репо, которая автоматически генерирует ASP.NET Core OData v4 контроллеры с кодом первого EF модели, используя T4. Он использует Microsoft.AspNetCore.OData.vNext 6.0.2-alpha-rtm. Может представлять интерес.

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

+0

Я пробовал много часов, чтобы заставить это работать. Но по какой-то причине я не мог заставить его преобразоваться, и у меня возникла очень странная ошибка, которую я не смог выполнить Google. У вас будет несколько минут, чтобы помочь? –

5

мне удалось заставить его работать, но я не использовал предоставленные маршрутизации OData потому что мне нужен больше зернистости. С помощью этого решения вы можете создать свой собственный веб-API, сохраняя при этом возможность использовать параметры запроса OData.

Примечания:

  • Я использовал 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'). Если он работает правильно, вы должны видеть только первые два объекта.

+0

это огромное спасибо! –

+0

подтвердил, что это работает с базой 2.0, также, что происходит, когда у нас есть несколько объектов с отношениями между ними? сможем ли мы расширять и все эти операции? –

+0

Это решение позволяет вам больше контролировать, как вы открываете свои объекты через OData, но недостатком является то, что он автоматически не генерирует маршруты. Таким образом, нет, вы не сможете расширить дочерние объекты. –

4

Я также получил 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

+0

Это выглядит как интересный подход, который может помочь, если вы просто хотите применить OData в конкретных запросах. – bigtlb

+0

работает ли это с ядром 2? –

+0

Поддерживает 461, так что да. – davidcarr