C# 多路由级别webapi的Dry方法

C# 多路由级别webapi的Dry方法,c#,asp.net-web-api,dry,asp.net-web-api-routing,asp.net-web-api2,C#,Asp.net Web Api,Dry,Asp.net Web Api Routing,Asp.net Web Api2,当我们对每种方法都有多个所需的路由级别时,有没有一种方法可以简化 我有一个假设的WebAPI项目,我用它来对这个问题进行一般性的研究。它给了我们一些来源的电影 public class MovieController : ApiController { // GET api/<controller> public IEnumerable<Movie> Get() { return MoviesDB.All(); }

当我们对每种方法都有多个所需的路由级别时,有没有一种方法可以简化

我有一个假设的WebAPI项目,我用它来对这个问题进行一般性的研究。它给了我们一些来源的电影

public class MovieController : ApiController
{
    // GET api/<controller>
    public IEnumerable<Movie> Get()
    {
        return MoviesDB.All();
    }

    // GET api/<controller>/5
    public Movie Get(int id)
    {
        return MoviesDB.ThisSpecificOne(id);
    }

    // POST api/<controller>
    public void Post([FromBody]Movie value)
    {
    }

    // PUT api/<controller>/5
    public void Put(int id, [FromBody]Movie value)
    {
    }

    // DELETE api/<controller>/5
    public void Delete(int id)
    {
    }
}
这一切都很好,但对于每个新层,您需要为每个方法添加一个新参数。感觉不太干燥

我可以将这些层注入构造函数而不是方法本身吗

也许我想根据这些层对控制器进行不同的初始化,所以我会根据类型和/或年份或类似的东西进行不同的回购


有没有解决办法?

< P>你是否考虑过使用ODATA?Web Api支持OData插件,使用它您可以将查询编写为url:例如?$filter=quare eq'horror'。如果出于某种原因,您不希望将数据作为OData返回,但希望使用OData的查询语法,那么您可以:

  • 用法:此库为IQueryable提供了一个扩展方法,用于解析查询字符串并将查询应用于任何IQueryable

  • 将ODataQueryOptions转换为数据库中的查询(有关将查询转换为HQL的示例,请参阅)


  • 将可选参数移动到查询字符串中是否可行

    e、 g.
    GET api/movie?流派=恐怖&年份=2014

    这将简化您的路线和控制器,以:

    config.Routes.MapHttpRoute(
        name: "Movies",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
    
    public class MovieController : ApiController
    {
        // GET api/<controller>?genre=<genre>&year=<year>
        public IEnumerable<Movie> Get(string genre = null, int? year = null)
        {
            return MoviesDB.All(string genre, int year);
        }
    
        // GET api/<controller>/5?genre=<genre>&year=<year>
        public Movie Get(int id, string genre = null, int? year = null)
        {
            return MoviesDB.ThisSpecificOne(string genre, int year, id);
        }
    
        // POST api/<controller>?genre=<genre>&year=<year>
        public void Post([FromBody]Movie value, string genre = null, int? year = null)
        {
        }
    
        // PUT api/<controller>/5?genre=<genre>&year=<year>
        public void Put(int id, [FromBody]Movie value, string genre = null, int? year = null)
        {
        }
    
        // DELETE api/<controller>/5?genre=<genre>&year=<year>
        public void Delete(int id, string genre = null, int? year = null)
        {
        }
    }
    
    config.Routes.MapHttpRoute(
    名称:“电影”,
    routeTemplate:“api/{controller}/{id}”,
    默认值:新建{id=RouteParameter.Optional}
    );
    公共类电影控制器:ApiController
    {
    //获取api/?流派=&年=
    公共IEnumerable Get(字符串类型=null,整年=null)
    {
    返回MoviesDB.All(字符串类型,整数年);
    }
    //获取api//5?类型=&年=
    公共电影获取(int-id,字符串类型=null,int?year=null)
    {
    returnmoviesdb.ThisSpecificOne(字符串类型,整数年,id);
    }
    //发布api/?流派=&年=
    公共作废帖子([FromBody]电影值,字符串类型=null,整年=null)
    {
    }
    //放置api//5?类型=&年=
    public void Put(int id,[FromBody]电影值,字符串类型=null,int?year=null)
    {
    }
    //删除api//5?类型=&年=
    公共无效删除(整数id,字符串类型=null,整数?年份=null)
    {
    }
    }
    
    如果你真的是说电影是按流派和年份存储的,那么我相信你的不那么干巴的解决方案实际上是正确的。这确实让我质疑构造这种多部分标识符的目的,比如你的例子中使用的电影,当然年份和类型只是关于电影的元信息,而不是标识符的一部分。更一般地说,我真的会争论一个由两个以上,或者至少三个以上部分组成的复合标识符是否适合任何软件的和平。代理密钥可以缓解这种情况下的开发痛苦

    在讨论您对重复的枯燥性的担忧时,我认为,由于对象的主键结构很少改变,所以这不是一个真正的大问题。更重要的是,主键中的更改始终会破坏所有向后兼容性

    作为一个噱头,您可以创建一个包含复杂ID的新类,如下所示:

    public class MovieId
    {
        public int Id { get; set; }
        public int Yead { get; set; }
        public string Genre { get; set; }
    }
    
    然后使控制器方法如下所示:

        public Movie Get( [FromBody]MovieId id )
        {
            return MoviesDB.ThisSpecificOne( id );
        }
    
    这是可行的,现在代码很好地遵循了干燥原则。问题是,复杂类型必须是一个主体参数,因此查询字符串将不再是漂亮的和自解释的,您必须创造性地使用路由来区分不同的get方法


    移动到业务层或DDD层,这种作为值对象的复合键是一种非常常见的场景,由于查询字符串在这里不重要,因此它实际上是一种非常可行且推荐的解决方案。

    我没有足够的信誉来添加2个以上的链接。。您可以在那里找到关于OData支持的信息,这些信息非常简洁。我对你投了赞成票,但我也对那些参数不是可选的实际路由URL的答案感兴趣,当你想限制搜索时,OData解决方案似乎效果更好。请注意,我不是在构建电影服务,我只是以它为例。我在问,如果您必须将每个调用约束到特定的子集/数据库等,可能是一个有点繁重的解决方案,但是您可以实现IHttpControllerSelector和IHttpActionSelector:这两个选项让您可以控制为给定的HttpRequestMessage执行哪个控制器的操作我怀疑,正如问题特别提到的“电影是按流派存储的,所以您需要流派+id组合”,这实际上意味着它们是主键的一部分(在RDBMS中)或者是对象的身份。因此,我真的看不出它们是如何可选的。
    public class MovieId
    {
        public int Id { get; set; }
        public int Yead { get; set; }
        public string Genre { get; set; }
    }
    
        public Movie Get( [FromBody]MovieId id )
        {
            return MoviesDB.ThisSpecificOne( id );
        }