Ejemplo n.º 1
0
        /// <summary>
        /// Registers the rule with the current <see cref="ModelContext"/>.
        /// </summary>
        public void Register(ModelType rootType = null)
        {
            // Determine if the rule is be registered for a specific root model type
            if (rootType != null)
            {
                if (this.rootType != null)
                {
                    throw new InvalidOperationException("Rules cannot be explicitly registered for more than one model type.");
                }
                this.rootType = rootType;
            }

            // Raise the Initialization event the first time the rule is registered
            if (Initialize != null)
            {
                Initialize(this, EventArgs.Empty);
                Initialize = null;
            }

            // Default the invocation type to PropertyChanged if none were assigned
            if ((int)InvocationTypes == 1)
            {
                InvocationTypes = RuleInvocationType.PropertyChanged;
            }

            // Automatically detect predicates if none were specified
            if (Predicates == null && ((InvocationTypes & (RuleInvocationType.PropertyChanged | RuleInvocationType.PropertyGet)) > 0))
            {
                SetPredicates(GetPredicates());
            }

            // Track and validate uniqueness of condition types
            HashSet <ConditionType> conditionTypes = RootType.GetExtension <HashSet <ConditionType> >();

            foreach (var conditionType in ConditionTypes)
            {
                if (conditionTypes.Contains(conditionType))
                {
                    throw new InvalidOperationException("Registered condition types must be unique for each model type: " + conditionType.Code);
                }
                conditionTypes.Add(conditionType);
            }

            // Track the rule registration for the root model type
            List <Rule> rules = RootType.GetExtension <List <Rule> >();

            rules.Add(this);

            // Do not perform model type event registration if the rule is not supposed to execute on the server
            if ((ExecutionLocation & RuleExecutionLocation.Server) == 0)
            {
                return;
            }

            // Init Invocation
            if ((InvocationTypes & RuleInvocationType.InitExisting) == RuleInvocationType.InitExisting ||
                (InvocationTypes & RuleInvocationType.InitNew) == RuleInvocationType.InitNew)
            {
                RootType.Init += (sender, e) =>
                {
                    if (((InvocationTypes & RuleInvocationType.InitExisting) == RuleInvocationType.InitExisting && !e.Instance.IsNew) ||
                        ((InvocationTypes & RuleInvocationType.InitNew) == RuleInvocationType.InitNew && e.Instance.IsNew))
                    {
                        Invoke(e.Instance, e);
                    }
                };
            }

            // Property Get Invocation
            if ((InvocationTypes & RuleInvocationType.PropertyGet) == RuleInvocationType.PropertyGet)
            {
                // Subscribe to property get notifications for all return values
                RootType.PropertyGet += (sender, e) =>
                {
                    // Determine if the rule is responsible for calculating the property being accessed
                    if (ReturnValues.Contains(e.Property.Name))
                    {
                        // Get the rule manager for the current instance
                        var manager = e.Instance.GetExtension <RuleManager>();

                        // Determine if the rule needs to be run
                        if ((e.IsFirstAccess && (!e.Property.IsPersisted || e.Instance.IsNew || manager.IsPendingInvocation(this))) || manager.IsPendingInvocation(this))
                        {
                            // Invoke the rule
                            Invoke(e.Instance, e);

                            // Mark the rule state as no longer requiring invocation
                            manager.SetPendingInvocation(this, false);
                        }
                    }
                };

                if (!InvocationTypes.HasFlag(RuleInvocationType.SuppressPropertyChanged))
                {
                    // Subscribe to property change notifications for all rule predicates
                    foreach (string predicate in Predicates)
                    {
                        RootType.GetPath(predicate).Change += (sender, e) =>
                        {
                            // Only invoke the rule if the instance is of the same type as the rule root type
                            if (RootType.IsInstanceOfType(e.Instance))
                            {
                                // Get the rule manager for the current instance
                                var manager = e.Instance.GetExtension <RuleManager>();

                                // Mark the rule state as requiring invocation
                                if (manager.SetPendingInvocation(this, true))
                                {
                                    // Raise property change notifications
                                    foreach (var property in ReturnValues)
                                    {
                                        e.Instance.Type.Properties[property].NotifyPathChange(e.Instance);
                                    }
                                }
                            }
                        };
                    }
                }

                // Mark the properties as calculated to denote that they are return values of a rule
                foreach (var property in ReturnValues)
                {
                    var prop = RootType.Properties[property];
                    if (prop != null)
                    {
                        prop.IsCalculated = true;
                    }
                }
            }

            // Property Change Invocation
            if ((InvocationTypes & RuleInvocationType.PropertyChanged) == RuleInvocationType.PropertyChanged)
            {
                // Subscribe to property change notifications for all rule predicates
                foreach (string predicate in Predicates)
                {
                    RootType.GetPath(predicate).Change += (sender, e) =>
                    {
                        // Only invoke the rule if the instance is of the same type as the rule root type
                        if (RootType.IsInstanceOfType(e.Instance))
                        {
                            // Get the rule manager for the current instance
                            var manager = e.Instance.GetExtension <RuleManager>();

                            // Register the rule to run if it is not already registered
                            if (manager.SetPendingInvocation(this, true))
                            {
                                // Invoke the rule when the last model event scope exits
                                ModelEventScope.OnExit(() =>
                                {
                                    // Mark the rule state as no longer requiring invocation
                                    manager.SetPendingInvocation(this, false);

                                    // Invoke the rule
                                    Invoke(e.Instance, e);
                                });
                            }
                        }
                    };
                }
            }

            // Allow subclasses to perform additional registration logic
            OnRegister();
        }