我正在开发一个项目,该项目使用汇编程序模式将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对全名进行排序)?
我的要求的关键是:
答案 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,属性属性)。