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
类的Areas
和Roles
中添加了virtual
关键字。当我这样做时,GetByID
和Get(filter: ..., includeProperties: ...)
方法都工作了,从而包括我的TEntity Flatten
参数中的区域/角色,并填充我的DTO中的字符串数组。
我想问题解决了,但是……
问题是,特别是对于GetById
方法,为什么它与virtual
关键字一起工作,而不是没有?
如果EF实际上从原始方法调用的时间开始考虑投影,为什么要包括这些实体?
如果在include之后进行任何类型的投影,则include不起作用。
这里有一篇关于这个问题的文章,以及如何解决这个问题。
这里也有一个SO问题。
编辑:为了回应你的编辑,我试着环顾四周,看看我是否能找到一个原因,为什么virtual
关键字会使你的Include
工作,但没有它不能工作。我找不到任何可以直接回答这个问题的答案。
这是我认为正在发生的事情,尽管:virtual
关键字,在导航属性,告诉实体框架,这个属性应该采用延迟加载。如果有时你想要急切地加载它们,那么你可以使用Include
。我认为这就是Include
的目的。我认为它试图专门寻找具有该名称的virtual
属性,当它找不到它时,毫无例外地优雅地死亡。如果你不把它标记为virtual
,我认为Include
的实现完全错过了它。我之所以这样猜测,是因为我所看到的关于Include
的文章都没有在延迟加载上下文之外提到它——这意味着要使用virtual
关键字。
IQueryable.Include
有一个严重的问题:底层对象必须是ObjectQuery
或DbQuery
类型,否则此方法将不起作用!
https://stackoverflow.com/a/6791874/2444725
Include
是泄漏的抽象,它只适用于实体框架。EF 4.1已经在泛型IQueryable
上包含了Include
,但它内部只将传递的泛型IQueryable
转换为泛型ObjectQuery
或DbQuery
,并调用它们的Include
。
当我向方法传递一组包含属性时,它不会修改带有连接的查询以包含相关实体。你知道为什么我的查询没有更新吗?
你似乎正在使用LinqKit:
IQueryable<TEntity> query = dbSet.AsExpandable();
扩展方法AsExpandable
获取IQueryable
参数并返回一个类型为ExpandableQuery
的新对象,该对象修饰了原来的ObjectQuery
或DbQuery
对象。扩展方法IQueryable.Include
,应用于ExpandableQuery
,不能进行转换并静默跳过。
为什么有了virtual关键字就可以了?
虚方法与include无关。相反,它会开启延迟加载。这意味着导航属性不会在第一个请求时加载,而是在实际使用这些属性时在单独的请求中加载。