在dotnet核心中创建动态EDM模型

时间:2018-01-05 08:52:38

标签: .net asp.net-web-api asp.net-core odata

我在https://github.com/OData/ODataSamples/tree/master/WebApi/v4/DynamicEdmModelCreation看到了动态Odata路线和模型生成的示例。

我需要相同的dot net core,但由于Asp.net和Asp.net核心在类/路由等方面有所不同,我无法将此代码转换为dot net core。

任何人都可以提供一些指导吗?

我的Startup.cs配置方法有这个 -

        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时,我会收到一些 source is null 错误

编辑(1月15日):

修改MatchAllRoutingConvention如下所示,根据需要重定向路由,但任何$元数据请求(抛出异常)都是如此。并且$ 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 };

    }

}

1 个答案:

答案 0 :(得分:2)

将此作为答案发布,因为我没有看到任何回复,希望这会对某人有所帮助。我修改了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类上工作。所以不确定是否有解决此问题的方法。

[编辑] 因此,我设法解决了这个过滤问题。你可以在IQueryable上使用这个库LinqToQueryString,而不是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);
    }