使用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
课上。当我这样做时,Administrator
和GetByID
方法都有效,因此包括我的TEntity Get(filter: ..., includeProperties: ...)
参数中的区域/角色,并在我的DTO中填充字符串数组。
我想问题已经解决了,但是......
问题是,特别是对于Flatten
方法,为什么它与GetById
关键字一起使用但不没有?
如果EF从原始方法调用的时间开始实际影响投影,为什么会包含这些实体?
答案 0 :(得分:2)
如果在包含后进行任何类型的投影,则包含不起作用。
Here's a post处理此问题以及如何解决此问题。
而且here's也是一个可以处理它的问题。
编辑:为了回应您的修改,我尝试四处查看是否可以找到virtual
关键字使Include
关键字工作但没有它的情况下工作的原因。我找不到任何可以直接回答的东西。
以下是我认为正在发生的事情:导航属性上的virtual
关键字告诉Entity Framework此属性应该使用延迟加载。如果您有时想要加载它们,那么您将使用Include
。我认为那是Include
的基础。我认为它试图专门查找具有该名称的virtual
属性,并且当它找不到它时,优雅地死亡,无异常。如果你没有将它标记为virtual
我认为Include
的实现完全错过了它。我基于这样一个事实,即我在Include
上看到的所有文章都没有在延迟加载的上下文之外提到它 - 这意味着使用virtual
关键字。
答案 1 :(得分:0)
IQueryable.Include
存在严重问题:基础对象必须是ObjectQuery
或DbQuery
类型,否则此方法无效!
Include
是漏洞抽象,它只适用于Entity框架。 EF 4.1已包含Include
而不是通用IQueryable
,但它在内部仅将通过的通用IQueryable
转换为通用ObjectQuery
或DbQuery
并调用其Include
。
https://stackoverflow.com/a/6791874/2444725
但是,当我向方法传递一组包含属性时,它永远不会使用连接修改查询以包含相关实体。我的查询没有更新的任何想法?
您好像正在使用LinqKit:
IQueryable<TEntity> query = dbSet.AsExpandable();
扩展方法AsExpandable
获取IQueryable
参数,并返回类型为ExpandableQuery
的新对象,用于装饰原始ObjectQuery
或DbQuery
对象。应用于IQueryable.Include
的扩展方法ExpandableQuery
无法进行转换,无法跳过。
为什么它适用于虚拟关键字但不是没有?
虚拟方法与include无关。相反,它打开延迟加载。这意味着导航属性不会在第一个请求中加载,而是在实际使用这些属性时加载到单独的请求中。