e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
229 lines
7.4 KiB
C#
229 lines
7.4 KiB
C#
// ---------------------------------------------------------------------------
|
|
// Copyright (C) 2005 Microsoft Corporation All Rights Reserved
|
|
// ---------------------------------------------------------------------------
|
|
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Workflow.ComponentModel;
|
|
using System.Workflow.ComponentModel.Compiler;
|
|
using System.Workflow.Activities.Common;
|
|
|
|
namespace System.Workflow.Activities.Rules
|
|
{
|
|
public enum RuleChainingBehavior
|
|
{
|
|
None,
|
|
UpdateOnly,
|
|
Full
|
|
};
|
|
|
|
[Serializable]
|
|
public class RuleSet
|
|
{
|
|
internal const string RuleSetTrackingKey = "RuleSet.";
|
|
internal string name;
|
|
internal string description;
|
|
internal List<Rule> rules;
|
|
internal RuleChainingBehavior behavior = RuleChainingBehavior.Full;
|
|
private bool runtimeInitialized;
|
|
private object syncLock = new object();
|
|
|
|
// keep track of cached data
|
|
[NonSerialized]
|
|
private RuleEngine cachedEngine;
|
|
[NonSerialized]
|
|
private RuleValidation cachedValidation;
|
|
|
|
public RuleSet()
|
|
{
|
|
this.rules = new List<Rule>();
|
|
}
|
|
|
|
public RuleSet(string name)
|
|
: this()
|
|
{
|
|
this.name = name;
|
|
}
|
|
|
|
public RuleSet(string name, string description)
|
|
: this(name)
|
|
{
|
|
this.description = description;
|
|
}
|
|
|
|
public string Name
|
|
{
|
|
get { return name; }
|
|
set
|
|
{
|
|
if (runtimeInitialized)
|
|
throw new InvalidOperationException(SR.GetString(SR.Error_CanNotChangeAtRuntime));
|
|
name = value;
|
|
}
|
|
}
|
|
|
|
public string Description
|
|
{
|
|
get { return description; }
|
|
set
|
|
{
|
|
if (runtimeInitialized)
|
|
throw new InvalidOperationException(SR.GetString(SR.Error_CanNotChangeAtRuntime));
|
|
description = value;
|
|
}
|
|
}
|
|
|
|
public RuleChainingBehavior ChainingBehavior
|
|
{
|
|
get { return behavior; }
|
|
set
|
|
{
|
|
if (runtimeInitialized)
|
|
throw new InvalidOperationException(SR.GetString(SR.Error_CanNotChangeAtRuntime));
|
|
behavior = value;
|
|
}
|
|
}
|
|
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
|
|
public ICollection<Rule> Rules
|
|
{
|
|
get { return rules; }
|
|
}
|
|
|
|
public bool Validate(RuleValidation validation)
|
|
{
|
|
if (validation == null)
|
|
throw new ArgumentNullException("validation");
|
|
|
|
// Validate each rule.
|
|
Dictionary<string, object> ruleNames = new Dictionary<string, object>();
|
|
foreach (Rule r in rules)
|
|
{
|
|
if (!string.IsNullOrEmpty(r.Name)) // invalid names caught when validating the rule
|
|
{
|
|
if (ruleNames.ContainsKey(r.Name))
|
|
{
|
|
// Duplicate rule name found.
|
|
ValidationError error = new ValidationError(Messages.Error_DuplicateRuleName, ErrorNumbers.Error_DuplicateConditions);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = r;
|
|
validation.AddError(error);
|
|
}
|
|
else
|
|
{
|
|
ruleNames.Add(r.Name, null);
|
|
}
|
|
}
|
|
|
|
r.Validate(validation);
|
|
}
|
|
|
|
if (validation.Errors == null || validation.Errors.Count == 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
public void Execute(RuleExecution ruleExecution)
|
|
{
|
|
// we have no way of knowing if the ruleset has been changed, so no caching done
|
|
if (ruleExecution == null)
|
|
throw new ArgumentNullException("ruleExecution");
|
|
if (ruleExecution.Validation == null)
|
|
throw new ArgumentException(SR.GetString(SR.Error_MissingValidationProperty), "ruleExecution");
|
|
|
|
RuleEngine engine = new RuleEngine(this, ruleExecution.Validation, ruleExecution.ActivityExecutionContext);
|
|
engine.Execute(ruleExecution);
|
|
}
|
|
|
|
internal void Execute(Activity activity, ActivityExecutionContext executionContext)
|
|
{
|
|
// this can be called from multiple threads if multiple workflows are
|
|
// running at the same time (only a single workflow is single-threaded)
|
|
// we want to only lock around the validation and preprocessing, so that
|
|
// execution can run in parallel.
|
|
|
|
if (activity == null)
|
|
throw new ArgumentNullException("activity");
|
|
|
|
Type activityType = activity.GetType();
|
|
RuleEngine engine = null;
|
|
lock (syncLock)
|
|
{
|
|
// do we have something useable cached?
|
|
if ((cachedEngine == null) || (cachedValidation == null) || (cachedValidation.ThisType != activityType))
|
|
{
|
|
// no cache (or its invalid)
|
|
RuleValidation validation = new RuleValidation(activityType, null);
|
|
engine = new RuleEngine(this, validation, executionContext);
|
|
cachedValidation = validation;
|
|
cachedEngine = engine;
|
|
}
|
|
else
|
|
{
|
|
// this will happen if the ruleset has already been processed
|
|
// we can simply use the previously processed engine
|
|
engine = cachedEngine;
|
|
}
|
|
}
|
|
|
|
// when we get here, we have a local RuleEngine all ready to go
|
|
// we are outside the lock, so these can run in parallel
|
|
engine.Execute(activity, executionContext);
|
|
}
|
|
|
|
public RuleSet Clone()
|
|
{
|
|
RuleSet newRuleSet = (RuleSet)this.MemberwiseClone();
|
|
newRuleSet.runtimeInitialized = false;
|
|
|
|
if (this.rules != null)
|
|
{
|
|
newRuleSet.rules = new List<Rule>();
|
|
foreach (Rule r in this.rules)
|
|
newRuleSet.rules.Add(r.Clone());
|
|
}
|
|
|
|
return newRuleSet;
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
RuleSet other = obj as RuleSet;
|
|
if (other == null)
|
|
return false;
|
|
if ((this.Name != other.Name)
|
|
|| (this.Description != other.Description)
|
|
|| (this.ChainingBehavior != other.ChainingBehavior)
|
|
|| (this.Rules.Count != other.Rules.Count))
|
|
return false;
|
|
// look similar, compare each rule
|
|
for (int i = 0; i < this.rules.Count; ++i)
|
|
{
|
|
if (!this.rules[i].Equals(other.rules[i]))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return base.GetHashCode();
|
|
}
|
|
|
|
internal void OnRuntimeInitialized()
|
|
{
|
|
lock (syncLock)
|
|
{
|
|
if (runtimeInitialized)
|
|
return;
|
|
|
|
foreach (Rule rule in rules)
|
|
{
|
|
rule.OnRuntimeInitialized();
|
|
}
|
|
runtimeInitialized = true;
|
|
}
|
|
}
|
|
}
|
|
}
|