EF Core - 無法訪問Select()中的導航屬性

c# entity-framework-core

我試圖訪問IdentityUser模型的導航角色屬性。

在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. 

一般承認的答案

實際上,如果我們使用Select() ,在EF Core 1.1 Include被忽略,請參考以下內容:

忽略包括部分: 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() ,它會按預期進行連接,因此性能更好。


熱門答案

您需要實現(執行子查詢,它將顯式收集角色),以便將其分配給您的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;到你的文件,以便能夠在ICollection上調用.ToList()

正如你在評論中所說,你想要的類型是一個字符串,所以你可以在user.Roles之後做任何事情,即執行如下的進一步投影:

user.Roles.Select(role=> role.RoleId).ToList()

請記住事後實現結果。



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow