/// <summary> /// Initializes null list properties when updated via a call to <see cref="ModelInstanceList.Add"/> or <see cref="ModelInstanceList.Update"/>. /// </summary> /// <param name="instance"></param> /// <param name="property"></param> /// <returns></returns> protected override void InitializeList(ModelInstance instance, ModelReferenceProperty property) { if (property is ReflectionReferenceProperty) { // Ensure the property is a writable BusinessObjectList property var propertyInfo = ((ReflectionReferenceProperty)property).PropertyInfo; var listType = propertyInfo.PropertyType; if (!listType.IsGenericType || listType.GetGenericTypeDefinition() != typeof(ICollection <>)) { throw new NotSupportedException("List initialization is only supported for lists of type ICollection<T>."); } if (!propertyInfo.CanWrite) { throw new NotSupportedException("List initialization is only supported for writeable properties."); } var itemType = ((ReflectionModelType)property.PropertyType).UnderlyingType; // Create the list var list = typeof(ObservableCollection <>).MakeGenericType(itemType).GetConstructor(Type.EmptyTypes).Invoke(null); // Assign the list to the property propertyInfo.SetValue(instance.Instance, list, null); } else { throw new NotSupportedException("List initialization is only supported for reflection-based properties."); } }
/// <summary> /// Gets the serializable value of a <see cref="ModelProperty"/>. /// </summary> /// <param name="property"></param> /// <param name="source"></param> /// <returns></returns> internal static object GetPropertyValue(ModelProperty property, IModelPropertySource source) { ModelReferenceProperty reference = property as ModelReferenceProperty; if (reference != null) { // Serialize lists if (reference.IsList) { return(source.GetList(reference).Select(item => GetReference(reference, item))); } // Serialize references else { return(GetReference(reference, source.GetReference(reference))); } } // Serialize values else { return(source.GetValue((ModelValueProperty)property)); } }
public ModelReferenceChangeEvent(ModelInstance instance, ModelReferenceProperty property, ModelInstance oldValue, ModelInstance newValue) : base(instance) { this.Property = property; this.OldValue = oldValue; this.OldValueId = (oldValue != null) ? oldValue.Id : null; this.NewValue = newValue; this.NewValueId = (newValue != null) ? newValue.Id : null; }
public ModelReferenceChangeEvent(ModelInstance instance, ModelReferenceProperty property, ModelInstance oldValue, ModelInstance newValue) : base(instance) { this.Property = property; this.OldValue = oldValue; this.OldValueId = (oldValue != null) ? oldValue.Id : null; this.NewValue = newValue; this.NewValueId = (newValue != null) ? newValue.Id : null; }
protected override System.Collections.IList ConvertToList(ModelReferenceProperty property, object list) { // If the list is managed by Entity Framework, convert to a list with listeners if (list is RelatedEnd) { Type d1 = typeof(CollectionWrapper <>); Type constructed = d1.MakeGenericType(((EntityModelType)property.PropertyType).UnderlyingType); var constructor = constructed.GetConstructors()[0]; return((IList)constructor.Invoke(new object[] { list })); } return(base.ConvertToList(property, list)); }
/// <summary> /// Gets the serializable representation of a <see cref="ModelInstance"/>. /// </summary> /// <param name="property"></param> /// <param name="instance"></param> /// <returns></returns> internal static object GetReference(ModelReferenceProperty property, ModelInstance instance) { if (instance == null) { return(null); } else if (instance.Type != property.PropertyType) { return new { id = instance.Id, type = JsonConverter.GetJsonReferenceType(instance.Type) } } ; else { return(instance.Id); } }
/// <summary> /// Ensure the destination instance path is valid when nulls are encountered along the path. /// </summary> /// <param name="instance"></param> /// <param name="property"></param> /// <param name="index"></param> bool EnsureDestinationInstance(ModelInstance instance, ModelReferenceProperty property, int index) { if (property.IsList) { ModelInstanceList list = instance.GetList(property); for (int i = list.Count; i <= index; i++) { list.Add(property.PropertyType.Create()); } } else { instance.SetReference(property, property.PropertyType.Create()); } return(true); }
protected override System.Collections.IList ConvertToList(ModelReferenceProperty property, object list) { return (IList)list; }
static JsonUtility() { serializer = new JsonSerializer { DateParseHandling = DateParseHandling.None, Converters = { new UtcDateTimeConverter() } }; serializableTypes = new HashSet <Type>(); // Register converters for types implementing IJsonSerializable or that have DataContract attributes // Include all types in ExoWeb and ExoRule automatically foreach (var converter in JsonConverter.Infer( typeof(ServiceHandler).Assembly.GetTypes().Union( typeof(Rule).Assembly.GetTypes().Where(type => typeof(Rule).IsAssignableFrom(type))))) { RegisterConverter(converter); } // Deserialize Value Change Event Func <JsonReader, ModelValueChangeEvent> deserializeValueChangeEvent = (reader) => { string p; ModelInstance instance = null; ModelValueProperty property = null; object oldValue = null; object newValue = null; while (reader.ReadProperty(out p)) { switch (p) { case "instance": instance = reader.ReadValue <ModelInstance>(); break; case "property": var propertyName = reader.ReadValue <string>(); property = (ModelValueProperty)instance.Type.Properties[propertyName]; if (property == null) { throw new SerializationException("Unable to deserialize ValueChange: property \"" + propertyName + "\" does not exist for type \"" + instance.Type.Name + "\"."); } break; case "oldValue": if (reader.TokenType == JsonToken.StartObject) { oldValue = reader.ReadValue(property.PropertyType); } else { oldValue = property.CoerceValue(reader.Value); reader.Read(); } break; case "newValue": if (reader.TokenType == JsonToken.StartObject) { newValue = reader.ReadValue(property.PropertyType); } else { newValue = property.CoerceValue(reader.Value); reader.Read(); } break; default: throw new ArgumentException("The specified property could not be deserialized.", p); } } return(new ModelValueChangeEvent(instance, property, oldValue, newValue)); }; // Deserialize Reference Change Event Func <JsonReader, ModelReferenceChangeEvent> deserializeReferenceChangeEvent = (reader) => { string p; ModelInstance instance = null; ModelReferenceProperty property = null; ModelInstance oldValue = null; ModelInstance newValue = null; while (reader.ReadProperty(out p)) { switch (p) { case "instance": instance = reader.ReadValue <ModelInstance>(); break; case "property": var propertyName = reader.ReadValue <string>(); property = (ModelReferenceProperty)instance.Type.Properties[propertyName]; if (property == null) { throw new SerializationException("Unable to deserialize ReferenceChange: property \"" + propertyName + "\" does not exist for type \"" + instance.Type.Name + "\"."); } break; case "oldValue": oldValue = reader.ReadValue <ModelInstance>(); break; case "newValue": newValue = reader.ReadValue <ModelInstance>(); break; default: throw new ArgumentException(@"The specified property could not be deserialized.", p); } } return(new ModelReferenceChangeEvent(instance, property, oldValue, newValue)); }; // Deserialize List Change Event Func <JsonReader, ModelListChangeEvent> deserializeListChangeEvent = (reader) => { string p; ModelInstance instance = null; ModelReferenceProperty property = null; ModelInstance[] added = null; ModelInstance[] removed = null; while (reader.ReadProperty(out p)) { switch (p) { case "instance": instance = reader.ReadValue <ModelInstance>(); break; case "property": var propertyName = reader.ReadValue <string>(); property = (ModelReferenceProperty)instance.Type.Properties[propertyName]; if (property == null) { throw new SerializationException("Unable to deserialize ListChange: property \"" + propertyName + "\" does not exist for type \"" + instance.Type.Name + "\"."); } break; case "added": added = reader.ReadValue <ModelInstance[]>(); break; case "removed": removed = reader.ReadValue <ModelInstance[]>(); break; default: throw new ArgumentException(@"The specified property could not be deserialized.", p); } } return(new ModelListChangeEvent(instance, property, added, removed)); }; // Deserialize Init New Event Func <JsonReader, ModelInitEvent.InitNew> deserializeInitNewEvent = (reader) => { string p; ModelInstance instance = null; while (reader.ReadProperty(out p)) { switch (p) { case "instance": instance = reader.ReadValue <ModelInstance>(); break; default: throw new ArgumentException(@"The specified property could not be deserialized.", p); } } return(new ModelInitEvent.InitNew(instance)); }; // Deserialize Init Existing Event Func <JsonReader, ModelInitEvent.InitExisting> deserializeInitExistingEvent = (reader) => { string p; ModelInstance instance = null; while (reader.ReadProperty(out p)) { switch (p) { case "instance": instance = reader.ReadValue <ModelInstance>(); break; default: throw new ArgumentException(@"The specified property could not be deserialized.", p); } } return(new ModelInitEvent.InitExisting(instance)); }; // Deserialize Delete Event Func <JsonReader, ModelDeleteEvent> deserializeDeleteEvent = (reader) => { string p; ModelInstance instance = null; bool isPendingDelete = false; while (reader.ReadProperty(out p)) { switch (p) { case "instance": instance = reader.ReadValue <ModelInstance>(); break; case "isPendingDelete": isPendingDelete = reader.ReadValue <bool>(); break; default: throw new ArgumentException(@"The specified property could not be deserialized.", p); } } return(new ModelDeleteEvent(instance, isPendingDelete)); }; // Construct Model Instance var createModelInstance = typeof(ModelInstance).GetConstructor( BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(ModelType), typeof(string) }, null); // Register custom converters for ModelType, ModelProperty, ModelMethod, ModelInstance, ModelEvent foreach (var converter in new JsonConverter[] { // Model Type new JsonConverter <ModelType>( (modelType, json) => { // Base Type if (modelType.BaseType != null) { json.Set("baseType", JsonConverter.GetJsonReferenceType(modelType.BaseType)); } // Base Type if (!String.IsNullOrEmpty(modelType.Format)) { json.Set("format", modelType.Format); } // Properties if (modelType.Properties.Any()) { json.Set("properties", modelType.Properties .Where(property => property.DeclaringType == modelType && ExoWeb.IncludeInClientModel(property)) .ToDictionary(property => property.Name)); } // Methods if (modelType.Methods.Any()) { json.Set("methods", modelType.Methods.ToDictionary(method => method.Name)); } // Rules var rules = Rule.GetRegisteredRules(modelType).ToList(); var typeRules = rules.Where(rule => (rule.ExecutionLocation & RuleExecutionLocation.Client) > 0 && !(rule is IPropertyRule)).ToArray(); if (typeRules.Any()) { json.Set("rules", typeRules); } // Condition Types var serverConditionTypes = rules .Where(rule => (rule.ExecutionLocation & RuleExecutionLocation.Client) == 0) .SelectMany(rule => rule.ConditionTypes) .ToArray(); if (serverConditionTypes.Any()) { json.Set("conditionTypes", serverConditionTypes); } // Exports var exports = json.Global <Dictionary <string, string> >("exports"); if (exports.Any()) { json.Set("exports", json.Global <Dictionary <string, string> >("exports")); } }, json => { throw new NotSupportedException("ModelType cannot be deserialized."); }), // Model Property new JsonConverter <ModelProperty>( (property, json) => { // Type string type = (property is ModelValueProperty ? JsonConverter.GetJsonValueType(((ModelValueProperty)property).PropertyType) ?? "Object" : JsonConverter.GetJsonReferenceType(((ModelReferenceProperty)property).PropertyType)) + (property.IsList ? "[]" : ""); json.Set("type", type); // IsStatic if (property.IsStatic) { json.Set("isStatic", true); } // IsPersisted if (!property.IsPersisted && !property.IsStatic) { json.Set("isPersisted", false); } // IsCalculated if (property.IsCalculated) { json.Set("isCalculated", true); } // Index int index = 0; foreach (ModelProperty p in property.DeclaringType.Properties) { if (p == property) { break; } if (ExoWeb.IncludeInClientModel(p) && !p.IsStatic) { index++; } } if (!property.IsStatic) { json.Set("index", index); } // Format string format = property.Format; if (!string.IsNullOrEmpty(format)) { json.Set("format", format); } // Label string label = property.Label; if (!string.IsNullOrEmpty(label)) { json.Set("label", label); } // Help Text string helptext = property.HelpText; if (!string.IsNullOrEmpty(helptext)) { json.Set("helptext", helptext); } // Rules var rules = Rule .GetRegisteredRules(property.DeclaringType) .Where(rule => (rule.ExecutionLocation & RuleExecutionLocation.Client) > 0 && rule is IPropertyRule && rule.RootType.Properties[((IPropertyRule)rule).Property] == property) .ToDictionary(rule => rule is ICalculationRule ? "calculated" : String.Format("{0}.{1}.{2}", rule.RootType.Name, ((IPropertyRule)rule).Property, ((IPropertyRule)rule).Name) == rule.Name ? ((IPropertyRule)rule).Name.Substring(0, 1).ToLower() + ((IPropertyRule)rule).Name.Substring(1) : rule.Name); if (rules.Any()) { json.Set("rules", rules); } // Default Value if (property is ModelValueProperty) { var defaultValue = ((ModelValueProperty)property).DefaultValue; if (defaultValue != null) { json.Set("defaultValue", ((ModelValueProperty)property).DefaultValue); } } }, json => { throw new NotSupportedException("ModelProperty cannot be deserialized."); }), // Model Method new JsonConverter <ModelMethod>( (method, json) => { // Parameters json.Set("parameters", method.Parameters.Select(p => p.Name)); // IsStatic json.Set("isStatic", method.IsStatic); }, json => { throw new NotSupportedException("ModelMethod cannot be deserialized."); }), // Model Instance new JsonConverter <ModelInstance>( (instance, json) => { json.Set("id", instance.Id); json.Set("type", instance.Type.Name); }, reader => { string p; ModelType type = null; string id = null; while (reader.ReadProperty(out p)) { switch (p) { case "type": type = ModelContext.Current.GetModelType(reader.ReadValue <string>()); break; case "id": id = reader.ReadValue <string>(); break; // Ignore case "isNew": reader.ReadValue <bool>(); break; default: throw new ArgumentException(@"The specified property could not be deserialized.", p); } } return((ModelInstance)createModelInstance.Invoke(new object[] { type, id })); }), // Model Event new JsonConverter <ModelEvent>( (modelEvent, json) => { throw new NotSupportedException("ModelEvent cannot be deserialized."); }, (reader) => { string p; if (reader.ReadProperty(out p) && p == "type") { string eventName = reader.ReadValue <string>(); switch (eventName) { case "ValueChange": return(deserializeValueChangeEvent(reader)); case "ReferenceChange": return(deserializeReferenceChangeEvent(reader)); case "ListChange": return(deserializeListChangeEvent(reader)); case "InitNew": return(deserializeInitNewEvent(reader)); case "InitExisting": return(deserializeInitExistingEvent(reader)); case "Delete": return(deserializeDeleteEvent(reader)); } throw new NotSupportedException(eventName + " event cannot be deserialized."); } else { throw new FormatException("The type parameter 'type' must be the first serialized value in model event json."); } }), // Model Value Change Event new JsonConverter <ModelValueChangeEvent>( (modelEvent, json) => { //Property sequence matters json.Set("type", "ValueChange"); json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId)); json.Set("property", modelEvent.Property.Name); json.Set("oldValue", modelEvent.OldValue); json.Set("newValue", modelEvent.NewValue); }, deserializeValueChangeEvent), // Model Reference Change Event new JsonConverter <ModelReferenceChangeEvent>( (modelEvent, json) => { json.Set("type", "ReferenceChange"); json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId)); json.Set("property", modelEvent.Property.Name); json.Set("oldValue", GetEventInstance(modelEvent.OldValue, modelEvent.OldValueId)); json.Set("newValue", GetEventInstance(modelEvent.NewValue, modelEvent.NewValueId)); }, deserializeReferenceChangeEvent), // Model List Change Event new JsonConverter <ModelListChangeEvent>( (modelEvent, json) => { json.Set("type", "ListChange"); json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId)); json.Set("property", modelEvent.Property.Name); json.Set("added", modelEvent.Added.Select((instance, index) => GetEventInstance(instance, modelEvent.AddedIds.ElementAt(index)))); json.Set("removed", modelEvent.Removed.Select((instance, index) => GetEventInstance(instance, modelEvent.RemovedIds.ElementAt(index)))); }, deserializeListChangeEvent), // Model Init New Event new JsonConverter <ModelInitEvent.InitNew>( (modelEvent, json) => { json.Set("type", "InitNew"); json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId)); }, deserializeInitNewEvent), // Model Init Existing Event new JsonConverter <ModelInitEvent.InitExisting>( (modelEvent, json) => { json.Set("type", "InitExisting"); json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId)); }, deserializeInitExistingEvent), // Model Delete Event new JsonConverter <ModelDeleteEvent>( (modelEvent, json) => { json.Set("type", "Delete"); json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId)); json.Set("isPendingDelete", modelEvent.IsPendingDelete); }, deserializeDeleteEvent), // Model Save Event new JsonConverter <ModelSaveEvent>( (modelEvent, json) => { json.Set("type", "Save"); json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId)); json.Set("added", modelEvent.Added.Select(instance => new Dictionary <string, string>() { { "type", instance.Type.Name }, { "oldId", instance.OriginalId }, { "newId", instance.Id } })); json.Set("modified", modelEvent.Modified); json.Set("deleted", modelEvent.Deleted); }, json => { throw new NotSupportedException("ModelSaveEvent cannot be deserialized."); }), // Condition Type new JsonConverter <ConditionType>( (conditionType, json) => { json.Set("code", conditionType.Code); json.Set("category", conditionType.Category.ToString()); if (conditionType.Sets != null && conditionType.Sets.Any()) { json.Set("sets", conditionType.Sets.Select(set => set.Name)); } json.Set("message", conditionType.Message); }, json => { throw new NotSupportedException("ConditionType cannot be deserialized."); }), // Condition new JsonConverter <Condition>( (condition, json) => { if (condition.Message != condition.Type.Message) { json.Set("message", condition.Message); } json.Set("targets", condition.Targets.Where(ct => ct.Target != null)); }, json => { throw new NotSupportedException("Condition cannot be deserialized."); }), // Condition Target new JsonConverter <ConditionTarget>( (conditionTarget, json) => { json.Set("instance", conditionTarget.Target); json.Set("properties", conditionTarget.Properties); }, json => { throw new NotSupportedException("ConditionTarget cannot be deserialized."); }), // Rule new JsonConverter <Rule>( (rule, json) => { if (rule is ICalculationRule) { var calculation = (ICalculationRule)rule; json.Set("onChangeOf", calculation.Predicates); if (rule is IClientCalculationRule) { var clientCalc = (IClientCalculationRule)rule; json.Set("calculate", clientCalc.FunctionBody); foreach (var export in clientCalc.Exports) { json.Global <Dictionary <string, string> >("exports")[export.Key] = export.Value; } } else { json.Set("calculate", calculation.Calculation); } } else if (rule is IConditionRule) { var condition = (IConditionRule)rule; json.Set("type", "condition"); json.Set("properties", condition.Properties); json.Set("onChangeOf", condition.Predicates); json.Set("conditionType", condition.ConditionType); json.Set("assert", condition.Condition); } else { throw new NotSupportedException("Rules of type " + rule.GetType().FullName + " cannot be serialized. Call ExoWeb.RegisterConverters() to register a converter to support serializing rules of this type."); } }, json => { throw new NotSupportedException("Rule cannot be deserialized."); }), // AllowedValuesRule new JsonConverter <AllowedValuesRule>( (rule, json) => { SerializePropertyRule(rule, json); var sourceExpression = rule.SourceExpression; if (sourceExpression != null) { json.Set("fn", sourceExpression.Expression); if (!String.IsNullOrEmpty(rule.Path)) { json.Set("onChangeOf", new string[] { rule.Path }); } } else { json.Set("source", rule.Source); } if (rule.IgnoreValidation) { json.Set("ignoreValidation", rule.IgnoreValidation); } }, json => { throw new NotSupportedException("AllowedValuesRule cannot be deserialized."); }), // CompareRule new JsonConverter <CompareRule>( (rule, json) => { SerializePropertyRule(rule, json); json.Set("compareOperator", rule.CompareOperator.ToString()); json.Set("compareSource", rule.CompareSource); }, json => { throw new NotSupportedException("CompareRule cannot be deserialized."); }), // ListLengthRule new JsonConverter <ListLengthRule>( (rule, json) => { SerializePropertyRule(rule, json); // Min if (rule.MinExpression != null) { json.Set("minFn", rule.MinExpression.Expression); } else { json.Set("min", rule.Minimum); } // Max if (rule.MaxExpression != null) { json.Set("maxFn", rule.MaxExpression.Expression); } else { json.Set("max", rule.Maximum); } // OnChangeOf if (!String.IsNullOrEmpty(rule.Path)) { json.Set("onChangeOf", new string[] { rule.Path }); } }, json => { throw new NotSupportedException("ListLengthRule cannot be deserialized."); }), // RangeRule new JsonConverter <RangeRule>( (rule, json) => { SerializePropertyRule(rule, json); // Min if (rule.MinExpression != null) { json.Set("minFn", rule.MinExpression.Expression); } else { json.Set("min", rule.Minimum); } // Max if (rule.MaxExpression != null) { json.Set("maxFn", rule.MaxExpression.Expression); } else { json.Set("max", rule.Maximum); } // OnChangeOf if (!String.IsNullOrEmpty(rule.Path)) { json.Set("onChangeOf", new string[] { rule.Path }); } }, json => { throw new NotSupportedException("RangeRule cannot be deserialized."); }), // ValidationRule new JsonConverter <ValidationRule>( (rule, json) => { SerializePropertyRule(rule, json); // Validation json.Set("isError", rule.ValidationExpression.Expression); // ErrorMessage if (rule.ErrorMessageResource != null) { json.Set("message", rule.ErrorMessageResource); } else { json.Set("message", rule.ErrorMessageExpression.Expression); } json.Set("properties", rule.AdditionalTargets); // OnChangeOf if (!String.IsNullOrEmpty(rule.Path)) { json.Set("onChangeOf", new string[] { rule.Path }); } }, json => { throw new NotSupportedException("ValidationRule cannot be deserialized."); }), // RequiredRule new JsonConverter <RequiredRule>( (rule, json) => { SerializePropertyRule(rule, json); if (rule.RequiredValue != null) { json.Set("requiredValue", rule.RequiredValue); } }, json => { throw new NotSupportedException("RequiredRule cannot be deserialized."); }), // RequiredIfRule new JsonConverter <RequiredIfRule>( (rule, json) => { SerializePropertyRule(rule, json); if (rule.RequiredExpression != null) { json.Set("fn", rule.RequiredExpression.Expression); // OnChangeOf if (!String.IsNullOrEmpty(rule.Path)) { json.Set("onChangeOf", new string[] { rule.Path }); } } else { json.Set("compareOperator", rule.CompareOperator.ToString()); json.Set("compareSource", rule.CompareSource); json.Set("compareValue", rule.CompareValue); } if (rule.RequiredValue != null) { json.Set("requiredValue", rule.RequiredValue); } }, json => { throw new NotSupportedException("RequiredIfRule cannot be deserialized."); }), // OwnerRule new JsonConverter <OwnerRule>( (rule, json) => SerializePropertyRule(rule, json), json => { throw new NotSupportedException("OwnerRule cannot be deserialized."); }), // StringLengthRule new JsonConverter <StringLengthRule>( (rule, json) => { SerializePropertyRule(rule, json); if (rule.Minimum > 0) { json.Set("min", rule.Minimum); } if (rule.Maximum > 0) { json.Set("max", rule.Maximum); } }, json => { throw new NotSupportedException("StringLengthRule cannot be deserialized."); }), // StringFormatRule new JsonConverter <StringFormatRule>( (rule, json) => { SerializePropertyRule(rule, json); if (!String.IsNullOrEmpty(rule.FormatDescription)) { json.Set("description", rule.FormatDescription); } json.Set("expression", rule.FormatExpression.ToString()); if (!String.IsNullOrEmpty(rule.ReformatExpression)) { json.Set("reformat", rule.ReformatExpression); } }, json => { throw new NotSupportedException("StringFormatRule cannot be deserialized."); }), }) { JsonUtility.RegisterConverter(converter); } // Cache the method info of the deserialize method // The non-generic version of this method was added in .NET 4.0 deserialize = serializer.GetType().GetMethod("Deserialize", new Type[] { typeof(string) }); }
protected override System.Collections.IList ConvertToList(ModelReferenceProperty property, object list) { return((IList)list); }
protected override object GetMissingPropertyValue(string jsPropertyName) { // special meta property if (jsPropertyName == "meta") { return(LazyDefineProperty("meta", new Meta(Engine, RealObject))); } // handle model properties string modelPropertyName; if (jsPropertyName.StartsWith(GetterPrefix)) { modelPropertyName = jsPropertyName.Substring(GetterPrefix.Length); } else if (jsPropertyName.StartsWith(SetterPrefix)) { throw new InvalidOperationException("Properties are read-only"); } else { throw new InvalidOperationException("Only property get accessors are supported on model objects: " + jsPropertyName); } ModelProperty property = RealObject.Type.Properties[modelPropertyName]; if (property == null) { throw new InvalidPropertyException(RealObject.Type, modelPropertyName); } if (property is ModelValueProperty) { // optimization: cast outside of delegate ModelValueProperty valueProperty = (ModelValueProperty)property; return(LazyDefineMethod(jsPropertyName, instance => { var value = instance.GetValue(valueProperty); if (value is decimal) { return decimal.ToDouble((decimal)value); } if (value is float) { return Convert.ToDouble((float)value); } if (value is long) { return Convert.ToInt32((long)value); } if (value is DateTime) { var dateTime = (DateTime)value; // Use the same serialization format as `JsonUtility.UtcDateTimeConverter`. var dateString = dateTime.ToUniversalTime().ToString(@"yyyy-MM-dd\THH:mm:ss.fff\Z"); var jsDate = Engine.Date.Construct(dateString); return jsDate; } return value; })); } ModelReferenceProperty refProperty = (ModelReferenceProperty)property; if (refProperty.IsList) { return(LazyDefineMethod(jsPropertyName, instance => { return factory.Wrap(instance.GetList(refProperty)); })); } else { return(LazyDefineMethod(jsPropertyName, instance => { return factory.Wrap(instance.GetReference(refProperty)); })); } }
/// <summary> /// Processes static property paths in order to determine the information to serialize. /// </summary> /// <param name="path"></param> internal static void PrepareStaticPath(string path, ServiceResponse response) { string type = null; string property = null; try { if (path.IndexOf('.') < 0) { throw new ArgumentException("'" + path + "' is not a valid static property path."); } // Split the static property reference int propertyIndex = path.LastIndexOf('.'); type = path.Substring(0, propertyIndex); property = path.Substring(propertyIndex + 1); // Get the model type ModelType modelType = ModelContext.Current.GetModelType(type); if (modelType == null) { throw new ArgumentException("'" + type + "' is not a valid model type for the static property path of '" + path + "'."); } // Get the model property ModelProperty modelProperty = modelType.Properties[property]; if (modelProperty == null || !modelProperty.IsStatic) { throw new ArgumentException("'" + property + "' is not a valid property for the static property path of '" + path + "'."); } // Add the property to the set of static properties to serialize response.GetModelTypeInfo(modelType).StaticProperties.Add(modelProperty); // Register instances for static reference properties to be serialized ModelReferenceProperty reference = modelProperty as ModelReferenceProperty; if (reference != null) { // Get the cached set of instances to be serialized for the property type ModelTypeInfo propertyTypeInfo = response.GetModelTypeInfo(reference.PropertyType); // Static lists if (reference.IsList) { foreach (ModelInstance instance in modelType.GetList(reference)) { ModelTypeInfo typeInfo = instance.Type == reference.PropertyType ? propertyTypeInfo : response.GetModelTypeInfo(instance.Type); if (!typeInfo.Instances.ContainsKey(instance.Id)) { typeInfo.Instances.Add(instance.Id, new ModelInstanceInfo(instance)); } } } // Static references else { ModelInstance instance = modelType.GetReference(reference); if (instance != null) { ModelTypeInfo typeInfo = instance.Type == reference.PropertyType ? propertyTypeInfo : response.GetModelTypeInfo(instance.Type); if (!typeInfo.Instances.ContainsKey(instance.Id)) { typeInfo.Instances.Add(instance.Id, new ModelInstanceInfo(instance)); } } } } } catch (Exception ex) { throw new ApplicationException(string.Format("Error preparing static path '{0}'{1}{2}: [{3}]", path, type == null ? string.Empty : (" for type '" + type + "'"), property == null ? string.Empty : (" and property '" + property + "'"), ex.Message), ex); } }
/// <summary> /// Gets the set of precondition rules created by the provider. /// </summary> /// <param name="name"></param> /// <returns></returns> public virtual IEnumerable <Rule> GetRules(Type sourceType, string name) { if (rules == null) { // Process each type var context = ModelContext.Current; rules = new List <Rule>(); foreach (var type in types.Select(t => context.GetModelType(t)).Where(t => t != null)) { // Process each instance property declared on the current type foreach (var property in type.Properties.Where(property => property.DeclaringType == type && !property.IsStatic)) { // Required Attribute foreach (var attr in property.GetAttributes <RequiredAttribute>().Take(1)) { object requiredValue = null; if (property is ModelValueProperty && ((ModelValueProperty)property).PropertyType == typeof(bool)) { requiredValue = true; } // Use the error message if one is specifed, otherwise use the default bahavior if (string.IsNullOrEmpty(attr.ErrorMessage)) { rules.Add(new RequiredRule(type.Name, property.Name, requiredValue)); } else { rules.Add(new RequiredRule(type.Name, property.Name, attr.ErrorMessage, requiredValue)); } } // String Length Attribute foreach (var attr in property.GetAttributes <StringLengthAttribute>().Take(1)) { // Use the error message if one is specifed, otherwise use the default bahavior if (string.IsNullOrEmpty(attr.ErrorMessage)) { rules.Add(new StringLengthRule(type.Name, property.Name, attr.MinimumLength, attr.MaximumLength)); } else { rules.Add(new StringLengthRule(type.Name, property.Name, attr.MinimumLength, attr.MaximumLength, attr.ErrorMessage)); } } // Range Attribute foreach (var attr in property.GetAttributes <RangeAttribute>().Take(1)) { // Use the error message if one is specifed, otherwise use the default bahavior if (string.IsNullOrEmpty(attr.ErrorMessage)) { rules.Add(new RangeRule(type.Name, property.Name, (IComparable)attr.Minimum, (IComparable)attr.Maximum)); } else { rules.Add(new RangeRule(type.Name, property.Name, (IComparable)attr.Minimum, (IComparable)attr.Maximum, attr.ErrorMessage)); } } //Compare Attribute foreach (var attr in property.GetAttributes <CompareAttribute>().Take(1)) { // Use the error message if one is specifed, otherwise use the default bahavior if (string.IsNullOrEmpty(attr.ErrorMessage)) { rules.Add(new CompareRule(type.Name, property.Name, attr.ComparisonPropertyName, attr.Operator)); } else { rules.Add(new CompareRule(type.Name, property.Name, attr.ComparisonPropertyName, attr.Operator, attr.ErrorMessage)); } } //// ListLength Attribute //foreach (var attr in property.GetAttributes<ListLengthAttribute>().Take(1)) //{ // // Use the error message if one is specifed, otherwise use the default bahavior // if (string.IsNullOrEmpty(attr.ErrorMessage)) // rules.Add(new ListLengthRule(type.Name, property.Name, attr.StaticLength, attr.LengthCompareProperty, attr.CompareOp)); // else // rules.Add(new ListLengthRule(type.Name, property.Name, attr.StaticLength, attr.LengthCompareProperty, attr.CompareOp, attr.ErrorMessage)); //} // Regular Expression Attribute foreach (var attr in property.GetAttributes <RegularExpressionAttribute>().Take(1)) { rules.Add(CreateFormatRule(type.Name, property.Name, attr)); } // Allowed Values Attribute ModelReferenceProperty reference = property as ModelReferenceProperty; if (reference != null) { string errorMessage = null; foreach (var attr in property.GetAttributes <AllowedValuesAttribute>().Take(1)) { errorMessage = attr.ErrorMessage; } foreach (var source in property.GetAttributes <AllowedValuesAttribute>() .Select(attr => attr.Source) .Union(reference.PropertyType.GetAttributes <AllowedValuesAttribute>() .Select(attr => attr.Source.Contains('.') ? attr.Source : reference.PropertyType.Name + '.' + attr.Source) .Take(1))) { rules.Add(string.IsNullOrEmpty(errorMessage) ? new AllowedValuesRule(type.Name, property.Name, source) : new AllowedValuesRule(type.Name, property.Name, source, errorMessage)); } } } } } return(rules); }
/// <summary> /// Initializes null list properties when updated via a call to <see cref="ModelInstanceList.Add"/> or <see cref="ModelInstanceList.Update"/>. /// </summary> /// <param name="instance"></param> /// <param name="property"></param> /// <returns></returns> protected override void InitializeList(ModelInstance instance, ModelReferenceProperty property) { if (property is ReflectionReferenceProperty) { // Ensure the property is a writable BusinessObjectList property var propertyInfo = ((ReflectionReferenceProperty)property).PropertyInfo; var listType = propertyInfo.PropertyType; if(!listType.IsGenericType || listType.GetGenericTypeDefinition() != typeof(ICollection<>)) throw new NotSupportedException("List initialization is only supported for lists of type ICollection<T>."); if (!propertyInfo.CanWrite) throw new NotSupportedException("List initialization is only supported for writeable properties."); var itemType = ((ReflectionModelType)property.PropertyType).UnderlyingType; // Create the list var list = typeof(ObservableCollection<>).MakeGenericType(itemType).GetConstructor(Type.EmptyTypes).Invoke(null); // Assign the list to the property propertyInfo.SetValue(instance.Instance, list, null); } else throw new NotSupportedException("List initialization is only supported for reflection-based properties."); }
protected override System.Collections.IList ConvertToList(ModelReferenceProperty property, object list) { // If the list is managed by Entity Framework, convert to a list with listeners if (list is RelatedEnd) { Type d1 = typeof(CollectionWrapper<>); Type constructed = d1.MakeGenericType(((EntityModelType) property.PropertyType).UnderlyingType); var constructor = constructed.GetConstructors()[0]; return (IList) constructor.Invoke(new object[] { list }); } return base.ConvertToList(property, list); }