/// <summary>
 /// If <see cref="IsReadOnly"/> is true then throw <see cref="FactFactoryException"/>.
 /// </summary>
 /// <exception cref="FactFactoryException">If <see cref="IsReadOnly"/> is true.</exception>
 protected virtual void CheckReadOnly()
 {
     if (IsReadOnly)
     {
         throw CommonHelper.CreateException(ErrorCode.InvalidOperation, $"Fact container is read-only.");
     }
 }
Example #2
0
        /// <inheritdoc/>
        public virtual TFactRuleCollection ValidateAndGetRules <TFactRule, TFactRuleCollection>(TFactRuleCollection ruleCollection)
            where TFactRule : IFactRule
            where TFactRuleCollection : IFactRuleCollection <TFactRule>
        {
            // Get a copy of the rules
            if (ruleCollection == null)
            {
                throw CommonHelper.CreateDeriveException(ErrorCode.InvalidData, "Rules cannot be null.");
            }

            IFactRuleCollection <TFactRule> rulesCopy = ruleCollection.Copy();

            if (rulesCopy == null)
            {
                throw CommonHelper.CreateDeriveException(ErrorCode.InvalidData, "IFactRuleCollection.Copy method return null.");
            }
            if (rulesCopy.Equals(ruleCollection))
            {
                throw CommonHelper.CreateDeriveException(ErrorCode.InvalidData, "IFactRuleCollection.Copy method return original rule collection.");
            }
            if (!(rulesCopy is TFactRuleCollection rules))
            {
                throw CommonHelper.CreateDeriveException(ErrorCode.InvalidData, "IFactRuleCollection.Copy method returned a different type of rules.");
            }

            rules.IsReadOnly = true;
            return(rules);
        }
Example #3
0
        /// <inheritdoc/>
        public virtual void ValidateContainer <TFactContainer>(TFactContainer container)
            where TFactContainer : IFactContainer
        {
            if (container == null)
            {
                throw CommonHelper.CreateDeriveException(ErrorCode.InvalidData, "Container cannot be null.");
            }
            if (container.Any(fact => fact is IBuildConditionFact))
            {
                throw CommonHelper.CreateDeriveException(ErrorCode.InvalidData, $"Container contains {nameof(IBuildConditionFact)} facts.");
            }
            if (container.Any(fact => fact is IRuntimeConditionFact))
            {
                throw CommonHelper.CreateDeriveException(ErrorCode.InvalidData, $"Container contains {nameof(IRuntimeConditionFact)} facts.");
            }

            IEqualityComparer <IFact> comparer = container.EqualityComparer ?? FactEqualityComparer.GetDefault();

            foreach (var fact in container)
            {
                if (container.Count(f => comparer.Equals(f, fact)) != 1)
                {
                    throw CommonHelper.CreateDeriveException(ErrorCode.InvalidData, $"Using the IEqualityComparer<IFact>, the '{fact.GetFactType().FactName}' fact was not found in the container or was found multiple times.");
                }
            }
        }
        /// <inheritdoc/>
        public virtual void DeriveWantAction <TFactRule, TFactRuleCollection, TWantAction, TFactContainer>(List <DeriveWantActionRequest <TFactRule, TFactRuleCollection, TWantAction, TFactContainer> > requests)
            where TFactRule : IFactRule
            where TFactRuleCollection : IFactRuleCollection <TFactRule>
            where TWantAction : IWantAction
            where TFactContainer : IFactContainer
        {
            Validate(requests);

            var treesByActions     = new Dictionary <WantActionInfo <TWantAction, TFactContainer>, List <TreeByFactRule <TFactRule, TWantAction, TFactContainer> > >();
            var deriveErrorDetails = new List <DeriveErrorDetail>();


            foreach (DeriveWantActionRequest <TFactRule, TFactRuleCollection, TWantAction, TFactContainer> request in requests)
            {
                var context = request.Context;

                if (!context.WantAction.Option.HasFlag(FactWorkOption.CanExecuteSync))
                {
                    deriveErrorDetails.Add(new DeriveErrorDetail(
                                               ErrorCode.InvalidOperation,
                                               ErrorResources.OnWantActionCannotBePerformedSynchronously(context.WantAction),
                                               context.WantAction,
                                               context.Container,
                                               null));
                    continue;
                }

                var requestForAction = new BuildTreesForWantActionRequest <TFactRule, TWantAction, TFactContainer>
                {
                    Context   = context,
                    FactRules = request
                                .Rules
                                .FindAll(factRule => factRule.Option.HasFlag(FactWorkOption.CanExecuteSync))
                                .SortByDescending(r => r, context.SingleEntity.GetRuleComparer <TFactRule, TWantAction, TFactContainer>(context)),
                };

                if (context.TreeBuilding.TryBuildTreesForWantAction(requestForAction, out var resultForAction))
                {
                    treesByActions.Add(resultForAction.WantActionInfo, resultForAction.TreesResult);
                }
                else
                {
                    deriveErrorDetails.Add(resultForAction.DeriveErrorDetail);
                }
            }

            // Check that we were able to adequately build the tree.
            if (deriveErrorDetails.Count != 0)
            {
                throw CommonHelper.CreateDeriveException(deriveErrorDetails);
            }

            foreach (var item in treesByActions)
            {
                item.Key.Context.TreeBuilding.CalculateTreeAndDeriveWantFacts(item.Key, item.Value);
            }
        }
        /// <inheritdoc/>
        /// <exception cref="FactFactoryException">Did not find fact type <typeparamref name="TFact"/>.</exception>
        /// <returns></returns>
        public virtual TFact GetFact <TFact>() where TFact : IFact
        {
            if (TryGetFact <TFact>(out var fact))
            {
                return(fact);
            }

            throw CommonHelper.CreateException(ErrorCode.InvalidData, $"Not found type fact with type {GetFactType<TFact>().FactName}.");
        }
        private void InnerAdd <TFact>(TFact fact, IEqualityComparer <IFact> comparer) where TFact : IFact
        {
            IFactType factType = fact.GetFactType();

            if (ContainerList.Contains(fact, comparer))
            {
                throw CommonHelper.CreateException(ErrorCode.InvalidData, $"The fact container already contains '{factType.FactName}' fact.");
            }

            ContainerList.Add(fact);
        }
Example #7
0
        /// <inheritdoc/>
        public TWantAction CreateWantAction <TWantAction>(Func <IEnumerable <IFact>, ValueTask> wantAction, List <IFactType> factTypes, FactWorkOption option) where TWantAction : IWantAction
        {
            var result = new WantAction(wantAction, factTypes, option);

            if (result is TWantAction converted)
            {
                return(converted);
            }

            throw CommonHelper.CreateException(
                      ErrorCode.InvalidData,
                      $"The result of the ISingleEntityOperations.CreateWantAction cannot be converted to the type {typeof(TWantAction).Name}.");
        }
Example #8
0
        /// <inheritdoc/>
        public virtual TFactResult CreateRuntimeConditionFact <TFactResult>() where TFactResult : IRuntimeConditionFact
        {
            var type       = typeof(TFact);
            var resultType = typeof(TFactResult);

            if (!resultType.IsAssignableFrom(type))
            {
                throw CommonHelper.CreateException(ErrorCode.InvalidFactType, $"{type.FullName} does not implement {resultType.FullName} type.");
            }
            else if (type.GetConstructor(Type.EmptyTypes) == null)
            {
                throw CommonHelper.CreateException(ErrorCode.InvalidFactType, $"{type.FullName} doesn't have a default constructor.");
            }


            return((TFactResult)Activator.CreateInstance(type, false));
        }
Example #9
0
        /// <inheritdoc/>
        public virtual async ValueTask <IFact> CalculateFactAsync <TFactRule, TWantAction, TFactContainer>(NodeByFactRule <TFactRule> node, IWantActionContext <TWantAction, TFactContainer> context)
            where TFactRule : IFactRule
            where TWantAction : IWantAction
            where TFactContainer : IFactContainer
        {
            (var rule, var buildSuccessConditions, var runtimeConditions) =
                (node.Info.Rule, node.Info.BuildSuccessConditions, node.Info.RuntimeConditions);

            foreach (IRuntimeConditionFact condition in runtimeConditions)
            {
                (bool calculated, IFact result) = await TryCalculateFactByRuntimeConditionAsync(rule, condition, context);

                if (calculated)
                {
                    return(result);
                }
            }

            using (var writer = context.Container.GetWriter())
            {
                buildSuccessConditions.ForEach(writer.Add);
                runtimeConditions.ForEach(writer.Add);
            }

            var requiredFacts = GetRequireFacts(rule, context);

            if (!CanInvokeWork(requiredFacts, rule, context.Cache))
            {
                throw CommonHelper.CreateDeriveException(ErrorCode.InvalidOperation, $"Can't calculate the '{rule}' rule.", context.WantAction, context.Container);
            }

            IFact fact = await Factory
                         .CreateObject(facts => rule.CalculateAsync(facts), requiredFacts)
                         .ConfigureAwait(false);

            fact.SetCalculateByRule();
            context.WantAction.AddUsedRule(rule);

            using (var writer = context.Container.GetWriter())
            {
                buildSuccessConditions.ForEach(writer.Remove);
                runtimeConditions.ForEach(writer.Remove);
            }

            return(fact);
        }
        /// <inheritdoc/>
        public virtual async ValueTask DeriveWantActionAsync <TFactRule, TFactRuleCollection, TWantAction, TFactContainer>(List <DeriveWantActionRequest <TFactRule, TFactRuleCollection, TWantAction, TFactContainer> > requests)
            where TFactRule : IFactRule
            where TFactRuleCollection : IFactRuleCollection <TFactRule>
            where TWantAction : IWantAction
            where TFactContainer : IFactContainer
        {
            Validate(requests);

            var treesByActions     = new Dictionary <WantActionInfo <TWantAction, TFactContainer>, List <TreeByFactRule <TFactRule, TWantAction, TFactContainer> > >();
            var deriveErrorDetails = new List <DeriveErrorDetail>();


            foreach (DeriveWantActionRequest <TFactRule, TFactRuleCollection, TWantAction, TFactContainer> request in requests)
            {
                var context = request.Context;

                var requestForAction = new BuildTreesForWantActionRequest <TFactRule, TWantAction, TFactContainer>
                {
                    Context   = context,
                    FactRules = request
                                .Rules
                                .SortByDescending(r => r, context.SingleEntity.GetRuleComparer <TFactRule, TWantAction, TFactContainer>(context)),
                };

                if (context.TreeBuilding.TryBuildTreesForWantAction(requestForAction, out var resultForAction))
                {
                    treesByActions.Add(resultForAction.WantActionInfo, resultForAction.TreesResult);
                }
                else
                {
                    deriveErrorDetails.Add(resultForAction.DeriveErrorDetail);
                }
            }

            // Check that we were able to adequately build the tree.
            if (deriveErrorDetails.Count != 0)
            {
                throw CommonHelper.CreateDeriveException(deriveErrorDetails);
            }

            foreach (var item in treesByActions)
            {
                await item.Key.Context.TreeBuilding.CalculateTreeAndDeriveWantFactsAsync(item.Key, item.Value);
            }
        }
Example #11
0
        /// <inheritdoc/>
        public virtual async ValueTask DeriveWantFactsAsync <TWantAction, TFactContainer>(WantActionInfo <TWantAction, TFactContainer> wantActionInfo)
            where TWantAction : IWantAction
            where TFactContainer : IFactContainer
        {
            (var context, var wantAction, var buildSuccessConditions, var runtimeConditions) =
                (wantActionInfo.Context, wantActionInfo.Context.WantAction, wantActionInfo.BuildSuccessConditions, wantActionInfo.RuntimeConditions);

            foreach (IRuntimeConditionFact condition in runtimeConditions)
            {
                if (!RuntimeCondition(condition, context))
                {
                    throw CommonHelper.CreateDeriveException(
                              ErrorCode.RuntimeCondition,
                              $"Failed to meet {context.Cache.GetFactType(condition).FactName} for {wantAction} and find another solution.");
                }
            }

            using (var writer = context.Container.GetWriter())
            {
                buildSuccessConditions.ForEach(writer.Add);
                runtimeConditions.ForEach(writer.Add);
            }

            var requiredFacts = GetRequireFacts(context.WantAction, context);

            if (!CanInvokeWork(requiredFacts, context.WantAction, context.Cache))
            {
                throw CommonHelper.CreateDeriveException(ErrorCode.InvalidOperation, $"Can't invoke the '{context.WantAction}' action.", context.WantAction, context.Container);
            }

            await context.WantAction.InvokeAsync(requiredFacts).ConfigureAwait(false);

            using (var writer = context.Container.GetWriter())
            {
                buildSuccessConditions.ForEach(writer.Remove);
                runtimeConditions.ForEach(writer.Remove);
            }
        }
        /// <summary>
        /// Get <see cref="TreeByFactRule{TFactRule, TWantAction, TFactContainer}"/> by <paramref name="request"/>.
        /// </summary>
        /// <typeparam name="TFactRule"></typeparam>
        /// <typeparam name="TWantAction"></typeparam>
        /// <typeparam name="TFactContainer"></typeparam>
        /// <param name="request"></param>
        /// <returns></returns>
        internal static List <TreeByFactRule <TFactRule, TWantAction, TFactContainer> > GetTreesByRequest <TFactRule, TWantAction, TFactContainer>(this BuildTreeForFactInfoRequest <TFactRule, TWantAction, TFactContainer> request)
            where TFactRule : IFactRule
            where TWantAction : IWantAction
            where TFactContainer : IFactContainer
        {
            var context   = request.Context;
            var factRules = request.Context.FactRules;

            if (factRules.IsNullOrEmpty())
            {
                throw CommonHelper.CreateDeriveException(ErrorCode.EmptyRuleCollection, "Rules cannot be null.");
            }

            var needRules = factRules
                            .Where(rule => rule.OutputFactType.EqualsFactType(request.WantFactType))
                            .ToList();

            if (needRules.IsNullOrEmpty())
            {
                throw CommonHelper.CreateDeriveException(ErrorCode.RuleNotFound, $"No rules found able to calculate fact {request.WantFactType.FactName}.");
            }

            var nodeInfos = needRules.ConvertAll(rule => new NodeByFactRuleInfo <TFactRule>
            {
                BuildSuccessConditions = new List <IBuildConditionFact>(rule.InputFactTypes.Count(type => type.IsFactType <IBuildConditionFact>())),
                BuildFailedConditions  = new List <IBuildConditionFact>(),
                RuntimeConditions      = new List <IRuntimeConditionFact>(rule.InputFactTypes.Count(type => type.IsFactType <IRuntimeConditionFact>())),
                Rule = rule,
                RequiredFactTypes = context.SingleEntity.GetRequiredTypesOfFacts(rule, context).ToList(),
                CompatibleRules   = rule.GetCompatibleRulesEx(context.FactRules, context),
            });

            return(nodeInfos.ConvertAll(info =>
            {
                var node = new NodeByFactRule <TFactRule>
                {
                    Childs = new List <NodeByFactRule <TFactRule> >(),
                    Info = info,
                };

                var tree = new TreeByFactRule <TFactRule, TWantAction, TFactContainer>
                {
                    Levels = new List <List <NodeByFactRule <TFactRule> > >
                    {
                        new List <NodeByFactRule <TFactRule> > {
                            node
                        },
                    },
                    NodeInfos = nodeInfos,
                    Root = node,
                    Context = context,
                };

                if (info.RequiredFactTypes.Count == 0)
                {
                    tree.Built();
                }

                return tree;
            }));
        }
Example #13
0
 /// <summary>
 /// Error creating version incompatibility.
 /// </summary>
 /// <param name="versionedFact"></param>
 /// <returns></returns>
 protected virtual FactFactoryException CreateIncompatibilityVersionException(IVersionFact versionedFact)
 {
     return(CommonHelper.CreateException(ErrorCode.InvalidFactType, $"Unable to compare versions {GetFactType().FactName} and {versionedFact.GetFactType().FactName}."));
 }