/// <summary> /// Adds an additional target to the condition. /// </summary> /// <param name="target"></param> /// <param name="properties"></param> public void AddTarget(object target, params string[] properties) { // Get the root target instance ModelInstance root = ModelInstance.GetModelInstance(target); // Set the properties to an empty array if null if (properties == null) { properties = new string[0]; } // Create a single condition target if the specified properties are all on the root if (!properties.Any(property => property.Contains('.') || property.Contains('{'))) { targets.Add(new ConditionTarget(this, root, properties)); } // Otherwise, process the property paths to create the necessary sources else { // Process each property path to build up the condition sources foreach (string property in properties) { AddTarget(root, root.Type.GetPath(property).FirstSteps); } } }
/// <summary> /// Gets the set of <see cref="Condition"/> instances associated with the specified <see cref="ModelInstance"/>. /// </summary> /// <param name="instance"></param> /// <returns></returns> public static IEnumerable <Condition> GetConditions(object instance) { ModelInstance modelInstance = ModelInstance.GetModelInstance(instance); if (modelInstance == null) { throw new ArgumentException("Specified instance is not a valid ModelInstance"); } return(modelInstance.GetExtension <RuleManager>().GetConditions()); }
protected override ValidationResult IsValid(object value, ValidationContext validationContext) { var instance = ModelInstance.GetModelInstance(validationContext.ObjectInstance); var source = new ModelSource(instance.Type, Source); // Get the member name by looking up using the display name, since the member name is mysteriously null for MVC3 projects var propertyName = validationContext.MemberName ?? validationContext.ObjectType.GetProperties() .Where(p => p.GetCustomAttributes(false).OfType <DisplayAttribute>() .Any(a => a.Name == validationContext.DisplayName)).Select(p => p.Name).FirstOrDefault(); var property = instance.Type.Properties[propertyName]; // Get the list of allowed values ModelInstanceList allowedValues = source.GetList(instance); if (allowedValues == null) { return(null); } // List Property if (property.IsList) { // Get the current property value ModelInstanceList items = instance.GetList((ModelReferenceProperty)property); // Determine whether the property value is in the list of allowed values if (!(items == null || items.All(item => allowedValues.Contains(item)))) { return(new ValidationResult("Invalid value", new string[] { propertyName })); } } // Reference Property else { // Get the current property value ModelInstance item = instance.GetReference((ModelReferenceProperty)property); // Determine whether the property value is in the list of allowed values if (!(item == null || allowedValues.Contains(item))) { return(new ValidationResult("Invalid value", new string[] { propertyName })); } } return(null); }
/// <summary> /// Sets whether the specified instance is pending deletion. /// </summary> /// <param name="instance"></param> /// <param name="isPendingDelete"></param> protected override void SetIsPendingDelete(object instance, bool isPendingDelete) { // Flag child entities for deletion where the instance is an owner ModelInstance modelInstance = ModelInstance.GetModelInstance(instance); foreach (var prop in ((EntityModelType)modelInstance.Type).Properties) { if (!prop.IsStatic && prop.IsList) { var modelRefProp = (ModelReferenceProperty)prop; if (modelRefProp.PropertyType is EntityModelType) { var entityModelType = (EntityModelType)modelRefProp.PropertyType; if (entityModelType.OwnerProperties.Values.Any(x => x.PropertyType == modelInstance.Type || x.PropertyType.IsSubType(modelInstance.Type))) { foreach (var childInstance in modelRefProp.GetInstances(modelInstance).ToList()) { childInstance.IsPendingDelete = true; } } } } } // Get the object state from the context var state = GetObjectContext().ObjectContext.ObjectStateManager.GetObjectStateEntry(instance); // Mark the instance as pending delete if (isPendingDelete) { state.ChangeState(EntityState.Deleted); ((IModelEntity)instance).IsInitialized = false; } // Mark the instance as added if the instance is new and is no longer being marked for deletion else if (GetId(instance) == null) { state.ChangeState(EntityState.Added); } // Otherwise, mark the instance as modified if the instance is existing and is no longer being marked for deletion else { state.ChangeState(EntityState.Modified); } }
protected override ValidationResult IsValid(object value, ValidationContext validationContext) { ModelInstance instance = ModelInstance.GetModelInstance(validationContext.ObjectInstance); // Get the member name by looking up using the display name, since the member name is mysteriously null for MVC3 projects var propertyName = validationContext.MemberName ?? validationContext.ObjectType.GetProperties() .Where(p => p.GetCustomAttributes(false).OfType <DisplayAttribute>() .Any(a => a.Name == validationContext.DisplayName)).Select(p => p.Name).FirstOrDefault(); ModelProperty sourceProperty = instance.Type.Properties[propertyName]; if (ComparisonPropertyName == null) { return(null); } ModelSource comparePropPath = new ModelSource(instance.Type, ComparisonPropertyName); object compareValue = comparePropPath.GetValue(instance); int comparison = ((IComparable)compareValue).CompareTo(value); switch (Operator) { case CompareOperator.Equal: return(comparison == 0 ? null : new ValidationResult("Invalid value", new string[] { propertyName })); case CompareOperator.NotEqual: return(comparison != 0 ? null : new ValidationResult("Invalid value", new string[] { propertyName })); case CompareOperator.GreaterThan: return(comparison < 0 ? null : new ValidationResult("Invalid value", new string[] { propertyName })); case CompareOperator.GreaterThanEqual: return(comparison <= 0 ? null : new ValidationResult("Invalid value", new string[] { propertyName })); case CompareOperator.LessThan: return(comparison > 0 ? null : new ValidationResult("Invalid value", new string[] { propertyName })); case CompareOperator.LessThanEqual: return(comparison >= 0 ? null : new ValidationResult("Invalid value", new string[] { propertyName })); } return(null); }
/// <summary> /// Creates or removes an condition on the specified target based on the specified condition. /// </summary> /// <param name="target"></param> /// <param name="condition"></param> /// <param name="properties"></param> public Condition When(string message, object target, Func <bool> condition, params string[] properties) { // Get the current condition if it exists ConditionTarget conditionTarget = ModelInstance.GetModelInstance(target).GetExtension <RuleManager>().GetCondition(this); // Add the condition on the target if it does not exist yet if (condition()) { // Create a new condition if one does not exist if (conditionTarget == null) { return(new Condition(this, message, target, properties)); } // Return the existing condition else { // Update the message if a different message was passed in if (!string.IsNullOrEmpty(message) && message != conditionTarget.Condition.Message) { conditionTarget.Condition.Message = message; } return(conditionTarget.Condition); } } // Destroy the condition if it exists on the target and is no longer valid if (conditionTarget != null) { conditionTarget.Condition.Destroy(); } // Return null to indicate that no condition was created return(null); }
/// <summary> /// Creates a new calculation for the specified property, including an action to set the property /// and the expression tree responsible for calculating the expected value. /// </summary> /// <param name="property"></param> /// <param name="action"></param> /// <param name="calculation"></param> internal Calculation(Expression <Func <TRoot, TProperty> > property, Expression <Func <TRoot, TProperty> > calculation) : base(null) { // Ensure the property argument is a valid expression if (property == null || !(property.Body is MemberExpression)) { throw new ArgumentException("The property expression must be a simple expression that returns a property from the root type (root => root.Property)", "property"); } // Ensure the property member expression is a property, not a field var propInfo = (PropertyInfo)((MemberExpression)property.Body).Member as PropertyInfo; if (propInfo == null) { throw new ArgumentException("Only properties can be calculated via rules, not fields.", "property"); } this.Property = propInfo.Name; this.calculation = calculation; // Perform delayed initialization to be able to reference the model type information Initialize += (s, e) => { var rootType = RootType; if (rootType == null) { throw new ApplicationException(string.Format("Type '{0}' is not a model type.", typeof(TRoot).FullName)); } // Get the model property var prop = rootType.Properties[propInfo.Name]; if (prop == null) { throw new ArgumentException("Only valid model properties can be calculated: " + propInfo.Name, "property"); } // List property if (prop is ModelReferenceProperty && prop.IsList) { // Compile the calculation outside the action lambda to cache via closure var getListItems = calculation.Compile(); this.Action = root => { // Get the source list var source = ModelInstance.GetModelInstance(root).GetList((ModelReferenceProperty)prop); // Get the set of items the list should contain var items = ((IEnumerable)getListItems(root)).Cast <object>().Select(instance => ModelInstance.GetModelInstance(instance)); // Update the list source.Update(items); }; } // Reference or value property else { // Ensure the property can be set var setMethod = propInfo.GetSetMethod(true); if (setMethod == null) { throw new ArgumentException("Read-only properties cannot be calculated: " + propInfo.Name, "property"); } // Create the expression to set the property using the calculation expression var root = Expression.Parameter(typeof(TRoot), "root"); var setter = Expression.Call(root, setMethod, Expression.Invoke(calculation, root)); this.Action = Expression.Lambda <Action <TRoot> >(setter, root).Compile(); } // Assert that this rule returns the value of the calculated property Returns(this.Property); // Register for change events as well if (this.path == null) { this.path = ModelContext.Current.GetModelType <TRoot>().GetPath(this.calculation).Path; } if (!String.IsNullOrEmpty(this.path)) { OnChangeOf(this.path); } }; // Mark the rule for both server and client execution by default RunOnServerAndClient(); }
/// <summary> /// Translates the specified instance of the source type into the destination type. /// </summary> /// <param name="source"></param> /// <param name="createDestinationInstance"></param> /// <returns></returns> public ModelInstance Translate(ModelInstance source) { // Create the destination instance var destination = createDestinationInstance(source); // Apply each property transaction from source to destination foreach (var property in Properties) { // Invoke the source expression for the mapping var value = property.SourceExpression == null ? null : property.SourceExpression.Invoke(source); // Translate reference properties if (property.ReferenceConverter != null) { // Translate list properties if (property.DestinationProperty.IsList) { var sourceList = ((IEnumerable)property.SourceExpression.Invoke(source)); if (sourceList != null) { var destinationList = property.DestinationSource.GetList(destination); foreach (var instance in property.ReferenceConverter.Translate(sourceList.Cast <object>().Select(i => ModelInstance.GetModelInstance(i)))) { destinationList.Add(instance); } } } // Translate instance properties else { var sourceInstance = ModelInstance.GetModelInstance(property.SourceExpression.Invoke(source)); var destinationInstance = property.ReferenceConverter.Translate(sourceInstance); property.DestinationSource.SetValue(destination, destinationInstance.Instance, EnsureDestinationInstance); } } // Translate value properties else { if (value != null && property.ValueConverter != null && !((ModelValueProperty)property.DestinationProperty).PropertyType.IsAssignableFrom(value.GetType())) { value = property.ValueConverter.ConvertFrom(null, Thread.CurrentThread.CurrentCulture, value); } else if (value != null && property.DelegateConverter != null) { value = property.DelegateConverter(value); } //the destination property is an enum, see if it is a concrete type //and has a converter associated with it. This is handled outside //the normal converter paradigm since enums are reference types in ExoModel if (value != null && property.DestinationProperty is IReflectionModelType && ((IReflectionModelType)property.DestinationProperty).UnderlyingType.BaseType == typeof(Enum)) { switch (value.ToString()) { //if the enum value is empty just set it to it's default value case "": value = 0; break; default: { var underlyingDestinationType = ((IReflectionModelType)property.DestinationProperty).UnderlyingType; var converter = TypeDescriptor.GetConverter(underlyingDestinationType); value = converter.ConvertFrom(value); } break; } } // Set the value on the destination instance property.DestinationSource.SetValue(destination, value, EnsureDestinationInstance); } } // Return the translated instance return(destination); }