c# - EF 核心导航属性未加载

标签 c# entity-framework-core navigation-properties

我正在修改我的应用程序,以便能够指定要加载到存储库中的导航属性。

模型:Team 和 TeamTunerUser 可以在 domain entites 中找到.

存储库:

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 导航属性从未加载,第二个断言 block 失败。

团队在这里配置(class):

    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修改源 Select 的方法( WhereOrderByIQueryable<> 等)并返回修改后的IQueryable<> .

在这里你只是忘记了使用结果查询,所以

queryable.Include(propertyName);

效果与

相同
queryable.Where(e => false);

没有效果

只需将代码更改为

queryable = queryable.Include(propertyName);

关于c# - EF 核心导航属性未加载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55634836/

相关文章:

c# - xamarin 与 Microsoft.EntityFrameworkCore.Sqlite 冲突

c# - 如何使用 Entity Framework Core 配置其他用户定义的数据类型?

c# - ef 核心忽略导航属性

c# - 使用 Include(string) 方法包含多个导航属性

c# - EF Core 6 中 entityEntry.Metadata.DefiningNavigationName() 的等效项是什么?

entity-framework - IValidatableObject 的 Validate 方法期间出现延迟加载问题

c# - C# 中的 Allow Nulls 列中不插入空值

c# - WebClient.DownloadString 给出第一次机会异常

c# - mongodb c#如何使用BSON文档

C# 项目与 Oracle.DataAccess.dll (v2) 在 Win 7 与 ORA-12154 错误