.Net WebApi OData控制器中的多个get方法

.Net WebApi OData控制器中的多个get方法,.net,asp.net-web-api,odata,.net,Asp.net Web Api,Odata,我想知道是否有可能在同一个ODataController中使用参数实现两种不同的get方法。这就是我试图做的,但它不起作用。我能做什么 在ODataController中: public class ProductsController : ODataController { private readonly ProductsService _service; public ProductsController() { _service = ne

我想知道是否有可能在同一个ODataController中使用参数实现两种不同的get方法。这就是我试图做的,但它不起作用。我能做什么

在ODataController中:

public class ProductsController : ODataController
{
    private readonly ProductsService _service;
    
    public ProductsController()
    {
        _service = new ProductsService();
    }

[EnableQuery]
public IQueryable<Product> Get()
{
    return _service.GetAll();
}

[EnableQuery]
public Product Get(int key)
{
    return _service.GetById(key);
}

[EnableQuery]
public IQueryable<Product> Get(string key)
{
    return _service.GetByFilter(key);
}
公共类产品控制器:ODataController
{
私有只读产品服务;
公共产品控制器()
{
_服务=新产品服务();
}
[启用查询]
公共IQueryable Get()
{
return_service.GetAll();
}
[启用查询]
公共产品获取(int键)
{
return _service.GetById(key);
}
[启用查询]
公共IQueryable获取(字符串键)
{
return\u service.GetByFilter(键);
}
在WebApiConfig.cs文件中,我有下一个配置:

ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
config.Routes.MapODataServiceRoute("odata", "api/odata", builder.GetEdmModel());
ODataModelBuilder=new ODataConventionModelBuilder();
建筑商实体集(“产品”);
config.Routes.MapODataServiceRoute(“odata”,“api/odata”,builder.getedModel());
我可以将不带参数的“get”方法与带参数的“get”方法一起使用,但我不能将这两种方法与带参数的“get”方法一起使用。我看过OdataRoute,但没有找到函数。我知道我可以将OData筛选器功能与泛型方法一起使用,但我想尝试不使用它。是否有其他解决方案


谢谢!

OData路由不支持重载OOTB这正是您在这里描述的两个
GET Collection
方法,但您希望其中一个与
GET Resource
方法具有相同的路由

第一个问题是,为什么希望相同的路由指向两个不同的方法,特别是当其中一个路由返回单个资源,而另一个路由将返回集合时


内置约定将已知URL路由到与预期签名匹配的方法。所有其他路由都需要作为离散函数或操作在Edm模型中注册。您也可以实现自己的路由

如果您打算让
Get(字符串键)
返回单个项目或集合,则参数大致相同。默认路由与处理该路由请求的两种不同方法之间仍然存在冲突。对于此类问题,存在直接重复issue on:

在OData中,我们将符合默认
~/Controller(key)
路由的资源的
键与有效的所有其他路由进行区分。对于所有其他非默认路由,我们只需在OData EDM模型中声明它们即可使其有效

还有其他可能的解决方案,如或。
然而,我发现最好尽量坚持OData标准约定,因此当遇到路由问题时,可以在OData传统方式和我们的业务需求之间定义一个简单的映射,并且您希望在全球范围内支持此语法,那么您可以使用一个简单的url重写模块

  • 在Edm模型中将自定义方法端点声明为单独的函数
  • 创建Url重写以在旧路由和OData路由之间映射
  • 在Edm模型中声明自定义函数: 要通过Edm模型访问自定义功能,我们需要进行两项更改:

  • 将方法名称更改为非
    Get
    。在本例中,我们将使用术语
    Search

     [HttpGet]
     [EnableQuery]
     public IQueryable<Product> Search(string key)
     {
         return _service.GetByFilter(key);
     }
    
    Url重写模块 如果可以,请尝试使用约定。选择OData是有原因的。如果确实使用重写模块,请在条件逻辑中尽量具体,以避免破坏可能正在使用的其他标准路由

    我尝试为常见的遗留查询路由保留重写解决方案


    我们是否可以假设您的模型有一个整数主
    字段?是否有一些背景资料说明您为什么要这样做?因为它与OData标准路由约定直接冲突,很难确定您的请求是由于对约定的无知,还是您有合理的业务需求。请同时提供示例URL和关于如何区分整数键和字符串键的推理。更糟糕的是,您在字符串版本中甚至没有使用
    key
    参数,因此不清楚您的实际意图。嗨,克里斯!非常感谢您的回答。答案太完整了。是的,我被要求尝试这样做,为了避免通过url传递一些api信息。您是对的,示例中有一个错误,它在函数“GetByFilter”中使用了一个关键参数。我将立即进行编辑。我要求的原因是REST api为同一路由赋予多个含义是违反直觉的。这会造成文档混乱,但会导致错误lso OData客户端将无法理解非常规端点。我试图确定您是否愿意为该功能使用不同的路径,因为这是我们在OData中的操作方式,我们向控制器添加操作和功能,以构建一组丰富的行为和对象图Hi Chris!再次感谢您的nswer。它太完整了。我已尝试实现您的示例,但在“products.EntityType.Collection.Function…”中配置ODataModel.In时遇到问题。我遇到以下错误:“EntityCollectionConfiguration”不包含“Function”的定义,并且没有可访问的扩展方法“Function”接受第一个参数找不到类型为“EntityCollectionConfiguration”的ent。您知道为什么会发生这种情况吗?再次感谢!!请尝试使用Microsoft.AspNet.OData.Builder添加
    ,这是Microsoft.AspNet.OData.7.5.2包中的
    
    
    ODataModelBuilder builder = new ODataConventionModelBuilder();
    var products = builder.EntitySet<Product>("Products");
    products.EntityType.Collection.Function(nameof(ProductsController.Search))
                                   .ReturnsCollectionFromEntitySet<Facility>("Products")
                                   .Parameter<string>("key");
    config.Routes.MapODataServiceRoute("odata", "api/odata", builder.GetEdmModel());
    
    public class ODataConventionUrlRewriter : OwinMiddleware
    {
        /// <summary>
        /// Create a Url Rewriter to manipulate incoming OData requests and rewrite or redirect to the correct request syntax
        /// </summary>
        /// <param name="next"></param>
        public ODataConventionUrlRewriter(OwinMiddleware next)
            : base(next)
        {
        }
        /// <summary>
        /// Process the incoming request, if it matches a known path, rewrite accordingly or redirect.
        /// </summary>
        /// <param name="context">OWin Request context to process</param>
        /// <returns>Chains the next processor if the url is OK or rewritten, otherwise the response is to redirect</returns>
        public override async Task Invoke(IOwinContext context)
        {
            // Match ANY /Controller(NonNumeric)
            // Rewrite to /Controller/Search(key='NonNumeric')
            var regex = new System.Text.RegularExpressions.Regex(@"\(([^\d=\'\(]+)\)$");
            match = regex.Match(context.Request.Path.Value);
            if (match != null && match.Success)
            {
                // We have to use redirect here, we can't affect the query inflight
                context.Response.Redirect($"{context.Request.Uri.GetLeftPart(UriPartial.Authority)}{regex.Replace(context.Request.Path.Value, $"/Search(key='{match.Groups[1].Value}')")}");
            }
            else
                await Next.Invoke(context);
        }
    }
    
    public void Configuration(IAppBuilder app)
    {
        ...
        // Rewrite URLs
        app.Use(typeof(ODataConventionUrlRewriter));
        ...
        // Register routes
        config.MapHttpAttributeRoutes();
        
        ODataModelBuilder builder = new ODataConventionModelBuilder();
        var products = builder.EntitySet<Product>("Products");
        products.EntityType.Collection.Function(nameof(ProductsController.Search))
                                      .ReturnsCollectionFromEntitySet<Facility>("Products")
                                      .Parameter<string>("key");
        config.Routes.MapODataServiceRoute("odata", "api/odata", builder.GetEdmModel());
    
        ...
        // Start Web API
        app.UseWebApi(config);
    
    }