在dotnet core中动态创建EDM模型

5
我看过了 https://github.com/OData/ODataSamples/tree/master/WebApi/v4/DynamicEdmModelCreation 上的示例,用于动态 Odata 路由和模型生成。
我需要在 dot net core 中实现相同的功能,但由于 Asp.net 和 Asp.net core 在类/路由等方面有所不同,因此我无法将此代码转换为 dot net core。
有人能提供一些指导吗?
我的 Startup.cs Configure 方法如下 -
        IEdmModel model = this.GetEdmModel(app.ApplicationServices);

        IList<IODataRoutingConvention> routingConventions = new List<IODataRoutingConvention>();
        routingConventions.Insert(0, new MatchAllRoutingConvention());
        app.UseMvc(routeBuilder => routeBuilder.MapODataServiceRoute("odata", "odata", model, new CustomPathHandler(), routingConventions));

CustomPathHandler.cs -

public class CustomPathHandler : DefaultODataPathHandler
{
    public override ODataPath Parse(string serviceRoot, string odataPath, IServiceProvider requestContainer)
    {
        var path =  base.Parse(serviceRoot, odataPath, requestContainer);
        return path;
    }

    public override string Link(ODataPath path)
    {
        return base.Link(path);
    }
}

ODataEndpointController.cs -

public class ODataEndpointController : ODataController
{
    [EnableQuery]
    [ODataRoute]
    public EdmEntityObjectCollection Get()
    {
        // Get entity set's EDM type: A collection type.            
        ODataPath path = Request.ODataFeature().Path;
        IEdmCollectionType collectionType = (IEdmCollectionType)path.EdmType;
        IEdmEntityTypeReference entityType = collectionType.ElementType.AsEntity();

        // Create an untyped collection with the EDM collection type.
        EdmEntityObjectCollection collection =
            new EdmEntityObjectCollection(new EdmCollectionTypeReference(collectionType));

        // Add untyped objects to collection.
        DataSourceProvider.Get(entityType, collection);

        return collection;
    }

MatchAllRoutingConvention.cs

public class MatchAllRoutingConvention : IODataRoutingConvention
{
    public IEnumerable<ControllerActionDescriptor> SelectAction(RouteContext routeContext)
    {
        ControllerActionDescriptor test = new ControllerActionDescriptor();
        test.ControllerName = "ODataEndpoint";
        return new List<ControllerActionDescriptor> { test }.AsEnumerable();

    }

}

这里有什么我做错了吗?当我尝试访问http://localhost:port/odata/Products时,我得到了一些源为空的错误。
编辑(15-Jan):
将MatchAllRoutingConvention修改如下可重定向路由,但任何$metadata请求也会这样做(抛出异常)。而$filter查询也不起作用。所以任何提示/建议都将是有帮助的-
public class MatchAllRoutingConvention : IODataRoutingConvention
{
    public IEnumerable<ControllerActionDescriptor> SelectAction(RouteContext routeContext)
    {
        ControllerActionDescriptor odataControllerDescriptor = new ControllerActionDescriptor
            {
                ControllerName = "ODataEndpoint",
                ActionName = "Get",
                Parameters = new List<ParameterDescriptor>(),
                FilterDescriptors = new List<FilterDescriptor>(),
                BoundProperties = new List<ParameterDescriptor>(),
                MethodInfo = typeof(ODataEndpointController).GetMethod("Get"),
                ControllerTypeInfo = typeof(ODataEndpointController).GetTypeInfo()
            };

            return new List<ControllerActionDescriptor> { odataControllerDescriptor };

    }

}

ASP.NET Core中的OData支持仍处于预发布状态。(尽管它似乎现在已经进入了beta状态,这是一件好事,考虑到SignalR仍处于alpha状态。)ASP.NET Core是完全不同的东西,而您正在查看的示例代码是针对ASP.NET MVC中稳定的OData。长话短说,很多东西,如果不是全部都已经发生了变化。不幸的是,由于它是预发布版,因此文档也非常缺乏。在这里,你几乎是自己摸索。如果您现在需要OData,我建议暂时使用Web Api。 - Chris Pratt
是的,你说得完全正确,我希望有人对Odata和相关类有更好的理解,以便在这方面给我指点迷津。 不幸的是,“预发布”、“测试版”、“未记录”等词汇并没有引起我的“上级”的重视。 - Arun
1个回答

3

我将此作为答案发布,因为我没有看到任何回复,希望这能帮助到某人。我修改了MatchAllRoutingConvention类如下 -

public class MatchAllRoutingConvention : IODataRoutingConvention
{
    public IEnumerable<ControllerActionDescriptor> SelectAction(RouteContext routeContext)
    {
        if (routeContext.RouteData.Values["odataPath"] == null || 
            routeContext.RouteData.Values["odataPath"].ToString() == "$metadata")
            return new MetadataRoutingConvention().SelectAction(routeContext);

        IActionDescriptorCollectionProvider actionCollectionProvider =
            routeContext.HttpContext.RequestServices.GetRequiredService<IActionDescriptorCollectionProvider>();

        IEnumerable<ControllerActionDescriptor> actionDescriptors = actionCollectionProvider
            .ActionDescriptors.Items.OfType<ControllerActionDescriptor>()
            .Where(c => c.ControllerName == "ODataEndpoint");

        return actionDescriptors;
    }

}

这样可以正确处理元数据请求以及简单实体请求。但是,它不能响应$filter、$select等操作。据我所知,ODataQueryOptions类用于应用OData过滤等操作,但它们无法用于非CLR类,而这就是问题所在。因此,不确定是否有解决此问题的方法。
[编辑] 因此,我已经成功地解决了这个过滤问题。有一个名为LinqToQueryString的库,你可以将其应用于IQueryable,而不是EdmEntityObjectCollection(显然不是那么容易)。首先将我的数据加载到字典中,在他们的网站上有一个示例。在该字典上首先应用OData查询选项字符串,然后将过滤后的结果加载到我的实体中,最后加载到EdmEntityObjectCollection中。
    var list = new List<IEdmEntityObject>();
    var dataList = new List<Dictionary<string, object>>();

    // Load data in Dictionary
    foreach (var item in data)
    {
         var dataItem = new Dictionary<string, object>();

         // Load data

         dataList.Add(dataItem);
    }

    var filteredDataList = dataList.AsQueryable().LinqToQuerystring(oDataQuery);

    // Now add filtered/sorted etc data in EdmEntityObject
    foreach (var item in filteredDataList)
    {
         var entity = new EdmEntityObject(entityType);
         foreach (var key in item.Keys)
            entity.TrySetPropertyValue(key, item[key].ToString());
         list.Add(entity);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接