public DataService(IModelSerializer serializer, FSO.Content.Content content) { this.Serializer = serializer; this.DataDefinition = content.DataDefinition; //Build Struct => Field[] maps for quicker serialization foreach (var derived in DataDefinition.DerivedStructs) { var type = MaskedStructUtils.FromID(derived.ID); List <StructField> fields = new List <StructField>(); var parent = DataDefinition.Structs.First(x => x.ID == derived.Parent); foreach (var field in parent.Fields) { var mask = derived.FieldMasks.FirstOrDefault(x => x.ID == field.ID); if (mask == null) { continue; } /* * var action = DerivedStructFieldMaskType.KEEP; * if (mask != null){ * action = mask.Type; * } * if (action == DerivedStructFieldMaskType.REMOVE){ * //These seems wrong, ServerMyAvatar and MyAvatar both exclude bookmarks by this logic * //continue; * } */ fields.Add(field); } MaskedStructToActualFields.Add(type, fields.ToArray()); } foreach (var _struct in DataDefinition.Structs) { StructToActualFields.Add(_struct.ID, _struct.Fields.ToArray()); } var assembly = Assembly.GetAssembly(typeof(DataService)); foreach (Type type in assembly.GetTypes()) { System.Attribute[] attributes = System.Attribute.GetCustomAttributes(type); foreach (Attribute attribute in attributes) { if (attribute is DataServiceModel) { var _struct = DataDefinition.GetStruct(type.Name); if (_struct != null) { ModelTypeById.Add(_struct.ID, type); ModelIdByType.Add(type, _struct.ID); } } } } }
public async void ApplyUpdate(cTSOTopicUpdateMessage update, ISecurityContext context) { try { var partialDotPath = new uint[update.DotPath.Length - 1]; Array.Copy(update.DotPath, partialDotPath, partialDotPath.Length); var path = await ResolveDotPath(partialDotPath); var target = path.GetValue(); if (target.Value == null) { throw new Exception("Cannot set property on null value"); } //Apply the change! var targetType = target.Value.GetType(); var finalPath = update.DotPath[update.DotPath.Length - 1]; var value = GetUpdateValue(update.Value); var provider = GetProvider(path.GetProvider()); var entity = path.GetEntity(); if (IsList(targetType)) { //Array, we expect the final path component to be an array index var arr = (IList)target.Value; var parent = path.GetParent(); var objectField = parent.Value.GetType().GetProperty(target.Name); if (finalPath < arr.Count) { //Update existing or remove on null (at index) if (IsNull(value)) { var removeItem = ((IList)target.Value)[(int)finalPath]; provider.DemandMutation(entity.Value, MutationType.ARRAY_REMOVE_ITEM, path.GetKeyPath(), removeItem, context); objectField.SetValue(parent.Value, GetGenericMethod(targetType, "RemoveAt").Invoke(target.Value, new object[] { (int)finalPath })); //TODO: make this async? if (target.Persist) { provider.PersistMutation(entity.Value, MutationType.ARRAY_REMOVE_ITEM, path.GetKeyPath(), removeItem); } } else { provider.DemandMutation(entity.Value, MutationType.ARRAY_SET_ITEM, path.GetKeyPath(), value, context); objectField.SetValue(parent.Value, GetGenericMethod(targetType, "SetItem").Invoke(target.Value, new object[] { (int)finalPath, value })); //arr[(int)finalPath] = value; //TODO: make this async? if (target.Persist) { provider.PersistMutation(entity.Value, MutationType.ARRAY_SET_ITEM, path.GetKeyPath(), value); } } } else if (finalPath >= arr.Count) { //Insert provider.DemandMutation(entity.Value, MutationType.ARRAY_SET_ITEM, path.GetKeyPath(), value, context); objectField.SetValue(parent.Value, GetGenericMethod(targetType, "Add").Invoke(target.Value, new object[] { value })); //arr.Add(value); if (target.Persist) { provider.PersistMutation(entity.Value, MutationType.ARRAY_SET_ITEM, path.GetKeyPath(), value); } } } else { //We expect a field value if (target.TypeId == 0) { throw new Exception("Trying to set field on unknown type"); } var _struct = DataDefinition.GetStruct(target.TypeId); var field = _struct.Fields.FirstOrDefault(x => x.ID == finalPath); if (field == null) { throw new Exception("Unknown field in dot path"); } var objectField = target.Value.GetType().GetProperty(field.Name); if (objectField == null) { throw new Exception("Unknown field in model: " + objectField.Name); } //If the value is null (0) and the field has a decoration of NullValueIndicatesDeletion //Delete the value instead of setting it var nullDelete = objectField.GetCustomAttribute <Key>(); if (nullDelete != null && IsNull(value)) { var parent = path.GetParent(); if (IsList(parent.Value)) { var listParent = path.Path[path.Path.Length - 3]; var lpField = listParent.Value.GetType().GetProperty(parent.Name); provider.DemandMutation(entity.Value, MutationType.ARRAY_REMOVE_ITEM, path.GetKeyPath(1), target.Value, context); lpField.SetValue(listParent.Value, GetGenericMethod(parent.Value.GetType(), "Remove", new Type[] { parent.Value.GetType().GenericTypeArguments[0] }) .Invoke(parent.Value, new object[] { target.Value })); //((IList)parent.Value).Remove(target.Value); if (parent.Persist) { provider.PersistMutation(entity.Value, MutationType.ARRAY_REMOVE_ITEM, path.GetKeyPath(1), target.Value); } } else { //TODO } } else { var persist = objectField.GetCustomAttribute <Persist>(); var clientSourced = (target.Value as AbstractModel)?.ClientSourced ?? false; //if the client is internally managing this value, do not update it. if (!clientSourced || objectField.GetCustomAttribute <ClientSourced>() == null) { provider.DemandMutation(entity.Value, MutationType.SET_FIELD_VALUE, path.GetKeyPath(objectField.Name), value, context); objectField.SetValue(target.Value, value); if (persist != null) { provider.PersistMutation(entity.Value, MutationType.SET_FIELD_VALUE, path.GetKeyPath(objectField.Name), value); } } } } } catch (Exception e) { if (e is SecurityException) { LOG.Error("Unauthorised data service update:" + e.Message); } else { LOG.Error(e, "Data service update failed."); } } }