Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do Multiple ThenInclude navigation props after One Include

For a TestType I wanted to include both navigation props Schoolclass and Subject.

I could do a:

.Include(t => t.TestType)
 .ThenInclude(x => x.Subject)

but not a:

.Include(t => t.TestType)
.ThenInclude(x => x.Subject)
.ThenInclude(x => x.Schoolclass)

Thus I tried a little trick and that worked:

I included the TestType 2 times...

var test = await context.Tests.Where(t => t.SchoolyearId == schoolyearId)
                                          .Include(t => t.TestType)
                                          .ThenInclude(x => x.Subject)
                                           .Include(t => t.TestType)
                                          .ThenInclude(x => x.Schoolclass)
                                           .AsNoTracking()
                                          .ToListAsync();

Is that the official approach or is there a better one?

UPDATE

   public class TestType
    {
        public TestType()
        {
            Tests = new HashSet<Test>();
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public int Weight { get; set; }
        public ISet<Test> Tests { get; set; }
        public Schoolyear Schoolyear { get; set; }  
        public Schoolclass Schoolclass { get; set; }   
        public Subject Subject { get; set; }
        public int SchoolyearId { get; set; }
    }
like image 556
Pascal Avatar asked Dec 03 '16 23:12

Pascal


People also ask

What are the additional functions available on the navigation prop?

There are several additional functions present on navigation prop based on the kind of the current navigator. If the navigator is a stack navigator, several alternatives to navigate and goBack are provided and you can use whichever you prefer. The functions are: If the navigator is a tab navigator, the following are also available:

How to include additional types in a list of navigation properties?

If you wish to include additional types based on the navigation properties of the type being included, then chain a call to ThenInclude<TEntity,TPreviousProperty,TProperty> (IIncludableQueryable<TEntity,IEnumerable<TPreviousProperty>>, Expression<Func<TPreviousProperty,TProperty>>) after this call.

How to get the navigation prop passed from the parent navigator?

This method returns the navigation prop from the parent navigator that the current navigator is nested in. For example, if you have a stack navigator and a tab navigator nested inside the stack, then you can use getParent inside a screen of the tab navigator to get the navigation prop passed from the stack navigator.

How do I get the navigation prop for each screen component?

Each screen component in your app is provided with the navigation prop automatically. It looks like this: isFocused - function that returns true if the screen is focused and false otherwise. It's important to highlight the navigation prop is not passed in to all components; only screen components receive this prop automatically!


Video Answer


3 Answers

the best way is that you write before, With two .Include(t => t.TestType)

var test = await context.Tests.Where(t => t.SchoolyearId == schoolyearId)
                                      .Include(t => t.TestType)
                                      .ThenInclude(x => x.Subject)
                                       .Include(t => t.TestType)
                                      .ThenInclude(x => x.Schoolclass)
                                       .AsNoTracking()
                                      .ToListAsync();

If you see the query result in SQL Profiler, the query is that you pretend, without repeating the include to TestType (only 1 join with that table)

You have another way to do it, but i prefer the before way!

.Include("TestType.Subject") 
.Include("TestType.Schoolclass")  
like image 64
Diego Avatar answered Nov 06 '22 07:11

Diego


If such type of includes is common in your codebase it may be useful to create a couple of extension methods like this:

        public static IQueryable<TEntity> IncludeMany<TEntity, TProperty>(
            [NotNull] this IQueryable<TEntity> source,
            [NotNull] Expression<Func<TEntity, TProperty>> navigationPropertyPath,
            [NotNull] params Expression<Func<TProperty, object>>[] nextProperties
            )
            where TEntity : class
        {
            foreach (var nextProperty in nextProperties)
            {
                source = source.Include(navigationPropertyPath)
                    .ThenInclude(nextProperty);
            }

            return source;
        }

        public static IQueryable<TEntity> IncludeMany<TEntity, TProperty>(
            [NotNull] this IQueryable<TEntity>source,
            [NotNull] Expression<Func<TEntity, IEnumerable<TProperty>>> navigationPropertyPath,
            [NotNull] params Expression<Func<TProperty, object>>[] nextProperties)
            where TEntity : class
        {
            foreach (var nextProperty in nextProperties)
            {
                source = source.Include(navigationPropertyPath)
                    .ThenInclude(nextProperty);
            }

            return source;
        }

With this, you'll be able to change your query to

var test = await context.Tests
    .Where(t => t.SchoolyearId == schoolyearId)
    .IncludeMany(t => t.TestType,
       x => x.Subject,
       x => x.Schoolclass
    )
    .AsNoTracking()
    .ToListAsync();

Although this does not support going more than 2 levels in depth.

like image 38
Aleksei Avatar answered Nov 06 '22 08:11

Aleksei


This approach is the only one that exists in 1.1.0 :)

Better one is little different - do not load all objects in one query (DB will join 4 tables - Tests, TestType, Schoolyear, Schoolclass), because this may cause unnecessary data growth (when you will have a lot or records in tests).

If some of your tables (Schoolyear? or even TestType?) contains relatively small amount of records (comparing to "main" Tests table) - you can load TestTypes.Include(x => x.Subject).Include(x => x.Schoolclass) first, save them in some list, and then query only Tests without extra "Include". But as soon as you already have TestType with all required dependencies in memory - they may be accessed from memory.

Moreover, if some of your tables are changing rarely - use Caching to read them from DB once and re-read when necessary (remove appropriate cache entry in controllers that manage this tables).

like image 35
Dmitry Avatar answered Nov 06 '22 07:11

Dmitry