Beispiel #1
0
        public static SeedingPlan CreateFor(SeedBucketInfo seedBucketInfo, ISeedableFilter filter)
        {
            System.Diagnostics.Debug.Assert(!seedBucketInfo.HasAnyErrors);

            if (!seedBucketInfo.ContainedSeedables.Any(seedable => filter.Accepts(seedable)))
            {
                return(Empty);
            }

            IEnumerable <SeedableInfo> alwaysRequiredSeeds = seedBucketInfo.ContainedSeedables.OfType <SeedInfo>().Where(seed => seed.IsAlwaysRequired);
            var filteredSeedables = seedBucketInfo.ContainedSeedables.Where(seedable => filter.Accepts(seedable));

            // Always required seeds must be at the beginning so that we ensure that they are always created first.
            var seedablesToSeed = alwaysRequiredSeeds.Union(filteredSeedables);

            var seedingSteps = new List <SeedInfo>(seedBucketInfo.ContainedSeedables.OfType <SeedInfo>().Count());

            foreach (var seedableInfo in seedablesToSeed)
            {
                RecursivelyBuildSeedingSteps(seedableInfo);
            }

            return(new SeedingPlan(seedingSteps));

            void RecursivelyBuildSeedingSteps(SeedableInfo seedableInfo)
            {
                if (seedableInfo is SeedInfo seedInfo && seedingSteps.Contains(seedInfo))
                {
                    return;
                }

                foreach (var requiredSeedable in seedableInfo.RequiredSeedables)
                {
                    RecursivelyBuildSeedingSteps(requiredSeedable);
                }

                // We have to add the seed info to the seeding plan at the
                // tail of the recursion so that the required seedables get
                // executed first (end up on the front of the seeding plan).
                if (seedableInfo is SeedInfo)
                {
                    seedingSteps.Add((SeedInfo)seedableInfo);
                }
            }
        }
Beispiel #2
0
        private async Task <SeedingReport> Seed(SeedBucketInfo seedBucketInfo, SeedBucketStartup?seedBucketStartup, ISeedableFilter filter)
        {
            if (seedBucketInfo.HasAnyErrors)
            {
                return(SeedingReport.CreateForSeedBucketHasErrors(seedBucketInfo));
            }

            var seedingPlan = SeedingPlan.CreateFor(seedBucketInfo, filter);

            // TODO: Check if the seeding plan is empty and return appropriate report.
            //       In the report show the used filter. "... no seeds or scenarios that satisfied the given filter: ...". Add the FriendlyDescription property to the ISeedablesFilter.

            return(await SeedSeedingPlan());

            async Task <SeedingReport> SeedSeedingPlan()
            {
                var singleSeedSeedingResults = new List <SingleSeedSeedingReport>();

                IServiceCollection serviceCollection = new ServiceCollection(); // TODO: We should have more of them. Think about lifecycle of the engine services, services in the different stages in the execution, etc.

                serviceCollection.AddSingleton(outputSink);
                try
                {
                    // TODO: Add creation of SeedingStartup and registration of services.
                    if (seedBucketStartup != null)
                    {
                        outputSink.WriteVerboseMessage($"Configuring service collection");

                        seedBucketStartup.ConfigureServices(serviceCollection);

                        // TODO: Check that the configuration does not override IOutputSink. Only the engine can add it. It must be singleton and the same one we have here.

                        outputSink.WriteVerboseMessage($"Initializing seeding");

                        using (var serviceScope = serviceCollection.BuildServiceProvider().CreateScope())
                        {
                            seedBucketStartup.InitializeSeeding(serviceScope.ServiceProvider);
                        }
                    }

                    // TODO: Add finer granulation of errors and seeding result - CreatingSeedingStartupFailed.
                }
                catch (Exception exception)
                {
                    outputSink.WriteError(exception.ToString()); // TODO: Output sink for exceptions.

                    return(SeedingReport.CreateForBuildingServiceProviderFailed(seedBucketInfo, seedingPlan));
                }

                var serviceProvider = serviceCollection.BuildServiceProvider();

                SeedInfo?currentSeedInfo;
                int      seedingStepNumber;

                for (int i = 0; i < seedingPlan.SeedingSteps.Count; i++)
                {
                    currentSeedInfo   = seedingPlan.SeedingSteps[i];
                    seedingStepNumber = i + 1;
                    try
                    {
                        bool hasSeeded;
                        using (var serviceScope = serviceProvider.CreateScope())
                        {
                            hasSeeded = await SeedSingleSeed(seedingStepNumber, serviceScope.ServiceProvider, currentSeedInfo);
                        }

                        singleSeedSeedingResults.Add(new SingleSeedSeedingReport(hasSeeded ? SingleSeedSeedingStatus.Seeded : SingleSeedSeedingStatus.Skipped, currentSeedInfo));
                    }
                    catch (Exception exception)
                    {
                        outputSink.WriteError(exception.ToString()); // TODO: Output sink that supports exceptions.

                        // TODO: Add finer granulation of errors and seeding result - CreatingSeedFailed.
                        // TODO: Add seed contract violation.
                        singleSeedSeedingResults.Add(new SingleSeedSeedingReport(SingleSeedSeedingStatus.Failed, currentSeedInfo));
                        return(SeedingReport.CreateForSeedingSingleSeedFailed(seedBucketInfo, seedingPlan, singleSeedSeedingResults));
                    }
                }

                outputSink.WriteConfirmation($"Seeding completed");

                return(SeedingReport.CreateForSucceeded(seedBucketInfo, seedingPlan, singleSeedSeedingResults));

                // Returns true if Seed() is called or false if the seed HasAlreadyYielded().
                async Task <bool> SeedSingleSeed(int seedingStep, IServiceProvider serviceProvider, SeedInfo seedInfo)
                {
                    System.Diagnostics.Debug.Assert(seedingStep > 0);

                    outputSink.WriteVerboseMessage($"Creating seed {seedInfo.FriendlyName}");

                    // TODO: Check if seedInfo.Type exists. Where to check that? Seeding should work only if Type exists. Where to add those checks?
                    var seed = (ISeed)ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider, seedInfo.Type);

                    // TODO: Create YieldOf objects and assign them to YieldOf properties.
                    // TODO: Think what to do if the Type or PropertyInfo is null.
                    foreach (var requiredYield in seedInfo.RequiredYields)
                    {
                        // TODO: Check if it is already created. If we have two properties of the same yield type. This must never be the case it should be an error in the seed definition.
                        // TOOD: Yield access property must be public or internal or protected. It also must have non private setter.

                        var yieldingSeed = (ISeed)ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider, requiredYield.YieldingSeed.Type);

                        var yield = ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider, requiredYield.Type);

                        var seedPropertyOnYield = yield.GetType().GetProperty("Seed", BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance);

                        // seedPropertyOnYield.SetValue(yield, yieldingSeed, BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.SetProperty, null, null, null);
                        seedPropertyOnYield.SetValue(yield, yieldingSeed);

                        requiredYield.YieldAccessProperty !.SetValue(seed, yield);
                    }

                    if (await seed.HasAlreadyYielded())
                    {
                        outputSink.WriteMessage($"Skipping {seedInfo.FriendlyName}");
                        return(false);
                    }

                    outputSink.WriteMessage($"Seeding {seedInfo.FriendlyName}");

                    await seed.Seed();

                    outputSink.WriteMessage($"Seeding {seedInfo.FriendlyName} completed");

                    return(true);
                }
            }
        }