簡體   English   中英

使用 ASP.NET Core OData 8.0 映射動態 odata 路由

[英]Mapping dynamic odata routes with ASP.NET Core OData 8.0

我有一個應用程序,其中 EDM 數據類型是在應用程序運行時生成的(它們甚至可以在運行時更改)。 松散地基於OData DynamicEDMModelCreation Sample - 重構為使用新的端點路由。 在那里,EDM model 在運行時動態生成,所有請求都轉發到相同的 controller。

現在我想更新到最新的ASP.NET Core OData 8.0並且整個路由發生了變化,因此當前的解決方法不再起作用。

我已經閱讀了更新Blog1 Blog2的兩篇博客文章,似乎我不能再使用“舊”解決方法了,因為端點內的 function MapODataRoute()現在已經消失了。 似乎沒有任何內置路由約定適用於我的用例,因為所有這些都要求 EDM model 在調試時出現。

也許我可以使用自定義IODataControllerActionConvention 我試圖通過將約定添加到路由約定來激活約定,但似乎我仍然缺少如何激活它的部分。

services.TryAddEnumerable(
    ServiceDescriptor.Transient<IODataControllerActionConvention, MyEntitySetRoutingConvention>());

這種方法是否有效? 甚至可以在新的 odata 預覽中激活動態 model 嗎? 或者有人知道如何為新的 odata 8.0 處理動態路由嗎?

因此,經過 5 天的內部 OData 調試后,我設法讓它工作。 以下是必要的步驟:

首先從您的 controller 中刪除所有 OData 調用/屬性,這可能會做一些時髦的事情( ODataRoutingAttributeAddOData()

創建一個簡單的 asp.net controller 與您喜歡的路線和 map 它在端點

[ApiController]
[Route("odata/v{version}/{Path?}")]
public class HandleAllController : ControllerBase { ... }

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory)
{
  ...
  app.UseEndpoints(endpoints =>
  {
    endpoints.MapControllers();
  }
}

創建並注冊您的 InputFormatWrapper 和 OutputFormatWrapper

public class ConfigureMvcOptionsFormatters : IConfigureOptions<MvcOptions> 
{
    private readonly IServiceProvider _services;
    public ConfigureMvcOptionsFormatters(IServiceProvider services)
    {
        _services = services;
    } 

    public void Configure(MvcOptions options)
    { 
        options.InputFormatters.Insert(0, new ODataInputFormatWrapper(_services));
        options.OutputFormatters.Insert(0, new OdataOutputFormatWrapper(_services));
    }
}

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.ConfigureOptions<ConfigureMvcOptionsFormatters>();
    ...
}

public class ODataInputFormatWrapper : InputFormatter
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ODataInputFormatter _oDataInputFormatter;

    public ODataInputFormatWrapper(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
        //JSON and default is first - see factory comments 
        _oDataInputFormatter = ODataInputFormatterFactory.Create().First();
    }
    
    public override bool CanRead(InputFormatterContext context)
    {
        if (!ODataWrapperHelper.IsRequestValid(context.HttpContext, _serviceProvider))
            return false;

        return _oDataInputFormatter.CanRead(context);
    }

    public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
    {
        return _oDataInputFormatter!.ReadRequestBodyAsync(context);
    }
}

// The OutputFormatWrapper looks like the InputFormatWrapper

ODataWrapperHelper ,您可以檢查內容並獲取/設置您的動態 edmModel。 最后有必要設置這些ODataFeature() ......這並不漂亮,但它完成了動態工作......

public static bool IsRequestValid(HttpContext context, IServiceProvider serviceProvider)
{
  //... Do stuff, get datasource
  var edmModel = dataSource!.GetModel();
  var oSegment = new EntitySetSegment(new EdmEntitySet(edmModel.EntityContainer, targetEntity, edmModel.SchemaElements.First(x => targetEntity == x.Name) as EdmEntityType));
   context.ODataFeature().Services = serviceProvider.CreateScope().ServiceProvider;
   context.ODataFeature().Model = edmModel;
   context.ODataFeature().Path = new ODataPath(oSegment);

   return true;
 }

現在到丑陋的東西:我們仍然需要在ConfigureServices(IServiceCollection services)中注冊一些 ODataService 添加一個AddCustomODataService(services)並自己注冊〜40個服務或做一些時髦的反射......

因此,如果 odata 團隊的某個人讀到此內容,請考慮打開Microsoft.AspNetCore.OData.Abstracts.ContainerBuilderExtensions

我創建了一個public class CustomODataServiceContainerBuilder: IContainerBuilder這是內部Microsoft.AspNetCore.OData.Abstracts.DefaultContainerBuilder的副本,我在那里添加了 function:

public void AddServices(IServiceCollection services)
{
  foreach (var service in Services)
  {
    services.Add(service);
   }
}

和丑陋的AddCustomODataServices(IServiceCollection services)

private void AddCustomODataService(IServiceCollection services)
{
    var builder = new CustomODataServiceContainerBuilder();
    builder.AddDefaultODataServices();

    //AddDefaultWebApiServices in ContainerBuilderExtensions is internal...
    var addDefaultWebApiServices = typeof(ODataFeature).Assembly.GetTypes()
            .First(x => x.FullName == "Microsoft.AspNetCore.OData.Abstracts.ContainerBuilderExtensions")
            .GetMethods(BindingFlags.Static|BindingFlags.Public)
            .First(x => x.Name == "AddDefaultWebApiServices");

    addDefaultWebApiServices.Invoke(null, new object?[]{builder});
    builder.AddServices(services);
}

現在 controller 應該再次工作(使用 odataQueryContext 和序列化) - 示例:

[HttpGet]
public Task<IActionResult> Get(CancellationToken cancellationToken)
{
   //... get model and entitytype
   var queryContext = new ODataQueryContext(model, entityType, null);
   var queryOptions = new ODataQueryOptions(queryContext, Request);

   return (Collection<IEdmEntityObject>)myCollection;
}

[HttpPost]
public Task<IActionResult> Post([FromBody] IEdmEntityObject entityDataObject, CancellationToken cancellationToken)
{
    //Do something with IEdmEntityObject
    return Ok()
}

這里有一個動態路由和動態 model 的示例:

https://github.com/OData/AspNetCoreOData/blob/master/sample/ODataDynamicModel/請參閱MyODataRoutingApplicationModelProviderMyODataRoutingMatcherPolicy ,它們會將自定義 IEdmModel 傳遞給 controller。

HandleAllController可以動態處理不同類型和 edm 模型。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM