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

时间:2013-02-28 16:34:10

标签: c# entity-framework automapper

使用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参数,而之前的区域/角色是null 我调用了Map,所以我不认为这与AutoMapper有关。

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

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

最后,如果由于导航属性不是Include而忽略virtual,我将virtual关键字添加到Areas和{{1}在我的Roles课上。当我这样做时,AdministratorGetByID方法都有效,因此包括我的TEntity Get(filter: ..., includeProperties: ...)参数中的区域/角色,并在我的DTO中填充字符串数组。

我想问题已经解决了,但是......

问题是,特别是对于Flatten方法,为什么它与GetById关键字一起使用但没有?

如果EF从原始方法调用的时间开始实际影响投影,为什么会包含这些实体?

2 个答案:

答案 0 :(得分:2)

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

Here's a post处理此问题以及如何解决此问题。

而且here's也是一个可以处理它的问题。

编辑:为了回应您的修改,我尝试四处查看是否可以找到virtual关键字使Include关键字工作但没有它的情况下工作的原因。我找不到任何可以直接回答的东西。

以下是我认为正在发生的事情:导航属性上的virtual关键字告诉Entity Framework此属性应该使用延迟加载。如果您有时想要加载它们,那么您将使用Include。我认为那是Include的基础。我认为它试图专门查找具有该名称的virtual属性,并且当它找不到它时,优雅地死亡,无异常。如果你没有将它标记为virtual我认为Include的实现完全错过了它。我基于这样一个事实,即我在Include上看到的所有文章都没有在延迟加载的上下文之外提到它 - 这意味着使用virtual关键字。

答案 1 :(得分:0)

IQueryable.Include存在严重问题:基础对象必须是ObjectQueryDbQuery类型,否则此方法无效!

  

Include是漏洞抽象,它只适用于Entity框架。 EF 4.1已包含Include而不是通用IQueryable,但它在内部仅将通过的通用IQueryable转换为通用ObjectQueryDbQuery并调用其Include

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

  

但是,当我向方法传递一组包含属性时,它永远不会使用连接修改查询以包含相关实体。我的查询没有更新的任何想法?

您好像正在使用LinqKit:

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

扩展方法AsExpandable获取IQueryable参数,并返回类型为ExpandableQuery的新对象,用于装饰原始ObjectQueryDbQuery对象。应用于IQueryable.Include的扩展方法ExpandableQuery无法进行转换,无法跳过。

  

为什么它适用于虚拟关键字但不是没有?

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