在控制器中进行 linq 查询是一种好习惯吗?

Posted

技术标签:

【中文标题】在控制器中进行 linq 查询是一种好习惯吗?【英文标题】:Is it good practice to have linq query in Controllers? 【发布时间】:2011-10-31 19:58:37 【问题描述】:

我对 MVC 模式不是很熟悉。你能告诉我以下三个控制器动作中哪一个更好吗?谢谢:)

(1) 有查询:

public ActionResult List()

   var query = repository.Query().Where(it => it.IsHandled).OrderBy(it => it.Id);
   // ...

(2) 有查询服务:

public ActionResult List() 

    var items = service.GetHandledItemsOrderById();
    // ...

(3) 行动有序:

public ActionResult List()

    var items = service.GetHandledItems().OrderBy(it => it.Id);
    // ...

如果我们选择(1),那么控制器中的业务逻辑太多了?

如果我们选择(2),可能会有很多类似GetXXXByYYY()的服务方法。

如果我们选择(3),为什么我们封装Where(it => it.IsHandled)而不是OrderBy(it => it.Id

有什么想法吗?

【问题讨论】:

【参考方案1】:

我确信意见可能会有所不同,但我已经学会了尽量在服务中保留尽可能多的业务逻辑。 3是我的选择。使用 1,您已经发现了问题。使用 2,您将在服务中引入显示优先级。使用 3,您可以在必要时处理显示首选项。如果您要为业务层引入另一个接口,您可能需要通过选择 2 进行不必要的代码迭代。

【讨论】:

页面需要显示按Id处理的商品。为什么“显示已处理(已处理)项目”是业务逻辑并且应该在服务中,而“按 id 显示项目顺序”是显示偏好? 我认为“显示已处理的项目”和“按 id 显示项目顺序”都是显示偏好?所以我们应该选择1? 我想我的问题应该是:(1)“Where(it => it.IsHandled)”是业务逻辑吗? (2) 是“OrderBy(it => it.Id)”的业务逻辑吗?【参考方案2】:

视情况而定。 :)

我的看法:

我喜欢保持服务松散,以尽量减少重复代码。我也是管道和过滤器的粉丝。

这就是我会做的(并且会做的)。

服务

public ICollection<Item> GetHandledItems<TKey>(OrderingOptions<Item, TKey> orderingOptions) 

   return repository
      .Query()
      .WhereHandled()
      .WithOrdering(orderingOptions)
      .ToList();     

ItemFilters.cs

public static IQueryable<Item> WhereHandled(this IQueryable<Item> source)

   return source.Where(it => it.IsHandled);


public static IOrderedQueryable<T> WithOrdering<T, TKey>(
   this IQueryable<T> source,
   OrderingOptions<T, TKey> orderingOptions)

   return orderingOptions.SortDescending 
      ? source.OrderByDescending(orderingOptions.OrderingKey) :                                                    
        source.OrderBy(orderingOptions.OrderingKey);

OrderingOptions.cs

 public class OrderingOptions<T,TKey>
 
    public OrderingOptions(Expression<Func<T,TKey>> orderingKey, bool sortDescending = false)
    
       OrderingKey = orderingKey;
       SortDescending = sortDescending;
    

    public Expression<Func<T,TKey>> OrderingKey  get; private set; 
    public bool SortDescending  get; private set; 
 

这样,你可以在Controller中指定排序:

var items = service.GetHandledItems(new OrderingOptions(it => it.Id));

上述与选项3的区别:

在返回到控制器之前实现了上述序列。选项 3 没有,这很危险(您最终可能会将查询返回到 View 并破坏 MVC 模式)。 通用“订购”POCO,可在任何地方使用,让您的查询保持 D-R-Y。 服务变得愚蠢,并且只是存储库和控制器之间的缓解器(这就是它应该做的,IMO)。将逻辑(例如过滤器)抽象到一个地方。

【讨论】:

谢谢。但是如果“GetHandledItems()”返回一个集合(不是 IQueryable),我们就不能对查询执行投影。它会影响性能。 @Dylan - 在执行查询之前,在服务中进行预测,无论是内联还是通过其他管道方法,例如:return repo.Query().WhereHandled().WithOrdering().AsSomeProjectedType() 还可以观看关于 MVC 管道和过滤器的 精彩 RobCon 视频:asp.net/mvc/videos/… 如果我们在service中做投影,需要在service层写“view model”或者“data transfer object”,我觉得VO和DTO应该在Controller层吧?并且,不同的页面需要不同的投影,例如页面1需要Property1和Property2,而pqge 2需要Property1、Property2和Property3。如何解决此类问题? @Dylan Lin。所有有效的问题,这里是答案。 1) DTO 投影应该进入控制器。你要转移到哪里?? DTO 应该在层之间或存储库和控制器之间。 2) ViewModel 投影应该在实现 之后进行。 Shaping 可以对查询进行。他们是两个不同的东西。 3) 使用 AutoMapper 创建 ViewModel 以简化上述操作。 4) 如果两个页面需要不同的属性,请调整服务查询以适应两个页面,并在控制器中进行不同的投影,或者如果它们有很大不同,请使用差异服务方法。

以上是关于在控制器中进行 linq 查询是一种好习惯吗?的主要内容,如果未能解决你的问题,请参考以下文章

使用“视图”进行分组是一种好习惯吗?

在 AngularJS 中结合 CREATE 和 EDIT 控制器是一种好习惯吗?

使用 MongoDB 数据库为每个查询打开一个新连接是一种好习惯吗?

始终连接 SQL 表是一种好习惯吗?

制作主视图的视图子视图而不是它视觉上的视图是一种好习惯吗?

一次提交多个文件是一种好习惯吗?