【问题标题】:Is it good practice to have linq query in Controllers?在控制器中进行 linq 查询是一种好习惯吗?
【发布时间】: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

有什么想法吗?

【问题讨论】:

    标签: asp.net-mvc model-view-controller controller business-logic


    【解决方案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) 如果两个页面需要不同的属性,请调整服务查询以适应两个页面,并在控制器中进行不同的投影,或者如果它们有很大不同,请使用差异服务方法。
    猜你喜欢
    • 2010-12-24
    • 2016-06-09
    • 2014-09-22
    • 2013-10-21
    • 2018-02-25
    • 1970-01-01
    • 2012-04-12
    • 2018-11-17
    • 2015-01-08
    相关资源
    最近更新 更多