使用未绑定函数的多个路由的OData服务

时间:2018-01-26 02:11:03

标签: asp.net-web-api2 odata asp.net-web-api-routing odata-v4

有谁知道如何在.NET服务中托管OData v4以使用多个路由?

我有以下内容:

config.MapODataServiceRoute("test1", "test1", GetEdmModelTest1());
config.MapODataServiceRoute("test2", "test2", GetEdmModelTest2());

每个GetEdmModel方法都有映射对象 我可以按照以下方式获得服务(这很好):

http://testing.com/test1/objects1()
http://testing.com/test2/objects2()

但是,如果我尝试调用类似以下的函数(将无效):

[HttpGet]
[ODataRoute("test1/TestFunction1()")]
public int TestFunction1()
{ return 1; }

会抛出以下错误:

  

控制器'Testing'中操作'TestFunction1'的路径模板'test1 / TestFunction1()'不是有效的OData路径模板。找不到段'test1'的资源。

然而,如果我删除“test2”的“MapODataServiceRoute”,那么只有一条路线,一切正常。

如何使用多条路线?

**我已在以下**发布了该问题的完整示例 https://github.com/OData/WebApi/issues/1223

**我已经尝试了下面列出的OData版本样本,但有以下问题**
https://github.com/OData/ODataSamples/tree/master/WebApi/v4/ODataVersioningSample
我之前尝试过“OData版本”示例,但它没有用。 似乎未绑定(未绑定是目标)不遵循相同的路由规则是正常的服务调用。

实施例。如果您下载“OData版本”示例并执行以下操作。

  1. 在V1中 - > WebApiConfig.cs添加
    builder.Function(nameof(Controller.ProductsV1Controller.Test)).Returns<string>();
  2. 在V2中 - &gt; WebApiConfig.cs添加
     builder.Function(nameof(Controller.ProductsV2Controller.Test)).Returns<string>();
  3. 在V1中 - &gt; ProductsV1Controller.cs添加
    [HttpGet] [ODataRoute("Test()")] public string Test() { return "V1_Test"; }
  4. 在V2中 - &gt; ProductsV2Controller.cs添加
    [HttpGet] [ODataRoute("Test()")] public string Test() { return "V2_Test"; }
  5. 现在打电话给它。 “/ versionbyroute / v1 / Test()”,您将获得“V2_Test”

    问题是“GetControllerName”在使用未绑定的函数/动作时不知道如何获取控制器。
    这就是为什么我试图“推断”控制器时发现的大多数示例代码都失败了。

2 个答案:

答案 0 :(得分:1)

查看OData Versioning Sample的初级读本。

  

麻烦的关键点通常是 DefaultHttpControllerSelector 按本地名称而不是fullname / namespace映射控制器。

如果您的实体类型和控制器名称在两个EdmModel中都是唯一的,那么您不需要做任何特殊的事情,它应该只是开箱即用。上面的示例利用了这个概念,强制您将一个字符串值注入控制器类的物理名称以使它们唯一,然后在ODataVersionControllerSelector GetControllerName中重写以将传入路由映射到自定义控制器名称

如果控制器的唯一名称看起来很难,并且您希望使用完整的命名空间(意味着您的控制器名称逻辑仍然是标准的),那么您当然可以实现自己的逻辑来在覆盖时选择特定的控制器类实例{ {1}}。只需覆盖DefaultHttpControllerSelector即可。此方法需要返回SelectController的实例,该实例比样本更复杂。

为了向您展示我的意思,我会将解决方案发布到旧项目的要求中,这与您的项目略有不同。我有一个WebAPI项目来管理对多个数据库的访问,这些数据库具有类似的模式,许多实体名称是相同的,这意味着这些控制器类将具有相同的名称。控制器由文件夹/命名空间构成,这样就有一个名为DB的根文件夹,然后每个数据库都有一个文件夹,然后是控制器。

enter image description here

  

您可以看到此项目有许多不同的模式,它们有效地映射到不断发展的解决方案的版本,此图像中的非DB命名空间是OData v4,v3和标准REST api的混合。有可能让所有这些野兽共存;)

HttpControllerSelector的这个覆盖检查运行时一次以缓存所有控制器类的列表,然后通过将路由前缀与正确的控制器类匹配来映射传入的路由请求。

HttpControllerDescriptor

答案 1 :(得分:1)

您可以使用Custsom MapODataServiceRoute。 以下是WebApiConfig.cs

中的示例

控制器在CustomMapODataServiceRoute中注册,并且有点麻烦,必须为每个控制器包含return new FileStreamResult(memStream, "image/bmp"); 。我的一个端点有22个独立的控制器,但到目前为止它都有效。

注册控制器 - 在同一项目中显示两个独立的OData端点,两者都包含自定义函数

typeof(NameOfController)

创建自定义地图OData服务路径

        // Continuing Education
        ODataConventionModelBuilder continuingEdBuilder = new ODataConventionModelBuilder();
        continuingEdBuilder.Namespace = "db_api.Models";
        var continuingEdGetCourse = continuingEdBuilder.Function("GetCourse");
        continuingEdGetCourse.Parameter<string>("term_code");
        continuingEdGetCourse.Parameter<string>("ssts_code");
        continuingEdGetCourse.Parameter<string>("ptrm_code");
        continuingEdGetCourse.Parameter<string>("subj_code_prefix");
        continuingEdGetCourse.Parameter<string>("crn");
        continuingEdGetCourse.ReturnsCollectionFromEntitySet<ContinuingEducationCoursesDTO>("ContinuingEducationCourseDTO");
        config.CustomMapODataServiceRoute(
            routeName: "odata - Continuing Education",
            routePrefix: "contEd",
            model: continuingEdBuilder.GetEdmModel(),
            controllers: new[] { typeof(ContinuingEducationController) }
            );

    // Active Directory OData Endpoint
    ODataConventionModelBuilder adBuilder = new ODataConventionModelBuilder();
        adBuilder.Namespace = "db_api.Models";
        // CMS Groups
        var cmsGroupFunc = adBuilder.Function("GetCMSGroups");
        cmsGroupFunc.Parameter<string>("user");
        cmsGroupFunc.ReturnsCollectionFromEntitySet<GenericValue>("GenericValue");
        // Departments
        var deptUsersFunc = adBuilder.Function("GetADDepartmentUsers");
        deptUsersFunc.Parameter<string>("department");
        deptUsersFunc.ReturnsCollectionFromEntitySet<ADUser>("ADUser");
        var adUsersFunc = adBuilder.Function("GetADUser");
        adUsersFunc.Parameter<string>("name");
        adUsersFunc.ReturnsCollectionFromEntitySet<ADUser>("ADUser");
        var deptFunc = adBuilder.Function("GetADDepartments");
        deptFunc.ReturnsCollectionFromEntitySet<GenericValue>("GenericValue");
        var instDeptFunc = adBuilder.Function("GetADInstructorDepartments");
        instDeptFunc.ReturnsCollectionFromEntitySet<GenericValue>("GenericValue");
        var adTitleFunc = adBuilder.Function("GetADTitles");
        adTitleFunc.ReturnsCollectionFromEntitySet<GenericValue>("GenericValue");
        var adOfficeFunc = adBuilder.Function("GetADOffices");
        adOfficeFunc.ReturnsCollectionFromEntitySet<GenericValue>("GenericValue");
        var adDistListFunc = adBuilder.Function("GetADDistributionLists");
        adDistListFunc.ReturnsCollectionFromEntitySet<GenericValue>("GenericValue");
        config.CustomMapODataServiceRoute(
            routeName: "odata - Active Directory",
            routePrefix: "ad",
            model: adBuilder.GetEdmModel(),
            controllers: new[] { typeof(DepartmentsController), typeof(CMSGroupsController)
            });