簡體   English   中英

EF核心導航屬性未加載

[英]EF core navigation property not loading

我正在修改我的應用程序,以便能夠指定要在存儲庫中加載的導航屬性。

模型:Team和TeamTunerUser可以在實體中找到。

倉庫:

namespace Sppd.TeamTuner.Infrastructure.DataAccess.EF.Repositories
{
    internal class Repository<TEntity> : IRepository<TEntity>
        where TEntity : BaseEntity
    {
        /// <summary>
        ///     Gets the entity set.
        /// </summary>
        protected DbSet<TEntity> Set => Context.Set<TEntity>();

        /// <summary>
        ///     Gets the DB context.
        /// </summary>
        protected TeamTunerContext Context { get; }

        public Repository(TeamTunerContext context)
        {
            Context = context;
        }

        public async Task<TEntity> GetAsync(Guid entityId, IEnumerable<string> includeProperties = null)
        {
            TEntity entity;
            try
            {
                entity = await GetQueryableWithIncludes(includeProperties).SingleAsync(e => e.Id == entityId);
            }
            catch (InvalidOperationException)
            {
                throw new EntityNotFoundException(typeof(TEntity), entityId.ToString());
            }

            return entity;
        }

        protected IQueryable<TEntity> GetQueryableWithIncludes(IEnumerable<string> includeProperties = null)
        {
            var queryable = Set;

            if (includeProperties == null)
            {
                return queryable;
            }

            foreach (var propertyName in includeProperties)
            {
                queryable.Include(propertyName);
            }

            return queryable;
        }
    }
}

測試:

    [Fact]
    public async Task TestNavigationPropertyLoading()
    {
        // Arrange
        var teamId = Guid.Parse(TestingConstants.Team.HOLY_COW);

        // Act
        Team createdTeamWithoutUsers;
        Team createdTeamWithUsers;
        using (var scope = ServiceProvider.CreateScope())
        {
            var teamRepository = scope.ServiceProvider.GetService<IRepository<Team>>();

            createdTeamWithoutUsers = await teamRepository.GetAsync(teamId);
            createdTeamWithUsers = await teamRepository.GetAsync(teamId, new[] {nameof(Team.Users)});
        }

        // Assert
        Assert.Null(createdTeamWithoutUsers.Leader);
        Assert.False(createdTeamWithoutUsers.Users.Any());
        Assert.False(createdTeamWithUsers.CoLeaders.Any());

        Assert.NotNull(createdTeamWithUsers.Leader);
        Assert.True(createdTeamWithUsers.Users.Any());
        Assert.True(createdTeamWithUsers.CoLeaders.Any());
    }

我的問題是, Users導航屬性永遠不會加載,並且第二個斷言塊會失敗。

團隊在此處配置( ):

    private static void ConfigureTeam(EntityTypeBuilder<Team> builder)
    {
        ConfigureDescriptiveEntity(builder);

        builder.HasMany(e => e.Users)
               .WithOne(e => e.Team);

        // Ignore calculated properties
        builder.Ignore(e => e.Members)
               .Ignore(e => e.Leader)
               .Ignore(e => e.CoLeaders);
    }

(調試)日志不包含任何有用的內容,除了我發現加載導航屬性所需的聯接未在SQL級別上執行:

2019-04-11 16:02:43,896 [14] DEBUG Microsoft.EntityFrameworkCore.Database.Connection - Opening connection to database 'Sppd.TeamTuner-TEST' on server '.\SQLEXPRESS'.
2019-04-11 16:02:43,901 [14] DEBUG Microsoft.EntityFrameworkCore.Database.Connection - Opened connection to database 'Sppd.TeamTuner-TEST' on server '.\SQLEXPRESS'.
2019-04-11 16:02:43,903 [14] DEBUG Microsoft.EntityFrameworkCore.Database.Command - Executing DbCommand [Parameters=[@__entityId_0='?' (DbType = Guid)], CommandType='Text', CommandTimeout='30']
SELECT TOP(2) [m].[Id], [m].[Avatar], [m].[CreatedById], [m].[CreatedOnUtc], [m].[DeletedById], [m].[DeletedOnUtc], [m].[Description], [m].[FederationId], [m].[IsDeleted], [m].[ModifiedById], [m].[ModifiedOnUtc], [m].[Name]
FROM [Team] AS [m]
WHERE ([m].[IsDeleted] = 0) AND ([m].[Id] = @__entityId_0)
2019-04-11 16:02:43,920 [12] INFO  Microsoft.EntityFrameworkCore.Database.Command - Executed DbCommand (16ms) [Parameters=[@__entityId_0='?' (DbType = Guid)], CommandType='Text', CommandTimeout='30']
SELECT TOP(2) [m].[Id], [m].[Avatar], [m].[CreatedById], [m].[CreatedOnUtc], [m].[DeletedById], [m].[DeletedOnUtc], [m].[Description], [m].[FederationId], [m].[IsDeleted], [m].[ModifiedById], [m].[ModifiedOnUtc], [m].[Name]
FROM [Team] AS [m]
WHERE ([m].[IsDeleted] = 0) AND ([m].[Id] = @__entityId_0)
2019-04-11 16:02:43,945 [12] DEBUG Microsoft.EntityFrameworkCore.Database.Command - A data reader was disposed.
2019-04-11 16:02:43,985 [12] DEBUG Microsoft.EntityFrameworkCore.Database.Connection - Closing connection to database 'Sppd.TeamTuner-TEST' on server '.\SQLEXPRESS'.
2019-04-11 16:02:43,988 [12] DEBUG Microsoft.EntityFrameworkCore.Database.Connection - Closed connection to database 'Sppd.TeamTuner-TEST' on server '.\SQLEXPRESS'.
2019-04-11 16:02:45,054 [12] DEBUG Microsoft.EntityFrameworkCore.Database.Connection - Opening connection to database 'Sppd.TeamTuner-TEST' on server '.\SQLEXPRESS'.
2019-04-11 16:02:45,057 [12] DEBUG Microsoft.EntityFrameworkCore.Database.Connection - Opened connection to database 'Sppd.TeamTuner-TEST' on server '.\SQLEXPRESS'.
2019-04-11 16:02:45,060 [12] DEBUG Microsoft.EntityFrameworkCore.Database.Command - Executing DbCommand [Parameters=[@__entityId_0='?' (DbType = Guid)], CommandType='Text', CommandTimeout='30']
SELECT TOP(2) [m].[Id], [m].[Avatar], [m].[CreatedById], [m].[CreatedOnUtc], [m].[DeletedById], [m].[DeletedOnUtc], [m].[Description], [m].[FederationId], [m].[IsDeleted], [m].[ModifiedById], [m].[ModifiedOnUtc], [m].[Name]
FROM [Team] AS [m]
WHERE ([m].[IsDeleted] = 0) AND ([m].[Id] = @__entityId_0)
2019-04-11 16:02:45,067 [14] INFO  Microsoft.EntityFrameworkCore.Database.Command - Executed DbCommand (7ms) [Parameters=[@__entityId_0='?' (DbType = Guid)], CommandType='Text', CommandTimeout='30']
SELECT TOP(2) [m].[Id], [m].[Avatar], [m].[CreatedById], [m].[CreatedOnUtc], [m].[DeletedById], [m].[DeletedOnUtc], [m].[Description], [m].[FederationId], [m].[IsDeleted], [m].[ModifiedById], [m].[ModifiedOnUtc], [m].[Name]
FROM [Team] AS [m]
WHERE ([m].[IsDeleted] = 0) AND ([m].[Id] = @__entityId_0)
2019-04-11 16:02:45,092 [14] DEBUG Microsoft.EntityFrameworkCore.Database.Command - A data reader was disposed.
2019-04-11 16:02:45,143 [14] DEBUG Microsoft.EntityFrameworkCore.Database.Connection - Closing connection to database 'Sppd.TeamTuner-TEST' on server '.\SQLEXPRESS'.
2019-04-11 16:02:45,153 [14] DEBUG Microsoft.EntityFrameworkCore.Database.Connection - Closed connection to database 'Sppd.TeamTuner-TEST' on server '.\SQLEXPRESS'.

我嘗試過的

  • 不指定字符串,而是指定要加載的導航屬性的表達式:

     protected IQueryable<TEntity> GetQueryableWithIncludes(IEnumerable<string> includeProperties = null) { var queryable = Set; if (includeProperties == null) { return queryable; } if (typeof(TEntity) == typeof(Team)) { // TODO: Remove this block once it works by including by string properties foreach (var propertyName in includeProperties) { if (propertyName == "Users") { queryable.OfType<Team>().Include(entity => entity.Users); } } } else { foreach (var propertyName in includeProperties) { queryable.Include(propertyName); } } return queryable; } 
  • 也明確配置用戶實體的關系:

     private static void ConfigureTeamTunerUser(EntityTypeBuilder<TeamTunerUser> builder) { ConfigureDescriptiveEntity(builder); builder.HasMany(e => e.CardLevels) .WithOne(e => e.User); builder.HasOne(e => e.Team) .WithMany(e => e.Users); // Indexes and unique constraint builder.HasIndex(e => e.Name) .IsUnique(); builder.HasIndex(e => e.SppdName) .IsUnique(); builder.HasIndex(e => e.Email) .IsUnique(); } 

我想念什么?

Include / ThenInclude (以及所有其他EF Core Queryable擴展)類似於常規的LINQ Queryable方法( SelectWhereOrderBy等),這些方法修改了源IQueryable<>返回修改后的IQueryable<>

在這里,您只是忘了使用結果查詢,因此

queryable.Include(propertyName);

與...具有相同的效果

queryable.Where(e => false);

沒有效果

只需將代碼更改為

queryable = queryable.Include(propertyName);

我注意到了一些問題。

您的方法僅適用於加載導航屬性的第一級。

foreach (var propertyName in includeProperties)
{
    queryable.Include(propertyName);
} 

您必須使用.ThenInclude()來加載嵌套的導航屬性。 但這打破了您將IEnumerable<string> includeProperties = null作為構造函數的方法。

第二個問題是您的測試本身。 它僅檢查.Any() ,但是根據測試的名稱,這是錯誤的斷言。 (我們不知道測試是否失敗,因為導航屬性從未加載加載成功,但是Users為零。您應該僅檢查導航屬性是否已加載。類似以下內容。

DbContext.Entry(createdTeamWithUsers).Navigation("Users").IsLoaded

您是否嘗試過將您的媒體資源標記為虛擬的? 您需要它來根據文檔啟用延遲加載導航:

延遲加載

然后,EF Core將為所有可以覆蓋的導航屬性啟用延遲加載-也就是說,它必須是虛擬的並且在可以繼承的類上。 例如,在以下實體中,Post.Blog和Blog.Posts導航屬性將被延遲加載。

來源:加載相關數據

您可以在EF核心中啟用LazyLoading ...

https://docs.microsoft.com/zh-cn/ef/core/querying/related-data

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM