DTO和IQueryable:组装和拆卸DTO

时间:2012-06-08 16:35:16

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

我正在开发一个项目,该项目使用汇编程序模式将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部分。

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

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

是否有一个处理此类实施的好策略?将DTO重新组合回其ORM实体(一旦返回到应用程序的服务层),这是一种好的做法吗?或者更好地传递更丰富的对象,这些对象更了解它们是什么(例如如何使用FirstName和LastName对全名进行排序)?

我的要求的关键是:

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

3 个答案:

答案 0 :(得分:4)

你有很多选择。首先,它确实能够被绑定到IQueryable,因为就开发时间而言,这是最快(也是最常见)的方式。

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

事实上,这是Telerik的另一个产品OpenAccess ORM的最佳实践建议。 OpenAccess SDK包含几个使用与您类似的体系结构的示例(尤其是WCF Plain Services)。该产品还提供code generation tool,可提供整个服务层。

答案 1 :(得分:0)

您是否只使用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();
    }

答案 2 :(得分:0)

使用此解决方案。

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

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

它仍然是IQueryable,因此效率非常高(仅依靠开发人员确保GridViews匹配GridViewModels,属性属性)。