使用OData查询而不暴露ORM模型?

时间:2016-06-03 15:51:43

标签: c# entity-framework asp.net-web-api odata

在我的web api 2项目中,如果我想对某些属性的查询使用OData库(看起来很棒且非常诱人),那将迫使客户端知道我的数据库模型的确切属性。这是一个好习惯吗?有没有办法避免这种解耦?

对于以下型号:

public class LetterEntity
    {
        public int Id {get; set;}

        public string Title {get; set;}

        public string Content {get; set;}

        public string Source {get; set;}

        public DateTime SendingTime {get; set;} 

        public string AnotherWierdString {get; set;
        ...
    }

    public class LetterDTO
    {
        public int Id {get; set;}

        public string Title {get; set;}

        public string LetterContent {get; set;}

        public string Source {get; set;}

        public DateTime SendingTime {get; set;} 
    }

    public class LetterInsideFolderDTO 
    {
        public string Title {get; set;}

        public string Source {get; set;}
    }


public class LettersController : ApiController
{
    // In this approach method, I hate the fact that a LetterEntity must be used for the query.
    [HttpGet]
    [Route("api/letters")]
    [EnableQuery]
    public IQueryable<LetterInsideFolderDTO> Get(ODataQueryOptions<LetterEntity> query) 
    { 
        IQueryable<Letter> letters = db.Letters;

        var afterQuery = query.ApplyTo(letters)

        IQueryable<LetterInsideFolderDTO> dtos = afterQuery.ProjectTo<LetterInsideFolderDTO>(afterQuery)

        return dtos;
    }


    // Is there a way to do something like the following?:  
    [HttpGet]
    [Route("api/letters")]
    [EnableQuery]
    public IQueryable<LetterInsideFolderDTO> Get(ODataQueryOptions<LetterDTO> query) 
    { 
        IQueryable<Letter> letters = db.Letters;

        // Convert the query to work on the entities somehow? Should I use a mapping between LetterDTO to LetterEntity?
        // I only have a map from LetterEntity to LetterDTO
        var afterQuery = query.ApplyTo(letters)

        IQueryable<LetterInsideFolderDTO> dtos = afterQuery.ProjectTo<LetterInsideFolderDTO>(afterQuery)

        return dtos;
    }
}

由于目前我直接在客户端查询中使用Entity模型,因此客户端和服务器之间存在强大的耦合。 例如,如果我想查询并获取Content字段中包含“abc”的所有字母,我需要路由到以下内容:

api/letters/?$filter=contains(Content,'abc')

如果明天我决定将该属性从“内容”更改为“LetterContent”,则所有客户代码都将被破坏。

我怎样才能超越它?

谢谢!

编辑:

请举个具体的例子, 我还不明白HATEOAS是什么(如果这有助于我解决这个问题), 文档服务如何帮助我?如果我决定更改我的EDM模型,它仍然会强制客户更改代码吗?

2 个答案:

答案 0 :(得分:0)

  
    

如果我想对某些属性的查询使用OData库(看起来很棒且非常诱人),那么会强制客户端知道我的数据库模型的确切属性。这是一个好习惯吗?

  

这取决于你的意思是“会迫使客户方知道确切的属性”。有两种方法:

  1. 使用datasvcutil生成的自动生成的代理。实际上,这会迫使客户端知道确切的属性,因为它们在客户端是硬编码的。当服务器端被更改时,客户端将被破坏 - 客户端/服务器紧密耦合。一般来说这很糟糕,但通常如果你需要smth来快速完成 - datasvcutil是你的工具。

  2. 了解您的客户阅读服务文档,以便他可以动态决定他可以查询哪些资源。你拥有一切 - 服务文档,元数据。

  3. 请记住,OData建立在REST架构之上,它具有一系列通过约束条件实现的优势。一些约束是Addressability和HATEOAS。

    可寻址性表示每个资源必须具有其地址。

    HATEOAS 意味着在任何特定时刻,基于当前资源表示的超媒体的客户端必须拥有他需要的所有信息来决定下一步的传输方式。

    为了知道客户必须在哪里过境

    1. 知道如何在他可能传输的数据流中查找资源(URL)。就像你获得带有文本和URL的数据一样 - 客户端必须知道如何查找URL。 URL可能有不同的含义 - 例如CRUD操作的几个URL。

    2. 使用资源获取该数据。一般来说,客户必须知道如何开始查询服务

    3. 通过配置文件解决第一点。配置文件 - 允许客户端了解与资源表示关联的其他语义(https://tools.ietf.org/html/rfc6906)。考虑一下OData文档。对于OData,您的客户必须知道OData中的数据是通过Atom或Json格式表示的。客户必须知道构建OData服务查询,获取特定记录等原则。

      如果客户端调用OData根地址 - 像... OData.svc,他将获得他可以查询的所有资源的列表(服务文档)。这就是第二点的解决方法。

      您可以通过$ metadata后缀进一步获取元数据。这将为您提供所有资源属性。

答案 1 :(得分:0)

我确实认为在大多数情况下直接暴露您的实体是一种不好的做法。我几乎在所有情况下都推荐DTO。它允许您在不破坏API的情况下发展数据库和业务逻辑。 OData有一些很好的用例,例如政府公布数据的开放数据计划。

我必须使用所有过滤和排序选项构建一个基本上是数据网格的应用程序。我想使用OData,但我没有找到对实体进行查询但是投影到DTO的方法,所以我构建了自己的库来将jqgrid过滤器转换为IQueryable查询 - https://github.com/KodarLtd/WebApiJQGridFilters请注意,我不建议使用此代码作为它不是功能齐全的图书馆,根本没有记录。我只是将其作为证据证明我对DTO方法的坚定态度。

我希望被证明是错误的,所以我可以使用OData但是为我的下一个项目返回DTO。