在单实体框架查询中返回父项和子项而没有返回IQueryable或IEnumerable?

ada*_*101 3 c# linq entity-framework automapper

我们有一个规则,即不公开服务层IQueryable<T>IEnumerable<T>不在服务层之外,因此下游代码无法修改查询数据库。这意味着我们返回的类型类似于IList或ICollection。

我想知道如何编写一次linq查询以在不将子集合定义为IQueryable或的情况下单次访问数据库来获取父项和子项IEnumerable

例如,假设从服务返回的类型是ICollection<Parent>在哪里Parent定义的:

public class Parent
{
    public int ParentId { get; set; }
    public string ParentName { get; set; }
    public ICollection<Child> Children { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

如果我将查询定义为此...

from p in dbContext.Parents
where p.Name.Contains("test")
select new Parent
{
    Children =from c in dbContext.Children
    where c.ParentId == p.ParentId
    select c;
}
Run Code Online (Sandbox Code Playgroud)

它不会编译,因为IQueryable没有实现ICollection。如果我将其添加.ToList()到父级和子级集合中,它将进行编译,但是现在它将为每个父级进行一次单独的数据库访问以获取其子级。

当我写这个问题时,我想到可能的答案是编写Linq查询以选择匿名类型,然后将其映射到实际类型以返回。我们使用可以协助映射的AutoMapper。有什么原因不起作用?我是否必须.ToList()在映射对象之前调用以确保映射时不会遇到相同的问题?

oct*_*ccl 5

如果您的Parent实体中具有如下导航属性:

public class Parent
{
    public int ParentId { get; set; }
    public string ParentName { get; set; }
    public virtual ICollection<Child> Children { get; set; }
   //..
}
Run Code Online (Sandbox Code Playgroud)

然后,我建议您在服务层中创建两个新类(可以是ParentViewModelChildViewModel)来保存查询结果。在这两个类中,仅声明表示层中所需的属性。然后使用Automapper将您的实体与ViewModel类映射

之后,您可以执行以下查询:

var query =dbContext.Parents
                    .Include(p=>p.Children) // load the related entities as part of the query
                    .Where(p=>p.ParentName.Contains("test"))
                    .ProjectTo<ParentViewModel>();
Run Code Online (Sandbox Code Playgroud)

使用ProjectToAutomapper的扩展方法。

如您在上面引用的链接中所见,Automapper支持嵌套映射,因此,如果您具有ParentViewModel类型的属性,ICollection<ChildViewModel>并且还Child使用其ViewModel 映射了实体,则Automapper会自动尝试从一种类型映射到另一种类型。

所有这一切将在您的数据库中进行一次往返,因为这ProjectTo是一种IQueryable<TEntity>扩展方法,并且已转换为Select