/// <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); }
/// <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/> 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); } }
/// <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; })); }