【问题标题】:DTOs and IQueryable: assembling and disassembling DTOsDTO 和 IQueryable:组装和拆卸 DTO
【发布时间】:2012-06-12 17:51:03
【问题描述】:

我正在开发一个项目,该项目使用汇编器模式将 LinqToEntity 实体组装成服务级别的数据传输对象,然后传递到客户端层以供使用。该方法已将实体对象转换为提供特定于服务调用的信息的简化的平面对象。

例如。

// Original Entity looks something like this
public class PersonEntity
{
   public int Id { get; set; }
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public int MotherId { get; set; }  
   public PersonEntity Mother { get; set; }
   // lots of other properties
}

// Flattened DTO
public class PersonSummaryInfo
{
  public int Id { get; set; }
  public string FullName { get; set; }
  public string MothersFullName { get; set; }
}

在本例中,汇编器将创建 PersonSummaryInfo,构建流程的 FullNames 部分。

我现在面临一些第 3 方控件 (Telerik ASP.NET MVC GridControl) 的问题,其中控件设置为根据其模型的属性进行过滤(使用 IQueryable)。似乎你有一个单层设计并将你的数据库实体直接泵入视图,我无法忍受。

试图将其合并到我的逻辑中,GridControl 绑定到我的 DTO 而不是实体,这一切都很好,直到它尝试对任何东西进行排序。我以一种非常通用的方式将所有 IQueryable 东西推入我的服务中,以使其对此负责。排序尝试排序,比如 DTO 上的 MothersFullName(它的行为是将“MothersFullName”作为字符串传递给您的排序逻辑),这被推送到我的服务,该服务通过反射尝试对实体进行排序,利用 IQueryable 惰性正在加载,但当然在执行查询时,会抛出异常,因为“MothersFullName”不是原始实体的属性。

是否有处理这种实现的好策略?一旦 DTO 回到应用程序的服务层,将其有效地“分解”回其 ORM 实体是一种好习惯吗?还是传递更丰富的对象更好,这些对象更了解它们是什么(例如如何使用 FirstName 和 LastName 对全名进行排序)?

我的要求的关键是:

  • 使用花哨的 Telerik 控件,因为他们喜欢它
  • 服务级别的延迟加载结果(即不带回 200 万条记录,只显示 10 条)
  • 支持过滤、分页等
  • 良好的架构实现

【问题讨论】:

    标签: .net asp.net-mvc entity-framework telerik dto


    【解决方案1】:

    您是否仅将 RadGrid 用于其开箱即用的排序功能?我遇到了同样的问题,最后只使用了 RadListView 并将寻呼机/排序插入其中。使用模板,您可以通过 SortExpession 属性准确地告诉它要排序的内容。然后它只是关于触发正确的事件。这是我的事件处理程序,你可以设置任何你能拿到的东西来触发它。我不确定这是否是一个解决方案,但希望它可以帮助您找到一个:

    protected void SortSearchTickets(object sender, RadComboBoxSelectedIndexChangedEventArgs e)
        {
            var selectedValue = e.Value;
            lsvSearchResults.SortExpressions.Clear();
            var sortExp = new RadListViewSortExpression();
    
            switch (selectedValue)
            {
                case "ID":
                    sortExp.FieldName = "TicketID";
                    break;
                case "TicketType":
                    sortExp.FieldName = "TypeDescription";
                    break;
                case "Subject":
                    sortExp.FieldName = "Subject";
                    break;
                case "Status":
                    sortExp.FieldName = "Status.Key";
                    break;
                case "DueDateDesc":
                    sortExp.FieldName = "DueDate";
                    sortExp.SortOrder = RadListViewSortOrder.Descending;
                    break;
                case "DueDateAsc":
                    sortExp.FieldName = "DueDate";
                    sortExp.SortOrder = RadListViewSortOrder.Ascending;
                    break;
                case "Assigned To":
                    sortExp.FieldName = "AssignedTo.Key";
                    break;
                case "Assigned By":
                    sortExp.FieldName = "AssignedBy.Key";
                    break;
                default:
                    break;
            }
            lsvSearchResults.SortExpressions.AddSortExpression(sortExp);
            lsvSearchResults.Rebind();
        }
    

    【讨论】:

    • 在我看来他使用的是 ASP.NET MVC 套件而不是 Web 表单。
    【解决方案2】:

    您有几个选择。首先,确实可以绑定到 IQueryable,因为当然就开发时间而言,这是最快(也是最常见)的方式。

    在您的情况下(ORM 之上的完整服务层)情况有点不同。我个人建议您稍微挖掘一下并将custom bindings 提供给您的网格。您将获得一个 GridCommand 对象,您可以查询该对象以进行排序和过滤,并使用该对象向您的服务层询问数据。这是一个很好的地方来提及解决您面临的问题的简单方法(这是您的表达式基于 DTO 属性)。您可以尝试使用Dynamic Linq。只需从表达式构造您的字符串查询并将它们传递给您的 DAL。

    事实上,这是 Telerik 的另一个产品 OpenAccess ORM 建议的最佳实践。 OpenAccess SDK 包含几个示例(尤其是 WCF 普通服务),它们使用与您的架构相似的架构。该产品还提供了一个code generation tool,它提供了一个完整的服务层。

    【讨论】:

    • 我现在正在使用自定义绑定并使用 GridCommand 对象来执行我的所有过滤,但是在对实体模型中不存在的列进行排序或过滤时,它对问题没有帮助.我现在很可能会创建一个特定于网格的 SQL 视图,并将其与一对一的 ViewModel 一起使用,而不是从服务层的多个实体中构造我自己的对象。
    【解决方案3】:

    采用这个解决方案。

    创建包含 Grid 特定列的 sql 视图。然后使数据传输对象使用与视图完全匹配的属性。不是实现这一目标的最干净或最强大的方法,但至少它可以从我不需要的项目中获取我的数据引用。

    没有使用动态 linq,而是保留了我的通用方法并使用反射来获取列名以进行匹配。没有因为我们已经实现了整个服务层而选择 Telerik 的 Open Access,但这听起来是一个不错的解决方案。

    它仍然是 IQueryable,因此它运行得非常高效(只是依赖开发人员来确保 GridViews 与 GridViewModels 匹配,一个属性对应一个属性)。

    【讨论】:

    • 我已选择此作为我案例的答案,但我也觉得 sovanesyan 的答案可能对其他有类似问题的人有效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多