【问题标题】:Entity Framework in n-layered application - Lazy loading vs. Eager loading patternsn 层应用程序中的实体框架 - 延迟加载与急切加载模式
【发布时间】:2011-02-06 09:16:17
【问题描述】:

这个问题让我无法入睡,因为一年以来我一直在努力寻找解决方案,但是......我的脑海中仍然没有发生任何事情。也许你可以帮助我,因为我认为这是一个非常普遍的问题。

我有一个 n 层应用程序:表示层、业务逻辑层、模型层。为简单起见,假设我的应用程序在表示层中包含一个允许用户搜索客户的表单。现在用户通过 UI 填充过滤器并单击一个按钮。发生了一些事情,请求到达表示层,以CustomerSearch(CustomerFilter myFilter) 之类的方法。这个业务逻辑层现在保持简单:在模型上创建查询并返回结果。

现在的问题是:您如何面对加载数据的问题?我的意思是业务逻辑层不知道该特定方法将仅由该表单调用。所以我认为它不知道请求表单是否只需要返回 Customer 对象或带有链接 Order 实体的 Customer 对象。

我试图更好地解释: 我们的表格只想列出按姓氏搜索的客户。它与订单无关。所以业务逻辑查询会是这样的:

(from c in ctx.CustomerSet
where c.Name.Contains(strQry) select c).ToList();

现在它可以正常工作了。两天后,您的老板要求您添加一个表格,让您可以像其他人一样搜索客户,并且您需要显示每个客户创建的订单总数。现在我想重用该查询并添加附加(包含)订单的逻辑片段并取回它。

你会如何处理这个请求?

这是我从现在开始的最好的(我认为)想法。我想听听你的意见: 我在 BLL 中的 CustomerSearch 方法不会直接创建查询,而是通过组成 ObjectQuery 的私有扩展方法传递:

private ObjectQuery<Customer> SearchCustomers(this ObjectQuery<Customer> qry, CustomerFilter myFilter)

private ObjectQuery<Customer> IncludeOrders(this ObjectQuery<Customer> qry)

但这并不能说服我,因为它看起来太复杂了。

谢谢, 马可

【问题讨论】:

    标签: entity-framework architecture lazy-loading eager-loading n-layer


    【解决方案1】:

    考虑将表示层和业务层之间的接口转移到 DTO,例如:-http://msdn.microsoft.com/en-us/magazine/ee236638.aspx

    Automapper 之类的东西可以减轻与迁移到 DTO 相关的大部分痛苦,并且迁移将明确您可以对查询结果做什么和不能做什么,即,如果它在 DTO 上,则它已加载,如果它不是您需要的不同的 DTO。

    您当前的计划听起来在表示层和数据层之间的耦合过于紧密。

    【讨论】:

    • 感谢您非常快速的答复。所以你认为这是必须在表示层使用 DTO 而不是业务实体的情况?谢谢马可
    • 听起来您的应用程序正在接近它变得有价值的大小/复杂性点。迁移到 DTO 总是需要权衡取舍,因为它们会增加整体的复杂性,但像 Automapper 这样的工具可以减少它变得有价值的点。
    • 感谢您的建议。我绝对会检查 DTO 和 Automapper。再次感谢您。
    【解决方案2】:

    我同意 Hightechrider 关于使用 DTO 的评论,但是您对业务实体有一个有效的问题。

    一种可能的解决方案(我在我正在开发的项目中使用这些方面的东西)是使用只读的 DTO(至少从表示层的角度来看。您的查询/获取操作只会返回 DTO ,这将为您提供延迟加载功能。

    您可以将业务层设置为在更新/创建对象/实体时返回包装 DTO 的可编辑对象。您的可编辑对象可以强制执行任何业务规则,然后当它被保存/传递到业务层时,它所包装的 DTO(带有更新的值)可以传递到数据层。

    public class Editable
    {
        //.......initialize this, other properties/methods....
    
        public bool CanEdit<TRet>(Expression<Func<Dto, TRet>> property)
        {
            //do something to determine can edit
            return true;
        }
    
        public bool Update<TRet>(Expression<Func<Dto, TRet>> property, TRet updatedValue)
        {
            if (CanEdit(property))
            {
                //set the value on the property of the DTO (somehow)
                return true;
            }
            return false;
        }
    
        public Dto ValueOf { get; private set;}
    }
    

    这使您能够强制用户是否可以从业务层获取可编辑对象,以及如果用户有权编辑对象的特定属性,则允许业务对象强制执行。我在工作的域中遇到的一个常见问题是,一些用户可以编辑所有属性,而其他用户则不能,而任何人都可以查看属性的值。此外,表示层能够根据业务层的要求和强制执行来确定向用户公开哪些内容是可编辑的。

    我的其他想法是您的业务层不能公开 IQueryable 或将标准表达式作为您传递给数据层的参数。例如,我有一个类似这样的页面构建查询:

    public class PageData
    {
        public int PageNum;
        public int TotalNumberPages;
        public IEnumerable<Dto> DataSet;
    }
    
    public class BL
    {
        public PageData GetPagedData(int pageNum, int itemsPerPage, Expression<Func<Dto, bool>> whereClause)
        {
            var dataCt = dataContext.Dtos.Where(whereClause).Count();
            var dataSet = dataContext.Dtos.Where(whereClause).Skip(pageNum * itemsPerPage).Take(itemsPerPage);
    
            var ret = new PageData
                            { 
                              //init this
                            };
    
            return ret;
        }
    }
    

    【讨论】:

    • 感谢您的建议!幸运的是我没有授权问题,但这对于其他项目来说是一个很好的解决方案。关于将查询(作为表达式)传递给 BL:我不太喜欢它,但这对我来说可能只是一个风格问题。我希望表示层尽可能地具有可读性和轻便性。即我喜欢看到 GetCustomersByCountry 和 GetCustomersByCity 之类的东西,而不是“通用”GetCustomers(query),但是 - 再次 - 我认为这只是一个语法问题。
    猜你喜欢
    • 2011-03-14
    • 2013-03-24
    • 2021-10-21
    • 2014-02-18
    • 1970-01-01
    • 2013-09-25
    • 2015-09-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多