function (instance, parameters, callback) {
        /// <summary>
        /// Insert a new object into a table.
        /// </summary>
        /// <param name="instance" type="Object">
        /// The instance to insert into the table.
        /// </param>
        /// <param name="parameters" type="Object" mayBeNull="true">
        /// An object of user-defined parameters and values to include in the request URI query string.
        /// </param>
        /// <param name="callback" type="Function">
        /// The callback to invoke when the insert is complete.
        /// </param>

        // Account for absent optional arguments
        if (_.isNull(callback) && (typeof parameters === 'function')) {
            callback = parameters;
            parameters = null;
        }

        // Validate the arguments
        Validate.notNull(instance, 'instance');
        if (!_.isNull(parameters)) {
            Validate.isValidParametersObject(parameters);
        }
        Validate.notNull(callback, 'callback');

        for (var i in idNames) {            
            if (!_.isNullOrZero(instance[idNames[i]])) {
                throw _.format(
                    Platform.getResourceString("MobileServiceTable_InsertIdAlreadySet"),
                    idPropertyName);
            }
        }

        // Construct the URL
        var urlFragment = _.url.combinePathSegments(tableRouteSeperatorName, this.getTableName());
        if (!_.isNull(parameters)) {
            var queryString = _.url.getQueryString(parameters);
            urlFragment = _.url.combinePathAndQuery(urlFragment, queryString);
        }

        // Make the request
        this.getMobileServiceClient()._request(
            'POST',
            urlFragment,
            instance,
            function (error, response) {
                if (!_.isNull(error)) {
                    callback(error, null);
                } else {
                    var result = _.fromJson(response.responseText);

                    result = Platform.allowPlatformToMutateOriginal(instance, result);
                    callback(null, result);
                }
            });
    });
    function (id, parameters, callback) {
        /// <summary>
        /// Gets an instance from a given table.
        /// </summary>
        /// <param name="id" type="Number" integer="true">
        /// The id of the instance to get from the table.
        /// </param>
        /// <param name="parameters" type="Object" mayBeNull="true">
        /// An object of user-defined parameters and values to include in the request URI query string.
        /// </param>
        /// <param name="callback" type="Function">
        /// The callback to invoke when the lookup is complete.
        /// </param>

        // Account for absent optional arguments
        if (_.isNull(callback) && (typeof parameters === 'function')) {
            callback = parameters;
            parameters = null;
        }

        // Validate the arguments
        Validate.isValidId(id, idPropertyName);
        if (!_.isNull(parameters)) {
            Validate.isValidParametersObject(parameters);
        }
        Validate.notNull(callback, 'callback');

        // Construct the URL
        var urlFragment = _.url.combinePathSegments(
                tableRouteSeperatorName,
                this.getTableName(),
                encodeURIComponent(id.toString()));

        var features = addQueryParametersFeaturesIfApplicable([], parameters);

        parameters = addSystemProperties(parameters, this.systemProperties);
        if (!_.isNull(parameters)) {
            var queryString = _.url.getQueryString(parameters);
            urlFragment = _.url.combinePathAndQuery(urlFragment, queryString);
        }

        // Make the request
        this.getMobileServiceClient()._request(
            'GET',
            urlFragment,
            null,
            false,
            null,
            features,
            function (error, response) {
                if (!_.isNull(error)) {
                    callback(error, null);
                } else {
                    var result = getItemFromResponse(response);
                    callback(null, result);
                }
            });
    });
    function (instance, parameters, callback) {
        /// <summary>
        /// Delete an object from a given table.
        /// </summary>
        /// <param name="instance" type="Object">
        /// The instance to delete from the table.
        /// </param>
        /// <param name="parameters" type="Object" mayBeNull="true">
        /// An object of user-defined parameters and values to include in the request URI query string.
        /// </param>
        /// <param name="callback" type="Function">
        /// The callback to invoke when the delete is complete.
        /// </param>

        // Account for absent optional arguments
        if (_.isNull(callback) && (typeof parameters === 'function')) {
            callback = parameters;
            parameters = null;
        }

        // Validate the arguments
        Validate.notNull(instance, 'instance');
        Validate.notNullOrZero(instance[idPropertyName], 'instance.' + idPropertyName);
        if (!_.isNull(parameters)) {
            Validate.isValidParametersObject(parameters);
        }
        Validate.notNull(callback, 'callback');

        // Contruct the URL
        var urlFragment =  _.url.combinePathSegments(
                tableRouteSeperatorName,
                this.getTableName(),
                instance[idPropertyName].toString());
        if (!_.isNull(parameters)) {
            var queryString = _.url.getQueryString(parameters);
            urlFragment = _.url.combinePathAndQuery(urlFragment, queryString);
        }

        // Make the request
        this.getMobileServiceClient()._request(
            'DELETE',
            urlFragment,
            null,
            function (error, response) {
                callback(error);
            });
    });
MobileServiceTable.prototype._read = function (query, parameters, callback) {
    /// <summary>
    /// Query a table.
    /// </summary>
    /// <param name="query" type="Object" mayBeNull="true">
    /// The query to execute.  It can be null or undefined to get the entire
    /// collection.
    /// </param>
    /// <param name="parameters" type="Object" mayBeNull="true">
    /// An object of user-defined parameters and values to include in the request URI query string.
    /// </param>
    /// <param name="callback" type="Function">
    /// The callback to invoke when the query is complete.
    /// </param>

    // Account for absent optional arguments
    if (_.isNull(callback))
    {
        if (_.isNull(parameters) && (typeof query === 'function')) {
            callback = query;
            query = null;
            parameters = null;
        } else if (typeof parameters === 'function') {
            callback = parameters;
            parameters = null;
            if (!_.isNull(query) && _.isObject(query)) {
                // This 'query' argument could be either the query or the user-defined 
                // parameters object since both are optional.  A query is either (a) a simple string 
                // or (b) an Object with an toOData member. A user-defined parameters object is just 
                // an Object.  We need to detect which of these has been passed in here.
                if (!_.isString(query) && _.isNull(query.toOData)) {
                    parameters = query;
                    query = null;
                }
            }
        }
    }

    // Validate the arguments
    if (query && _.isString(query)) {
        Validate.notNullOrEmpty(query, 'query');
    }
    if (!_.isNull(parameters)) {
        Validate.isValidParametersObject(parameters, 'parameters');
    }
    Validate.notNull(callback, 'callback');

    // Get the query string
    var tableName = this.getTableName();
    var queryString = null;
    var projection = null;
    if (_.isString(query)) {
        queryString = query;
    } else if (_.isObject(query) && !_.isNull(query.toOData)) {
        if (query.getComponents) {
            var components = query.getComponents();
            projection = components.projection;
            if (components.table) {
                // If the query has a table name, make sure it's compatible with
                // the table executing the query
                
                if (tableName !== components.table) {
                    var message = _.format(Platform.getResourceString("MobileServiceTable_ReadMismatchedQueryTables"), tableName, components.table);
                    callback(_.createError(message), null);
                    return;
                }

                // The oDataQuery will include the table name; we need to remove
                // because the url fragment already includes the table name.
                var oDataQuery = query.toOData();
                queryString = oDataQuery.replace(new RegExp('^/' + components.table), '');
            }
        }
    }

    // Add any user-defined query string parameters
    if (!_.isNull(parameters)) {
        var userDefinedQueryString = _.url.getQueryString(parameters);
        if (!_.isNullOrEmpty(queryString)) {
            queryString += '&' + userDefinedQueryString;
        }
        else {
            queryString = userDefinedQueryString;
        }
    }
    
    // Construct the URL
    var urlFragment = _.url.combinePathSegments(tableRouteSeperatorName, tableName);
    if (!_.isNull(queryString)) {
        urlFragment = _.url.combinePathAndQuery(urlFragment, queryString);
    }

    // Make the request
    this.getMobileServiceClient()._request(
        'GET',
        urlFragment,
        null,
        function (error, response) {
            var values = null;
            if (_.isNull(error)) {
                // Parse the response
                values = _.fromJson(response.responseText);

                // If the values include the total count, we'll attach that
                // directly to the array
                if (values &&
                    !Array.isArray(values) &&
                    typeof values.count !== 'undefined' &&
                    typeof values.results !== 'undefined') {
                    // Create a new total count property on the values array
                    values.results.totalCount = values.count;
                    values = values.results;
                }

                // If we have a projection function, apply it to each item
                // in the collection
                if (projection !== null) {
                    var i = 0;
                    for (i = 0; i < values.length; i++) {
                        values[i] = projection.call(values[i]);
                    }
                }
            }
            callback(error, values);
        });
};
    function (instance, parameters, callback) {
        /// <summary>
        ///  Refresh the current instance with the latest values from the
        ///  table.
        /// </summary>
        /// <param name="instance" type="Object">
        /// The instance to refresh.
        /// </param>
        /// <param name="parameters" type="Object" mayBeNull="true">
        /// An object of user-defined parameters and values to include in the request URI query string.
        /// </param>
        /// <param name="callback" type="Function">
        /// The callback to invoke when the refresh is complete.
        /// </param>

        // Account for absent optional arguments
        if (_.isNull(callback) && (typeof parameters === 'function')) {
            callback = parameters;
            parameters = null;
        }

        // Validate the arguments
        Validate.notNull(instance, 'instance');
        if (_.isNullOrZero(instance[idPropertyName]))
        {
            callback(null, instance);
            return;
        }

        if (!_.isNull(parameters)) {
            Validate.isValidParametersObject(parameters, 'parameters');
        }
        Validate.notNull(callback, 'callback');

        // Construct the URL

        var urlFragment = _.url.combinePathSegments(
                tableRouteSeperatorName,
                this.getTableName());
        urlFragment = _.url.combinePathAndQuery(urlFragment, "?$filter=id eq " + instance[idPropertyName].toString());

        if (!_.isNull(parameters)) {
            var queryString = _.url.getQueryString(parameters);
            urlFragment = _.url.combinePathAndQuery(urlFragment, queryString);
        }

        // Make the request
        this.getMobileServiceClient()._request(
            'GET',
            urlFragment,
            instance,
            function (error, response) {
                if (!_.isNull(error)) {
                    callback(error, null);
                } else {
                    var result = _.fromJson(response.responseText);
                    if (Array.isArray(result)) {
                        result = result[0]; //get first object from array
                    }

                    if (!result) {
                        throw _.format(
                            Platform.getResourceString("MobileServiceTable_NotSingleObject"),
                            idPropertyName);
                    }

                    result = Platform.allowPlatformToMutateOriginal(instance, result);
                    callback(null, result);
                }
            });
    });
    function (apiName, options, callback) {   
        /// <summary>
        /// Invokes the specified custom api and returns a response object.
        /// </summary>
        /// <param name="apiName">
        /// The custom api to invoke.
        /// </param>
        /// <param name="options" mayBeNull="true">
        /// Contains additional parameter information, valid values are:
        /// body: The body of the HTTP request.
        /// method: The HTTP method to use in the request, with the default being POST,
        /// parameters: Any additional query string parameters, 
        /// headers: HTTP request headers, specified as an object.
        /// </param>
        /// <param name="callback" type="Function" mayBeNull="true">
        /// Optional callback accepting (error, results) parameters.
        /// </param>

        Validate.isString(apiName, 'apiName');

        // Account for absent optional arguments
        if (_.isNull(callback)) {
            if (typeof options === 'function') {
                callback = options;
                options = null;
            }
        }
        Validate.notNull(callback, 'callback');

        var parameters, method, body, headers;
        if (!_.isNull(options)) {
            parameters = options.parameters;
            if (!_.isNull(parameters)) {
                Validate.isValidParametersObject(options.parameters);
            }

            method = options.method;
            body = options.body;
            headers = options.headers;
        }
        if (_.isNull(method)) {
            method = "POST";
        }

        // Construct the URL
        var urlFragment = _.url.combinePathSegments("api", apiName);
        if (!_.isNull(parameters)) {
            var queryString = _.url.getQueryString(parameters);
            urlFragment = _.url.combinePathAndQuery(urlFragment, queryString);
        }

        // Make the request
        this._request(
            method,
            urlFragment,
            body,
            null,
            headers,
            function (error, response) {
                if (!_.isNull(error)) {
                    callback(error, null);
                } else {
                    if (typeof response.getResponseHeader === 'undefined') { // (when using IframeTransport, IE9)
                        try {
                            response.result = _.fromJson(response.responseText);
                        } catch(e) {
                            // Do nothing, since we don't know the content-type, failing may be ok
                        }
                    } else if (response.getResponseHeader('Content-Type').toLowerCase().indexOf('json') !== -1) {
                        response.result = _.fromJson(response.responseText);
                    }

                    callback(null, response);
                }
            });

    });
    function (instance, parameters, callback) {
        /// <summary>
        /// Delete an object from a given table.
        /// </summary>
        /// <param name="instance" type="Object">
        /// The instance to delete from the table.
        /// </param>
        /// <param name="parameters" type="Object" mayBeNull="true">
        /// An object of user-defined parameters and values to include in the request URI query string.
        /// </param>
        /// <param name="callback" type="Function">
        /// The callback to invoke when the delete is complete.
        /// </param>

        // Account for absent optional arguments
        if (_.isNull(callback) && (typeof parameters === 'function')) {
            callback = parameters;
            parameters = null;
        }        

        // Validate the arguments
        Validate.notNull(instance, 'instance');
        Validate.isValidId(instance[idPropertyName], 'instance.' + idPropertyName);
        Validate.notNull(callback, 'callback');

        var headers = {};
        var features = [];
        if (_.isString(instance[idPropertyName])) {
            if (!_.isNullOrEmpty(instance.__version)) {
                headers['If-Match'] = getEtagFromVersion(instance.__version);
                features.push(WindowsAzure.MobileServiceClient._zumoFeatures.OptimisticConcurrency);
            }
        }

        features = addQueryParametersFeaturesIfApplicable(features, parameters);

        parameters = addSystemProperties(parameters, this.systemProperties);
        if (!_.isNull(parameters)) {
            Validate.isValidParametersObject(parameters);
        }

        // Contruct the URL
        var urlFragment =  _.url.combinePathSegments(
                tableRouteSeperatorName,
                this.getTableName(),
                encodeURIComponent(instance[idPropertyName].toString()));
        if (!_.isNull(parameters)) {
            var queryString = _.url.getQueryString(parameters);
            urlFragment = _.url.combinePathAndQuery(urlFragment, queryString);
        }

        // Make the request
        this.getMobileServiceClient()._request(
            'DELETE',
            urlFragment,
            null,
            false,
            headers,
            features,
            function (error, response) {
                if (!_.isNull(error)) {
                    setServerItemIfPreconditionFailed(error);
                }
                callback(error);
            });
    });
    function (instance, parameters, callback) {
        /// <summary>
        ///  Refresh the current instance with the latest values from the
        ///  table.
        /// </summary>
        /// <param name="instance" type="Object">
        /// The instance to refresh.
        /// </param>
        /// <param name="parameters" type="Object" mayBeNull="true">
        /// An object of user-defined parameters and values to include in the request URI query string.
        /// </param>
        /// <param name="callback" type="Function">
        /// The callback to invoke when the refresh is complete.
        /// </param>

        // Account for absent optional arguments
        if (_.isNull(callback) && (typeof parameters === 'function')) {
            callback = parameters;
            parameters = null;
        }

        // Validate the arguments
        Validate.notNull(instance, 'instance');
        if (!_.isValidId(instance[idPropertyName], idPropertyName))
        {
            if (typeof instance[idPropertyName] === 'string' && instance[idPropertyName] !== '') {
                throw _.format(Platform.getResourceString("Validate_InvalidId"), idPropertyName);
            } else {
                callback(null, instance);
            }
            return;
        }

        if (!_.isNull(parameters)) {
            Validate.isValidParametersObject(parameters, 'parameters');
        }
        Validate.notNull(callback, 'callback');

        // Construct the URL
        var urlFragment = _.url.combinePathSegments(
                tableRouteSeperatorName,
                this.getTableName());

        if (typeof instance[idPropertyName] === 'string') {
            var id = encodeURIComponent(instance[idPropertyName]).replace(/\'/g, '%27%27');
            urlFragment = _.url.combinePathAndQuery(urlFragment, "?$filter=id eq '" + id + "'");
        } else {
            urlFragment = _.url.combinePathAndQuery(urlFragment, "?$filter=id eq " + encodeURIComponent(instance[idPropertyName].toString()));
        }

        if (!_.isNull(parameters)) {
            var queryString = _.url.getQueryString(parameters);
            urlFragment = _.url.combinePathAndQuery(urlFragment, queryString);
        }

        var features = [WindowsAzure.MobileServiceClient._zumoFeatures.TableRefreshCall];
        features = addQueryParametersFeaturesIfApplicable(features, parameters);

        // Make the request
        this.getMobileServiceClient()._request(
            'GET',
            urlFragment,
            instance,
            false,
            null,
            features,
            function (error, response) {
                if (!_.isNull(error)) {
                    callback(error, null);
                } else {
                    var result = _.fromJson(response.responseText);
                    if (Array.isArray(result)) {
                        result = result[0]; //get first object from array
                    }

                    if (!result) {
                        var message =_.format(
                            Platform.getResourceString("MobileServiceTable_NotSingleObject"),
                            idPropertyName);
                        callback(_.createError(message), null);
                    }

                    result = Platform.allowPlatformToMutateOriginal(instance, result);
                    callback(null, result);
                }
            });
    });