我正在尝试访问IdentityUser模型的导航Roles属性。
在GetQueryable函数内部我设置了include属性
protected virtual IQueryable<TEntity> GetQueryable<TEntity>(
Expression<Func<TEntity, bool>> filter = null,
string includeProperties = null
)
where TEntity : class, IEntity
{
IQueryable<TEntity> query = context.Set<TEntity>();
if (filter != null)
{
query = query.Where(filter);
}
if (includeProperties != null)
{
query = query.Include(includeProperties);
}
return query;
}
如果我执行以下查询,则会成功填充roles属性:
return GetQueryable<ApplicationUser>(e => e.Id == id, "Roles").SingleOrDefault();
但是当我使用跟随Dto的投影(选择)时:
public class ApplicationUserDto: BaseDto
{
public string Email { get; set; }
public string Name { get; set; }
public List<IdentityUserRole<string>> Roles{ get; set; }
public static Expression<Func<ApplicationUser, ApplicationUserDto>> SelectProperties = (user) => new ApplicationUserDto {
Id = user.Id,
Email = user.Email,
Name = user.Name,
Roles = (List<IdentityUserRole<string>>)user.Roles
};
}
然后以下查询崩溃:
return GetQueryable<ApplicationUser>(e => e.Id == id, "Roles").Select(ApplicationUserDto.SelectProperties).SingleOrDefault();
出现以下错误:
System.InvalidOperationException: The type of navigation property 'Roles' on the entity type 'IdentityUser<string, IdentityUserClaim<string>, IdentityUserRole
<string>, IdentityUserLogin<string>>' is 'ICollection<IdentityUserRole<string>>' for which it was not possible to create a concrete instance. Either initialize the
property before use, add a public parameterless constructor to the type, or use a type which can be assigned a HashSet<> or List<>.
它还会记录警告:
warn: Microsoft.EntityFrameworkCore.Query.RelationalQueryCompilationContextFactory[6]
The Include operation for navigation: 'user.Roles' was ignored because the target navigation is not reachable in the final query results.
答案 0 :(得分:1)
您需要实现(执行子查询,它将显式收集角色),以便将其分配给您的DTO:
public class ApplicationUserDto: BaseDto
{
public string Email { get; set; }
public string Name { get; set; }
public List<IdentityUserRole<string>> Roles{ get; set; }
public static Expression<Func<ApplicationUser, ApplicationUserDto>> SelectProperties = (user) => new ApplicationUserDto {
Id = user.Id,
Email = user.Email,
Name = user.Name,
Roles = user.Roles.ToList()
};
}
请记得添加:
using System.Linq;
到您的文件,以便能够在.ToList()
上致电ICollection
。
正如你在评论中所说,你想要的类型是一个字符串,所以你可以在user.Roles
之后做任何事情,即进行如下的进一步投影:
user.Roles.Select(role=> role.RoleId).ToList()
请记住事后实现结果。
答案 1 :(得分:0)
实际上,如果我们使用Include
,则会忽略EF Core 1.1 Select()
,请参阅以下内容:
忽略包含部分: https://docs.microsoft.com/en-us/ef/core/querying/related-data
这就是它显示警告的原因:
warn: Microsoft.EntityFrameworkCore.Query.RelationalQueryCompilationContextFactory[6]
The Include operation for navigation: 'user.Roles' was ignored because the target navigation is not reachable in the final query results.
和ToList()
也无效。因此,导航属性需要另一个投影才能工作。
public static Expression<Func<ApplicationRole, ApplicationRoleDto>> SelectProperties = (role) => new ApplicationRoleDto
{
Id = role.Id,
Name = role.Name.Substring(0, role.Name.Length - role.TenantId.Length),
Description = role.Description,
// another projection on Claims navigation property
Claims = role.Claims.Select(claim => claim.ClaimValue).ToList(),
};
注意:在这种情况下,它们也是性能问题,因为如果我们在列表上使用Select(),则不会发生急切加载(因为忽略了Include())它会生成一个单独的sql查询以获取结果集中每个元素的导航属性。
另一方面,如果我们仅使用Include()
,它会按预期进行连接,因此效果会更好。