/// <summary> /// Delete an instance from the table. /// </summary> /// <param name="instance">The instance to delete.</param> /// <returns> /// A task that will complete when the delete has finished. /// </returns> public async Task DeleteAsync(T instance) { if (instance == null) { throw new ArgumentNullException("instance"); } // Serialize the instance JObject value = MobileServiceTableSerializer.Serialize(instance).AsObject(); // Send the request await this.DeleteAsync(value); // Clear the instance ID since it's no longer associated with that // ID on the server (note that reflection is goodly enough to turn // null into the correct value for us). SerializableType type = SerializableType.Get(typeof(T)); type.IdMember.SetValue(instance, null); }
/// <summary> /// Refresh the current instance with the latest values from the /// table. /// </summary> /// <param name="instance">The instance to refresh.</param> /// <returns> /// A task that will complete when the refresh has finished. /// </returns> public async Task RefreshAsync(T instance) { if (instance == null) { throw new ArgumentNullException("instance"); } // Only refresh if it's already on the server SerializableType type = SerializableType.Get(typeof(T)); object id = type.IdMember.GetValue(instance); if (!SerializableType.IsDefaultIdValue(id)) { // Get the latest version of this element JObject obj = await this.GetSingleValueAsync(id); // Deserialize that value back into the current instance MobileServiceTableSerializer.Deserialize(obj, instance); } }
/// <summary> /// Delete an instance from the table. /// </summary> /// <param name="instance">The instance to delete.</param> /// <param name="parameters">A dictionary of user-defined parameters and values to include in the request URI query string.</param> /// <returns> /// A task that will complete when the delete has finished. /// </returns> public Task DeleteAsync(T instance, IDictionary <string, string> parameters) { if (instance == null) { throw new ArgumentNullException("instance"); } // Serialize the instance JsonObject value = MobileServiceTableSerializer.Serialize(instance).AsObject(); // Send the request return(DeleteAsync(value, parameters) .ContinueWith(t => { // Clear the instance ID since it's no longer associated with that // ID on the server (note that reflection is goodly enough to turn // null into the correct value for us). SerializableType type = SerializableType.Get(typeof(T)); type.IdMember.SetValue(instance, null); })); }
/// <summary> /// Add an ordering constraint for an OrderBy/ThenBy call. /// </summary> /// <param name="expression">The ordering method call.</param> /// <param name="ascending"> /// Whether the order is ascending or descending. /// </param> private void AddOrdering(MethodCallExpression expression, bool ascending) { // Keep updating with the deepest nested expression structure we // can get to so that we can provide a more detailed error message Expression deepest = expression; // We only allow OrderBy(x => x.Member) expressions. Anything else // will result in a NotSupportedException. if (expression != null && expression.Arguments.Count >= 2) { LambdaExpression lambda = StripQuote(expression.Arguments[1]) as LambdaExpression; if (lambda != null) { deepest = lambda.Body ?? lambda; // Find the name of the member being ordered MemberExpression memberAccess = lambda.Body as MemberExpression; if (memberAccess != null) { if (memberAccess.Expression.NodeType == ExpressionType.Parameter) { SerializableMember member = SerializableType.GetMember(memberAccess.Member); if (member != null && member.Name != null) { // Add the ordering this.query.Ordering.Add(new KeyValuePair <string, bool>(member.Name, ascending)); return; } } } } } throw new NotSupportedException( string.Format( CultureInfo.InvariantCulture, Resources.MobileServiceTableQueryTranslator_GetOrdering_Unsupported, expression != null && expression.Method != null ? expression.Method.Name : null, deepest != null ? deepest.ToString() : null)); }
/// <summary> /// Compile the query into a MobileServiceTableQueryDescription. /// </summary> /// <returns>The compiled OData query.</returns> internal MobileServiceTableQueryDescription Compile() { // Compile the query from the underlying IQueryable's expression // tree MobileServiceTableQueryDescription compiledQuery = MobileServiceTableQueryTranslator.Translate(this.Query.Expression); // Forward along the request for the total count compiledQuery.IncludeTotalCount = this.RequestTotalCount; // Forward along the user-defined query string parameters compiledQuery.Parameters = this.Parameters; // Associate the current table with the compiled query if (string.IsNullOrEmpty(compiledQuery.TableName)) { SerializableType type = SerializableType.Get( compiledQuery.ProjectionArgumentType ?? typeof(T)); compiledQuery.TableName = type.TableName; } return(compiledQuery); }
/// <summary> /// Gets a reference to a table and its data operations. /// </summary> /// <typeparam name="T"> /// The type of the elements in the table. This implies the name of /// the table will either be the type's name or the value of the /// DataTableAttribute applied to the type. /// </typeparam> /// <returns>A reference to the table.</returns> public IMobileServiceTable <T> GetTable <T>() { SerializableType type = SerializableType.Get(typeof(T)); return(new MobileServiceTable <T>(type.TableName, this)); }
/// <summary> /// Deserialize a JSON value into an instance. /// </summary> /// <param name="value">The JSON value.</param> /// <param name="instance">The instance to deserialize into.</param> /// <param name="ignoreCustomSerialization"> /// A value to indicate whether or not custom serialization should be /// ignored if the instance implements /// ICustomMobileServiceTableSerialization. This flag is used by /// implementations of ICustomMobileServiceTableSerialization that want /// to invoke the default serialization behavior. /// </param> public static void Deserialize(JToken value, object instance, bool ignoreCustomSerialization) { if (value == null) { throw new ArgumentNullException("value"); } else if (instance == null) { throw new ArgumentNullException("instance"); } // If the instance implements // ICustomMobileServiceTableSerialization, allow it to handle its // own deserialization. if (!ignoreCustomSerialization) { ICustomMobileServiceTableSerialization custom = instance as ICustomMobileServiceTableSerialization; if (custom != null) { custom.Deserialize(value); return; } } // Get the Mobile Services specific type info SerializableType type = SerializableType.Get(instance.GetType()); // Get the object to deserialize JObject obj = value.AsObject(); if (obj == null) { throw new ArgumentException( string.Format( CultureInfo.InvariantCulture, Resources.MobileServiceTableSerializer_Deserialize_NeedObject, value.Type), "value"); } // Create a set of required members that we can remove from as we // process their values from the object. If there's anything left // in the set after processing all of the values, then we weren't // given all of our required properties. HashSet <SerializableMember> requiredMembers = new HashSet <SerializableMember>(type.Members.Values.Where(m => m.IsRequired)); // Walk through all of the members defined in the object and // deserialize them into the instance one at a time foreach (KeyValuePair <string, JToken> assignment in obj.GetPropertyValues().OrderBy(a => type.GetMemberOrder(a.Key))) { // Look up the instance member corresponding to the JSON member SerializableMember member = null; if (type.Members.TryGetValue(assignment.Key, out member)) { // Remove the member from the required list (does nothing // if it wasn't present) requiredMembers.Remove(member); // Convert the property value into a CLR value using either // the converter or a standard simple JSON mapping. This // will throw for JSON arrays or objects. Also note that // we'll still run the value returned from the converter // through the ChangeType call below to make writing // converters a little easier (but it should be a no-op // for most folks anyway). object propertyValue = null; if (member.Converter != null) { propertyValue = member.Converter.ConvertFromJson(assignment.Value); } else if (!assignment.Value.TryConvert(out propertyValue)) { throw new ArgumentException( string.Format( CultureInfo.InvariantCulture, Resources.MobileServiceTableSerializer_Deserialize_CannotDeserializeValue, assignment.Value.ToString(), type.Type.FullName, member.MemberName), "value"); } // Change the type of the value to the desired property // type (mostly to handle things like casting issues) and // set the value on the instance. object convertedValue = TypeExtensions.ChangeType(propertyValue, member.Type); member.SetValue(instance, convertedValue); } } // Ensure we were provided all of the required properties. if (requiredMembers.Count > 0) { throw new SerializationException( string.Format( CultureInfo.InvariantCulture, Resources.MobileServiceTableSerializer_Deserialize_MissingRequired, type.Type.FullName, string.Join(", ", requiredMembers.Select(m => m.Name)))); } }
/// <summary> /// Serialize an instance to a JSON value. /// </summary> /// <param name="instance">The instance to serialize.</param> /// <returns>The serialized JSON value.</returns> /// <param name="ignoreCustomSerialization"> /// A value to indicate whether or not custom serialization should be /// ignored if the instance implements /// ICustomMobileServiceTableSerialization. This flag is used by /// implementations of ICustomMobileServiceTableSerialization that /// want to invoke the default serialization behavior. /// </param> public static JToken Serialize(object instance, bool ignoreCustomSerialization) { if (instance == null) { throw new ArgumentNullException("instance"); } // If the instance implements // ICustomMobileServiceTableSerialization, allow it to handle its // own serialization. if (!ignoreCustomSerialization) { ICustomMobileServiceTableSerialization custom = instance as ICustomMobileServiceTableSerialization; if (custom != null) { return(custom.Serialize()); } } // Get the Mobile Services specific type info SerializableType type = SerializableType.Get(instance.GetType()); // Create a new JSON object to represent the instance JObject obj = new JObject(); foreach (SerializableMember member in type.Members.Values.OrderBy(m => m.Order)) { // Get the value to serialize object value = member.GetValue(instance); if (member.Converter != null) { // If there's a user defined converter, we can apply that // and set the value directly. obj.Set(member.Name, member.Converter.ConvertToJson(value)); } else if (member == type.IdMember && SerializableType.IsDefaultIdValue(value)) { // Special case the ID member so we don't write out any // value if wasn't set (i.e., has a 0 or null value). This // allows us flexibility for the type of the ID column // which is currently a long in SQL but could someday be a // GUID in something like a document database. At some // point we might also change the server to quietly ignore // default values for the ID column on insert. } else if (!obj.TrySet(member.Name, value)) { throw new ArgumentException( string.Format( CultureInfo.InvariantCulture, Resources.MobileServiceTableSerializer_Serialize_UnknownType, member.Name, member.Type.FullName, type.Type.Name), "instance"); } } return(obj); }