コード例 #1
0
        public void ShouldDisplayCorrect_CurrentIncludePath_WhenRemovingNavigation()
        {
            var context = new EagerLoadContext(Mock.Of <DbContext>(), Mock.Of <IIncludeStrategy>());

            var bookNavigationMock = new Mock <INavigation>();

            bookNavigationMock.Setup(nav => nav.Name).Returns(nameof(Book));
            var authorNavigationMock = new Mock <INavigation>();

            authorNavigationMock.Setup(nav => nav.Name).Returns(nameof(Author));
            var publisherNavigationMock = new Mock <INavigation>();

            publisherNavigationMock.Setup(nav => nav.Name).Returns(nameof(Publisher));

            context.SetCurrentNavigation(bookNavigationMock.Object);
            context.SetCurrentNavigation(authorNavigationMock.Object);
            context.SetCurrentNavigation(publisherNavigationMock.Object);

            Assert.Equal($"{nameof(Book)}.{nameof(Author)}.{nameof(Publisher)}", context.CurrentIncludePath);

            context.RemoveCurrentNavigation();
            Assert.Equal($"{nameof(Book)}.{nameof(Author)}", context.CurrentIncludePath);

            context.RemoveCurrentNavigation();
            Assert.Equal($"{nameof(Book)}", context.CurrentIncludePath);

            context.RemoveCurrentNavigation();
            Assert.Equal(string.Empty, context.CurrentIncludePath);

            context.RemoveCurrentNavigation();
            Assert.Equal(string.Empty, context.CurrentIncludePath);
        }
コード例 #2
0
        public void WhenUsingCachedResult_TheCacheShouldBeSetAndUsedOnFurtherCalls()
        {
            var expectedIncludes = new List <string>()
            {
                "Test", "Cache"
            };

            var includeFinderMock = new Mock <IncludeFinder>();

            includeFinderMock.Setup(finder => finder.BuildIncludePathsForRootType(It.IsAny <EagerLoadContext>()))
            .Returns(expectedIncludes);

            var queryIncluderMock = new Mock <QueryIncluder>();

            var engine = new IncludeEngine(includeFinderMock.Object, queryIncluderMock.Object);

            var query   = new Book[0].AsQueryable();
            var context = new EagerLoadContext(Mock.Of <DbContext>(), Mock.Of <IIncludeStrategy>());

            engine.RunIncludesForType(query, context);
            engine.RunIncludesForType(query, context);

            Assert.Single(IncludeEngine.CachedIncludePaths);

            includeFinderMock.Verify(finder => finder.BuildIncludePathsForRootType(It.IsAny <EagerLoadContext>()), Times.Once);
        }
コード例 #3
0
        public void ShouldNotEagerLoad_WhenNavigationIsNotAllowedOnTheRootEntity()
        {
            var attribute = new EagerLoadAttribute(notOnRoot: true);

            var navigationHelperMock = new Mock <NavigationHelper>();

            navigationHelperMock.Setup(helper => helper.GetTypeForNavigation(It.IsAny <INavigation>()))
            .Returns(typeof(Book));

            var strategy = new EagerLoadAttributeIncludeStrategy(navigationHelperMock.Object);
            var context  = new EagerLoadContext(Mock.Of <DbContext>(), strategy);

            var firstNavigationMock = SetupGenericNavigationMock();

            EagerLoadAttributeIncludeStrategy.EagerLoadAttributeCache.TryAdd(firstNavigationMock.Object, attribute);

            context.SetCurrentNavigation(firstNavigationMock.Object);
            Assert.False(strategy.ShouldIncludeCurrentNavigation(context));

            var secondNavigationMock = SetupGenericNavigationMock();

            EagerLoadAttributeIncludeStrategy.EagerLoadAttributeCache.TryAdd(secondNavigationMock.Object, new EagerLoadAttribute());

            context.SetCurrentNavigation(secondNavigationMock.Object);

            context.SetCurrentNavigation(firstNavigationMock.Object);
            Assert.True(strategy.ShouldIncludeCurrentNavigation(context));
        }
コード例 #4
0
        public void WhenUsingReadOnlyCachedResult_TheCacheShouldBeNotBeSet()
        {
            var expectedIncludes = new List <string>()
            {
                "Test", "Cache"
            };

            var includeFinderMock = new Mock <IncludeFinder>();

            includeFinderMock.Setup(finder => finder.BuildIncludePathsForRootType(It.IsAny <EagerLoadContext>()))
            .Returns(expectedIncludes);

            var queryIncluderMock = new Mock <QueryIncluder>();

            var engine = new IncludeEngine(includeFinderMock.Object, queryIncluderMock.Object);

            var query   = new Book[0].AsQueryable();
            var context = new EagerLoadContext(Mock.Of <DbContext>(), Mock.Of <IIncludeStrategy>(),
                                               includeExecution: IncludeExecution.ReadOnlyCache);

            engine.RunIncludesForType(query, context);

            Assert.Empty(IncludeEngine.CachedIncludePaths);

            Assert.Equal(expectedIncludes, context.IncludePathsToInclude);
        }
コード例 #5
0
        public static IQueryable <TEntity> EagerLoadWithStrategy <TEntity>(this IQueryable <TEntity> query, DbContext dbContext, IIncludeStrategy includeStrategy,
                                                                           IncludeExecution includeExecution, params string[] navigationPropertiesToIgnore) where TEntity : class
        {
            var context = new EagerLoadContext(dbContext, includeStrategy, navigationPropertiesToIgnore, includeExecution, typeof(TEntity));

            return(query.EagerLoadWithContext(context));
        }
コード例 #6
0
        public void ExecuteBeforeInclude_WillLogTheIncludePaths_IfaLoggerCanBeObtained()
        {
            var context = new EagerLoadContext(Mock.Of <DbContext>(), Mock.Of <IIncludeStrategy>(),
                                               rootType: typeof(IncludeStrategyTests));

            PathsFound.ForEach(i => context.IncludePathsToInclude.Add(i));

            var strategyMock = new Mock <IncludeStrategy>();

            strategyMock.Setup(s => s.ExecuteBeforeInclude(context)).CallBase();
            var strategy = strategyMock.Object;

            var loggerMock = new Mock <ILogger>();

            var serviceProviderMock = new Mock <IServiceProvider>();

            serviceProviderMock.Setup(sp => sp.GetService(typeof(ILogger <IncludeStrategy>))).Returns(null);
            serviceProviderMock.Setup(sp => sp.GetService(typeof(ILogger))).Returns(loggerMock.Object);
            context.ServiceProvider = serviceProviderMock.Object;

            strategy.ExecuteBeforeInclude(context);

            serviceProviderMock.VerifyAll();
            //loggerMock.Verify(log => log.Log(It.IsAny<LogLevel>(), It.IsAny<EventId>(), It.IsAny<FormattedLogValues>(),
            //                                It.IsAny<Exception>(), It.IsAny<Func<string, Exception, string>>()));
        }
コード例 #7
0
        public void WhenUsingReCachedResult_TheCacheShouldBeReSet()
        {
            var expectedIncludes = new List <string>()
            {
                "Test", "Cache"
            };
            var notExpectedIncludes = new List <string>()
            {
                "Test"
            };

            IncludeEngine.CachedIncludePaths.TryAdd(typeof(Book), notExpectedIncludes);

            var includeFinderMock = new Mock <IncludeFinder>();

            includeFinderMock.Setup(finder => finder.BuildIncludePathsForRootType(It.IsAny <EagerLoadContext>()))
            .Returns(expectedIncludes);

            var queryIncluderMock = new Mock <QueryIncluder>();

            var engine = new IncludeEngine(includeFinderMock.Object, queryIncluderMock.Object);

            var query   = new Book[0].AsQueryable();
            var context = new EagerLoadContext(Mock.Of <DbContext>(), Mock.Of <IIncludeStrategy>(),
                                               includeExecution: IncludeExecution.Recache);

            engine.RunIncludesForType(query, context);

            Assert.Equal(expectedIncludes, context.IncludePathsToInclude);
            var cachedIncludes = IncludeEngine.CachedIncludePaths.GetOrAdd(typeof(Book), t => null);

            Assert.Equal(expectedIncludes, cachedIncludes);

            includeFinderMock.Verify(finder => finder.BuildIncludePathsForRootType(It.IsAny <EagerLoadContext>()), Times.Once);
        }
コード例 #8
0
        private static EagerLoadAttribute GetAttributeForNavigation(EagerLoadContext context)
        {
            var attribute = EagerLoadAttributeCache.GetOrAdd(context.CurrentNavigation, nav =>
                                                             Attribute.GetCustomAttributes(nav.PropertyInfo, typeof(EagerLoadAttribute))
                                                             .OfType <EagerLoadAttribute>().FirstOrDefault());

            return(attribute);
        }
コード例 #9
0
        internal INavigation[] GetNavigationsForType(EagerLoadContext context, Type type)
        {
            return(CachedTypeNavigations.ContainsKey(type) ? CachedTypeNavigations[type] :
                   CachedTypeNavigations.GetOrAdd(type, GetNavigationsForType));

            INavigation[] GetNavigationsForType(Type typeToFind)
            {
                return(context.DbContext.Model.FindEntityType(typeToFind).GetNavigations().ToArray());
            }
        }
コード例 #10
0
        public void ShouldNotEagerLoad_WhenAttributeDoesNotExistOnNavigation()
        {
            var strategy = new EagerLoadAttributeIncludeStrategy();
            var context  = new EagerLoadContext(Mock.Of <DbContext>(), strategy);

            var navigationMock = SetupGenericNavigationMock();

            context.SetCurrentNavigation(navigationMock.Object);

            Assert.False(strategy.ShouldIncludeCurrentNavigation(context));
        }
コード例 #11
0
        public void ShouldEagerLoad_ForAllOffRoot()
        {
            var strategy       = new AllNavigationsIncludeStrategy();
            var context        = new EagerLoadContext(new Mock <DbContext>().Object, strategy);
            var navigationMock = new Mock <INavigation>();

            navigationMock.Setup(nav => nav.Name).Returns(nameof(Book));
            context.SetCurrentNavigation(navigationMock.Object);

            Assert.True(strategy.ShouldIncludeCurrentNavigation(context));
        }
コード例 #12
0
        public void GlobalSetup()
        {
            _testDbContext = new TestDbContext(new DbContextOptionsBuilder <TestDbContext>()
                                               .UseInMemoryDatabase(Guid.NewGuid().ToString()).Options);

            _strategy = new EagerLoadAttributeIncludeStrategy();

            _context = new EagerLoadContext(_testDbContext, _strategy, new string[0], IncludeExecution.NoCache, typeof(Book));

            _includeFinder = new Engine.IncludeFinder();
        }
コード例 #13
0
        public void ShouldNotEagerLoad_WhenNavigationIsNotValid()
        {
            var strategy = new EagerLoadAttributeIncludeStrategy();
            var context  = new EagerLoadContext(Mock.Of <DbContext>(), strategy);

            var navigationMock = new Mock <INavigation>();

            navigationMock.Setup(nav => nav.Name).Returns(nameof(Book));
            context.SetCurrentNavigation(navigationMock.Object);

            Assert.False(strategy.ShouldIncludeCurrentNavigation(context));
        }
コード例 #14
0
        public void ShouldDoNothing_WhenNavigationAddedIsNull()
        {
            var context = new EagerLoadContext(Mock.Of <DbContext>(), Mock.Of <IIncludeStrategy>());

            Assert.Equal(string.Empty, context.CurrentIncludePath);
            Assert.Null(context.CurrentNavigation);

            context.SetCurrentNavigation(null);

            Assert.Equal(string.Empty, context.CurrentIncludePath);
            Assert.Null(context.CurrentNavigation);
        }
コード例 #15
0
        public void ShouldDisplayCorrect_CurrentIncludePath_WhenAddingFirstNavigation()
        {
            var context = new EagerLoadContext(Mock.Of <DbContext>(), Mock.Of <IIncludeStrategy>());

            var navigationMock = new Mock <INavigation>();

            navigationMock.Setup(nav => nav.Name).Returns(nameof(Book));

            context.SetCurrentNavigation(navigationMock.Object);

            Assert.Equal(nameof(Book), context.CurrentIncludePath);
        }
コード例 #16
0
        public void ShouldReturnTheCorrectNavigations_ForMappedTypes()
        {
            var context = new EagerLoadContext(TestDbContext.Instance, new Mock <IIncludeStrategy>().Object);

            var expectedNavigations = TestDbContext.Instance.Model.FindEntityType(typeof(Book)).GetNavigations().ToList();

            var navigationFinder = new NavigationFinder();

            var actualNavigations = navigationFinder.GetNavigationsForType(context, typeof(Book)).ToList();

            Assert.Equal(expectedNavigations.Count, actualNavigations.Count);
            expectedNavigations.ForEach(nav => { Assert.Contains(nav, actualNavigations); });
        }
コード例 #17
0
        public void ShouldReturnOriginalQuery_IfNoIncludePathsGetFound()
        {
            var includeFinderMock = new Mock <IncludeFinder>();
            var queryIncluderMock = new Mock <QueryIncluder>();

            var engine = new IncludeEngine(includeFinderMock.Object, queryIncluderMock.Object);

            var query   = new Book[0].AsQueryable();
            var context = new EagerLoadContext(Mock.Of <DbContext>(), Mock.Of <IIncludeStrategy>());

            Assert.Equal(query, engine.RunIncludesForType(query, context));

            queryIncluderMock.VerifyNoOtherCalls();
        }
コード例 #18
0
        public void ShouldSetRootType_IfNoRootTypeOnTheContext()
        {
            var includeFinderMock = new Mock <IncludeFinder>();
            var queryIncluderMock = new Mock <QueryIncluder>();

            var engine = new IncludeEngine(includeFinderMock.Object, queryIncluderMock.Object);

            var query   = new Book[0].AsQueryable();
            var context = new EagerLoadContext(Mock.Of <DbContext>(), Mock.Of <IIncludeStrategy>());

            engine.RunIncludesForType(query, context);

            Assert.Equal(typeof(Book), context.RootType);
        }
コード例 #19
0
        public void ShouldNotEagerLoad_IfIncludePathToConsiderShouldBeIgnored()
        {
            var strategy = new AllNavigationsIncludeStrategy();
            var context  = new EagerLoadContext(new Mock <DbContext>().Object, strategy);

            context.IncludePathsToIgnore.Add(nameof(INavigation));
            //context.ParentIncludePath = "";
            var navigationMock = new Mock <INavigation>();

            navigationMock.Setup(nav => nav.Name).Returns(nameof(INavigation));
            context.SetCurrentNavigation(navigationMock.Object);

            Assert.False(strategy.ShouldIncludeCurrentNavigation(context));
        }
コード例 #20
0
ファイル: IncludeStrategy.cs プロジェクト: jsaret/EfEagerLoad
        public virtual void ExecuteBeforeInclude(EagerLoadContext context)
        {
            var loggerInject = context.ServiceProvider?.GetService(typeof(ILogger <IncludeStrategy>)) ??
                               context.ServiceProvider?.GetService(typeof(ILogger));

            if (!(loggerInject is ILogger logger))
            {
                return;
            }

            var includePaths = JsonSerializer.Serialize(context.IncludePathsToInclude);

            logger.LogInformation($"{IncludeLogMessage}{context.RootType.Name}: {includePaths}");
        }
コード例 #21
0
        public void ShouldNotEagerLoad_WhenAttributeSetToNever()
        {
            var strategy = new EagerLoadAttributeIncludeStrategy();
            var context  = new EagerLoadContext(Mock.Of <DbContext>(), strategy);

            var navigationMock = SetupGenericNavigationMock();
            var navigation     = navigationMock.Object;

            context.SetCurrentNavigation(navigation);
            var attribute = new EagerLoadAttribute(never: true);

            EagerLoadAttributeIncludeStrategy.EagerLoadAttributeCache.TryAdd(navigation, attribute);

            Assert.False(strategy.ShouldIncludeCurrentNavigation(context));
        }
コード例 #22
0
        public void FilterIncludePathsBeforeInclude_RemovesPathsThatMatchTheExcludeList()
        {
            var pathsToFilter = new[] { "Book.Author" };
            var context       = new EagerLoadContext(Mock.Of <DbContext>(), Mock.Of <IIncludeStrategy>(), pathsToFilter);

            PathsFound.ForEach(i => context.IncludePathsToInclude.Add(i));
            var strategyMock = new Mock <IncludeStrategy>();

            strategyMock.Setup(s => s.FilterIncludePathsBeforeInclude(context)).CallBase();
            var strategy = strategyMock.Object;

            strategy.FilterIncludePathsBeforeInclude(context);

            Assert.Equal(6, context.IncludePathsToInclude.Count);
        }
コード例 #23
0
ファイル: Program.cs プロジェクト: jsaret/EfEagerLoad
        public static async Task Run()
        {
            var serviceProvider = await SetupServices();

            var testRunner = serviceProvider.GetRequiredService <TestRunner>();

            EagerLoadContext.InitializeServiceProvider(serviceProvider);
            await testRunner.RunTest1();

            //await testRunner.RunTest2();
            //await testRunner.RunTest3();
            //await testRunner.RunTest4();

            //await testRunner.RunTest5();
            //await testRunner.RunTest6();
        }
コード例 #24
0
        public void ShouldReturnOriginalQuery_WithNoExecution_WhenIncludeExecutionIsSkip()
        {
            var includeFinderMock = new Mock <IncludeFinder>();
            var queryIncluderMock = new Mock <QueryIncluder>();

            var engine = new IncludeEngine(includeFinderMock.Object, queryIncluderMock.Object);

            var query   = new Book[0].AsQueryable();
            var context = new EagerLoadContext(Mock.Of <DbContext>(), Mock.Of <IIncludeStrategy>(), includeExecution: IncludeExecution.Skip);


            Assert.Equal(query, engine.RunIncludesForType(query, context));

            includeFinderMock.VerifyNoOtherCalls();
            queryIncluderMock.VerifyNoOtherCalls();
        }
コード例 #25
0
        public void ShouldEagerLoad_WhenAttributeExist()
        {
            var strategy       = new AttributeExistsIncludeStrategy <EagerLoadAttribute>();
            var context        = new EagerLoadContext(new Mock <DbContext>().Object, strategy);
            var navigationMock = new Mock <INavigation>();

            navigationMock.Setup(nav => nav.Name).Returns(nameof(Book));

            context.SetCurrentNavigation(navigationMock.Object);

            navigationMock.Setup(nav => nav.PropertyInfo)
            .Returns(GetType().GetProperty(nameof(HasEagerLoadAttribute)));
            Assert.True(strategy.ShouldIncludeCurrentNavigation(context));

            navigationMock.Setup(nav => nav.PropertyInfo)
            .Returns(GetType().GetProperty(nameof(HasManyIncludingEagerLoadAttribute)));
            Assert.True(strategy.ShouldIncludeCurrentNavigation(context));
        }
コード例 #26
0
        public void ShouldUseSuppliedPathPredicate_ToTestIfTheNavigationShouldBeIncluded()
        {
            var pathChecked = string.Empty;
            var strategy    = new PredicateIncludeStrategy(path =>
            {
                pathChecked = path;
                return(true);
            });

            var context        = new EagerLoadContext(Mock.Of <DbContext>(), strategy);
            var navigationMock = new Mock <INavigation>();

            navigationMock.Setup(nav => nav.Name).Returns(nameof(Book));

            context.SetCurrentNavigation(navigationMock.Object);

            Assert.True(strategy.ShouldIncludeCurrentNavigation(context));

            Assert.Equal(nameof(Book), pathChecked);
        }
コード例 #27
0
        private void SetupPreFilteredIncludePaths(EagerLoadContext context)
        {
            switch (context.IncludeExecution)
            {
            case IncludeExecution.Cached:
            {
                context.IncludePathsToInclude = CachedIncludePaths.GetOrAdd(context.RootType, (type) =>
                                                                            _includeFinder.BuildIncludePathsForRootType(context)?.ToList() ?? new List <string>(0));
                break;
            }

            case IncludeExecution.ReadOnlyCache:
            {
                context.IncludePathsToInclude = CachedIncludePaths.ContainsKey(context.RootType) ?
                                                CachedIncludePaths.GetValueOrDefault(context.RootType) :
                                                _includeFinder.BuildIncludePathsForRootType(context);
                break;
            }

            case IncludeExecution.NoCache:
            {
                context.IncludePathsToInclude = _includeFinder.BuildIncludePathsForRootType(context);
                break;
            }

            case IncludeExecution.Recache:
            {
                context.IncludePathsToInclude = _includeFinder.BuildIncludePathsForRootType(context);
                CachedIncludePaths.AddOrUpdate(context.RootType,
                                               (t) => context.IncludePathsToInclude?.ToList() ?? new List <string>(0),
                                               (t, l) => context.IncludePathsToInclude?.ToList() ?? new List <string>(0));
                break;
            }

            default: throw new ArgumentOutOfRangeException();
            }

            context.IncludePathsToInclude = (context.IncludePathsToInclude == null) ?
                                            new List <string>(0) :
                                            new List <string>(context.IncludePathsToInclude);
        }
コード例 #28
0
ファイル: IncludeStrategy.cs プロジェクト: jsaret/EfEagerLoad
        public virtual void FilterIncludePathsBeforeInclude(EagerLoadContext context)
        {
            //var
            foreach (var pathToExclude in context.IncludePathsToIgnore)
            {
                for (var i = context.IncludePathsToInclude.Count - 1; i >= 0; i--)
                {
                    if (context.IncludePathsToInclude[i].StartsWith(pathToExclude, StringComparison.InvariantCultureIgnoreCase))
                    {
                        context.IncludePathsToInclude.Remove(context.IncludePathsToInclude[i]);
                    }
                }
            }

            //foreach (var navigationPath in context.IncludePathsToInclude.ToArray())
            //{
            //    if (context.IncludePathsToIgnore.Any(nav => navigationPath.StartsWith(nav)))
            //    {
            //        context.IncludePathsToInclude.Remove(navigationPath);
            //    }
            //}
        }
コード例 #29
0
ファイル: IncludeFinder.cs プロジェクト: jsaret/EfEagerLoad
        private void BuildIncludesForEagerLoadContext(EagerLoadContext context)
        {
            BuildIncludesForEagerLoadContext();

            void BuildIncludesForEagerLoadContext()
            {
                var navigationsToConsider = _navigationFinder.GetNavigationsForType(context, context.CurrentType ?? context.RootType);

                foreach (var navigation in navigationsToConsider)
                {
                    context.SetCurrentNavigation(navigation);

                    if (context.IncludeStrategy.ShouldIncludeCurrentNavigation(context))
                    {
                        context.IncludePathsToInclude.Add(context.CurrentIncludePath);
                        BuildIncludesForEagerLoadContext();
                    }

                    context.RemoveCurrentNavigation();
                }
            }
        }
コード例 #30
0
        public void ShouldSetFinderResults_IntoTheContext()
        {
            var expectedIncludes = new List <string>()
            {
                "Test", "Cache"
            };

            var includeFinderMock = new Mock <IncludeFinder>();

            includeFinderMock.Setup(finder => finder.BuildIncludePathsForRootType(It.IsAny <EagerLoadContext>()))
            .Returns(expectedIncludes);

            var queryIncluderMock = new Mock <QueryIncluder>();

            var engine = new IncludeEngine(includeFinderMock.Object, queryIncluderMock.Object);

            var query   = new Book[0].AsQueryable();
            var context = new EagerLoadContext(Mock.Of <DbContext>(), Mock.Of <IIncludeStrategy>());

            engine.RunIncludesForType(query, context);

            Assert.Equal(expectedIncludes, context.IncludePathsToInclude);
        }