IQueryable< T>.Include(字符串路径)不修改查询以包含连接

本文关键字:修改 查询 连接 包含 路径 Include 字符串 IQueryable | 更新日期: 2023-09-27 18:01:31

使用Entity Framework 5,我有一个通用方法,它从我的上下文中检索实体,并使用可选参数进行过滤,包括相关实体,并设置结果的顺序。但是,当我向该方法传递一组包含属性时,它绝不会修改带有连接的查询以包含相关实体。你知道为什么我的查询没有更新吗?

方法:

public virtual IQueryable<TEntity> Get(
    Expression<Func<TEntity, bool>> filter = null,
    string includeProperties = "",
    Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null)
{        
    IQueryable<TEntity> query = dbSet.AsExpandable();
    if (filter != null)
    {
        query = query.Where(filter);
    }
    foreach (var includeProperty in includeProperties.Split
        (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
    {
        query = query.Include(includeProperty);
    }
    if (orderBy != null)
    {
        return orderBy(query);
    }
    else
    {
        return query;
    }
}

foreach (var includeProperty...之前的查询,之后保持不变

{SELECT 
[Extent1].[Pid] AS [Pid],     
[Extent1].[Created] AS [Created], 
[Extent1].[Creator] AS [Creator]
FROM [Administrator] AS [Extent1]}

编辑……更多信息

最初我在以下POCO类上使用以下方法调用:AdministratorDTO admin = unitOfWork.AdministratorComponent.GetByID(userPid);:

public virtual TEntity GetByID(object id)
{
    return dbSet.Find(id);
}
public class Administrator : IPrincipal
{
    [Key]
    [Required]
    [StringLength(1024)]
    [Display(Name = "PID")]
    public string Pid { get; set; }
    public DateTime Created { get; set; }
    public string Creator { get; set; }
    ...
    public ICollection<Role> Roles { get; set; }
    public ICollection<Area> Areas { get; set; }
    ...
}

和使用AutoMapper映射到具有以下配置的DTO:

public class AdministratorDTO
{
    [Key]
    [Required]
    [StringLength(1024)]
    [Display(Name = "PID")]
    public string Pid { get; set; }
    ...
    public string[] Roles { get; set; }
    public string[] Areas { get; set; }
}

public class WebApiApplication : System.Web.HttpApplication
{
...
AutoMapperConfiguration.Configure();
}
public static class AutoMapperConfiguration
{
    public static void Configure()
    {
        ConfigureAdministratorMapping();
        ...
    }
    private static void ConfigureAdministratorMapping()
    {
        Mapper.CreateMap<Administrator, AdministratorDTO>()
            .ForMember(dest => dest.Roles, 
                opt => opt.MapFrom(src => src.Roles == null ? 
                    null : src.Roles.Select(r => r.Name).ToArray())) 
            .ForMember(dest => dest.Areas, 
                opt => opt.MapFrom(src => src.Areas == null ?
                    null : src.Areas.Select(a => a.Id).ToArray()));
        ...
    }
}
public class BusinessComponent<TEntity, TDto> 
    : IBusinessComponent<TEntity, TDto>
        where TEntity : class
        where TDto : class
{
    ...
    protected TDto Flatten(TEntity entity)
    {
        return Mapper.Map<TEntity, TDto>(entity);
    }
}

我的理解是,如果我没有将Administrator的导航属性(区域和角色)标记为virtual,它们将被急切地加载,但我一直在DTO中得到一个空字符串[]。

我查看了进入Flatten方法的TEntity参数,在调用Map之前,区域/角色是null ,所以我认为这与AutoMapper无关。

接下来,我尝试使用以下方法调用:

AdministratorDTO admin = unitOfWork.AdministratorComponent
    .Get(filter: a => a.Pid == "csherman", includeProperties: "Roles, Areas")
    .SingleOrDefault();

最后,以防Include因为导航属性不是virtual而被忽略,我在Administrator类的AreasRoles中添加了virtual关键字。当我这样做时,GetByIDGet(filter: ..., includeProperties: ...)方法都工作了,从而包括我的TEntity Flatten参数中的区域/角色,并填充我的DTO中的字符串数组。

我想问题解决了,但是……

问题是,特别是对于GetById方法,为什么它与virtual关键字一起工作,而不是没有?

如果EF实际上从原始方法调用的时间开始考虑投影,为什么要包括这些实体?

IQueryable< T>.Include(字符串路径)不修改查询以包含连接

如果在include之后进行任何类型的投影,则include不起作用。

这里有一篇关于这个问题的文章,以及如何解决这个问题。

这里也有一个SO问题。

编辑:为了回应你的编辑,我试着环顾四周,看看我是否能找到一个原因,为什么virtual关键字会使你的Include工作,但没有它不能工作。我找不到任何可以直接回答这个问题的答案。

这是我认为正在发生的事情,尽管:virtual关键字,在导航属性,告诉实体框架,这个属性应该采用延迟加载。如果有时你想要急切地加载它们,那么你可以使用Include。我认为这就是Include的目的。我认为它试图专门寻找具有该名称的virtual属性,当它找不到它时,毫无例外地优雅地死亡。如果你不把它标记为virtual,我认为Include的实现完全错过了它。我之所以这样猜测,是因为我所看到的关于Include的文章都没有在延迟加载上下文之外提到它——这意味着要使用virtual关键字。

IQueryable.Include有一个严重的问题:底层对象必须是ObjectQueryDbQuery类型,否则此方法将不起作用!

Include是泄漏的抽象,它只适用于实体框架。EF 4.1已经在泛型IQueryable上包含了Include,但它内部只将传递的泛型IQueryable转换为泛型ObjectQueryDbQuery,并调用它们的Include

https://stackoverflow.com/a/6791874/2444725

当我向方法传递一组包含属性时,它不会修改带有连接的查询以包含相关实体。你知道为什么我的查询没有更新吗?

你似乎正在使用LinqKit:

IQueryable<TEntity> query = dbSet.AsExpandable();

扩展方法AsExpandable获取IQueryable参数并返回一个类型为ExpandableQuery的新对象,该对象修饰了原来的ObjectQueryDbQuery对象。扩展方法IQueryable.Include,应用于ExpandableQuery,不能进行转换并静默跳过。

为什么有了virtual关键字就可以了?

虚方法与include无关。相反,它会开启延迟加载。这意味着导航属性不会在第一个请求时加载,而是在实际使用这些属性时在单独的请求中加载。