【问题标题】:Is it possible to use ODataQueryOptions with DTO's?是否可以将 ODataQueryOptions 与 DTO 一起使用?
【发布时间】:2014-02-08 15:27:38
【问题描述】:

ContentType --> EF 模型

ContentTypes --> DTO

在我的 OData 控制器中:

   public Task<IQueryable<ContentTypes>> Get(ODataQueryOptions<ContentTypes> options) 
   {
        var result = options.ApplyTo(_repository.Query().Get()
            .Where(u => u.UserId == userId)
            .OrderBy(o => o.Description))
            .Cast<ContentTypes>();

        return result;
   }

我在尝试应用 ODataQueryOptions 时收到错误 500。由于该类已经继承了ODataController,我还需要做options.ApplyTo(...)吗?

【问题讨论】:

    标签: c#-4.0 odata dto


    【解决方案1】:

    解决方案是确保返回类型是 DTO 的类型,并且将 ODataQueryOptions 应用于 EF 实体。然后我使用 Automapper 将结果映射到 DTO。

    我已根据@Schandlich 的建议更新了答案,但仍然存在一些问题:

        [Queryable]
        public virtual IHttpActionResult Get(ODataQueryOptions<ContentType> options)
        {
            var userId = 102;   // mock
    
            try
            {
                var results = options.ApplyTo(_uow.Repository<ContentType>()
                    .Query()
                    .Get()
                    .Include(u => u.User)
                    .Where(u => u.UserId == userId)
                    .OrderBy(o => o.Description)).Cast<ContentType>()
                    .Select(x => new ContentTypeDTO()
                    {
                        //projection goes here
                        ContentTypeId = x.ContentTypeId,
                        Description = x.Description,
                        UserDTO = new UserDTO 
                        { 
                            UserId = x.UserId,
                            UserName = x.User.UserName
                        }
                    });
    
                return this.Ok(results);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    

    使用ODataQueryOptions 的原因是我希望EF 在数据库调用级别处理过滤。否则,我将返回所有记录,然后Queryable 将开始返回,例如结果的第一页。

    我删除了 Automapper 代码,但很好奇为什么不使用它?

    然而,正如@Schandlich 指出的那样,这不适用于$select$expand

    【讨论】:

    • EF 过滤器进来的原因是因为你处理投影的方式。看看我上面发布的答案。我还会查看您的存储库并确保其中没有 ToList() 或其他强制执行方法。此外,当您使用 $select 或 $expand 时,这会中断,因为结果不是 ContentType 类型。
    • @Schandlich - 您的建议部分有效,当我排除 options.ApplyTo() 时,我没有得到所有其余的 OData 信息,例如 $inlinecount=allpages 的值,因此没有分页详细信息.我不想从数据库返回所有记录,然后在客户端过滤。如果有数千条记录怎么办?你说$select$expand 失败是正确的。我怎么能得到这个工作。另外,为什么不使用 Automapper 来做一些工作呢?
    • 这是一篇关于为什么不使用 Automapper 的好文章。 devtrends.co.uk/blog/…。分页不起作用的原因是在应用它之前的某个时间点,查询已经在访问数据库。我不知道是 AutoMapper 导致了问题还是您的存储库是。我可能是演员表。我会努力让它开箱即用,而不是试图强制扩展并选择与您的设置一起使用。 beyondtheduck.com/…
    • 这是我编写和使用的解决方案。我没有从控制器调用 EF,而是创建了自己的接口而不是 IRepository。实现该接口的数据源只是从 EF 到您的“ContentType”版本进行干净的投影。滚动我自己的存储库使 Unity 更容易处理我的数据上下文。
    • 谢谢,今晚晚些时候我有一些阅读材料。控制器方法正在工作 - 获取数据/分页等。我尝试删除 options.ApplyTo() 并仅使用 DF db 上下文,而不是 uow/repository,但同样的问题 - 没有分页详细信息(我猜是额外的 @ 987654336@/etc. 过滤也会失败)。我添加了这个问题:stackoverflow.com/questions/21688857/…
    【解决方案2】:

    根据@ElHaix 的回答更新。我不能更强烈地反对使用 AutoMapper 从这样的数据源进行映射。这也假设应用到存储库的查询是在调用数据库之前应用的。

    [Queryable]
    public virtual IHttpResult Get()
    {
        var userId = 102;   // mock
    
        try
        {
            var results = _uow.Repository<ContentType>()
                .Query()
                .Get()
                .Where(u => u.UserId == userId)
                .OrderBy(o => o.Description)
                .Select(x => new ContentTypeDTO() 
                {
                    //projection goes here
                });
    
            return this.Ok(results);
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
    

    另外,我会尝试对 ElHaix 的答案进行 $select。

    【讨论】:

    • 使用[Queryable] 属性并删除options.ApplyTo(),我从服务器获得31 行。由于我想在服务器端应用过滤,使用options.ApplyTo(),执行要求10条记录的sql查询(而不是稍后在客户端过滤)。
    【解决方案3】:

    我能够通过 AutoMapper Project() 扩展来实现这一点。 $select、$filter等都应用于数据库查询。

     [TestMethod]
            public void DataShaping_With_AutoMapper_And_OData_Select_Test()
            {
                OracleMonitor myMonitor = new OracleMonitor();
                myMonitor.IsActive = true;
    
                var dbcontext = new MyDbContext();
                
                var datasource = dbcontext.Datasouces;
    
                Assert.IsNotNull(datasource);
    
                SetupAutoMapper();
    
                var odataQuery = Extensions.CreateDummyODataQuery<DataSourceDTO>("$expand=Fields($select=Description)&$select=Name");
    
                var withShaping = datasource.Project().To<DataSourceDTO>();
    
                Assert.IsNotNull(withShaping);
    
                var withODataQuery = odataQuery.ApplyTo(withShaping);
                Assert.IsNotNull(withODataQuery);
    
                string strJson = JsonConvert.SerializeObject(withODataQuery);
    
                Assert.IsFalse(String.IsNullOrEmpty(strJson));
            }

    【讨论】:

    • 有一次我使用了 Automapper...不再使用了。我建议你看看为什么使用 AutoMapper 和 EntityFramework 将 DTO 映射到实体是可怕的:rogeralsing.com/2013/12/01/…
    猜你喜欢
    • 2014-06-04
    • 2021-11-12
    • 2016-04-01
    • 2011-01-20
    • 2018-08-11
    • 2021-08-05
    • 2019-03-18
    • 2014-03-15
    • 2021-03-31
    相关资源
    最近更新 更多