diff --git a/Parse/Abstractions/Platform/Objects/IParseObjectController.cs b/Parse/Abstractions/Platform/Objects/IParseObjectController.cs index a37456c2..f6b46b1a 100644 --- a/Parse/Abstractions/Platform/Objects/IParseObjectController.cs +++ b/Parse/Abstractions/Platform/Objects/IParseObjectController.cs @@ -12,7 +12,7 @@ public interface IParseObjectController Task SaveAsync(IObjectState state, IDictionary operations, string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default); - IList> SaveAllAsync(IList states, IList> operationsList, string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default); + Task>> SaveAllAsync(IList states, IList> operationsList, string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default); Task DeleteAsync(IObjectState state, string sessionToken, CancellationToken cancellationToken = default); diff --git a/Parse/Abstractions/Platform/Objects/IParseObjectCurrentController.cs b/Parse/Abstractions/Platform/Objects/IParseObjectCurrentController.cs index 4a948c45..501df0e1 100644 --- a/Parse/Abstractions/Platform/Objects/IParseObjectCurrentController.cs +++ b/Parse/Abstractions/Platform/Objects/IParseObjectCurrentController.cs @@ -2,52 +2,51 @@ using System.Threading.Tasks; using Parse.Abstractions.Infrastructure; -namespace Parse.Abstractions.Platform.Objects +namespace Parse.Abstractions.Platform.Objects; + +/// +/// IParseObjectCurrentController controls the single-instance +/// persistence used throughout the code-base. Sample usages are and +/// . +/// +/// Type of object being persisted. +public interface IParseObjectCurrentController where T : ParseObject { /// - /// IParseObjectCurrentController controls the single-instance - /// persistence used throughout the code-base. Sample usages are and - /// . + /// Persists current . /// - /// Type of object being persisted. - public interface IParseObjectCurrentController where T : ParseObject - { - /// - /// Persists current . - /// - /// to be persisted. - /// The cancellation token. - Task SetAsync(T obj, CancellationToken cancellationToken = default); + /// to be persisted. + /// The cancellation token. + Task SetAsync(T obj, CancellationToken cancellationToken = default); - /// - /// Gets the persisted current . - /// - /// The cancellation token. - Task GetAsync(IServiceHub serviceHub, CancellationToken cancellationToken = default); + /// + /// Gets the persisted current . + /// + /// The cancellation token. + Task GetAsync(IServiceHub serviceHub, CancellationToken cancellationToken = default); - /// - /// Returns a that resolves to true if current - /// exists. - /// - /// The cancellation token. - Task ExistsAsync(CancellationToken cancellationToken = default); + /// + /// Returns a that resolves to true if current + /// exists. + /// + /// The cancellation token. + Task ExistsAsync(CancellationToken cancellationToken = default); - /// - /// Returns true if the given is the persisted current - /// . - /// - /// The object to check. - /// true if obj is the current persisted . - bool IsCurrent(T obj); + /// + /// Returns true if the given is the persisted current + /// . + /// + /// The object to check. + /// true if obj is the current persisted . + bool IsCurrent(T obj); - /// - /// Nullifies the current from memory. - /// - void ClearFromMemory(); + /// + /// Nullifies the current from memory. + /// + void ClearFromMemory(); - /// - /// Clears current from disk. - /// - void ClearFromDisk(); - } + /// + /// Clears current from disk. + /// + Task ClearFromDiskAsync(); } diff --git a/Parse/Abstractions/Platform/Sessions/IParseSessionController.cs b/Parse/Abstractions/Platform/Sessions/IParseSessionController.cs index 3b89aff9..98c64519 100644 --- a/Parse/Abstractions/Platform/Sessions/IParseSessionController.cs +++ b/Parse/Abstractions/Platform/Sessions/IParseSessionController.cs @@ -3,16 +3,15 @@ using Parse.Abstractions.Infrastructure; using Parse.Abstractions.Platform.Objects; -namespace Parse.Abstractions.Platform.Sessions +namespace Parse.Abstractions.Platform.Sessions; + +public interface IParseSessionController { - public interface IParseSessionController - { - Task GetSessionAsync(string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default); + Task GetSessionAsync(string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default); - Task RevokeAsync(string sessionToken, CancellationToken cancellationToken = default); + Task RevokeAsync(string sessionToken, CancellationToken cancellationToken = default); - Task UpgradeToRevocableSessionAsync(string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default); + Task UpgradeToRevocableSessionAsync(string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default); - bool IsRevocableSessionToken(string sessionToken); - } + bool IsRevocableSessionToken(string sessionToken); } diff --git a/Parse/Abstractions/Platform/Users/IParseCurrentUserController.cs b/Parse/Abstractions/Platform/Users/IParseCurrentUserController.cs index 225c2e21..97437fd0 100644 --- a/Parse/Abstractions/Platform/Users/IParseCurrentUserController.cs +++ b/Parse/Abstractions/Platform/Users/IParseCurrentUserController.cs @@ -3,12 +3,11 @@ using Parse.Abstractions.Infrastructure; using Parse.Abstractions.Platform.Objects; -namespace Parse.Abstractions.Platform.Users +namespace Parse.Abstractions.Platform.Users; + +public interface IParseCurrentUserController : IParseObjectCurrentController { - public interface IParseCurrentUserController : IParseObjectCurrentController - { - Task GetCurrentSessionTokenAsync(IServiceHub serviceHub, CancellationToken cancellationToken = default); + Task GetCurrentSessionTokenAsync(IServiceHub serviceHub, CancellationToken cancellationToken = default); - Task LogOutAsync(IServiceHub serviceHub, CancellationToken cancellationToken = default); - } + Task LogOutAsync(IServiceHub serviceHub, CancellationToken cancellationToken = default); } diff --git a/Parse/Abstractions/Platform/Users/IParseUserController.cs b/Parse/Abstractions/Platform/Users/IParseUserController.cs index b5ee7b73..76666cfc 100644 --- a/Parse/Abstractions/Platform/Users/IParseUserController.cs +++ b/Parse/Abstractions/Platform/Users/IParseUserController.cs @@ -5,22 +5,20 @@ using Parse.Abstractions.Infrastructure; using Parse.Abstractions.Platform.Objects; -namespace Parse.Abstractions.Platform.Users +namespace Parse.Abstractions.Platform.Users; + +public interface IParseUserController { - public interface IParseUserController - { - Task SignUpAsync(IObjectState state, IDictionary operations, IServiceHub serviceHub, CancellationToken cancellationToken = default); + Task SignUpAsync(IObjectState state, IDictionary operations, IServiceHub serviceHub, CancellationToken cancellationToken = default); - Task LogInAsync(string username, string password, IServiceHub serviceHub, CancellationToken cancellationToken = default); + Task LogInAsync(string username, string password, IServiceHub serviceHub, CancellationToken cancellationToken = default); - Task LogInAsync(string authType, IDictionary data, IServiceHub serviceHub, CancellationToken cancellationToken = default); + Task LogInAsync(string authType, IDictionary data, IServiceHub serviceHub, CancellationToken cancellationToken = default); - Task GetUserAsync(string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default); + Task GetUserAsync(string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default); - Task RequestPasswordResetAsync(string email, CancellationToken cancellationToken = default); + Task RequestPasswordResetAsync(string email, CancellationToken cancellationToken = default); - bool RevocableSessionEnabled { get; set; } + bool RevocableSessionEnabled { get; set; } - object RevocableSessionEnabledMutex { get; } - } } diff --git a/Parse/Infrastructure/CacheController.cs b/Parse/Infrastructure/CacheController.cs index ec8d64ba..9710f1c7 100644 --- a/Parse/Infrastructure/CacheController.cs +++ b/Parse/Infrastructure/CacheController.cs @@ -192,13 +192,18 @@ FileBackedCache EnsureCacheExists(FileInfo file = default) /// Loads a settings dictionary from the file wrapped by . /// /// A storage dictionary containing the deserialized content of the storage file targeted by the instance - public Task> LoadAsync() + public async Task> LoadAsync() { - // Check if storage dictionary is already created from the controllers file (create if not) + // Ensure the cache is created before loading EnsureCacheExists(); - // Load storage dictionary content async and return the resulting dictionary type - return Queue.Enqueue(toAwait => toAwait.ContinueWith(_ => Cache.LoadAsync().OnSuccess(__ => Cache as IDataCache)).Unwrap(), CancellationToken.None); + // Load the cache content asynchronously + return await Queue.Enqueue(async (toAwait) => + { + await toAwait.ConfigureAwait(false); // Wait for any prior tasks in the queue + await Cache.LoadAsync().ConfigureAwait(false); // Load the cache + return Cache as IDataCache; // Return the cache as IDataCache + }, CancellationToken.None).ConfigureAwait(false); } /// @@ -206,15 +211,19 @@ public Task> LoadAsync() /// /// The data to be saved. /// A data cache containing the saved data. - public Task> SaveAsync(IDictionary contents) - { - return Queue.Enqueue(toAwait => toAwait.ContinueWith(_ => + public async Task> SaveAsync(IDictionary contents) { + // Ensure the cache exists and update it with the provided contents EnsureCacheExists().Update(contents); - return Cache.SaveAsync().OnSuccess(__ => Cache as IDataCache); - }).Unwrap()); + + // Save the cache asynchronously + await Cache.SaveAsync().ConfigureAwait(false); + + // Return the cache as IDataCache + return Cache as IDataCache; } + /// /// /// diff --git a/Parse/Infrastructure/Control/ParseSetOperation.cs b/Parse/Infrastructure/Control/ParseSetOperation.cs index 2abb4d74..b5cdd9cc 100644 --- a/Parse/Infrastructure/Control/ParseSetOperation.cs +++ b/Parse/Infrastructure/Control/ParseSetOperation.cs @@ -2,27 +2,26 @@ using Parse.Abstractions.Infrastructure.Control; using Parse.Infrastructure.Data; -namespace Parse.Infrastructure.Control -{ - public class ParseSetOperation : IParseFieldOperation - { - public ParseSetOperation(object value) => Value = value; +namespace Parse.Infrastructure.Control; - public object Encode(IServiceHub serviceHub) - { - return PointerOrLocalIdEncoder.Instance.Encode(Value, serviceHub); - } +public class ParseSetOperation : IParseFieldOperation +{ + public ParseSetOperation(object value) => Value = value; - public IParseFieldOperation MergeWithPrevious(IParseFieldOperation previous) - { - return this; - } + public object Encode(IServiceHub serviceHub) + { + return PointerOrLocalIdEncoder.Instance.Encode(Value, serviceHub); + } - public object Apply(object oldValue, string key) - { - return Value; - } + public IParseFieldOperation MergeWithPrevious(IParseFieldOperation previous) + { + return this; + } - public object Value { get; private set; } + public object Apply(object oldValue, string key) + { + return Value; } + + public object Value { get; private set; } } diff --git a/Parse/Infrastructure/Data/ParseDataDecoder.cs b/Parse/Infrastructure/Data/ParseDataDecoder.cs index 57c92db1..03957c0d 100644 --- a/Parse/Infrastructure/Data/ParseDataDecoder.cs +++ b/Parse/Infrastructure/Data/ParseDataDecoder.cs @@ -19,95 +19,226 @@ public class ParseDataDecoder : IParseDataDecoder IParseObjectClassController ClassController { get; } public ParseDataDecoder(IParseObjectClassController classController) => ClassController = classController; - private static DateTime? DecodeDateTime(object value) + + + static string[] Types { get; } = { "Date", "Bytes", "Pointer", "File", "GeoPoint", "Object", "Relation" }; + + public object Decode(object data, IServiceHub serviceHub) { try { - // Handle cases where the value is already a DateTime - if (value is DateTime dateTime) + // Handle dictionary objects + if (data is IDictionary dictionary) { - return dateTime; + return DecodeDictionary(dictionary, serviceHub); } - // Handle string representations of dates - if (value is string dateString) + // Handle list objects + if (data is IList list) { - if (DateTime.TryParse(dateString, out var parsedDate)) - { - return parsedDate; - } + return DecodeList(list, serviceHub); } - // Handle Unix timestamp (milliseconds since epoch) - if (value is long unixTimestamp) + // Handle primitive types (strings, numbers, etc.) + if (data is string str) { - return DateTimeOffset.FromUnixTimeMilliseconds(unixTimestamp).UtcDateTime; + return DecodeString(str); } - // Handle Unix timestamp (seconds since epoch) - if (value is int unixTimestampSeconds) + if (data is long || data is int) + { + Debug.WriteLine($"Integer data processed: {data}"); + return data; + } + if (data is bool) { - return DateTimeOffset.FromUnixTimeSeconds(unixTimestampSeconds).UtcDateTime; + Debug.WriteLine($"Bool data processed: {data}"); + return data; } + + // Fallback for unsupported types + Debug.WriteLine($"Unsupported data type encountered: {data.GetType()}"); + return data; } catch (Exception ex) { - Debug.WriteLine($"Failed to decode DateTime value: {value}, Error: {ex.Message}"); + Debug.WriteLine($"Decode failed: {ex.Message}"); + return data; // Return raw data on failure + } + } + + private object DecodeDictionary(IDictionary dictionary, IServiceHub serviceHub) + { + // Handle "__op" operations + if (dictionary.ContainsKey("__op")) + { + Debug.WriteLine("Decoding operation field (__op)."); + return ParseFieldOperations.Decode(dictionary); + } + + // Handle "__type" objects + if (dictionary.TryGetValue("__type", out var type) && Types.Contains(type.ToString())) + { + Debug.WriteLine($"Decoding Parse type object: {type}"); + return DecodeByType(dictionary, type.ToString(), serviceHub); } - // Return null if decoding fails - return null; + // Handle Parse object metadata (e.g., className, objectId) + if (dictionary.ContainsKey("className")) + { + return DecodeObjectState(dictionary); + } + + // Recursively decode nested dictionaries + return dictionary.ToDictionary(pair => pair.Key, pair => + { + try + { + return Decode(pair.Value, serviceHub); + } + catch + { + Debug.WriteLine($"Failed to decode nested field: {pair.Key}"); + return pair.Value; // Return raw value if decoding fails + } + }); } - static string[] Types { get; } = { "Date", "Bytes", "Pointer", "File", "GeoPoint", "Object", "Relation" }; - public object Decode(object data, IServiceHub serviceHub) + private object DecodeList(IList list, IServiceHub serviceHub) { - if (data is IDictionary dictionary) + return list.Select(item => { try { - var state = new MutableObjectState - { - ClassName = dictionary.ContainsKey("className") ? dictionary["className"]?.ToString() : null, - ObjectId = dictionary.ContainsKey("objectId") ? dictionary["objectId"]?.ToString() : null, - CreatedAt = dictionary.ContainsKey("createdAt") ? DecodeDateTime(dictionary["createdAt"]) : null, - UpdatedAt = dictionary.ContainsKey("updatedAt") ? DecodeDateTime(dictionary["updatedAt"]) : null, - IsNew = dictionary.ContainsKey("isNew") && Convert.ToBoolean(dictionary["isNew"]), - //EmailVerified = dictionary.ContainsKey("emailVerified") && Convert.ToBoolean(dictionary["emailVerified"]), - //Username = dictionary.ContainsKey("username") ? dictionary["username"]?.ToString() : null, - //Email = dictionary.ContainsKey("email") ? dictionary["email"]?.ToString() : null, - //SessionToken = dictionary.ContainsKey("sessionToken") ? dictionary["sessionToken"]?.ToString() : null, - ServerData = dictionary - }; - - return state; + return Decode(item, serviceHub); } - catch (Exception ex) + catch { - Debug.WriteLine($"Failed to decode MutableObjectState: {ex.Message}"); - throw; // Let the caller handle decoding errors + Debug.WriteLine("Failed to decode list item. Returning raw value."); + return item; // Return raw value on failure } + }).ToList(); + } + + private object DecodeString(string str) + { + // Example: Identify session tokens or other meaningful strings + if (str.StartsWith("r:")) + { + Debug.WriteLine($"Valid session token detected: {str}"); + return str; } - Debug.WriteLine("Data is not a compatible object for decoding. " + data.GetType()); - if (data.GetType() == typeof(string)) + Debug.WriteLine($"String data processed: {str}"); + return str; + } + + private object DecodeObjectState(IDictionary dictionary) + { + try { - Debug.WriteLine($"Data is not a compatible object for decoding. {data.GetType()} {data}"); + var state = new MutableObjectState + { + ClassName = dictionary.ContainsKey("className") ? dictionary["className"]?.ToString() : null, + ObjectId = dictionary.ContainsKey("objectId") ? dictionary["objectId"]?.ToString() : null, + CreatedAt = dictionary.ContainsKey("createdAt") ? DecodeDateTime(dictionary["createdAt"]) : null, + UpdatedAt = dictionary.ContainsKey("updatedAt") ? DecodeDateTime(dictionary["updatedAt"]) : null, + IsNew = dictionary.ContainsKey("isNew") && Convert.ToBoolean(dictionary["isNew"]), + ServerData = dictionary + }; + + Debug.WriteLine($"Successfully decoded MutableObjectState for {state.ClassName}, ObjectId: {state.ObjectId}"); + return state; } - else if (data.GetType() == typeof(Int64)) + catch (Exception ex) { - Debug.WriteLine($"Data is not a compatible object for decoding. {data.GetType()} Value: {data}"); - } - else + Debug.WriteLine($"Failed to decode MutableObjectState: {ex.Message}"); + throw; // Let the caller handle errors + } + } + + private object DecodeByType(IDictionary dictionary, string type, IServiceHub serviceHub) + { + switch (type) { - Debug.WriteLine("Data is not a compatible object for decoding. Unknown Type"); + case "Date": + return DecodeDateTime(dictionary["iso"]); + case "Pointer": + return DecodePointer(dictionary); + case "GeoPoint": + return DecodeGeoPoint(dictionary); + default: + Debug.WriteLine($"Unsupported Parse type: {type}"); + return dictionary; // Return raw dictionary for unsupported types } + } - - return null; - //throw new InvalidCastException("Input data cannot be cast to IObjectState."); + private DateTime DecodeDateTime(object data) + { + return DateTime.Parse(data.ToString()); // Assumes ISO-8601 format + } + + private object DecodePointer(IDictionary dictionary) + { + return new { ClassName = dictionary["className"], ObjectId = dictionary["objectId"] }; } + private object DecodeGeoPoint(IDictionary dictionary) + { + return new { Latitude = dictionary["latitude"], Longitude = dictionary["longitude"] }; + } + + + + //static string[] Types { get; } = { "Date", "Bytes", "Pointer", "File", "GeoPoint", "Object", "Relation" }; + //public object Decode(object data, IServiceHub serviceHub) + //{ + // if (data is IDictionary dictionary) + // { + // try + // { + // var state = new MutableObjectState + // { + // ClassName = dictionary.ContainsKey("className") ? dictionary["className"]?.ToString() : null, + // ObjectId = dictionary.ContainsKey("objectId") ? dictionary["objectId"]?.ToString() : null, + // CreatedAt = dictionary.ContainsKey("createdAt") ? DecodeDateTime(dictionary["createdAt"]) : null, + // UpdatedAt = dictionary.ContainsKey("updatedAt") ? DecodeDateTime(dictionary["updatedAt"]) : null, + // IsNew = dictionary.ContainsKey("isNew") && Convert.ToBoolean(dictionary["isNew"]), + // //EmailVerified = dictionary.ContainsKey("emailVerified") && Convert.ToBoolean(dictionary["emailVerified"]), + // //Username = dictionary.ContainsKey("username") ? dictionary["username"]?.ToString() : null, + // //Email = dictionary.ContainsKey("email") ? dictionary["email"]?.ToString() : null, + // SessionToken = dictionary.ContainsKey("sessionToken") ? dictionary["sessionToken"]?.ToString() : null, + // ServerData = dictionary + // }; + + // return state; + // } + // catch (Exception ex) + // { + // Debug.WriteLine($"Failed to decode MutableObjectState: {ex.Message}"); + // throw; // Let the caller handle decoding errors + // } + // } + // Debug.WriteLine("Data is not a compatible object for decoding. " + data.GetType()); + + // if (data.GetType() == typeof(string)) + // { + // Debug.WriteLine($"Data is not a compatible object for decoding. {data.GetType()} {data}"); + // } + // else if (data.GetType() == typeof(Int64)) + // { + // Debug.WriteLine($"Data is not a compatible object for decoding. {data.GetType()} Value: {data}"); + // } + // else + // { + // Debug.WriteLine("Data is not a compatible object for decoding. Unknown Type"); + // } + + + // return null; + // //throw new InvalidCastException("Input data cannot be cast to IObjectState."); + //} + //public object Decode(object data, IServiceHub serviceHub) //{ // try @@ -178,92 +309,6 @@ private IDictionary NormalizeDictionary(IDictionary pair.Key, pair => pair.Value); } - private object DecodeByType(IDictionary dictionary, string type, IServiceHub serviceHub) - { - try - { - dictionary = NormalizeDictionary(dictionary); // Normalize input dictionary - - switch (type) - { - case "Date": - if (dictionary.TryGetValue("iso", out var iso)) - { - if (iso is string isoString) - { - return ParseDate(isoString); - } - else - { - Debug.WriteLine($"Unexpected type for 'iso': {iso.GetType()}"); - throw new ArgumentException($"Invalid type for 'iso' field. Expected string, got {iso.GetType()}."); - } - } - Debug.WriteLine("Missing 'iso' field for Date."); - throw new ArgumentException("Invalid or missing 'iso' field for Date."); - - // Handle other cases similarly - case "Bytes": - if (dictionary.TryGetValue("base64", out var base64) && base64 is string base64String) - { - return Convert.FromBase64String(base64String); - } - throw new ArgumentException("Invalid or missing 'base64' field for Bytes."); - - case "Pointer": - if (dictionary.TryGetValue("className", out var className) && className is string classNameString && - dictionary.TryGetValue("objectId", out var objectId) && objectId is string objectIdString) - { - return DecodePointer(classNameString, objectIdString, serviceHub); - } - throw new ArgumentException("Invalid or missing fields for Pointer."); - - case "File": - if (dictionary.TryGetValue("name", out var name) && name is string nameString && - dictionary.TryGetValue("url", out var url) && url is string urlString) - { - return new ParseFile(nameString, new Uri(urlString)); - } - throw new ArgumentException("Invalid or missing fields for File."); - - case "GeoPoint": - if (dictionary.TryGetValue("latitude", out var latitude) && - dictionary.TryGetValue("longitude", out var longitude)) - { - return new ParseGeoPoint( - Conversion.To(latitude), - Conversion.To(longitude) - ); - } - throw new ArgumentException("Invalid or missing fields for GeoPoint."); - - case "Object": - if (dictionary.TryGetValue("className", out var objectClassName) && objectClassName is string objectClassNameString) - { - var state = ParseObjectCoder.Instance.Decode(dictionary, this, serviceHub); - return ClassController.GenerateObjectFromState(state, objectClassNameString, serviceHub); - } - throw new ArgumentException("Invalid or missing fields for Object."); - - case "Relation": - if (dictionary.TryGetValue("className", out var relationClassName) && relationClassName is string relationClassNameString) - { - return serviceHub.CreateRelation(null, null, relationClassNameString); - } - throw new ArgumentException("Invalid or missing fields for Relation."); - - default: - throw new NotSupportedException($"Unsupported __type: {type}"); - } - } - catch (Exception ex) - { - Debug.WriteLine($"DecodeByType failed for type '{type}': {ex.Message}"); - throw; // Re-throw to preserve stack trace - } - } - - protected virtual object DecodePointer(string className, string objectId, IServiceHub serviceHub) { return ClassController.CreateObjectWithoutData(className, objectId, serviceHub); diff --git a/Parse/Infrastructure/Data/ParseDataEncoder.cs b/Parse/Infrastructure/Data/ParseDataEncoder.cs index 1e336376..4fbf25ed 100644 --- a/Parse/Infrastructure/Data/ParseDataEncoder.cs +++ b/Parse/Infrastructure/Data/ParseDataEncoder.cs @@ -6,72 +6,71 @@ using Parse.Abstractions.Infrastructure.Control; using Parse.Infrastructure.Utilities; -namespace Parse.Infrastructure.Data +namespace Parse.Infrastructure.Data; + +/// +/// A ParseEncoder can be used to transform objects such as into JSON +/// data structures. +/// +/// +public abstract class ParseDataEncoder { - /// - /// A ParseEncoder can be used to transform objects such as into JSON - /// data structures. - /// - /// - public abstract class ParseDataEncoder + public static bool Validate(object value) { - public static bool Validate(object value) - { - return value is null || value.GetType().IsPrimitive || value is string || value is ParseObject || value is ParseACL || value is ParseFile || value is ParseGeoPoint || value is ParseRelationBase || value is DateTime || value is byte[] || Conversion.As>(value) is { } || Conversion.As>(value) is { }; - } + return value is null || value.GetType().IsPrimitive || value is string || value is ParseObject || value is ParseACL || value is ParseFile || value is ParseGeoPoint || value is ParseRelationBase || value is DateTime || value is byte[] || Conversion.As>(value) is { } || Conversion.As>(value) is { }; + } - // If this object has a special encoding, encode it and return the encoded object. Otherwise, just return the original object. + // If this object has a special encoding, encode it and return the encoded object. Otherwise, just return the original object. - public object Encode(object value, IServiceHub serviceHub) + public object Encode(object value, IServiceHub serviceHub) + { + return value switch { - return value switch + DateTime { } date => new Dictionary { - DateTime { } date => new Dictionary - { - ["iso"] = date.ToString(ParseClient.DateFormatStrings.First(), CultureInfo.InvariantCulture), - ["__type"] = "Date" - }, - byte[] { } bytes => new Dictionary - { - ["__type"] = "Bytes", - ["base64"] = Convert.ToBase64String(bytes) - }, - ParseObject { } entity => EncodeObject(entity), - IJsonConvertible { } jsonConvertible => jsonConvertible.ConvertToJSON(), - { } when Conversion.As>(value) is { } dictionary => dictionary.ToDictionary(pair => pair.Key, pair => Encode(pair.Value, serviceHub)), - { } when Conversion.As>(value) is { } list => EncodeList(list, serviceHub), + ["iso"] = date.ToString(ParseClient.DateFormatStrings.First(), CultureInfo.InvariantCulture), + ["__type"] = "Date" + }, + byte[] { } bytes => new Dictionary + { + ["__type"] = "Bytes", + ["base64"] = Convert.ToBase64String(bytes) + }, + ParseObject { } entity => EncodeObject(entity), + IJsonConvertible { } jsonConvertible => jsonConvertible.ConvertToJSON(), + { } when Conversion.As>(value) is { } dictionary => dictionary.ToDictionary(pair => pair.Key, pair => Encode(pair.Value, serviceHub)), + { } when Conversion.As>(value) is { } list => EncodeList(list, serviceHub), - // TODO (hallucinogen): convert IParseFieldOperation to IJsonConvertible + // TODO (hallucinogen): convert IParseFieldOperation to IJsonConvertible - IParseFieldOperation { } fieldOperation => fieldOperation.Encode(serviceHub), - _ => value - }; - } + IParseFieldOperation { } fieldOperation => fieldOperation.Encode(serviceHub), + _ => value + }; + } - protected abstract IDictionary EncodeObject(ParseObject value); + protected abstract IDictionary EncodeObject(ParseObject value); - object EncodeList(IList list, IServiceHub serviceHub) - { - List encoded = new List { }; + object EncodeList(IList list, IServiceHub serviceHub) + { + List encoded = new List { }; - // We need to explicitly cast `list` to `List` rather than `IList` because IL2CPP is stricter than the usual Unity AOT compiler pipeline. + // We need to explicitly cast `list` to `List` rather than `IList` because IL2CPP is stricter than the usual Unity AOT compiler pipeline. - if (ParseClient.IL2CPPCompiled && list.GetType().IsArray) - { - list = new List(list); - } + if (ParseClient.IL2CPPCompiled && list.GetType().IsArray) + { + list = new List(list); + } - foreach (object item in list) + foreach (object item in list) + { + if (!Validate(item)) { - if (!Validate(item)) - { - throw new ArgumentException("Invalid type for value in an array"); - } - - encoded.Add(Encode(item, serviceHub)); + throw new ArgumentException("Invalid type for value in an array"); } - return encoded; + encoded.Add(Encode(item, serviceHub)); } + + return encoded; } } diff --git a/Parse/Infrastructure/Data/ParseObjectCoder.cs b/Parse/Infrastructure/Data/ParseObjectCoder.cs index c197928c..f0c68c28 100644 --- a/Parse/Infrastructure/Data/ParseObjectCoder.cs +++ b/Parse/Infrastructure/Data/ParseObjectCoder.cs @@ -73,7 +73,10 @@ public IObjectState Decode(IDictionary data, IParseDataDecoder d } if (!mutableData.ContainsKey("ACL")) { - serverData["ACL"] = Extract(mutableData, "ACL", (obj) => new ParseACL(obj as IDictionary)); + var e = Extract(mutableData, "ACL", (obj) => + { + return new ParseACL(obj as IDictionary); + }); } if (createdAt != null && updatedAt == null) @@ -102,7 +105,7 @@ public IObjectState Decode(IDictionary data, IParseDataDecoder d //Email = email, //Username = username, //EmailVerified = emailVerified, - //SessionToken = sessionToken + SessionToken = sessionToken }; } diff --git a/Parse/Infrastructure/Data/PointerOrLocalIdEncoder.cs b/Parse/Infrastructure/Data/PointerOrLocalIdEncoder.cs index bd114567..997ad585 100644 --- a/Parse/Infrastructure/Data/PointerOrLocalIdEncoder.cs +++ b/Parse/Infrastructure/Data/PointerOrLocalIdEncoder.cs @@ -1,30 +1,29 @@ using System; using System.Collections.Generic; -namespace Parse.Infrastructure.Data +namespace Parse.Infrastructure.Data; + +/// +/// A that encodes as pointers. If the object does not have an , uses a local id. +/// +public class PointerOrLocalIdEncoder : ParseDataEncoder { - /// - /// A that encodes as pointers. If the object does not have an , uses a local id. - /// - public class PointerOrLocalIdEncoder : ParseDataEncoder - { - public static PointerOrLocalIdEncoder Instance { get; } = new PointerOrLocalIdEncoder { }; + public static PointerOrLocalIdEncoder Instance { get; } = new PointerOrLocalIdEncoder { }; - protected override IDictionary EncodeObject(ParseObject value) + protected override IDictionary EncodeObject(ParseObject value) + { + if (value.ObjectId is null) { - if (value.ObjectId is null) - { - // TODO (hallucinogen): handle local id. For now we throw. + // TODO (hallucinogen): handle local id. For now we throw. - throw new InvalidOperationException("Cannot create a pointer to an object without an objectId."); - } - - return new Dictionary - { - ["__type"] = "Pointer", - ["className"] = value.ClassName, - ["objectId"] = value.ObjectId - }; + throw new InvalidOperationException("Cannot create a pointer to an object without an objectId."); } + + return new Dictionary + { + ["__type"] = "Pointer", + ["className"] = value.ClassName, + ["objectId"] = value.ObjectId + }; } } diff --git a/Parse/Infrastructure/Execution/ParseCommandRunner.cs b/Parse/Infrastructure/Execution/ParseCommandRunner.cs index 4299b377..8feb7e84 100644 --- a/Parse/Infrastructure/Execution/ParseCommandRunner.cs +++ b/Parse/Infrastructure/Execution/ParseCommandRunner.cs @@ -9,176 +9,194 @@ using Parse.Abstractions.Platform.Users; using Parse.Infrastructure.Utilities; -namespace Parse.Infrastructure.Execution +namespace Parse.Infrastructure.Execution; + +/// +/// The command runner for all SDK operations that need to interact with the targeted deployment of Parse Server. +/// +public class ParseCommandRunner : IParseCommandRunner { + IWebClient WebClient { get; } + + IParseInstallationController InstallationController { get; } + + IMetadataController MetadataController { get; } + + IServerConnectionData ServerConnectionData { get; } + + Lazy UserController { get; } + + IWebClient GetWebClient() + { + return WebClient; + } + /// - /// The command runner for all SDK operations that need to interact with the targeted deployment of Parse Server. + /// Creates a new Parse SDK command runner. /// - public class ParseCommandRunner : IParseCommandRunner + /// The implementation instance to use. + /// The implementation instance to use. + public ParseCommandRunner(IWebClient webClient, IParseInstallationController installationController, IMetadataController metadataController, IServerConnectionData serverConnectionData, Lazy userController) { - IWebClient WebClient { get; } - - IParseInstallationController InstallationController { get; } + WebClient = webClient; + InstallationController = installationController; + MetadataController = metadataController; + ServerConnectionData = serverConnectionData; + UserController = userController; + } + /// + /// Runs a specified . + /// + /// The to run. + /// An instance to push upload progress data to. + /// An instance to push download progress data to. + /// An asynchronous operation cancellation token that dictates if and when the operation should be cancelled. + /// + public async Task>> RunCommandAsync( + ParseCommand command, + IProgress uploadProgress = null, + IProgress downloadProgress = null, + CancellationToken cancellationToken = default) + { + // Prepare the command + var preparedCommand = await PrepareCommand(command).ConfigureAwait(false); - IMetadataController MetadataController { get; } + // Execute the command + var response = await GetWebClient() + .ExecuteAsync(preparedCommand, uploadProgress, downloadProgress, cancellationToken) + .ConfigureAwait(false); - IServerConnectionData ServerConnectionData { get; } + cancellationToken.ThrowIfCancellationRequested(); - Lazy UserController { get; } + // Extract response + var statusCode = response.Item1; + var content = response.Item2; + var responseCode = (int) statusCode; - IWebClient GetWebClient() + if (responseCode >= 500) { - return WebClient; + // Server error, return InternalServerError + throw new ParseFailureException(ParseFailureException.ErrorCode.InternalServerError, content); } - - /// - /// Creates a new Parse SDK command runner. - /// - /// The implementation instance to use. - /// The implementation instance to use. - public ParseCommandRunner(IWebClient webClient, IParseInstallationController installationController, IMetadataController metadataController, IServerConnectionData serverConnectionData, Lazy userController) + else if(responseCode == 201) { - WebClient = webClient; - InstallationController = installationController; - MetadataController = metadataController; - ServerConnectionData = serverConnectionData; - UserController = userController; - } - /// - /// Runs a specified . - /// - /// The to run. - /// An instance to push upload progress data to. - /// An instance to push download progress data to. - /// An asynchronous operation cancellation token that dictates if and when the operation should be cancelled. - /// - public Task>> RunCommandAsync(ParseCommand command, IProgress uploadProgress = null, IProgress downloadProgress = null, CancellationToken cancellationToken = default) + } + else if(responseCode == 404) { - return PrepareCommand(command).ContinueWith(commandTask => GetWebClient().ExecuteAsync(commandTask.Result, uploadProgress, downloadProgress, cancellationToken).OnSuccess(task => + throw new ParseFailureException(ParseFailureException.ErrorCode.ERROR404, "Error 404"); + } + if (string.IsNullOrEmpty(content)) { - cancellationToken.ThrowIfCancellationRequested(); + return new Tuple>(statusCode, null); + } - Tuple response = task.Result; - string content = response.Item2; - int responseCode = (int) response.Item1; + // Try to parse the content + IDictionary contentJson = null; + try + { + contentJson = content.StartsWith("[") + ? new Dictionary { ["results"] = JsonUtilities.Parse(content) } + : JsonUtilities.Parse(content) as IDictionary; - if (responseCode >= 500) + // Add className if "username" exists + if (contentJson?.ContainsKey("username") == true) { - // Server error, return InternalServerError. - - throw new ParseFailureException(ParseFailureException.ErrorCode.InternalServerError, response.Item2); + contentJson["className"] = "_User"; } - else if (content is { }) - { - IDictionary contentJson = default; - - try - { - // TODO: Newer versions of Parse Server send the failure results back as HTML. - - contentJson = content.StartsWith("[") ? new Dictionary { ["results"] = JsonUtilities.Parse(content) } : JsonUtilities.Parse(content) as IDictionary; - // Check if "username" exists in contentJson and add "className" if so. - if (contentJson != null && contentJson.ContainsKey("username")) - { - contentJson["className"] = "_User"; // Add the className field - } - } - catch (Exception e) - { - return new Tuple>( - HttpStatusCode.BadRequest, // Use an appropriate error status code - new Dictionary - { - { "error", "Invalid or alternatively-formatted response received from server." }, - { "exception", e.Message } - } - ); - } - - if (responseCode < 200 || responseCode > 299) + } + catch (Exception ex) + { + return new Tuple>( + HttpStatusCode.BadRequest, + new Dictionary { - - return new Tuple>( - (HttpStatusCode) (contentJson.ContainsKey("code") ? (int) (long) contentJson["code"] : 400), // Use the code from contentJson or fallback to 400 (BadRequest) - new Dictionary - { - { "error", contentJson.ContainsKey("error") ? contentJson["error"] as string : content }, - { "code", contentJson.ContainsKey("code") ? contentJson["code"] : null } - } - ); + { "error", "Invalid or alternatively-formatted response received from server." }, + { "exception", ex.Message } } - - return new Tuple>(response.Item1, contentJson); - } - return new Tuple>(response.Item1, null); - })).Unwrap(); + ); } - Task PrepareCommand(ParseCommand command) + // Check if response status code is outside the success range + if (responseCode < 200 || responseCode > 299) { - ParseCommand newCommand = new ParseCommand(command) - { - Resource = ServerConnectionData.ServerURI - }; - - Task installationIdFetchTask = InstallationController.GetAsync().ContinueWith(task => - { - lock (newCommand.Headers) + return new Tuple>( + (HttpStatusCode) (contentJson?.ContainsKey("code") == true ? (int) (long) contentJson["code"] : 400), + new Dictionary { - newCommand.Headers.Add(new KeyValuePair("X-Parse-Installation-Id", task.Result.ToString())); + { "error", contentJson?.ContainsKey("error") == true ? contentJson["error"] as string : content }, + { "code", contentJson?.ContainsKey("code") == true ? contentJson["code"] : null } } + ); + } - return newCommand; - }); + // Return successful response + return new Tuple>(statusCode, contentJson); + } - // Locks needed due to installationFetchTask continuation newCommand.Headers.Add-call-related race condition (occurred once in Unity). - // TODO: Consider removal of installationFetchTask variable. + Task PrepareCommand(ParseCommand command) + { + ParseCommand newCommand = new ParseCommand(command) + { + Resource = ServerConnectionData.ServerURI + }; + Task installationIdFetchTask = InstallationController.GetAsync().ContinueWith(task => + { lock (newCommand.Headers) { - newCommand.Headers.Add(new KeyValuePair("X-Parse-Application-Id", ServerConnectionData.ApplicationID)); - newCommand.Headers.Add(new KeyValuePair("X-Parse-Client-Version", ParseClient.Version.ToString())); + newCommand.Headers.Add(new KeyValuePair("X-Parse-Installation-Id", task.Result.ToString())); + } - if (ServerConnectionData.Headers != null) - { - foreach (KeyValuePair header in ServerConnectionData.Headers) - { - newCommand.Headers.Add(header); - } - } + return newCommand; + }); - if (!String.IsNullOrEmpty(MetadataController.HostManifestData.Version)) - { - newCommand.Headers.Add(new KeyValuePair("X-Parse-App-Build-Version", MetadataController.HostManifestData.Version)); - } + // Locks needed due to installationFetchTask continuation newCommand.Headers.Add-call-related race condition (occurred once in Unity). + // TODO: Consider removal of installationFetchTask variable. - if (!String.IsNullOrEmpty(MetadataController.HostManifestData.ShortVersion)) - { - newCommand.Headers.Add(new KeyValuePair("X-Parse-App-Display-Version", MetadataController.HostManifestData.ShortVersion)); - } + lock (newCommand.Headers) + { + newCommand.Headers.Add(new KeyValuePair("X-Parse-Application-Id", ServerConnectionData.ApplicationID)); + newCommand.Headers.Add(new KeyValuePair("X-Parse-Client-Version", ParseClient.Version.ToString())); - if (!String.IsNullOrEmpty(MetadataController.EnvironmentData.OSVersion)) + if (ServerConnectionData.Headers != null) + { + foreach (KeyValuePair header in ServerConnectionData.Headers) { - newCommand.Headers.Add(new KeyValuePair("X-Parse-OS-Version", MetadataController.EnvironmentData.OSVersion)); + newCommand.Headers.Add(header); } + } - if (!String.IsNullOrEmpty(ServerConnectionData.MasterKey)) - { - newCommand.Headers.Add(new KeyValuePair("X-Parse-Master-Key", ServerConnectionData.MasterKey)); - } - else - { - newCommand.Headers.Add(new KeyValuePair("X-Parse-Windows-Key", ServerConnectionData.Key)); - } + if (!String.IsNullOrEmpty(MetadataController.HostManifestData.Version)) + { + newCommand.Headers.Add(new KeyValuePair("X-Parse-App-Build-Version", MetadataController.HostManifestData.Version)); + } - if (UserController.Value.RevocableSessionEnabled) - { - newCommand.Headers.Add(new KeyValuePair("X-Parse-Revocable-Session", "1")); - } + if (!String.IsNullOrEmpty(MetadataController.HostManifestData.ShortVersion)) + { + newCommand.Headers.Add(new KeyValuePair("X-Parse-App-Display-Version", MetadataController.HostManifestData.ShortVersion)); + } + + if (!String.IsNullOrEmpty(MetadataController.EnvironmentData.OSVersion)) + { + newCommand.Headers.Add(new KeyValuePair("X-Parse-OS-Version", MetadataController.EnvironmentData.OSVersion)); } - return installationIdFetchTask; + if (!String.IsNullOrEmpty(ServerConnectionData.MasterKey)) + { + newCommand.Headers.Add(new KeyValuePair("X-Parse-Master-Key", ServerConnectionData.MasterKey)); + } + else + { + newCommand.Headers.Add(new KeyValuePair("X-Parse-Windows-Key", ServerConnectionData.Key)); + } + + if (UserController.Value.RevocableSessionEnabled) + { + newCommand.Headers.Add(new KeyValuePair("X-Parse-Revocable-Session", "1")); + } } + + return installationIdFetchTask; } } diff --git a/Parse/Infrastructure/Execution/UniversalWebClient.cs b/Parse/Infrastructure/Execution/UniversalWebClient.cs index d6af9f3d..5305b312 100644 --- a/Parse/Infrastructure/Execution/UniversalWebClient.cs +++ b/Parse/Infrastructure/Execution/UniversalWebClient.cs @@ -11,127 +11,108 @@ using Parse.Infrastructure.Utilities; using BCLWebClient = System.Net.Http.HttpClient; -namespace Parse.Infrastructure.Execution +namespace Parse.Infrastructure.Execution; + +/// +/// A universal implementation of . +/// +public class UniversalWebClient : IWebClient { - /// - /// A universal implementation of . - /// - public class UniversalWebClient : IWebClient + static HashSet ContentHeaders { get; } = new HashSet { - static HashSet ContentHeaders { get; } = new HashSet - { - { "Allow" }, - { "Content-Disposition" }, - { "Content-Encoding" }, - { "Content-Language" }, - { "Content-Length" }, - { "Content-Location" }, - { "Content-MD5" }, - { "Content-Range" }, - { "Content-Type" }, - { "Expires" }, - { "Last-Modified" } - }; - - public UniversalWebClient() : this(new BCLWebClient { }) { } - - public UniversalWebClient(BCLWebClient client) => Client = client; - - BCLWebClient Client { get; set; } - - public Task> ExecuteAsync(WebRequest httpRequest, IProgress uploadProgress, IProgress downloadProgress, CancellationToken cancellationToken) - { - uploadProgress ??= new Progress { }; - downloadProgress ??= new Progress { }; + { "Allow" }, + { "Content-Disposition" }, + { "Content-Encoding" }, + { "Content-Language" }, + { "Content-Length" }, + { "Content-Location" }, + { "Content-MD5" }, + { "Content-Range" }, + { "Content-Type" }, + { "Expires" }, + { "Last-Modified" } + }; + + public UniversalWebClient() : this(new BCLWebClient { }) { } + + public UniversalWebClient(BCLWebClient client) => Client = client; + + BCLWebClient Client { get; set; } + + public async Task> ExecuteAsync( +WebRequest httpRequest, +IProgress uploadProgress, +IProgress downloadProgress, +CancellationToken cancellationToken) + { + uploadProgress ??= new Progress(); + downloadProgress ??= new Progress(); - HttpRequestMessage message = new HttpRequestMessage(new HttpMethod(httpRequest.Method), httpRequest.Target); + using var message = new HttpRequestMessage(new HttpMethod(httpRequest.Method), httpRequest.Target); + + Stream data = httpRequest.Data; + if (data != null || httpRequest.Method.Equals("POST", StringComparison.OrdinalIgnoreCase)) + { + message.Content = new StreamContent(data ?? new MemoryStream(new byte[0])); + } - // Fill in zero-length data if method is post. - if ((httpRequest.Data is null && httpRequest.Method.ToLower().Equals("post") ? new MemoryStream(new byte[0]) : httpRequest.Data) is Stream { } data) - { - message.Content = new StreamContent(data); - } - if (httpRequest.Headers != null) + // Add headers to the message + if (httpRequest.Headers != null) + { + foreach (var header in httpRequest.Headers) { - foreach (KeyValuePair header in httpRequest.Headers) + if (ContentHeaders.Contains(header.Key)) + { + message.Content.Headers.TryAddWithoutValidation(header.Key, header.Value); + } + else { - if (ContentHeaders.Contains(header.Key)) - { - message.Content.Headers.Add(header.Key, header.Value); - } - else - { - message.Headers.Add(header.Key, header.Value); - } + message.Headers.TryAddWithoutValidation(header.Key, header.Value); } } + } + + // Avoid aggressive caching + message.Headers.Add("Cache-Control", "no-cache"); + message.Headers.IfModifiedSince = DateTimeOffset.UtcNow; + + uploadProgress.Report(new DataTransferLevel { Amount = 0 }); - // Avoid aggressive caching on Windows Phone 8.1. + using var response = await Client.SendAsync(message, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + uploadProgress.Report(new DataTransferLevel { Amount = 1 }); - message.Headers.Add("Cache-Control", "no-cache"); - message.Headers.IfModifiedSince = DateTimeOffset.UtcNow; + using var responseStream = await response.Content.ReadAsStreamAsync(); + using var resultStream = new MemoryStream(); - // TODO: (richardross) investigate progress here, maybe there's something we're missing in order to support this. + var buffer = new byte[4096]; + int bytesRead; + long totalLength = response.Content.Headers.ContentLength ?? -1; + long readSoFar = 0; - uploadProgress.Report(new DataTransferLevel { Amount = 0 }); + // Read response stream and report progress + while ((bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0) + { + await resultStream.WriteAsync(buffer, 0, bytesRead, cancellationToken); + readSoFar += bytesRead; - return Client.SendAsync(message, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ContinueWith(httpMessageTask => + if (totalLength > 0) { - HttpResponseMessage response = httpMessageTask.Result; - uploadProgress.Report(new DataTransferLevel { Amount = 1 }); + downloadProgress.Report(new DataTransferLevel { Amount = 1.0 * readSoFar / totalLength }); + } + } - return response.Content.ReadAsStreamAsync().ContinueWith(streamTask => - { - MemoryStream resultStream = new MemoryStream { }; - Stream responseStream = streamTask.Result; - - int bufferSize = 4096, bytesRead = 0; - byte[] buffer = new byte[bufferSize]; - long totalLength = -1, readSoFar = 0; - - try - { - totalLength = responseStream.Length; - } - catch { }; - - return InternalExtensions.WhileAsync(() => responseStream.ReadAsync(buffer, 0, bufferSize, cancellationToken).OnSuccess(readTask => (bytesRead = readTask.Result) > 0), () => - { - cancellationToken.ThrowIfCancellationRequested(); - - return resultStream.WriteAsync(buffer, 0, bytesRead, cancellationToken).OnSuccess(_ => - { - cancellationToken.ThrowIfCancellationRequested(); - readSoFar += bytesRead; - - if (totalLength > -1) - { - downloadProgress.Report(new DataTransferLevel { Amount = 1.0 * readSoFar / totalLength }); - } - }); - }).ContinueWith(_ => - { - responseStream.Dispose(); - return _; - }).Unwrap().OnSuccess(_ => - { - // If getting stream size is not supported, then report download only once. - - if (totalLength == -1) - { - downloadProgress.Report(new DataTransferLevel { Amount = 1.0 }); - } - - byte[] resultAsArray = resultStream.ToArray(); - resultStream.Dispose(); - - // Assume UTF-8 encoding. - - return new Tuple(response.StatusCode, Encoding.UTF8.GetString(resultAsArray, 0, resultAsArray.Length)); - }); - }); - }).Unwrap().Unwrap(); + // Report final progress if total length was unknown + if (totalLength == -1) + { + downloadProgress.Report(new DataTransferLevel { Amount = 1.0 }); } + + // Convert response to string (assuming UTF-8 encoding) + var resultAsArray = resultStream.ToArray(); + string responseContent = Encoding.UTF8.GetString(resultAsArray); + + return new Tuple(response.StatusCode, responseContent); } + } diff --git a/Parse/Infrastructure/ParseFailureException.cs b/Parse/Infrastructure/ParseFailureException.cs index fca939fd..086b7b33 100644 --- a/Parse/Infrastructure/ParseFailureException.cs +++ b/Parse/Infrastructure/ParseFailureException.cs @@ -1,266 +1,269 @@ using System; -namespace Parse.Infrastructure +namespace Parse.Infrastructure; + +/// +/// Exceptions that may occur when sending requests to Parse. +/// +public class ParseFailureException : Exception { /// - /// Exceptions that may occur when sending requests to Parse. + /// Error codes that may be delivered in response to requests to Parse. /// - public class ParseFailureException : Exception + public enum ErrorCode { /// - /// Error codes that may be delivered in response to requests to Parse. - /// - public enum ErrorCode - { - /// - /// Error code indicating that an unknown error or an error unrelated to Parse - /// occurred. - /// - OtherCause = -1, - - /// - /// Error code indicating that something has gone wrong with the server. - /// If you get this error code, it is Parse's fault. Please report the bug. - /// - InternalServerError = 1, - - /// - /// Error code indicating the connection to the Parse servers failed. - /// - ConnectionFailed = 100, - - /// - /// Error code indicating the specified object doesn't exist. - /// - ObjectNotFound = 101, - - /// - /// Error code indicating you tried to query with a datatype that doesn't - /// support it, like exact matching an array or object. - /// - InvalidQuery = 102, - - /// - /// Error code indicating a missing or invalid classname. Classnames are - /// case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the - /// only valid characters. - /// - InvalidClassName = 103, - - /// - /// Error code indicating an unspecified object id. - /// - MissingObjectId = 104, - - /// - /// Error code indicating an invalid key name. Keys are case-sensitive. They - /// must start with a letter, and a-zA-Z0-9_ are the only valid characters. - /// - InvalidKeyName = 105, - - /// - /// Error code indicating a malformed pointer. You should not see this unless - /// you have been mucking about changing internal Parse code. - /// - InvalidPointer = 106, - - /// - /// Error code indicating that badly formed JSON was received upstream. This - /// either indicates you have done something unusual with modifying how - /// things encode to JSON, or the network is failing badly. - /// - InvalidJSON = 107, - - /// - /// Error code indicating that the feature you tried to access is only - /// available internally for testing purposes. - /// - CommandUnavailable = 108, - - /// - /// You must call Parse.initialize before using the Parse library. - /// - NotInitialized = 109, - - /// - /// Error code indicating that a field was set to an inconsistent type. - /// - IncorrectType = 111, - - /// - /// Error code indicating an invalid channel name. A channel name is either - /// an empty string (the broadcast channel) or contains only a-zA-Z0-9_ - /// characters and starts with a letter. - /// - InvalidChannelName = 112, - - /// - /// Error code indicating that push is misconfigured. - /// - PushMisconfigured = 115, - - /// - /// Error code indicating that the object is too large. - /// - ObjectTooLarge = 116, - - /// - /// Error code indicating that the operation isn't allowed for clients. - /// - OperationForbidden = 119, - - /// - /// Error code indicating the result was not found in the cache. - /// - CacheMiss = 120, - - /// - /// Error code indicating that an invalid key was used in a nested - /// JSONObject. - /// - InvalidNestedKey = 121, - - /// - /// Error code indicating that an invalid filename was used for ParseFile. - /// A valid file name contains only a-zA-Z0-9_. characters and is between 1 - /// and 128 characters. - /// - InvalidFileName = 122, - - /// - /// Error code indicating an invalid ACL was provided. - /// - InvalidACL = 123, - - /// - /// Error code indicating that the request timed out on the server. Typically - /// this indicates that the request is too expensive to run. - /// - Timeout = 124, - - /// - /// Error code indicating that the email address was invalid. - /// - InvalidEmailAddress = 125, - - /// - /// Error code indicating that a unique field was given a value that is - /// already taken. - /// - DuplicateValue = 137, - - /// - /// Error code indicating that a role's name is invalid. - /// - InvalidRoleName = 139, - - /// - /// Error code indicating that an application quota was exceeded. Upgrade to - /// resolve. - /// - ExceededQuota = 140, - - /// - /// Error code indicating that a Cloud Code script failed. - /// - ScriptFailed = 141, - - /// - /// Error code indicating that a Cloud Code validation failed. - /// - ValidationFailed = 142, - - /// - /// Error code indicating that deleting a file failed. - /// - FileDeleteFailed = 153, - - /// - /// Error code indicating that the application has exceeded its request limit. - /// - RequestLimitExceeded = 155, - - /// - /// Error code indicating that the provided event name is invalid. - /// - InvalidEventName = 160, - - /// - /// Error code indicating that the username is missing or empty. - /// - UsernameMissing = 200, - - /// - /// Error code indicating that the password is missing or empty. - /// - PasswordMissing = 201, - - /// - /// Error code indicating that the username has already been taken. - /// - UsernameTaken = 202, - - /// - /// Error code indicating that the email has already been taken. - /// - EmailTaken = 203, - - /// - /// Error code indicating that the email is missing, but must be specified. - /// - EmailMissing = 204, - - /// - /// Error code indicating that a user with the specified email was not found. - /// - EmailNotFound = 205, - - /// - /// Error code indicating that a user object without a valid session could - /// not be altered. - /// - SessionMissing = 206, - - /// - /// Error code indicating that a user can only be created through signup. - /// - MustCreateUserThroughSignup = 207, - - /// - /// Error code indicating that an an account being linked is already linked - /// to another user. - /// - AccountAlreadyLinked = 208, - - /// - /// Error code indicating that the current session token is invalid. - /// - InvalidSessionToken = 209, - - /// - /// Error code indicating that a user cannot be linked to an account because - /// that account's id could not be found. - /// - LinkedIdMissing = 250, - - /// - /// Error code indicating that a user with a linked (e.g. Facebook) account - /// has an invalid session. - /// - InvalidLinkedSession = 251, - - /// - /// Error code indicating that a service being linked (e.g. Facebook or - /// Twitter) is unsupported. - /// - UnsupportedService = 252 - } - - internal ParseFailureException(ErrorCode code, string message, Exception cause = null) : base(message, cause) => Code = code; - - /// - /// The Parse error code associated with the exception. - /// - public ErrorCode Code { get; private set; } + /// Error code indicating that an unknown error or an error unrelated to Parse + /// occurred. + /// + OtherCause = -1, + + /// + /// Error code indicating that something has gone wrong with the server. + /// If you get this error code, it is Parse's fault. Please report the bug. + /// + InternalServerError = 1, + + /// + /// Error code indicating the connection to the Parse servers failed. + /// + ConnectionFailed = 100, + + /// + /// Error code indicating the specified object doesn't exist. + /// + ObjectNotFound = 101, + + /// + /// Error code indicating you tried to query with a datatype that doesn't + /// support it, like exact matching an array or object. + /// + InvalidQuery = 102, + + /// + /// Error code indicating a missing or invalid classname. Classnames are + /// case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the + /// only valid characters. + /// + InvalidClassName = 103, + + /// + /// Error code indicating an unspecified object id. + /// + MissingObjectId = 104, + + /// + /// Error code indicating an invalid key name. Keys are case-sensitive. They + /// must start with a letter, and a-zA-Z0-9_ are the only valid characters. + /// + InvalidKeyName = 105, + + /// + /// Error code indicating a malformed pointer. You should not see this unless + /// you have been mucking about changing internal Parse code. + /// + InvalidPointer = 106, + + /// + /// Error code indicating that badly formed JSON was received upstream. This + /// either indicates you have done something unusual with modifying how + /// things encode to JSON, or the network is failing badly. + /// + InvalidJSON = 107, + + /// + /// Error code indicating that the feature you tried to access is only + /// available internally for testing purposes. + /// + CommandUnavailable = 108, + + /// + /// You must call Parse.initialize before using the Parse library. + /// + NotInitialized = 109, + + /// + /// Error code indicating that a field was set to an inconsistent type. + /// + IncorrectType = 111, + + /// + /// Error code indicating an invalid channel name. A channel name is either + /// an empty string (the broadcast channel) or contains only a-zA-Z0-9_ + /// characters and starts with a letter. + /// + InvalidChannelName = 112, + + /// + /// Error code indicating that push is misconfigured. + /// + PushMisconfigured = 115, + + /// + /// Error code indicating that the object is too large. + /// + ObjectTooLarge = 116, + + /// + /// Error code indicating that the operation isn't allowed for clients. + /// + OperationForbidden = 119, + + /// + /// Error code indicating the result was not found in the cache. + /// + CacheMiss = 120, + + /// + /// Error code indicating that an invalid key was used in a nested + /// JSONObject. + /// + InvalidNestedKey = 121, + + /// + /// Error code indicating that an invalid filename was used for ParseFile. + /// A valid file name contains only a-zA-Z0-9_. characters and is between 1 + /// and 128 characters. + /// + InvalidFileName = 122, + + /// + /// Error code indicating an invalid ACL was provided. + /// + InvalidACL = 123, + + /// + /// Error code indicating that the request timed out on the server. Typically + /// this indicates that the request is too expensive to run. + /// + Timeout = 124, + + /// + /// Error code indicating that the email address was invalid. + /// + InvalidEmailAddress = 125, + + /// + /// Error code indicating that a unique field was given a value that is + /// already taken. + /// + DuplicateValue = 137, + + /// + /// Error code indicating that a role's name is invalid. + /// + InvalidRoleName = 139, + + /// + /// Error code indicating that an application quota was exceeded. Upgrade to + /// resolve. + /// + ExceededQuota = 140, + + /// + /// Error code indicating that a Cloud Code script failed. + /// + ScriptFailed = 141, + + /// + /// Error code indicating that a Cloud Code validation failed. + /// + ValidationFailed = 142, + + /// + /// Error code indicating that deleting a file failed. + /// + FileDeleteFailed = 153, + + /// + /// Error code indicating that the application has exceeded its request limit. + /// + RequestLimitExceeded = 155, + + /// + /// Error code indicating that the provided event name is invalid. + /// + InvalidEventName = 160, + + /// + /// Error code indicating that the username is missing or empty. + /// + UsernameMissing = 200, + + /// + /// Error code indicating that the password is missing or empty. + /// + PasswordMissing = 201, + + /// + /// Error code indicating that the username has already been taken. + /// + UsernameTaken = 202, + + /// + /// Error code indicating that the email has already been taken. + /// + EmailTaken = 203, + + /// + /// Error code indicating that the email is missing, but must be specified. + /// + EmailMissing = 204, + + /// + /// Error code indicating that a user with the specified email was not found. + /// + EmailNotFound = 205, + + /// + /// Error code indicating that a user object without a valid session could + /// not be altered. + /// + SessionMissing = 206, + + /// + /// Error code indicating that a user can only be created through signup. + /// + MustCreateUserThroughSignup = 207, + + /// + /// Error code indicating that an an account being linked is already linked + /// to another user. + /// + AccountAlreadyLinked = 208, + + /// + /// Error code indicating that the current session token is invalid. + /// + InvalidSessionToken = 209, + + /// + /// Error code indicating that a user cannot be linked to an account because + /// that account's id could not be found. + /// + LinkedIdMissing = 250, + + /// + /// Error code indicating that a user with a linked (e.g. Facebook) account + /// has an invalid session. + /// + InvalidLinkedSession = 251, + + /// + /// Error code indicating that a service being linked (e.g. Facebook or + /// Twitter) is unsupported. + /// + UnsupportedService = 252, + /// + /// ERROR 404 + /// + ERROR404 = 404 } + + internal ParseFailureException(ErrorCode code, string message, Exception cause = null) : base(message, cause) => Code = code; + + /// + /// The Parse error code associated with the exception. + /// + public ErrorCode Code { get; private set; } } diff --git a/Parse/Infrastructure/Utilities/InternalExtensions.cs b/Parse/Infrastructure/Utilities/InternalExtensions.cs index ede40baa..3e3c8794 100644 --- a/Parse/Infrastructure/Utilities/InternalExtensions.cs +++ b/Parse/Infrastructure/Utilities/InternalExtensions.cs @@ -4,116 +4,129 @@ using System.Runtime.ExceptionServices; using System.Threading.Tasks; -namespace Parse.Infrastructure.Utilities +namespace Parse.Infrastructure.Utilities; +/// +/// Provides helper methods that allow us to use terser code elsewhere. +/// +public static class InternalExtensions { /// - /// Provides helper methods that allow us to use terser code elsewhere. + /// Ensures a task (even null) is awaitable. /// - public static class InternalExtensions + public static Task Safe(this Task task) => + task ?? Task.FromResult(default(T)); + + /// + /// Ensures a task (even null) is awaitable. + /// + public static Task Safe(this Task task) => + task ?? Task.CompletedTask; + + public delegate void PartialAccessor(ref T arg); + + /// + /// Gets the value from a dictionary or returns the default value if the key is not found. + /// + public static TValue GetOrDefault(this IDictionary self, TKey key, TValue defaultValue) => + self.TryGetValue(key, out var value) ? value : defaultValue; + + /// + /// Compares two collections for equality. + /// + public static bool CollectionsEqual(this IEnumerable a, IEnumerable b) => + ReferenceEquals(a, b) || (a != null && b != null && a.SequenceEqual(b)); + + /// + /// Executes a continuation on a task that returns a result on success. + /// + public static async Task OnSuccess(this Task task, Func> continuation) { - /// - /// Ensures a task (even null) is awaitable. - /// - /// - /// - /// - public static Task Safe(this Task task) + if (task.IsFaulted) { - return task ?? Task.FromResult(default(T)); + var ex = task.Exception?.Flatten(); + ExceptionDispatchInfo.Capture(ex?.InnerExceptions[0] ?? ex).Throw(); } - - /// - /// Ensures a task (even null) is awaitable. - /// - /// - /// - public static Task Safe(this Task task) + else if (task.IsCanceled) { - return task ?? Task.FromResult(null); + var tcs = new TaskCompletionSource(); + tcs.SetCanceled(); + return await tcs.Task.ConfigureAwait(false); } - public delegate void PartialAccessor(ref T arg); + // Ensure continuation returns a Task, then await with ConfigureAwait + var resultTask = continuation(task); + return await resultTask.ConfigureAwait(false); + } - public static TValue GetOrDefault(this IDictionary self, - TKey key, - TValue defaultValue) + + /// + /// Executes a continuation on a task that has a result type. + /// + public static async Task OnSuccess(this Task task, Func, Task> continuation) + { + if (task.IsFaulted) { - if (self.TryGetValue(key, out TValue value)) - return value; - return defaultValue; + var ex = task.Exception?.Flatten(); + ExceptionDispatchInfo.Capture(ex?.InnerExceptions[0] ?? ex).Throw(); } - - public static bool CollectionsEqual(this IEnumerable a, IEnumerable b) + else if (task.IsCanceled) { - return Equals(a, b) || - a != null && b != null && - a.SequenceEqual(b); + var tcs = new TaskCompletionSource(); + tcs.SetCanceled(); + return await tcs.Task.ConfigureAwait(false); } - public static Task OnSuccess(this Task task, Func, TResult> continuation) + // Ensure continuation returns a Task, then await with ConfigureAwait + var resultTask = continuation(task); + return await resultTask.ConfigureAwait(false); + } + + /// + /// Executes a continuation on a task and returns void. + /// + public static async Task OnSuccess(this Task task, Action continuation) + { + if (task.IsFaulted) { - return ((Task) task).OnSuccess(t => continuation((Task) t)); + var ex = task.Exception?.Flatten(); + ExceptionDispatchInfo.Capture(ex?.InnerExceptions[0] ?? ex).Throw(); } - - public static Task OnSuccess(this Task task, Action> continuation) + else if (task.IsCanceled) { - return task.OnSuccess((Func, object>) (t => - { - continuation(t); - return null; - })); + task = Task.CompletedTask; } - public static Task OnSuccess(this Task task, Func continuation) - { - return task.ContinueWith(t => - { - if (t.IsFaulted) - { - AggregateException ex = t.Exception.Flatten(); - if (ex.InnerExceptions.Count == 1) - ExceptionDispatchInfo.Capture(ex.InnerExceptions[0]).Throw(); - else - ExceptionDispatchInfo.Capture(ex).Throw(); - // Unreachable - var q = Task.FromResult(default(TResult)); - return q; - } - else if (t.IsCanceled) - { - TaskCompletionSource tcs = new TaskCompletionSource(); - tcs.SetCanceled(); - var e = tcs.Task; - return e; - } - else - { - var s = Task.FromResult(continuation(t)); + continuation(task); + await task.ConfigureAwait(false); + } - return s; - } - }).Unwrap(); + /// + /// Executes a continuation on a task and returns void, for tasks with result. + /// + public static async Task OnSuccess(this Task task, Action> continuation) + { + if (task.IsFaulted) + { + var ex = task.Exception?.Flatten(); + ExceptionDispatchInfo.Capture(ex?.InnerExceptions[0] ?? ex).Throw(); } - - public static Task OnSuccess(this Task task, Action continuation) + else if (task.IsCanceled) { - return task.OnSuccess((Func) (t => - { - continuation(t); - return null; - })); + task = Task.FromResult(default); // Handle canceled task by returning a completed Task } - public static Task WhileAsync(Func> predicate, Func body) + continuation(task); + await task.ConfigureAwait(false); + } + + /// + /// Executes an asynchronous loop until the predicate evaluates to false. + /// + public static async Task WhileAsync(Func> predicate, Func body) + { + while (await predicate().ConfigureAwait(false)) { - Func iterate = null; - iterate = () => predicate().OnSuccess(t => - { - if (!t.Result) - return Task.FromResult(0); - return body().OnSuccess(_ => iterate()).Unwrap(); - }).Unwrap(); - return iterate(); + await body().ConfigureAwait(false); } } } diff --git a/Parse/Infrastructure/Utilities/JsonUtilities.cs b/Parse/Infrastructure/Utilities/JsonUtilities.cs index 3b08d1d6..6d725506 100644 --- a/Parse/Infrastructure/Utilities/JsonUtilities.cs +++ b/Parse/Infrastructure/Utilities/JsonUtilities.cs @@ -5,6 +5,7 @@ using System.Reflection; using System.Text; using System.Text.RegularExpressions; +using Parse.Platform.Objects; namespace Parse.Infrastructure.Utilities; @@ -322,11 +323,11 @@ private bool Accept(char[] condition) return match; } } - /// /// Parses a JSON-text as defined in http://tools.ietf.org/html/rfc4627, returning an /// IDictionary<string, object> or an IList<object> depending on whether /// the value was an array or dictionary. Nested objects also match these types. + /// Gracefully handles invalid JSON or HTML responses. /// public static object Parse(string input) { @@ -338,18 +339,65 @@ public static object Parse(string input) } input = input.Trim(); - JsonStringParser parser = new JsonStringParser(input); - if ((parser.ParseObject(out object output) || parser.ParseArray(out output)) && - parser.CurrentIndex == input.Length) + try + { + JsonStringParser parser = new JsonStringParser(input); + + if ((parser.ParseObject(out object output) || parser.ParseArray(out output)) && + parser.CurrentIndex == input.Length) + { + return output; + } + } + catch { - return output; + // Fallback handling for non-JSON input } - throw new ArgumentException("Input JSON was invalid."); + // Detect HTML responses + if (input.StartsWith(" + { + { "error", "Non-JSON response" }, + { "type", "HTML" }, + { "content", ExtractTextFromHtml(input) } + }; + } + + // If input is not JSON or HTML, throw an exception + throw new ArgumentException("Input data is neither valid JSON nor recognizable HTML."); + } + + /// + /// Extracts meaningful text from an HTML response, such as the contents of
 tags.
+    /// 
+ private static string ExtractTextFromHtml(string html) + { + try + { + int startIndex = html.IndexOf("
", StringComparison.OrdinalIgnoreCase);
+            int endIndex = html.IndexOf("
", StringComparison.OrdinalIgnoreCase); + + if (startIndex != -1 && endIndex != -1) + { + startIndex += 5; // Skip "
"
+                return html.Substring(startIndex, endIndex - startIndex).Trim();
+            }
+
+            // If no 
 tags, return raw HTML as fallback
+            return html;
+        }
+        catch
+        {
+            return "Unable to extract meaningful content from HTML.";
+        }
     }
 
 
+
     /// 
     /// Encodes a dictionary into a JSON string. Supports values that are
     /// IDictionary<string, object>, IList<object>, strings,
@@ -435,10 +483,27 @@ public static string Encode(object obj)
             return (bool) obj ? "true" : "false";
         if (!obj.GetType().GetTypeInfo().IsPrimitive)
         {
+            if (obj is MutableObjectState state)
+            {
+                // Convert MutableObjectState to a dictionary
+                var stateDict = new Dictionary
+            {
+                { "ObjectId", state.ObjectId },
+                { "ClassName", state.ClassName },
+                { "CreatedAt", state.CreatedAt },
+                { "UpdatedAt", state.UpdatedAt },
+                { "ServerData", state.ServerData }
+            };
+
+                // Encode the dictionary recursively
+                return Encode(stateDict);
+            }
+
             Debug.WriteLine("Unable to encode objects of type " + obj.GetType());
-            return string.Empty;
+            return "null"; // Return "null" for unsupported types
         }
-            
+
         return Convert.ToString(obj, CultureInfo.InvariantCulture);
     }
+
 }
diff --git a/Parse/Infrastructure/Utilities/TaskQueue.cs b/Parse/Infrastructure/Utilities/TaskQueue.cs
index 475343eb..1fee0f55 100644
--- a/Parse/Infrastructure/Utilities/TaskQueue.cs
+++ b/Parse/Infrastructure/Utilities/TaskQueue.cs
@@ -2,71 +2,64 @@
 using System.Threading;
 using System.Threading.Tasks;
 
-namespace Parse.Infrastructure.Utilities
+namespace Parse.Infrastructure.Utilities;
+
+/// 
+/// A helper class for enqueuing tasks
+/// 
+public class TaskQueue
 {
     /// 
-    /// A helper class for enqueuing tasks
+    /// We only need to keep the tail of the queue. Cancelled tasks will
+    /// just complete normally/immediately when their turn arrives.
     /// 
-    public class TaskQueue
-    {
-        /// 
-        /// We only need to keep the tail of the queue. Cancelled tasks will
-        /// just complete normally/immediately when their turn arrives.
-        /// 
-        Task Tail { get; set; }
+    private Task? Tail { get; set; } = Task.CompletedTask; // Start with a completed task to simplify logic.
 
-        /// 
-        /// Gets a cancellable task that can be safely awaited and is dependent
-        /// on the current tail of the queue. This essentially gives us a proxy
-        /// for the tail end of the queue whose awaiting can be cancelled.
-        /// 
-        /// A cancellation token that cancels
-        /// the task even if the task is still in the queue. This allows the
-        /// running task to return immediately without breaking the dependency
-        /// chain. It also ensures that errors do not propagate.
-        /// A new task that should be awaited by enqueued tasks.
-        private Task GetTaskToAwait(CancellationToken cancellationToken)
+    /// 
+    /// Gets a task that can be awaited and is dependent on the current queue's tail.
+    /// 
+    /// A cancellation token to cancel waiting for the task.
+    /// A task representing the tail of the queue.
+    private Task GetTaskToAwait(CancellationToken cancellationToken)
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
-            {
-                return (Tail ?? Task.FromResult(true)).ContinueWith(task => { }, cancellationToken);
-            }
+            // Ensure the returned task is cancellable even if it's already completed.
+            return Tail?.ContinueWith(
+                _ => { },
+                cancellationToken,
+                TaskContinuationOptions.ExecuteSynchronously,
+                TaskScheduler.Default) ?? Task.CompletedTask;
         }
+    }
 
-        /// 
-        /// Enqueues a task created by . If the task is
-        /// cancellable (or should be able to be cancelled while it is waiting in the
-        /// queue), pass a cancellationToken.
-        /// 
-        /// The type of task.
-        /// A function given a task to await once state is
-        /// snapshotted (e.g. after capturing session tokens at the time of the save call).
-        /// Awaiting this task will wait for the created task's turn in the queue.
-        /// A cancellation token that can be used to
-        /// cancel waiting in the queue.
-        /// The task created by the taskStart function.
-        public T Enqueue(Func taskStart, CancellationToken cancellationToken = default) where T : Task
-        {
-            Task oldTail;
-            T task;
+    /// 
+    /// Enqueues a task to be executed after the current tail of the queue.
+    /// 
+    /// The type of task.
+    /// A function that creates a new task dependent on the current queue state.
+    /// A cancellation token to cancel the waiting task.
+    /// The newly created task.
+    public T Enqueue(Func taskStart, CancellationToken cancellationToken = default) where T : Task
+    {
+        T task;
 
-            lock (Mutex)
-            {
-                oldTail = Tail ?? Task.FromResult(true);
+        lock (Mutex)
+        {
+            var oldTail = Tail ?? Task.CompletedTask;
 
-                // The task created by taskStart is responsible for waiting the
-                // task passed to it before doing its work (this gives it an opportunity
-                // to do startup work or save state before waiting for its turn in the queue
-                task = taskStart(GetTaskToAwait(cancellationToken));
+            // Create the new task using the tail task as a dependency.
+            task = taskStart(GetTaskToAwait(cancellationToken));
 
-                // The tail task should be dependent on the old tail as well as the newly-created
-                // task. This prevents cancellation of the new task from causing the queue to run
-                // out of order.
-                Tail = Task.WhenAll(oldTail, task);
-            }
-            return task;
+            // Update the tail to include the newly created task.
+            Tail = Task.WhenAll(oldTail, task);
         }
 
-        public object Mutex { get; } = new object { };
+        return task;
     }
+    /// 
+    /// Synchronization object to protect shared state.
+    /// 
+    public readonly object Mutex = new();
+
 }
diff --git a/Parse/Platform/Cloud/ParseCloudCodeController.cs b/Parse/Platform/Cloud/ParseCloudCodeController.cs
index 37c11b88..550a8286 100644
--- a/Parse/Platform/Cloud/ParseCloudCodeController.cs
+++ b/Parse/Platform/Cloud/ParseCloudCodeController.cs
@@ -10,23 +10,31 @@
 using Parse.Infrastructure.Data;
 using Parse.Infrastructure.Execution;
 
-namespace Parse.Platform.Cloud
+namespace Parse.Platform.Cloud;
+
+public class ParseCloudCodeController : IParseCloudCodeController
 {
-    public class ParseCloudCodeController : IParseCloudCodeController
-    {
-        IParseCommandRunner CommandRunner { get; }
+    IParseCommandRunner CommandRunner { get; }
+
+    IParseDataDecoder Decoder { get; }
 
-        IParseDataDecoder Decoder { get; }
+    public ParseCloudCodeController(IParseCommandRunner commandRunner, IParseDataDecoder decoder) => (CommandRunner, Decoder) = (commandRunner, decoder);
 
-        public ParseCloudCodeController(IParseCommandRunner commandRunner, IParseDataDecoder decoder) => (CommandRunner, Decoder) = (commandRunner, decoder);
+    public async Task CallFunctionAsync(string name, IDictionary parameters, string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default)
+    {
+        // Run the command asynchronously and await the result
+        var commandResult = await CommandRunner.RunCommandAsync(
+            new ParseCommand($"functions/{Uri.EscapeUriString(name)}", method: "POST", sessionToken: sessionToken,
+            data: NoObjectsEncoder.Instance.Encode(parameters, serviceHub) as IDictionary),
+            cancellationToken: cancellationToken).ConfigureAwait(false);
+
+        // Decode the result and handle it
+        var decoded = Decoder.Decode(commandResult.Item2, serviceHub) as IDictionary;
 
-        public Task CallFunctionAsync(string name, IDictionary parameters, string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default)
-        {
-            return CommandRunner.RunCommandAsync(new ParseCommand($"functions/{Uri.EscapeUriString(name)}", method: "POST", sessionToken: sessionToken, data: NoObjectsEncoder.Instance.Encode(parameters, serviceHub) as IDictionary), cancellationToken: cancellationToken).OnSuccess(task =>
-        {
-            IDictionary decoded = Decoder.Decode(task.Result.Item2, serviceHub) as IDictionary;
-            return !decoded.ContainsKey("result") ? default : Conversion.To(decoded["result"]);
-        });
-        }
+        // Return the decoded result or the default value if not found
+        return decoded?.ContainsKey("result") == true
+            ? Conversion.To(decoded["result"])
+            : default;
     }
+
 }
diff --git a/Parse/Platform/Configuration/ParseConfiguration.cs b/Parse/Platform/Configuration/ParseConfiguration.cs
index 90493ecc..c2a814b6 100644
--- a/Parse/Platform/Configuration/ParseConfiguration.cs
+++ b/Parse/Platform/Configuration/ParseConfiguration.cs
@@ -4,84 +4,83 @@
 using Parse.Infrastructure.Data;
 using Parse.Infrastructure.Utilities;
 
-namespace Parse.Platform.Configuration
+namespace Parse.Platform.Configuration;
+
+/// 
+/// The ParseConfig is a representation of the remote configuration object,
+/// that enables you to add things like feature gating, a/b testing or simple "Message of the day".
+/// 
+public class ParseConfiguration : IJsonConvertible
 {
-    /// 
-    /// The ParseConfig is a representation of the remote configuration object,
-    /// that enables you to add things like feature gating, a/b testing or simple "Message of the day".
-    /// 
-    public class ParseConfiguration : IJsonConvertible
-    {
-        IDictionary Properties { get; } = new Dictionary { };
+    IDictionary Properties { get; } = new Dictionary { };
 
-        IServiceHub Services { get; }
+    IServiceHub Services { get; }
 
-        internal ParseConfiguration(IServiceHub serviceHub) => Services = serviceHub;
+    internal ParseConfiguration(IServiceHub serviceHub) => Services = serviceHub;
 
-        ParseConfiguration(IDictionary properties, IServiceHub serviceHub) : this(serviceHub) => Properties = properties;
+    ParseConfiguration(IDictionary properties, IServiceHub serviceHub) : this(serviceHub) => Properties = properties;
 
-        internal static ParseConfiguration Create(IDictionary configurationData, IParseDataDecoder decoder, IServiceHub serviceHub)
-        {
-            return new ParseConfiguration(decoder.Decode(configurationData["params"], serviceHub) as IDictionary, serviceHub);
-        }
+    internal static ParseConfiguration Create(IDictionary configurationData, IParseDataDecoder decoder, IServiceHub serviceHub)
+    {
+        return new ParseConfiguration(decoder.Decode(configurationData["params"], serviceHub) as IDictionary, serviceHub);
+    }
 
-        /// 
-        /// Gets a value for the key of a particular type.
-        /// 
-        /// The type to convert the value to. Supported types are
-        /// ParseObject and its descendents, Parse types such as ParseRelation and ParseGeopoint,
-        /// primitive types,IList<T>, IDictionary<string, T> and strings.
-        /// The key of the element to get.
-        /// The property is retrieved
-        /// and  is not found.
-        /// The property under this 
-        /// key was found, but of a different type.
-        public T Get(string key)
-        {
-            return Conversion.To(Properties[key]);
-        }
+    /// 
+    /// Gets a value for the key of a particular type.
+    /// 
+    /// The type to convert the value to. Supported types are
+    /// ParseObject and its descendents, Parse types such as ParseRelation and ParseGeopoint,
+    /// primitive types,IList<T>, IDictionary<string, T> and strings.
+    /// The key of the element to get.
+    /// The property is retrieved
+    /// and  is not found.
+    /// The property under this 
+    /// key was found, but of a different type.
+    public T Get(string key)
+    {
+        return Conversion.To(Properties[key]);
+    }
 
-        /// 
-        /// Populates result with the value for the key, if possible.
-        /// 
-        /// The desired type for the value.
-        /// The key to retrieve a value for.
-        /// The value for the given key, converted to the
-        /// requested type, or null if unsuccessful.
-        /// true if the lookup and conversion succeeded, otherwise false.
-        public bool TryGetValue(string key, out T result)
-        {
-            if (Properties.ContainsKey(key))
-                try
-                {
-                    T temp = Conversion.To(Properties[key]);
-                    result = temp;
-                    return true;
-                }
-                catch
-                {
-                    // Could not convert, do nothing.
-                }
+    /// 
+    /// Populates result with the value for the key, if possible.
+    /// 
+    /// The desired type for the value.
+    /// The key to retrieve a value for.
+    /// The value for the given key, converted to the
+    /// requested type, or null if unsuccessful.
+    /// true if the lookup and conversion succeeded, otherwise false.
+    public bool TryGetValue(string key, out T result)
+    {
+        if (Properties.ContainsKey(key))
+            try
+            {
+                T temp = Conversion.To(Properties[key]);
+                result = temp;
+                return true;
+            }
+            catch
+            {
+                // Could not convert, do nothing.
+            }
 
-            result = default;
-            return false;
-        }
+        result = default;
+        return false;
+    }
 
-        /// 
-        /// Gets a value on the config.
-        /// 
-        /// The key for the parameter.
-        /// The property is
-        /// retrieved and  is not found.
-        /// The value for the key.
-        virtual public object this[string key] => Properties[key];
+    /// 
+    /// Gets a value on the config.
+    /// 
+    /// The key for the parameter.
+    /// The property is
+    /// retrieved and  is not found.
+    /// The value for the key.
+    virtual public object this[string key] => Properties[key];
 
-        IDictionary IJsonConvertible.ConvertToJSON()
+    IDictionary IJsonConvertible.ConvertToJSON()
+    {
+        return new Dictionary
         {
-            return new Dictionary
-            {
-                ["params"] = NoObjectsEncoder.Instance.Encode(Properties, Services)
-            };
-        }
+            ["params"] = NoObjectsEncoder.Instance.Encode(Properties, Services)
+        };
     }
 }
diff --git a/Parse/Platform/Configuration/ParseConfigurationController.cs b/Parse/Platform/Configuration/ParseConfigurationController.cs
index 13b298b2..233dcee7 100644
--- a/Parse/Platform/Configuration/ParseConfigurationController.cs
+++ b/Parse/Platform/Configuration/ParseConfigurationController.cs
@@ -8,41 +8,41 @@
 using Parse;
 using Parse.Infrastructure.Execution;
 
-namespace Parse.Platform.Configuration
+namespace Parse.Platform.Configuration;
+
+/// 
+/// Config controller.
+/// 
+internal class ParseConfigurationController : IParseConfigurationController
 {
+    private IParseCommandRunner CommandRunner { get; }
+    private IParseDataDecoder Decoder { get; }
+    public IParseCurrentConfigurationController CurrentConfigurationController { get; }
+
     /// 
-    /// Config controller.
+    /// Initializes a new instance of the  class.
     /// 
-    internal class ParseConfigurationController : IParseConfigurationController
+    public ParseConfigurationController(IParseCommandRunner commandRunner, ICacheController storageController, IParseDataDecoder decoder)
+    {
+        CommandRunner = commandRunner;
+        CurrentConfigurationController = new ParseCurrentConfigurationController(storageController, decoder);
+        Decoder = decoder;
+    }
+
+    public async Task FetchConfigAsync(string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default)
     {
-        IParseCommandRunner CommandRunner { get; }
-
-        IParseDataDecoder Decoder { get; }
-
-        public IParseCurrentConfigurationController CurrentConfigurationController { get; }
-
-        /// 
-        /// Initializes a new instance of the  class.
-        /// 
-        public ParseConfigurationController(IParseCommandRunner commandRunner, ICacheController storageController, IParseDataDecoder decoder)
-        {
-            CommandRunner = commandRunner;
-            CurrentConfigurationController = new ParseCurrentConfigurationController(storageController, decoder);
-            Decoder = decoder;
-        }
-
-        public Task FetchConfigAsync(string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default)
-        {
-            return CommandRunner.RunCommandAsync(new ParseCommand("config", method: "GET", sessionToken: sessionToken, data: default), cancellationToken: cancellationToken).OnSuccess(task =>
-        {
-            cancellationToken.ThrowIfCancellationRequested();
-            return Decoder.BuildConfiguration(task.Result.Item2, serviceHub);
-        }).OnSuccess(task =>
-        {
-            cancellationToken.ThrowIfCancellationRequested();
-            CurrentConfigurationController.SetCurrentConfigAsync(task.Result);
-            return task;
-        }).Unwrap();
-        }
+        cancellationToken.ThrowIfCancellationRequested();
+
+        // Fetch configuration via the command runner (returns a Task)
+        var commandResult = await CommandRunner.RunCommandAsync(new ParseCommand("config", method: "GET", sessionToken: sessionToken, null, null),null, null).ConfigureAwait(false);
+
+        // Build the configuration using the decoder (assuming BuildConfiguration is async)
+        var config = Decoder.BuildConfiguration(commandResult.Item2, serviceHub);
+
+        // Set the current configuration (assuming SetCurrentConfigAsync is async)
+        await CurrentConfigurationController.SetCurrentConfigAsync(config).ConfigureAwait(false);
+
+        return config;
     }
+
 }
diff --git a/Parse/Platform/Configuration/ParseCurrentConfigurationController.cs b/Parse/Platform/Configuration/ParseCurrentConfigurationController.cs
index 11baa0d0..6ddf37b7 100644
--- a/Parse/Platform/Configuration/ParseCurrentConfigurationController.cs
+++ b/Parse/Platform/Configuration/ParseCurrentConfigurationController.cs
@@ -5,63 +5,55 @@
 using Parse.Abstractions.Platform.Configuration;
 using Parse.Infrastructure.Utilities;
 
-namespace Parse.Platform.Configuration
+namespace Parse.Platform.Configuration;
+
+/// 
+/// Parse current config controller.
+/// 
+internal class ParseCurrentConfigurationController : IParseCurrentConfigurationController
 {
-    /// 
-    /// Parse current config controller.
-    /// 
-    internal class ParseCurrentConfigurationController : IParseCurrentConfigurationController
-    {
-        static string CurrentConfigurationKey { get; } = "CurrentConfig";
+    private static readonly string CurrentConfigurationKey = "CurrentConfig";
 
-        TaskQueue TaskQueue { get; }
+    private ParseConfiguration _currentConfiguration;
+    private readonly ICacheController _storageController;
+    private readonly IParseDataDecoder _decoder;
 
-        ParseConfiguration CurrentConfiguration { get; set; }
+    public ParseCurrentConfigurationController(ICacheController storageController, IParseDataDecoder decoder)
+    {
+        _storageController = storageController;
+        _decoder = decoder;
+    }
 
-        ICacheController StorageController { get; }
+    public async Task GetCurrentConfigAsync(IServiceHub serviceHub)
+    {
+        if (_currentConfiguration != null)
+            return _currentConfiguration;
 
-        IParseDataDecoder Decoder { get; }
+        var data = await _storageController.LoadAsync();
+        data.TryGetValue(CurrentConfigurationKey, out var storedData);
 
-        /// 
-        /// Initializes a new instance of the  class.
-        /// 
-        public ParseCurrentConfigurationController(ICacheController storageController, IParseDataDecoder decoder)
-        {
-            StorageController = storageController;
-            Decoder = decoder;
-            TaskQueue = new TaskQueue { };
-        }
+        _currentConfiguration = storedData is string configString
+            ? _decoder.BuildConfiguration(ParseClient.DeserializeJsonString(configString), serviceHub)
+            : new ParseConfiguration(serviceHub);
 
-        public Task GetCurrentConfigAsync(IServiceHub serviceHub)
-        {
-            return TaskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ => CurrentConfiguration is { } ? Task.FromResult(CurrentConfiguration) : StorageController.LoadAsync().OnSuccess(task =>
-        {
-            task.Result.TryGetValue(CurrentConfigurationKey, out object data);
-            return CurrentConfiguration = data is string { } configuration ? Decoder.BuildConfiguration(ParseClient.DeserializeJsonString(configuration), serviceHub) : new ParseConfiguration(serviceHub);
-        })), CancellationToken.None).Unwrap();
-        }
+        return _currentConfiguration;
+    }
 
-        public Task SetCurrentConfigAsync(ParseConfiguration target)
-        {
-            return TaskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ =>
-        {
-            CurrentConfiguration = target;
-            return StorageController.LoadAsync().OnSuccess(task => task.Result.AddAsync(CurrentConfigurationKey, ParseClient.SerializeJsonString(((IJsonConvertible) target).ConvertToJSON())));
-        }).Unwrap().Unwrap(), CancellationToken.None);
-        }
+    public async Task SetCurrentConfigAsync(ParseConfiguration target)
+    {
+        _currentConfiguration = target;
 
-        public Task ClearCurrentConfigAsync()
-        {
-            return TaskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ =>
-        {
-            CurrentConfiguration = null;
-            return StorageController.LoadAsync().OnSuccess(task => task.Result.RemoveAsync(CurrentConfigurationKey));
-        }).Unwrap().Unwrap(), CancellationToken.None);
-        }
+        var data = await _storageController.LoadAsync();
+        await data.AddAsync(CurrentConfigurationKey, ParseClient.SerializeJsonString(((IJsonConvertible) target).ConvertToJSON()));
+    }
 
-        public Task ClearCurrentConfigInMemoryAsync()
-        {
-            return TaskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ => CurrentConfiguration = null), CancellationToken.None);
-        }
+    public async Task ClearCurrentConfigAsync()
+    {
+        _currentConfiguration = null;
+
+        var data = await _storageController.LoadAsync();
+        await data.RemoveAsync(CurrentConfigurationKey);
     }
+
+    public Task ClearCurrentConfigInMemoryAsync() => Task.Run(() => _currentConfiguration = null);
 }
diff --git a/Parse/Platform/Files/ParseFile.cs b/Parse/Platform/Files/ParseFile.cs
index 85731c8c..4b4bca00 100644
--- a/Parse/Platform/Files/ParseFile.cs
+++ b/Parse/Platform/Files/ParseFile.cs
@@ -7,166 +7,181 @@
 using Parse.Infrastructure.Utilities;
 using Parse.Platform.Files;
 
-namespace Parse
+namespace Parse;
+
+public static class FileServiceExtensions
 {
-    public static class FileServiceExtensions
+    /// 
+    /// Saves the file to the Parse cloud.
+    /// 
+    /// The cancellation token.
+    public static Task SaveFileAsync(this IServiceHub serviceHub, ParseFile file, CancellationToken cancellationToken = default)
     {
-        /// 
-        /// Saves the file to the Parse cloud.
-        /// 
-        /// The cancellation token.
-        public static Task SaveFileAsync(this IServiceHub serviceHub, ParseFile file, CancellationToken cancellationToken = default)
-        {
-            return serviceHub.SaveFileAsync(file, default, cancellationToken);
-        }
+        return serviceHub.SaveFileAsync(file, default, cancellationToken);
+    }
+
+    /// 
+    /// Saves the file to the Parse cloud.
+    /// 
+    /// The progress callback.
+    /// The cancellation token.
+    public static async Task SaveFileAsync(
+this IServiceHub serviceHub,
+ParseFile file,
+IProgress progress,
+CancellationToken cancellationToken = default)
+    {
+        var result = await file.TaskQueue.Enqueue(
+            async toAwait => await serviceHub.FileController.SaveAsync(
+                file.State,
+                file.DataStream,
+                serviceHub.GetCurrentSessionToken(),
+                progress,
+                cancellationToken
+            ).ConfigureAwait(false),
+            cancellationToken
+        ).ConfigureAwait(false);
+
+        file.State = result; // Update the file state with the result
+    }
 
-        /// 
-        /// Saves the file to the Parse cloud.
-        /// 
-        /// The progress callback.
-        /// The cancellation token.
-        public static Task SaveFileAsync(this IServiceHub serviceHub, ParseFile file, IProgress progress, CancellationToken cancellationToken = default)
-        {
-            return file.TaskQueue.Enqueue(toAwait => serviceHub.FileController.SaveAsync(file.State, file.DataStream, serviceHub.GetCurrentSessionToken(), progress, cancellationToken), cancellationToken).OnSuccess(task => file.State = task.Result);
-        }
 
 #pragma warning disable CS1030 // #warning directive
 #warning Make serviceHub null by default once dependents properly inject it when needed.
 
-        /// 
-        /// Saves the file to the Parse cloud.
-        /// 
-        /// The cancellation token.
-        public static Task SaveAsync(this ParseFile file, IServiceHub serviceHub, CancellationToken cancellationToken = default)
-        {
-            return serviceHub.SaveFileAsync(file, cancellationToken);
-        }
-#pragma warning restore CS1030 // #warning directive
-
-        /// 
-        /// Saves the file to the Parse cloud.
-        /// 
-        /// The progress callback.
-        /// The cancellation token.
-        public static Task SaveAsync(this ParseFile file, IServiceHub serviceHub, IProgress progress, CancellationToken cancellationToken = default)
-        {
-            return serviceHub.SaveFileAsync(file, progress, cancellationToken);
-        }
+    /// 
+    /// Saves the file to the Parse cloud.
+    /// 
+    /// The cancellation token.
+    public static Task SaveAsync(this ParseFile file, IServiceHub serviceHub, CancellationToken cancellationToken = default)
+    {
+        return serviceHub.SaveFileAsync(file, cancellationToken);
     }
+#pragma warning restore CS1030 // #warning directive
 
     /// 
-    /// ParseFile is a local representation of a file that is saved to the Parse cloud.
+    /// Saves the file to the Parse cloud.
     /// 
-    /// 
-    /// The workflow is to construct a  with data and a filename,
-    /// then save it and set it as a field on a ParseObject:
-    ///
-    /// 
-    /// var file = new ParseFile("hello.txt",
-    ///     new MemoryStream(Encoding.UTF8.GetBytes("hello")));
-    /// await file.SaveAsync();
-    /// var obj = new ParseObject("TestObject");
-    /// obj["file"] = file;
-    /// await obj.SaveAsync();
-    /// 
-    /// 
-    public class ParseFile : IJsonConvertible
+    /// The progress callback.
+    /// The cancellation token.
+    public static Task SaveAsync(this ParseFile file, IServiceHub serviceHub, IProgress progress, CancellationToken cancellationToken = default)
     {
-        internal FileState State { get; set; }
+        return serviceHub.SaveFileAsync(file, progress, cancellationToken);
+    }
+}
+
+/// 
+/// ParseFile is a local representation of a file that is saved to the Parse cloud.
+/// 
+/// 
+/// The workflow is to construct a  with data and a filename,
+/// then save it and set it as a field on a ParseObject:
+///
+/// 
+/// var file = new ParseFile("hello.txt",
+///     new MemoryStream(Encoding.UTF8.GetBytes("hello")));
+/// await file.SaveAsync();
+/// var obj = new ParseObject("TestObject");
+/// obj["file"] = file;
+/// await obj.SaveAsync();
+/// 
+/// 
+public class ParseFile : IJsonConvertible
+{
+    internal FileState State { get; set; }
 
-        internal Stream DataStream { get; }
+    internal Stream DataStream { get; }
 
-        internal TaskQueue TaskQueue { get; } = new TaskQueue { };
+    internal TaskQueue TaskQueue { get; } = new TaskQueue { };
 
-        #region Constructor
+    #region Constructor
 
 #pragma warning disable CS1030 // #warning directive
 #warning Make IServiceHub optionally null once all dependents are injecting it if necessary.
 
-        internal ParseFile(string name, Uri uri, string mimeType = null) => State = new FileState
+    internal ParseFile(string name, Uri uri, string mimeType = null) => State = new FileState
+    {
+        Name = name,
+        Location = uri,
+        MediaType = mimeType
+    };
+#pragma warning restore CS1030 // #warning directive
+
+    /// 
+    /// Creates a new file from a byte array and a name.
+    /// 
+    /// The file's name, ideally with an extension. The file name
+    /// must begin with an alphanumeric character, and consist of alphanumeric
+    /// characters, periods, spaces, underscores, or dashes.
+    /// The file's data.
+    /// To specify the content-type used when uploading the
+    /// file, provide this parameter.
+    public ParseFile(string name, byte[] data, string mimeType = null) : this(name, new MemoryStream(data), mimeType) { }
+
+    /// 
+    /// Creates a new file from a stream and a name.
+    /// 
+    /// The file's name, ideally with an extension. The file name
+    /// must begin with an alphanumeric character, and consist of alphanumeric
+    /// characters, periods, spaces, underscores, or dashes.
+    /// The file's data.
+    /// To specify the content-type used when uploading the
+    /// file, provide this parameter.
+    public ParseFile(string name, Stream data, string mimeType = null)
+    {
+        State = new FileState
         {
             Name = name,
-            Location = uri,
             MediaType = mimeType
         };
-#pragma warning restore CS1030 // #warning directive
 
-        /// 
-        /// Creates a new file from a byte array and a name.
-        /// 
-        /// The file's name, ideally with an extension. The file name
-        /// must begin with an alphanumeric character, and consist of alphanumeric
-        /// characters, periods, spaces, underscores, or dashes.
-        /// The file's data.
-        /// To specify the content-type used when uploading the
-        /// file, provide this parameter.
-        public ParseFile(string name, byte[] data, string mimeType = null) : this(name, new MemoryStream(data), mimeType) { }
-
-        /// 
-        /// Creates a new file from a stream and a name.
-        /// 
-        /// The file's name, ideally with an extension. The file name
-        /// must begin with an alphanumeric character, and consist of alphanumeric
-        /// characters, periods, spaces, underscores, or dashes.
-        /// The file's data.
-        /// To specify the content-type used when uploading the
-        /// file, provide this parameter.
-        public ParseFile(string name, Stream data, string mimeType = null)
-        {
-            State = new FileState
-            {
-                Name = name,
-                MediaType = mimeType
-            };
-
-            DataStream = data;
-        }
+        DataStream = data;
+    }
 
-        #endregion
+    #endregion
 
-        #region Properties
+    #region Properties
 
-        /// 
-        /// Gets whether the file still needs to be saved.
-        /// 
-        public bool IsDirty => State.Location == null;
+    /// 
+    /// Gets whether the file still needs to be saved.
+    /// 
+    public bool IsDirty => State.Location == null;
 
-        /// 
-        /// Gets the name of the file. Before save is called, this is the filename given by
-        /// the user. After save is called, that name gets prefixed with a unique identifier.
-        /// 
-        [ParseFieldName("name")]
-        public string Name => State.Name;
+    /// 
+    /// Gets the name of the file. Before save is called, this is the filename given by
+    /// the user. After save is called, that name gets prefixed with a unique identifier.
+    /// 
+    [ParseFieldName("name")]
+    public string Name => State.Name;
 
-        /// 
-        /// Gets the MIME type of the file. This is either passed in to the constructor or
-        /// inferred from the file extension. "unknown/unknown" will be used if neither is
-        /// available.
-        /// 
-        public string MimeType => State.MediaType;
+    /// 
+    /// Gets the MIME type of the file. This is either passed in to the constructor or
+    /// inferred from the file extension. "unknown/unknown" will be used if neither is
+    /// available.
+    /// 
+    public string MimeType => State.MediaType;
 
-        /// 
-        /// Gets the url of the file. It is only available after you save the file or after
-        /// you get the file from a .
-        /// 
-        [ParseFieldName("url")]
-        public Uri Url => State.SecureLocation;
+    /// 
+    /// Gets the url of the file. It is only available after you save the file or after
+    /// you get the file from a .
+    /// 
+    [ParseFieldName("url")]
+    public Uri Url => State.SecureLocation;
 
-        #endregion
+    #endregion
 
-        IDictionary IJsonConvertible.ConvertToJSON()
+    IDictionary IJsonConvertible.ConvertToJSON()
+    {
+        if (IsDirty)
         {
-            if (IsDirty)
-            {
-                throw new InvalidOperationException("ParseFile must be saved before it can be serialized.");
-            }
-
-            return new Dictionary
-            {
-                ["__type"] = "File",
-                ["name"] = Name,
-                ["url"] = Url.AbsoluteUri
-            };
+            throw new InvalidOperationException("ParseFile must be saved before it can be serialized.");
         }
+
+        return new Dictionary
+        {
+            ["__type"] = "File",
+            ["name"] = Name,
+            ["url"] = Url.AbsoluteUri
+        };
     }
 }
diff --git a/Parse/Platform/Files/ParseFileController.cs b/Parse/Platform/Files/ParseFileController.cs
index 0545f8e4..82616371 100644
--- a/Parse/Platform/Files/ParseFileController.cs
+++ b/Parse/Platform/Files/ParseFileController.cs
@@ -10,47 +10,61 @@
 using Parse.Infrastructure.Execution;
 using Parse.Infrastructure.Utilities;
 
-namespace Parse.Platform.Files
+namespace Parse.Platform.Files;
+
+public class ParseFileController : IParseFileController
 {
-    public class ParseFileController : IParseFileController
+    private IParseCommandRunner CommandRunner { get; }
+
+    public ParseFileController(IParseCommandRunner commandRunner) => CommandRunner = commandRunner;
+
+    public async Task SaveAsync(FileState state, Stream dataStream, string sessionToken, IProgress progress, CancellationToken cancellationToken = default)
     {
-        IParseCommandRunner CommandRunner { get; }
+        // If the file is already uploaded, no need to re-upload.
+        if (state.Location != null)
+            return state;
 
-        public ParseFileController(IParseCommandRunner commandRunner) => CommandRunner = commandRunner;
+        if (cancellationToken.IsCancellationRequested)
+            return await Task.FromCanceled(cancellationToken);
 
-        public Task SaveAsync(FileState state, Stream dataStream, string sessionToken, IProgress progress, CancellationToken cancellationToken = default)
-        {
-            if (state.Location != null)
-                // !isDirty
+        long oldPosition = dataStream.Position;
 
-                return Task.FromResult(state);
+        try
+        {
+            // Execute the file upload command
+            var result = await CommandRunner.RunCommandAsync(
+                new ParseCommand($"files/{state.Name}", method: "POST", sessionToken: sessionToken, contentType: state.MediaType, stream: dataStream),
+                uploadProgress: progress,
+                cancellationToken: cancellationToken).ConfigureAwait(false);
 
-            if (cancellationToken.IsCancellationRequested)
-                return Task.FromCanceled(cancellationToken);
+            // Extract the result
+            var jsonData = result.Item2;
 
-            long oldPosition = dataStream.Position;
+            // Ensure the cancellation token hasn't been triggered during processing
+            cancellationToken.ThrowIfCancellationRequested();
 
-            return CommandRunner.RunCommandAsync(new ParseCommand($"files/{state.Name}", method: "POST", sessionToken: sessionToken, contentType: state.MediaType, stream: dataStream), uploadProgress: progress, cancellationToken: cancellationToken).OnSuccess(uploadTask =>
+            return new FileState
             {
-                Tuple> result = uploadTask.Result;
-                IDictionary jsonData = result.Item2;
-                cancellationToken.ThrowIfCancellationRequested();
-
-                return new FileState
-                {
-                    Name = jsonData["name"] as string,
-                    Location = new Uri(jsonData["url"] as string, UriKind.Absolute),
-                    MediaType = state.MediaType
-                };
-            }).ContinueWith(task =>
-            {
-                // Rewind the stream on failure or cancellation (if possible).
+                Name = jsonData["name"] as string,
+                Location = new Uri(jsonData["url"] as string, UriKind.Absolute),
+                MediaType = state.MediaType
+            };
+        }
+        catch (OperationCanceledException)
+        {
+            // Handle the cancellation properly, resetting the stream if it can seek
+            if (dataStream.CanSeek)
+                dataStream.Seek(oldPosition, SeekOrigin.Begin);
 
-                if ((task.IsFaulted || task.IsCanceled) && dataStream.CanSeek)
-                    dataStream.Seek(oldPosition, SeekOrigin.Begin);
+            throw; // Re-throw to allow the caller to handle the cancellation
+        }
+        catch (Exception)
+        {
+            // If an error occurs, reset the stream position and rethrow
+            if (dataStream.CanSeek)
+                dataStream.Seek(oldPosition, SeekOrigin.Begin);
 
-                return task;
-            }).Unwrap();
+            throw; // Re-throw to allow the caller to handle the error
         }
     }
 }
diff --git a/Parse/Platform/Installations/ParseCurrentInstallationController.cs b/Parse/Platform/Installations/ParseCurrentInstallationController.cs
index 4e3f8b6a..c16bfa3e 100644
--- a/Parse/Platform/Installations/ParseCurrentInstallationController.cs
+++ b/Parse/Platform/Installations/ParseCurrentInstallationController.cs
@@ -6,115 +6,122 @@
 using Parse.Abstractions.Platform.Objects;
 using Parse.Infrastructure.Utilities;
 
-namespace Parse.Platform.Installations
+namespace Parse.Platform.Installations;
+internal class ParseCurrentInstallationController : IParseCurrentInstallationController
 {
-    internal class ParseCurrentInstallationController : IParseCurrentInstallationController
-    {
-        static string ParseInstallationKey { get; } = nameof(CurrentInstallation);
-
-        object Mutex { get; } = new object { };
-
-        TaskQueue TaskQueue { get; } = new TaskQueue { };
-
-        IParseInstallationController InstallationController { get; }
-
-        ICacheController StorageController { get; }
-
-        IParseInstallationCoder InstallationCoder { get; }
+    private static readonly string ParseInstallationKey = nameof(CurrentInstallation);
+    private readonly object Mutex = new object();
+    private readonly TaskQueue TaskQueue = new TaskQueue();
 
-        IParseObjectClassController ClassController { get; }
+    private readonly IParseInstallationController InstallationController;
+    private readonly ICacheController StorageController;
+    private readonly IParseInstallationCoder InstallationCoder;
+    private readonly IParseObjectClassController ClassController;
 
-        public ParseCurrentInstallationController(IParseInstallationController installationIdController, ICacheController storageController, IParseInstallationCoder installationCoder, IParseObjectClassController classController)
-        {
-            InstallationController = installationIdController;
-            StorageController = storageController;
-            InstallationCoder = installationCoder;
-            ClassController = classController;
-        }
-
-        ParseInstallation CurrentInstallationValue { get; set; }
+    private ParseInstallation CurrentInstallationValue { get; set; }
 
-        internal ParseInstallation CurrentInstallation
+    internal ParseInstallation CurrentInstallation
+    {
+        get
         {
-            get
+            lock (Mutex)
             {
-                lock (Mutex)
-                {
-                    return CurrentInstallationValue;
-                }
+                return CurrentInstallationValue;
             }
-            set
+        }
+        set
+        {
+            lock (Mutex)
             {
-                lock (Mutex)
-                {
-                    CurrentInstallationValue = value;
-                }
+                CurrentInstallationValue = value;
             }
         }
+    }
 
-        public Task SetAsync(ParseInstallation installation, CancellationToken cancellationToken)
-        {
-            return TaskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ =>
-        {
-            Task saveTask = StorageController.LoadAsync().OnSuccess(storage => installation is { } ? storage.Result.AddAsync(ParseInstallationKey, JsonUtilities.Encode(InstallationCoder.Encode(installation))) : storage.Result.RemoveAsync(ParseInstallationKey)).Unwrap();
-            CurrentInstallation = installation;
-
-            return saveTask;
-        }).Unwrap(), cancellationToken);
-        }
+    public ParseCurrentInstallationController(
+        IParseInstallationController installationIdController,
+        ICacheController storageController,
+        IParseInstallationCoder installationCoder,
+        IParseObjectClassController classController)
+    {
+        InstallationController = installationIdController;
+        StorageController = storageController;
+        InstallationCoder = installationCoder;
+        ClassController = classController;
+    }
 
-        public Task GetAsync(IServiceHub serviceHub, CancellationToken cancellationToken = default)
+    public async Task SetAsync(ParseInstallation installation, CancellationToken cancellationToken)
+    {
+        // Update the current installation in memory and disk asynchronously
+        await TaskQueue.Enqueue(async (toAwait) =>
         {
-            ParseInstallation cachedCurrent;
-            cachedCurrent = CurrentInstallation;
-
-            if (cachedCurrent is { })
-                return  Task.FromResult(cachedCurrent);
+            var storage = await StorageController.LoadAsync().ConfigureAwait(false);
+            if (installation != null)
+            {
+                await storage.AddAsync(ParseInstallationKey, JsonUtilities.Encode(InstallationCoder.Encode(installation))).ConfigureAwait(false);
+            }
             else
-                return  TaskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ => StorageController.LoadAsync().OnSuccess(stroage =>
             {
-                Task fetchTask;
-                stroage.Result.TryGetValue(ParseInstallationKey, out object temp);
-                ParseInstallation installation = default;
-
-                if (temp is string installationDataString)
-                {
-                    IDictionary installationData = JsonUtilities.Parse(installationDataString) as IDictionary;
-                    installation = InstallationCoder.Decode(installationData, serviceHub);
+                await storage.RemoveAsync(ParseInstallationKey).ConfigureAwait(false);
+            }
+            CurrentInstallation = installation;
+        }, cancellationToken).ConfigureAwait(false);
+    }
 
-                    fetchTask = Task.FromResult(null);
-                }
-                else
-                {
-                    installation = ClassController.CreateObject(serviceHub);
-                    fetchTask = InstallationController.GetAsync().ContinueWith(t => installation.SetIfDifferent("installationId", t.Result.ToString()));
-                }
 
-                CurrentInstallation = installation;
-                return fetchTask.ContinueWith(task => installation);
-            })).Unwrap().Unwrap(), cancellationToken);
+    public async Task GetAsync(IServiceHub serviceHub, CancellationToken cancellationToken = default)
+    {
+        // Check if the installation is already cached
+        var cachedCurrent = CurrentInstallation;
+        if (cachedCurrent != null)
+        {
+            return cachedCurrent;
         }
 
-        public Task ExistsAsync(CancellationToken cancellationToken)
+        var storage = await StorageController.LoadAsync().ConfigureAwait(false);
+        if (storage.TryGetValue(ParseInstallationKey, out object temp) && temp is string installationDataString)
         {
-            return CurrentInstallation is { } ? Task.FromResult(true) : TaskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ => StorageController.LoadAsync().OnSuccess(storageTask => storageTask.Result.ContainsKey(ParseInstallationKey))).Unwrap(), cancellationToken);
+            var installationData = JsonUtilities.Parse(installationDataString) as IDictionary;
+            var installation = InstallationCoder.Decode(installationData, serviceHub);
+            CurrentInstallation = installation;
+            return installation;
         }
-
-        public bool IsCurrent(ParseInstallation installation)
+        else
         {
-            return CurrentInstallation == installation;
+            var installation = ClassController.CreateObject(serviceHub);
+            var installationId = await InstallationController.GetAsync().ConfigureAwait(false);
+            installation.SetIfDifferent("installationId", installationId.ToString());
+            CurrentInstallation = installation;
+            return installation;
         }
+    }
 
-        public void ClearFromMemory()
+    public async Task ExistsAsync(CancellationToken cancellationToken)
+    {
+        // Check if the current installation exists in memory or storage
+        if (CurrentInstallation != null)
         {
-            CurrentInstallation = default;
+            return true;
         }
 
-        public void ClearFromDisk()
-        {
-            ClearFromMemory();
+        var storage = await StorageController.LoadAsync().ConfigureAwait(false);
+        return storage.ContainsKey(ParseInstallationKey);
+    }
 
-            TaskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ => StorageController.LoadAsync().OnSuccess(storage => storage.Result.RemoveAsync(ParseInstallationKey))).Unwrap().Unwrap(), CancellationToken.None);
-        }
+    public bool IsCurrent(ParseInstallation installation)
+    {
+        return CurrentInstallation == installation;
+    }
+
+    public void ClearFromMemory()
+    {
+        CurrentInstallation = null;
+    }
+
+    public async Task ClearFromDiskAsync()
+    {
+        ClearFromMemory();
+        var storage = await StorageController.LoadAsync().ConfigureAwait(false);
+        await storage.RemoveAsync(ParseInstallationKey).ConfigureAwait(false);
     }
-}
+}
\ No newline at end of file
diff --git a/Parse/Platform/Installations/ParseInstallation.cs b/Parse/Platform/Installations/ParseInstallation.cs
index 460b2f78..df26a0fa 100644
--- a/Parse/Platform/Installations/ParseInstallation.cs
+++ b/Parse/Platform/Installations/ParseInstallation.cs
@@ -190,10 +190,8 @@ protected override bool CheckKeyMutable(string key)
             return !ImmutableKeys.Contains(key);
         }
 
-        protected override Task SaveAsync(Task toAwait, CancellationToken cancellationToken)
+        protected override async Task SaveAsync(Task toAwait, CancellationToken cancellationToken)
         {
-            Task platformHookTask = null;
-
             if (Services.CurrentInstallationController.IsCurrent(this))
 #pragma warning disable CS1030 // #warning directive
             {
@@ -210,8 +208,25 @@ protected override Task SaveAsync(Task toAwait, CancellationToken cancellationTo
                 //platformHookTask = Client.InstallationDataFinalizer.FinalizeAsync(this);
             }
 #pragma warning restore CS1030 // #warning directive
+            Task platformHookTask = ParseClient.Instance.InstallationDataFinalizer.FinalizeAsync(this); 
+
+            // Wait for the platform task, then proceed with saving the main task.
+            try
+            {
+                _ = platformHookTask.Safe().ConfigureAwait(false);
+                _ = base.SaveAsync(toAwait, cancellationToken).ConfigureAwait(false);
+                if (!Services.CurrentInstallationController.IsCurrent(this))
+                {
+                    _ = Services.CurrentInstallationController.SetAsync(this, cancellationToken).ConfigureAwait(false);
+                }
+            }
+            catch (Exception ex)
+            {
+                // Log or handle the exception
+                // You can log it or rethrow if necessary
+                Console.Error.WriteLine(ex);
+            }
 
-            return platformHookTask.Safe().OnSuccess(_ => base.SaveAsync(toAwait, cancellationToken)).Unwrap().OnSuccess(_ => Services.CurrentInstallationController.IsCurrent(this) ? Task.CompletedTask : Services.CurrentInstallationController.SetAsync(this, cancellationToken)).Unwrap();
         }
 
         /// 
diff --git a/Parse/Platform/Installations/ParseInstallationController.cs b/Parse/Platform/Installations/ParseInstallationController.cs
index 1c733b14..929e7411 100644
--- a/Parse/Platform/Installations/ParseInstallationController.cs
+++ b/Parse/Platform/Installations/ParseInstallationController.cs
@@ -4,62 +4,76 @@
 using Parse.Abstractions.Platform.Installations;
 using Parse.Infrastructure.Utilities;
 
-namespace Parse.Platform.Installations
+namespace Parse.Platform.Installations;
+
+public class ParseInstallationController : IParseInstallationController
 {
-    public class ParseInstallationController : IParseInstallationController
-    {
-        static string InstallationIdKey { get; } = "InstallationId";
+    static string InstallationIdKey { get; } = "InstallationId";
+
+    object Mutex { get; } = new object { };
 
-        object Mutex { get; } = new object { };
+    Guid? InstallationId { get; set; }
 
-        Guid? InstallationId { get; set; }
+    ICacheController StorageController { get; }
 
-        ICacheController StorageController { get; }
+    public ParseInstallationController(ICacheController storageController) => StorageController = storageController;
 
-        public ParseInstallationController(ICacheController storageController) => StorageController = storageController;
+    public async Task SetAsync(Guid? installationId)
+    {
+        // Directly handle the async calls without using locks
+        var storage = await StorageController.LoadAsync().ConfigureAwait(false);
 
-        public Task SetAsync(Guid? installationId)
+        // Update the installationId and modify storage accordingly
+        if (installationId.HasValue)
         {
-            lock (Mutex)
-            {
-#pragma warning disable CS1030 // #warning directive
-#warning Should refactor here if this operates correctly.
+            await storage.AddAsync(InstallationIdKey, installationId.Value.ToString()).ConfigureAwait(false);
+        }
+        else
+        {
+            await storage.RemoveAsync(InstallationIdKey).ConfigureAwait(false);
+        }
+
+        // Set the current installationId
+        InstallationId = installationId;
+    }
 
-                Task saveTask = installationId is { } ? StorageController.LoadAsync().OnSuccess(storage => storage.Result.AddAsync(InstallationIdKey, installationId.ToString())).Unwrap() : StorageController.LoadAsync().OnSuccess(storage => storage.Result.RemoveAsync(InstallationIdKey)).Unwrap();
-#pragma warning restore CS1030 // #warning directive
 
-                InstallationId = installationId;
-                return saveTask;
+    public async Task GetAsync()
+    {
+        lock (Mutex)
+        {
+            if (InstallationId != null)
+            {
+                return InstallationId;
             }
         }
 
-        public Task GetAsync()
+        // Await the asynchronous storage loading task
+        var storageResult = await StorageController.LoadAsync();
+
+        // Try to get the installation ID from the storage result
+        if (storageResult.TryGetValue(InstallationIdKey, out object id) && id is string idString && Guid.TryParse(idString, out Guid parsedId))
         {
             lock (Mutex)
-                if (InstallationId is { })
-                    return Task.FromResult(InstallationId);
-
-            return StorageController.LoadAsync().OnSuccess(storageTask =>
             {
-                storageTask.Result.TryGetValue(InstallationIdKey, out object id);
-
-                try
-                {
-                    lock (Mutex)
-                        return Task.FromResult(InstallationId = new Guid(id as string));
-                }
-                catch (Exception)
-                {
-                    Guid newInstallationId = Guid.NewGuid();
-                    return SetAsync(newInstallationId).OnSuccess(_ => newInstallationId);
-                }
-            })
-            .Unwrap();
+                InstallationId = parsedId; // Cache the parsed ID
+                return InstallationId;
+            }
         }
 
-        public Task ClearAsync()
+        // If no valid ID is found, generate a new one
+        Guid newInstallationId = Guid.NewGuid();
+        await SetAsync(newInstallationId); // Save the new ID
+
+        lock (Mutex)
         {
-            return SetAsync(null);
+            InstallationId = newInstallationId; // Cache the new ID
+            return InstallationId;
         }
     }
+
+    public Task ClearAsync()
+    {
+        return SetAsync(null);
+    }
 }
diff --git a/Parse/Platform/Objects/MutableObjectState.cs b/Parse/Platform/Objects/MutableObjectState.cs
index 8f40da7e..16f54100 100644
--- a/Parse/Platform/Objects/MutableObjectState.cs
+++ b/Parse/Platform/Objects/MutableObjectState.cs
@@ -20,7 +20,7 @@ public class MutableObjectState : IObjectState
     public DateTime? CreatedAt { get; set; }
     //public string Username { get; set; } // Added
     //public string Email { get; set; } // Added
-    //public string SessionToken { get; set; } // Added
+    public string SessionToken { get; set; } // Added
 
     public IDictionary ServerData { get; set; } = new Dictionary();
 
diff --git a/Parse/Platform/Objects/ParseObject.cs b/Parse/Platform/Objects/ParseObject.cs
index 0b67898e..79a70559 100644
--- a/Parse/Platform/Objects/ParseObject.cs
+++ b/Parse/Platform/Objects/ParseObject.cs
@@ -15,1176 +15,1215 @@
 using Parse.Infrastructure.Data;
 using System.Diagnostics;
 
-namespace Parse
+namespace Parse;
+
+/// 
+/// The ParseObject is a local representation of data that can be saved and
+/// retrieved from the Parse cloud.
+/// 
+/// 
+/// The basic workflow for creating new data is to construct a new ParseObject,
+/// use the indexer to fill it with data, and then use SaveAsync() to persist to the
+/// database.
+/// 
+/// 
+/// The basic workflow for accessing existing data is to use a ParseQuery
+/// to specify which existing data to retrieve.
+/// 
+/// 
+public class ParseObject : IEnumerable>, INotifyPropertyChanged
 {
+    internal static string AutoClassName { get; } = "_Automatic";
+
+    internal static ThreadLocal CreatingPointer { get; } = new ThreadLocal(() => false);
+
+    internal TaskQueue TaskQueue { get; } = new TaskQueue { };
+
+    /// 
+    /// The  instance being targeted. This should generally not be set except when an object is being constructed, as otherwise race conditions may occur. The preferred method to set this property is via calling .
+    /// 
+    public IServiceHub Services { get; set; }
+
     /// 
-    /// The ParseObject is a local representation of data that can be saved and
-    /// retrieved from the Parse cloud.
+    /// Constructs a new ParseObject with no data in it. A ParseObject constructed in this way will
+    /// not have an ObjectId and will not persist to the database until 
+    /// is called.
+    /// 
     /// 
-    /// 
-    /// The basic workflow for creating new data is to construct a new ParseObject,
-    /// use the indexer to fill it with data, and then use SaveAsync() to persist to the
-    /// database.
-    /// 
-    /// 
-    /// The basic workflow for accessing existing data is to use a ParseQuery
-    /// to specify which existing data to retrieve.
-    /// 
+    /// Class names must be alphanumerical plus underscore, and start with a letter. It is recommended
+    /// to name classes in PascalCase.
     /// 
-    public class ParseObject : IEnumerable>, INotifyPropertyChanged
-    {
-        internal static string AutoClassName { get; } = "_Automatic";
-
-        internal static ThreadLocal CreatingPointer { get; } = new ThreadLocal(() => false);
-
-        internal TaskQueue TaskQueue { get; } = new TaskQueue { };
-
-        /// 
-        /// The  instance being targeted. This should generally not be set except when an object is being constructed, as otherwise race conditions may occur. The preferred method to set this property is via calling .
-        /// 
-        public IServiceHub Services { get; set; }
-
-        /// 
-        /// Constructs a new ParseObject with no data in it. A ParseObject constructed in this way will
-        /// not have an ObjectId and will not persist to the database until 
-        /// is called.
-        /// 
-        /// 
-        /// Class names must be alphanumerical plus underscore, and start with a letter. It is recommended
-        /// to name classes in PascalCase.
-        /// 
-        /// The className for this ParseObject.
-        /// The  implementation instance to target for any resources. This paramater can be effectively set after construction via .
-        public ParseObject(string className, IServiceHub serviceHub = default)
-        {
-            // Validate serviceHub
-            if (serviceHub == null && ParseClient.Instance == null)
-            {
-                Debug.WriteLine("Warning: Both serviceHub and ParseClient.Instance are null. ParseObject requires explicit initialization via Bind(IServiceHub).");
+    /// The className for this ParseObject.
+    /// The  implementation instance to target for any resources. This paramater can be effectively set after construction via .
+    public ParseObject(string className, IServiceHub serviceHub = default)
+    {
+        // Validate serviceHub
+        if (serviceHub == null && ParseClient.Instance == null)
+        {
+            Debug.WriteLine("Warning: Both serviceHub and ParseClient.Instance are null. ParseObject requires explicit initialization via Bind(IServiceHub).");
 
-                //throw new InvalidOperationException("A valid IServiceHub or ParseClient.Instance must be available to construct a ParseObject.");
-            }
+            //throw new InvalidOperationException("A valid IServiceHub or ParseClient.Instance must be available to construct a ParseObject.");
+        }
 
-            Services = serviceHub ?? ParseClient.Instance;
+        Services = serviceHub ?? ParseClient.Instance;
 
-            // Validate and set className
-            if (string.IsNullOrWhiteSpace(className))
-            {
-                throw new ArgumentException("You must specify a Parse class name when creating a new ParseObject.");
-            }
+        // Validate and set className
+        if (string.IsNullOrWhiteSpace(className))
+        {
+            throw new ArgumentException("You must specify a Parse class name when creating a new ParseObject.");
+        }
 
-            if (AutoClassName.Equals(className))
-            {
-                className = GetType().GetParseClassName() ?? throw new ArgumentException("Unable to determine class name for ParseObject.");
-            }
-            if (Services is not null)
+        if (AutoClassName.Equals(className))
+        {
+            className = GetType().GetParseClassName() ?? throw new ArgumentException("Unable to determine class name for ParseObject.");
+        }
+        if (Services is not null)
+        {
+            // Validate against factory requirements
+            if (!Services.ClassController.GetClassMatch(className, GetType()) && GetType() != typeof(ParseObject))
             {
-                // Validate against factory requirements
-                if (!Services.ClassController.GetClassMatch(className, GetType()) && GetType() != typeof(ParseObject))
-                {
-                    throw new ArgumentException("You must create this type of ParseObject using ParseObject.Create() or the proper subclass.");
-                }
+                throw new ArgumentException("You must create this type of ParseObject using ParseObject.Create() or the proper subclass.");
             }
+        }
 
-            // Initialize state
-            State = new MutableObjectState { ClassName = className };
-            OnPropertyChanged(nameof(ClassName));
-            OperationSetQueue.AddLast(new Dictionary());
+        // Initialize state
+        State = new MutableObjectState { ClassName = className };
+        OnPropertyChanged(nameof(ClassName));
+        OperationSetQueue.AddLast(new Dictionary());
 
-            // Handle pointer creation
-            bool isPointer = CreatingPointer.Value;
-            CreatingPointer.Value = false;
+        // Handle pointer creation
+        bool isPointer = CreatingPointer.Value;
+        CreatingPointer.Value = false;
 
-            Fetched = !isPointer;
-            IsDirty = !isPointer;
+        Fetched = !isPointer;
+        IsDirty = !isPointer;
 
-            if (!isPointer)
-            {
-                SetDefaultValues();
-            }
+        if (!isPointer)
+        {
+            SetDefaultValues();
         }
+    }
 
 
-        #region ParseObject Creation
+    #region ParseObject Creation
 
-        /// 
-        /// Constructor for use in ParseObject subclasses. Subclasses must specify a ParseClassName attribute. Subclasses that do not implement a constructor accepting  will need to be bond to an implementation instance via  after construction.
-        /// 
-        protected ParseObject(IServiceHub serviceHub = default) : this(AutoClassName, serviceHub) { }
+    /// 
+    /// Constructor for use in ParseObject subclasses. Subclasses must specify a ParseClassName attribute. Subclasses that do not implement a constructor accepting  will need to be bond to an implementation instance via  after construction.
+    /// 
+    protected ParseObject(IServiceHub serviceHub = default) : this(AutoClassName, serviceHub) { }
 
-        /// 
-        /// Attaches the given  implementation instance to this  or -derived class instance.
-        /// 
-        /// The serviceHub to use for all operations.
-        /// The instance which was mutated.
-        public ParseObject Bind(IServiceHub serviceHub)
-        {
-            return (Instance: this, Services = serviceHub).Instance;
-        }
+    /// 
+    /// Attaches the given  implementation instance to this  or -derived class instance.
+    /// 
+    /// The serviceHub to use for all operations.
+    /// The instance which was mutated.
+    public ParseObject Bind(IServiceHub serviceHub)
+    {
+        return (Instance: this, Services = serviceHub).Instance;
+    }
 
-        /// 
-        /// Occurs when a property value changes.
-        /// 
-        public event PropertyChangedEventHandler PropertyChanged
+    /// 
+    /// Occurs when a property value changes.
+    /// 
+    public event PropertyChangedEventHandler PropertyChanged
+    {
+        add
         {
-            add
-            {
-                PropertyChangedHandler.Add(value);
-            }
-            remove
-            {
-                PropertyChangedHandler.Remove(value);
-            }
+            PropertyChangedHandler.Add(value);
         }
-
-        /// 
-        /// Gets or sets the ParseACL governing this object.
-        /// 
-        [ParseFieldName("ACL")]
-        public ParseACL ACL
+        remove
         {
-            get => GetProperty(default, nameof(ACL));
-            set => SetProperty(value, nameof(ACL));
+            PropertyChangedHandler.Remove(value);
         }
+    }
+
+    /// 
+    /// Gets or sets the ParseACL governing this object.
+    /// 
+    [ParseFieldName("ACL")]
+    public ParseACL ACL
+    {
+        get => GetProperty(default, nameof(ACL));
+        set => SetProperty(value, nameof(ACL));
+    }
+
+    /// 
+    /// Gets the class name for the ParseObject.
+    /// 
+    public string ClassName => State.ClassName;
+
+    /// 
+    /// Gets the first time this object was saved as the server sees it, so that if you create a
+    /// ParseObject, then wait a while, and then call , the
+    /// creation time will be the time of the first  call rather than
+    /// the time the object was created locally.
+    /// 
+    [ParseFieldName("createdAt")]
+    public DateTime? CreatedAt => State.CreatedAt;
 
-        /// 
-        /// Gets the class name for the ParseObject.
-        /// 
-        public string ClassName => State.ClassName;
-
-        /// 
-        /// Gets the first time this object was saved as the server sees it, so that if you create a
-        /// ParseObject, then wait a while, and then call , the
-        /// creation time will be the time of the first  call rather than
-        /// the time the object was created locally.
-        /// 
-        [ParseFieldName("createdAt")]
-        public DateTime? CreatedAt => State.CreatedAt;
-
-        /// 
-        /// Gets whether the ParseObject has been fetched.
-        /// 
-        public bool IsDataAvailable
-        {
-            get
+    /// 
+    /// Gets whether the ParseObject has been fetched.
+    /// 
+    public bool IsDataAvailable
+    {
+        get
+        {
+            lock (Mutex)
             {
-                lock (Mutex)
-                {
-                    return Fetched;
-                }
+                return Fetched;
             }
         }
+    }
 
-        /// 
-        /// Indicates whether this ParseObject has unsaved changes.
-        /// 
-        public bool IsDirty
+    /// 
+    /// Indicates whether this ParseObject has unsaved changes.
+    /// 
+    public bool IsDirty
+    {
+        get
         {
-            get
-            {
-                lock (Mutex)
-                {
-                    return CheckIsDirty(true);
-                }
-            }
-            internal set
+            lock (Mutex)
             {
-                lock (Mutex)
-                {
-                    Dirty = value;
-                    OnPropertyChanged(nameof(IsDirty));
-                }
+                return CheckIsDirty(true);
             }
         }
-
-        /// 
-        /// Returns true if this object was created by the Parse server when the
-        /// object might have already been there (e.g. in the case of a Facebook
-        /// login)
-        /// 
-        public bool IsNew
+        internal set
         {
-            get => State.IsNew;
-            internal set
+            lock (Mutex)
             {
-                MutateState(mutableClone => mutableClone.IsNew = value);
-                OnPropertyChanged(nameof(IsNew));
+                Dirty = value;
+                OnPropertyChanged(nameof(IsDirty));
             }
         }
+    }
 
-        /// 
-        /// Gets a set view of the keys contained in this object. This does not include createdAt,
-        /// updatedAt, or objectId. It does include things like username and ACL.
-        /// 
-        public ICollection Keys
+    /// 
+    /// Returns true if this object was created by the Parse server when the
+    /// object might have already been there (e.g. in the case of a Facebook
+    /// login)
+    /// 
+    public bool IsNew
+    {
+        get => State.IsNew;
+        internal set
         {
-            get
-            {
-                lock (Mutex)
-                {
-                    return EstimatedData.Keys;
-                }
-            }
+            MutateState(mutableClone => mutableClone.IsNew = value);
+            OnPropertyChanged(nameof(IsNew));
         }
+    }
 
-        /// 
-        /// Gets or sets the object id. An object id is assigned as soon as an object is
-        /// saved to the server. The combination of a  and an
-        ///  uniquely identifies an object in your application.
-        /// 
-        [ParseFieldName("objectId")]
-        public string ObjectId
+    /// 
+    /// Gets a set view of the keys contained in this object. This does not include createdAt,
+    /// updatedAt, or objectId. It does include things like username and ACL.
+    /// 
+    public ICollection Keys
+    {
+        get
         {
-            get => State.ObjectId;
-            set
+            lock (Mutex)
             {
-                IsDirty = true;
-                SetObjectIdInternal(value);
+                return EstimatedData.Keys;
             }
         }
+    }
 
-        /// 
-        /// Gets the last time this object was updated as the server sees it, so that if you make changes
-        /// to a ParseObject, then wait a while, and then call , the updated time
-        /// will be the time of the  call rather than the time the object was
-        /// changed locally.
-        /// 
-        [ParseFieldName("updatedAt")]
-        public DateTime? UpdatedAt => State.UpdatedAt;
+    /// 
+    /// Gets or sets the object id. An object id is assigned as soon as an object is
+    /// saved to the server. The combination of a  and an
+    ///  uniquely identifies an object in your application.
+    /// 
+    [ParseFieldName("objectId")]
+    public string ObjectId
+    {
+        get => State.ObjectId;
+        set
+        {
+            IsDirty = true;
+            SetObjectIdInternal(value);
+        }
+    }
 
-        public IDictionary CurrentOperations
+    /// 
+    /// Gets the last time this object was updated as the server sees it, so that if you make changes
+    /// to a ParseObject, then wait a while, and then call , the updated time
+    /// will be the time of the  call rather than the time the object was
+    /// changed locally.
+    /// 
+    [ParseFieldName("updatedAt")]
+    public DateTime? UpdatedAt => State.UpdatedAt;
+
+    public IDictionary CurrentOperations
+    {
+        get
         {
-            get
+            lock (Mutex)
             {
-                lock (Mutex)
-                {
-                    return OperationSetQueue.Last.Value;
-                }
+                return OperationSetQueue.Last.Value;
             }
         }
+    }
 
-        internal object Mutex { get; } = new object { };
+    internal object Mutex { get; } = new object { };
 
-        public IObjectState State { get; private set; }
+    public IObjectState State { get; private set; }
 
-        internal bool CanBeSerialized
+    internal bool CanBeSerialized
+    {
+        get
         {
-            get
-            {
-                // This method is only used for batching sets of objects for saveAll
-                // and when saving children automatically. Since it's only used to
-                // determine whether or not save should be called on them, it only
-                // needs to examine their current values, so we use estimatedData.
+            // This method is only used for batching sets of objects for saveAll
+            // and when saving children automatically. Since it's only used to
+            // determine whether or not save should be called on them, it only
+            // needs to examine their current values, so we use estimatedData.
 
-                lock (Mutex)
-                {
-                    return Services.CanBeSerializedAsValue(EstimatedData);
-                }
+            lock (Mutex)
+            {
+                return Services.CanBeSerializedAsValue(EstimatedData);
             }
         }
+    }
 
-        bool Dirty { get; set; }
+    bool Dirty { get; set; }
 
-        internal IDictionary EstimatedData { get; } = new Dictionary { };
+    internal IDictionary EstimatedData { get; } = new Dictionary { };
 
-        internal bool Fetched { get; set; }
+    internal bool Fetched { get; set; }
 
-        bool HasDirtyChildren
+    bool HasDirtyChildren
+    {
+        get
         {
-            get
+            lock (Mutex)
             {
-                lock (Mutex)
-                {
-                    return FindUnsavedChildren().FirstOrDefault() != null;
-                }
+                return FindUnsavedChildren().FirstOrDefault() != null;
             }
         }
+    }
 
-        LinkedList> OperationSetQueue { get; } = new LinkedList>();
-
-        SynchronizedEventHandler PropertyChangedHandler { get; } = new SynchronizedEventHandler();
-
-        /// 
-        /// Gets or sets a value on the object. It is recommended to name
-        /// keys in partialCamelCaseLikeThis.
-        /// 
-        /// The key for the object. Keys must be alphanumeric plus underscore
-        /// and start with a letter.
-        /// The property is
-        /// retrieved and  is not found.
-        /// The value for the key.
-        virtual public object this[string key]
-        {
-            get
-            {
-                lock (Mutex)
-                {
-                    CheckGetAccess(key);
-                    object value = EstimatedData[key];
-                    //TODO THIS WILL THROW, MAKE IT END GRACEFULLY
-                    // A relation may be deserialized without a parent or key. Either way,
-                    // make sure it's consistent.
-
-                    if (value is ParseRelationBase relation)
-                    {
-                        relation.EnsureParentAndKey(this, key);
-                    }
+    LinkedList> OperationSetQueue { get; } = new LinkedList>();
 
-                    return value;
-                }
-            }
-            set
-            {
-                lock (Mutex)
-                {
-                    CheckKeyIsMutable(key);
-                    Set(key, value);
-                }
-            }
-        }
+    SynchronizedEventHandler PropertyChangedHandler { get; } = new SynchronizedEventHandler();
 
-        /// 
-        /// Adds a value for the given key, throwing an Exception if the key
-        /// already has a value.
-        /// 
-        /// 
-        /// This allows you to use collection initialization syntax when creating ParseObjects,
-        /// such as:
-        /// 
-        /// var obj = new ParseObject("MyType")
-        /// {
-        ///     {"name", "foo"},
-        ///     {"count", 10},
-        ///     {"found", false}
-        /// };
-        /// 
-        /// 
-        /// The key for which a value should be set.
-        /// The value for the key.
-        public void Add(string key, object value)
+    /// 
+    /// Gets or sets a value on the object. It is recommended to name
+    /// keys in partialCamelCaseLikeThis.
+    /// 
+    /// The key for the object. Keys must be alphanumeric plus underscore
+    /// and start with a letter.
+    /// The property is
+    /// retrieved and  is not found.
+    /// The value for the key.
+    virtual public object this[string key]
+    {
+        get
         {
             lock (Mutex)
             {
-                if (ContainsKey(key))
+                CheckGetAccess(key);
+                object value = EstimatedData[key];
+                //TODO THIS WILL THROW, MAKE IT END GRACEFULLY
+                // A relation may be deserialized without a parent or key. Either way,
+                // make sure it's consistent.
+
+                if (value is ParseRelationBase relation)
                 {
-                    throw new ArgumentException("Key already exists", key);
+                    relation.EnsureParentAndKey(this, key);
                 }
 
-                this[key] = value;
+                return value;
             }
         }
-
-        /// 
-        /// Atomically adds objects to the end of the list associated with the given key.
-        /// 
-        /// The key.
-        /// The objects to add.
-        public void AddRangeToList(string key, IEnumerable values)
+        set
         {
             lock (Mutex)
             {
                 CheckKeyIsMutable(key);
-                PerformOperation(key, new ParseAddOperation(values.Cast()));
+                Set(key, value);
             }
         }
+    }
 
-        /// 
-        /// Atomically adds objects to the end of the list associated with the given key,
-        /// only if they are not already present in the list. The position of the inserts are not
-        /// guaranteed.
-        /// 
-        /// The key.
-        /// The objects to add.
-        public void AddRangeUniqueToList(string key, IEnumerable values)
+    /// 
+    /// Adds a value for the given key, throwing an Exception if the key
+    /// already has a value.
+    /// 
+    /// 
+    /// This allows you to use collection initialization syntax when creating ParseObjects,
+    /// such as:
+    /// 
+    /// var obj = new ParseObject("MyType")
+    /// {
+    ///     {"name", "foo"},
+    ///     {"count", 10},
+    ///     {"found", false}
+    /// };
+    /// 
+    /// 
+    /// The key for which a value should be set.
+    /// The value for the key.
+    public void Add(string key, object value)
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
+            if (ContainsKey(key))
             {
-                CheckKeyIsMutable(key);
-                PerformOperation(key, new ParseAddUniqueOperation(values.Cast()));
+                throw new ArgumentException("Key already exists", key);
             }
-        }
-
-        #endregion
 
-        /// 
-        /// Atomically adds an object to the end of the list associated with the given key.
-        /// 
-        /// The key.
-        /// The object to add.
-        public void AddToList(string key, object value)
-        {
-            AddRangeToList(key, new[] { value });
+            this[key] = value;
         }
+    }
 
-        /// 
-        /// Atomically adds an object to the end of the list associated with the given key,
-        /// only if it is not already present in the list. The position of the insert is not
-        /// guaranteed.
-        /// 
-        /// The key.
-        /// The object to add.
-        public void AddUniqueToList(string key, object value)
+    /// 
+    /// Atomically adds objects to the end of the list associated with the given key.
+    /// 
+    /// The key.
+    /// The objects to add.
+    public void AddRangeToList(string key, IEnumerable values)
+    {
+        lock (Mutex)
         {
-            AddRangeUniqueToList(key, new object[] { value });
+            CheckKeyIsMutable(key);
+            PerformOperation(key, new ParseAddOperation(values.Cast()));
         }
+    }
 
-        /// 
-        /// Returns whether this object has a particular key.
-        /// 
-        /// The key to check for
-        public bool ContainsKey(string key)
+    /// 
+    /// Atomically adds objects to the end of the list associated with the given key,
+    /// only if they are not already present in the list. The position of the inserts are not
+    /// guaranteed.
+    /// 
+    /// The key.
+    /// The objects to add.
+    public void AddRangeUniqueToList(string key, IEnumerable values)
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
-            {
-                return EstimatedData.ContainsKey(key);
-            }
+            CheckKeyIsMutable(key);
+            PerformOperation(key, new ParseAddUniqueOperation(values.Cast()));
         }
+    }
+
+    #endregion
 
-        /// 
-        /// Deletes this object on the server.
-        /// 
-        /// The cancellation token.
-        public Task DeleteAsync(CancellationToken cancellationToken = default)
+    /// 
+    /// Atomically adds an object to the end of the list associated with the given key.
+    /// 
+    /// The key.
+    /// The object to add.
+    public void AddToList(string key, object value)
+    {
+        AddRangeToList(key, new[] { value });
+    }
+
+    /// 
+    /// Atomically adds an object to the end of the list associated with the given key,
+    /// only if it is not already present in the list. The position of the insert is not
+    /// guaranteed.
+    /// 
+    /// The key.
+    /// The object to add.
+    public void AddUniqueToList(string key, object value)
+    {
+        AddRangeUniqueToList(key, new object[] { value });
+    }
+
+    /// 
+    /// Returns whether this object has a particular key.
+    /// 
+    /// The key to check for
+    public bool ContainsKey(string key)
+    {
+        lock (Mutex)
         {
-            return TaskQueue.Enqueue(toAwait => DeleteAsync(toAwait, cancellationToken), cancellationToken);
+            return EstimatedData.ContainsKey(key);
         }
+    }
 
-        /// 
-        /// Gets a value for the key of a particular type.
-        /// The type to convert the value to. Supported types are
-        /// ParseObject and its descendents, Parse types such as ParseRelation and ParseGeopoint,
-        /// primitive types,IList<T>, IDictionary<string, T>, and strings.
-        /// The key of the element to get.
-        /// The property is
-        /// retrieved and  is not found.
-        /// 
-        public T Get(string key)
-        {
-            return Conversion.To(this[key]);
-        }
+    /// 
+    /// Gets a value for the key of a particular type.
+    /// The type to convert the value to. Supported types are
+    /// ParseObject and its descendents, Parse types such as ParseRelation and ParseGeopoint,
+    /// primitive types,IList<T>, IDictionary<string, T>, and strings.
+    /// The key of the element to get.
+    /// The property is
+    /// retrieved and  is not found.
+    /// 
+    public T Get(string key)
+    {
+        return Conversion.To(this[key]);
+    }
 
-        /// 
-        /// Access or create a Relation value for a key.
-        /// 
-        /// The type of object to create a relation for.
-        /// The key for the relation field.
-        /// A ParseRelation for the key.
-        public ParseRelation GetRelation(string key) where T : ParseObject
-        {
-            // All the sanity checking is done when add or remove is called.
+    /// 
+    /// Access or create a Relation value for a key.
+    /// 
+    /// The type of object to create a relation for.
+    /// The key for the relation field.
+    /// A ParseRelation for the key.
+    public ParseRelation GetRelation(string key) where T : ParseObject
+    {
+        // All the sanity checking is done when add or remove is called.
 
-            TryGetValue(key, out ParseRelation relation);
-            return relation ?? new ParseRelation(this, key);
-        }
+        TryGetValue(key, out ParseRelation relation);
+        return relation ?? new ParseRelation(this, key);
+    }
 
-        /// 
-        /// A helper function for checking whether two ParseObjects point to
-        /// the same object in the cloud.
-        /// 
-        public bool HasSameId(ParseObject other)
+    /// 
+    /// A helper function for checking whether two ParseObjects point to
+    /// the same object in the cloud.
+    /// 
+    public bool HasSameId(ParseObject other)
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
-            {
-                return other is { } && Equals(ClassName, other.ClassName) && Equals(ObjectId, other.ObjectId);
-            }
+            return other is { } && Equals(ClassName, other.ClassName) && Equals(ObjectId, other.ObjectId);
         }
+    }
 
-        #region Atomic Increment
+    #region Atomic Increment
 
-        /// 
-        /// Atomically increments the given key by 1.
-        /// 
-        /// The key to increment.
-        public void Increment(string key)
-        {
-            Increment(key, 1);
-        }
+    /// 
+    /// Atomically increments the given key by 1.
+    /// 
+    /// The key to increment.
+    public void Increment(string key)
+    {
+        Increment(key, 1);
+    }
 
-        /// 
-        /// Atomically increments the given key by the given number.
-        /// 
-        /// The key to increment.
-        /// The amount to increment by.
-        public void Increment(string key, long amount)
+    /// 
+    /// Atomically increments the given key by the given number.
+    /// 
+    /// The key to increment.
+    /// The amount to increment by.
+    public void Increment(string key, long amount)
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
-            {
-                CheckKeyIsMutable(key);
-                PerformOperation(key, new ParseIncrementOperation(amount));
-            }
+            CheckKeyIsMutable(key);
+            PerformOperation(key, new ParseIncrementOperation(amount));
         }
+    }
 
-        /// 
-        /// Atomically increments the given key by the given number.
-        /// 
-        /// The key to increment.
-        /// The amount to increment by.
-        public void Increment(string key, double amount)
+    /// 
+    /// Atomically increments the given key by the given number.
+    /// 
+    /// The key to increment.
+    /// The amount to increment by.
+    public void Increment(string key, double amount)
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
-            {
-                CheckKeyIsMutable(key);
-                PerformOperation(key, new ParseIncrementOperation(amount));
-            }
+            CheckKeyIsMutable(key);
+            PerformOperation(key, new ParseIncrementOperation(amount));
         }
+    }
 
-        /// 
-        /// Indicates whether key is unsaved for this ParseObject.
-        /// 
-        /// The key to check for.
-        /// true if the key has been altered and not saved yet, otherwise
-        /// false.
-        public bool IsKeyDirty(string key)
+    /// 
+    /// Indicates whether key is unsaved for this ParseObject.
+    /// 
+    /// The key to check for.
+    /// true if the key has been altered and not saved yet, otherwise
+    /// false.
+    public bool IsKeyDirty(string key)
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
-            {
-                return CurrentOperations.ContainsKey(key);
-            }
+            return CurrentOperations.ContainsKey(key);
         }
+    }
 
-        /// 
-        /// Removes a key from the object's data if it exists.
-        /// 
-        /// The key to remove.
-        public virtual void Remove(string key)
+    /// 
+    /// Removes a key from the object's data if it exists.
+    /// 
+    /// The key to remove.
+    public virtual void Remove(string key)
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
-            {
-                CheckKeyIsMutable(key);
-                PerformOperation(key, ParseDeleteOperation.Instance);
-            }
+            CheckKeyIsMutable(key);
+            PerformOperation(key, ParseDeleteOperation.Instance);
         }
+    }
 
-        /// 
-        /// Atomically removes all instances of the objects in 
-        /// from the list associated with the given key.
-        /// 
-        /// The key.
-        /// The objects to remove.
-        public void RemoveAllFromList(string key, IEnumerable values)
+    /// 
+    /// Atomically removes all instances of the objects in 
+    /// from the list associated with the given key.
+    /// 
+    /// The key.
+    /// The objects to remove.
+    public void RemoveAllFromList(string key, IEnumerable values)
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
-            {
-                CheckKeyIsMutable(key);
-                PerformOperation(key, new ParseRemoveOperation(values.Cast()));
-            }
+            CheckKeyIsMutable(key);
+            PerformOperation(key, new ParseRemoveOperation(values.Cast()));
         }
+    }
 
-        /// 
-        /// Clears any changes to this object made since the last call to .
-        /// 
-        public void Revert()
+    /// 
+    /// Clears any changes to this object made since the last call to .
+    /// 
+    public void Revert()
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
+            if (CurrentOperations.Count > 0)
             {
-                if (CurrentOperations.Count > 0)
-                {
-                    CurrentOperations.Clear();
-                    RebuildEstimatedData();
-                    OnPropertyChanged(nameof(IsDirty));
-                }
+                CurrentOperations.Clear();
+                RebuildEstimatedData();
+                OnPropertyChanged(nameof(IsDirty));
             }
         }
+    }
 
-        /// 
-        /// Saves this object to the server.
-        /// 
-        /// The cancellation token.
-        public Task SaveAsync(CancellationToken cancellationToken = default)
-        {
-            return TaskQueue.Enqueue(toAwait => SaveAsync(toAwait, cancellationToken), cancellationToken);
-        }
+    /// 
+    /// Saves this object to the server.
+    /// 
+    /// The cancellation token.
+    public Task SaveAsync(CancellationToken cancellationToken = default)
+    {
+        return TaskQueue.Enqueue(toAwait => SaveAsync(toAwait, cancellationToken), cancellationToken);
+    }
 
-        /// 
-        /// Populates result with the value for the key, if possible.
-        /// 
-        /// The desired type for the value.
-        /// The key to retrieve a value for.
-        /// The value for the given key, converted to the
-        /// requested type, or null if unsuccessful.
-        /// true if the lookup and conversion succeeded, otherwise
-        /// false.
-        public bool TryGetValue(string key, out T result)
+    /// 
+    /// Populates result with the value for the key, if possible.
+    /// 
+    /// The desired type for the value.
+    /// The key to retrieve a value for.
+    /// The value for the given key, converted to the
+    /// requested type, or null if unsuccessful.
+    /// true if the lookup and conversion succeeded, otherwise
+    /// false.
+    public bool TryGetValue(string key, out T result)
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
+            if (ContainsKey(key))
             {
-                if (ContainsKey(key))
+                try
                 {
-                    try
-                    {
-                        T temp = Conversion.To(this[key]);
-                        result = temp;
-                        return true;
-                    }
-                    catch
-                    {
-                        result = default;
-                        return false;
-                    }
+                    T temp = Conversion.To(this[key]);
+                    result = temp;
+                    return true;
+                }
+                catch
+                {
+                    result = default;
+                    return false;
                 }
-
-                result = default;
-                return false;
             }
+
+            result = default;
+            return false;
         }
+    }
 
-        #endregion
+    #endregion
 
-        #region Delete Object
+    #region Delete Object
 
-        internal Task DeleteAsync(Task toAwait, CancellationToken cancellationToken)
+    /// 
+    /// Deletes this object on the server.
+    /// 
+    /// The cancellation token.
+    public Task DeleteAsync(CancellationToken cancellationToken = default)
+    {
+        return TaskQueue.Enqueue(async toAwait =>
         {
-            if (ObjectId == null)
-            {
-                return Task.FromResult(0);
-            }
-
-            string sessionToken = Services.GetCurrentSessionToken();
-
-            return toAwait.OnSuccess(_ => Services.ObjectController.DeleteAsync(State, sessionToken, cancellationToken)).Unwrap().OnSuccess(_ => IsDirty = true);
-        }
+            await DeleteAsyncInternal(cancellationToken).ConfigureAwait(false);
+            return toAwait;  // Ensure the task is returned to the queue
+        }, cancellationToken);
+    }
 
-        internal virtual Task FetchAsyncInternal(Task toAwait, CancellationToken cancellationToken)
-        {
-            return toAwait.OnSuccess(_ => ObjectId == null ? throw new InvalidOperationException("Cannot refresh an object that hasn't been saved to the server.") : Services.ObjectController.FetchAsync(State, Services.GetCurrentSessionToken(), Services, cancellationToken)).Unwrap().OnSuccess(task =>
+    internal async Task DeleteAsyncInternal(CancellationToken cancellationToken)
+    {
+        if (ObjectId == null)
         {
-            HandleFetchResult(task.Result);
-            return this;
-        });
+            return; // No need to delete if the object doesn't have an ID
         }
 
-        #endregion
+        var sessionToken = Services.GetCurrentSessionToken();
+        await Services.ObjectController.DeleteAsync(State, sessionToken, cancellationToken).ConfigureAwait(false);
+        IsDirty = true;
+    }
 
-        #region Fetch Object(s)
 
-        /// 
-        /// Fetches this object with the data from the server.
-        /// 
-        /// The cancellation token.
-        internal Task FetchAsyncInternal(CancellationToken cancellationToken)
+    #region Fetch Object(s)
+    /// 
+    /// Fetches this object with the data from the server.
+    /// 
+    /// The cancellation token.
+    internal Task FetchAsync(CancellationToken cancellationToken)
+    {
+        return TaskQueue.Enqueue(async toAwait =>
         {
-            return TaskQueue.Enqueue(toAwait => FetchAsyncInternal(toAwait, cancellationToken), cancellationToken);
-        }
+            await FetchAsyncInternal(cancellationToken).ConfigureAwait(false);
+            return this;  // Ensures the task is returned to the queue after fetch
+        }, cancellationToken);
+    }
 
-        internal Task FetchIfNeededAsyncInternal(Task toAwait, CancellationToken cancellationToken)
+    internal async Task FetchIfNeededAsync(CancellationToken cancellationToken)
+    {
+        if (!IsDataAvailable)
         {
-            return !IsDataAvailable ? FetchAsyncInternal(toAwait, cancellationToken) : Task.FromResult(this);
+            return await FetchAsyncInternal(cancellationToken).ConfigureAwait(false);
         }
-
-        /// 
-        /// If this ParseObject has not been fetched (i.e.  returns
-        /// false), fetches this object with the data from the server.
-        /// 
-        /// The cancellation token.
-        internal Task FetchIfNeededAsyncInternal(CancellationToken cancellationToken)
+        return this;
+    }
+    internal async Task FetchIfNeededAsyncInternal(Task toAwait, CancellationToken cancellationToken)
+    {
+        if (IsDataAvailable)
         {
-            return TaskQueue.Enqueue(toAwait => FetchIfNeededAsyncInternal(toAwait, cancellationToken), cancellationToken);
+            return this;
         }
 
-        internal void HandleFailedSave(IDictionary operationsBeforeSave)
+        await toAwait.ConfigureAwait(false);
+        return await FetchAsyncInternal(cancellationToken).ConfigureAwait(false);
+    }
+
+    /// 
+    /// If this ParseObject has not been fetched (i.e.  returns
+    /// false), fetches this object with the data from the server.
+    /// 
+    /// The cancellation token.
+    internal Task FetchIfNeededAsyncInternal(CancellationToken cancellationToken)
+    {
+        return TaskQueue.Enqueue(async toAwait =>
         {
-            lock (Mutex)
-            {
-                LinkedListNode> opNode = OperationSetQueue.Find(operationsBeforeSave);
-                IDictionary nextOperations = opNode.Next.Value;
-                bool wasDirty = nextOperations.Count > 0;
-                OperationSetQueue.Remove(opNode);
+            return await FetchIfNeededAsyncInternal(toAwait, cancellationToken).ConfigureAwait(false);
+        }, cancellationToken);
+    }
 
-                // Merge the data from the failed save into the next save.
+    internal virtual async Task FetchAsyncInternal(CancellationToken cancellationToken)
+    {
+        if (ObjectId == null)
+        {
+            throw new InvalidOperationException("Cannot refresh an object that hasn't been saved to the server.");
+        }
 
-                foreach (KeyValuePair pair in operationsBeforeSave)
-                {
-                    IParseFieldOperation operation1 = pair.Value;
+        var sessionToken = Services.GetCurrentSessionToken();
+        var result = await Services.ObjectController.FetchAsync(State, sessionToken, Services, cancellationToken).ConfigureAwait(false);
+        HandleFetchResult(result);
+        return this;
+    }
 
-                    nextOperations.TryGetValue(pair.Key, out IParseFieldOperation operation2);
-                    nextOperations[pair.Key] = operation2 is { } ? operation2.MergeWithPrevious(operation1) : operation1;
-                }
+    #endregion
 
-                if (!wasDirty && nextOperations == CurrentOperations && operationsBeforeSave.Count > 0)
-                {
-                    OnPropertyChanged(nameof(IsDirty));
-                }
-            }
-        }
 
-        public virtual void HandleFetchResult(IObjectState serverState)
+    internal void HandleFailedSave(IDictionary operationsBeforeSave)
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
+            // Attempt to find the node in the OperationSetQueue
+            LinkedListNode> opNode = OperationSetQueue.Find(operationsBeforeSave);
+            if (opNode == null)
             {
-                MergeFromServer(serverState);
+                // If not found, gracefully exit or perform cleanup as needed
+                return; // Gracefully exit
             }
-        }
 
-        internal virtual void HandleSave(IObjectState serverState)
-        {
-            lock (Mutex)
+            IDictionary nextOperations = opNode.Next.Value;
+            bool wasDirty = nextOperations.Count > 0;
+            OperationSetQueue.Remove(opNode);
+
+            // Merge the data from the failed save into the next save.
+
+            foreach (KeyValuePair pair in operationsBeforeSave)
             {
-                IDictionary operationsBeforeSave = OperationSetQueue.First.Value;
-                OperationSetQueue.RemoveFirst();
+                IParseFieldOperation operation1 = pair.Value;
 
-                // Merge the data from the save and the data from the server into serverData.
+                nextOperations.TryGetValue(pair.Key, out IParseFieldOperation operation2);
+                nextOperations[pair.Key] = operation2 is { } ? operation2.MergeWithPrevious(operation1) : operation1;
+            }
 
-                MutateState(mutableClone => mutableClone.Apply(operationsBeforeSave));
-                MergeFromServer(serverState);
+            if (!wasDirty && nextOperations == CurrentOperations && operationsBeforeSave.Count > 0)
+            {
+                OnPropertyChanged(nameof(IsDirty));
             }
         }
+    }
 
-        internal void MergeFromObject(ParseObject other)
+    public virtual void HandleFetchResult(IObjectState serverState)
+    {
+        lock (Mutex)
         {
-            // If they point to the same instance, we don't need to merge
+            MergeFromServer(serverState);
+        }
+    }
 
-            lock (Mutex)
-            {
-                if (this == other)
-                {
-                    return;
-                }
-            }
+    internal virtual void HandleSave(IObjectState serverState)
+    {
+        lock (Mutex)
+        {
+            IDictionary operationsBeforeSave = OperationSetQueue.First.Value;
+            OperationSetQueue.RemoveFirst();
 
-            // Clear out any changes on this object.
+            // Merge the data from the save and the data from the server into serverData.
 
-            if (OperationSetQueue.Count != 1)
-            {
-                throw new InvalidOperationException("Attempt to MergeFromObject during save.");
-            }
+            MutateState(mutableClone => mutableClone.Apply(operationsBeforeSave));
+            MergeFromServer(serverState);
+        }
+    }
 
-            OperationSetQueue.Clear();
+    internal void MergeFromObject(ParseObject other)
+    {
+        // If they point to the same instance, we don't need to merge
 
-            foreach (IDictionary operationSet in other.OperationSetQueue)
+        lock (Mutex)
+        {
+            if (this == other)
             {
-                OperationSetQueue.AddLast(operationSet.ToDictionary(entry => entry.Key, entry => entry.Value));
+                return;
             }
+        }
 
-            lock (Mutex)
-            {
-                State = other.State;
-            }
+        // Clear out any changes on this object.
 
-            RebuildEstimatedData();
+        if (OperationSetQueue.Count != 1)
+        {
+            throw new InvalidOperationException("Attempt to MergeFromObject during save.");
         }
 
-        internal virtual void MergeFromServer(IObjectState serverState)
+        OperationSetQueue.Clear();
+
+        foreach (IDictionary operationSet in other.OperationSetQueue)
+        {
+            OperationSetQueue.AddLast(operationSet.ToDictionary(entry => entry.Key, entry => entry.Value));
+        }
+
+        lock (Mutex)
         {
-            // Make a new serverData with fetched values.
+            State = other.State;
+        }
 
-            Dictionary newServerData = serverState.ToDictionary(t => t.Key, t => t.Value);
+        RebuildEstimatedData();
+    }
 
-            lock (Mutex)
+    internal virtual void MergeFromServer(IObjectState serverState)
+    {
+        // Make a new serverData with fetched values.
+
+        Dictionary newServerData = serverState.ToDictionary(t => t.Key, t => t.Value);
+
+        lock (Mutex)
+        {
+            // Trigger handler based on serverState
+
+            if (serverState.ObjectId != null)
             {
-                // Trigger handler based on serverState
+                // If the objectId is being merged in, consider this object to be fetched.
 
-                if (serverState.ObjectId != null)
-                {
-                    // If the objectId is being merged in, consider this object to be fetched.
+                Fetched = true;
+                OnPropertyChanged(nameof(IsDataAvailable));
+            }
 
-                    Fetched = true;
-                    OnPropertyChanged(nameof(IsDataAvailable));
-                }
+            if (serverState.UpdatedAt != null)
+            {
+                OnPropertyChanged(nameof(UpdatedAt));
+            }
 
-                if (serverState.UpdatedAt != null)
-                {
-                    OnPropertyChanged(nameof(UpdatedAt));
-                }
+            if (serverState.CreatedAt != null)
+            {
+                OnPropertyChanged(nameof(CreatedAt));
+            }
 
-                if (serverState.CreatedAt != null)
-                {
-                    OnPropertyChanged(nameof(CreatedAt));
-                }
+            // We cache the fetched object because subsequent Save operation might flush the fetched objects into Pointers.
 
-                // We cache the fetched object because subsequent Save operation might flush the fetched objects into Pointers.
+            IDictionary fetchedObject = CollectFetchedObjects();
 
-                IDictionary fetchedObject = CollectFetchedObjects();
+            foreach (KeyValuePair pair in serverState)
+            {
+                object value = pair.Value;
 
-                foreach (KeyValuePair pair in serverState)
+                if (value is ParseObject)
                 {
-                    object value = pair.Value;
+                    // Resolve fetched object.
 
-                    if (value is ParseObject)
-                    {
-                        // Resolve fetched object.
+                    ParseObject entity = value as ParseObject;
 
-                        ParseObject entity = value as ParseObject;
-
-                        if (fetchedObject.ContainsKey(entity.ObjectId))
-                        {
-                            value = fetchedObject[entity.ObjectId];
-                        }
+                    if (fetchedObject.ContainsKey(entity.ObjectId))
+                    {
+                        value = fetchedObject[entity.ObjectId];
                     }
-                    newServerData[pair.Key] = value;
                 }
-
-                IsDirty = false;
-                MutateState(mutableClone => mutableClone.Apply(serverState.MutatedClone(mutableClone => mutableClone.ServerData = newServerData)));
+                newServerData[pair.Key] = value;
             }
+
+            IsDirty = false;
+            var s = serverState.MutatedClone(mutableClone => mutableClone.ServerData = newServerData);
+            MutateState(mutableClone => mutableClone.Apply(s));
         }
+    }
 
-        internal void MutateState(Action mutator)
+    internal void MutateState(Action mutator)
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
-            {
-                State = State.MutatedClone(mutator);
+            State = State.MutatedClone(mutator);
 
-                // Refresh the estimated data.
+            // Refresh the estimated data.
 
-                RebuildEstimatedData();
-            }
+            RebuildEstimatedData();
         }
+    }
 
-        /// 
-        /// Override to run validations on key/value pairs. Make sure to still
-        /// call the base version.
-        /// 
-        internal virtual void OnSettingValue(ref string key, ref object value)
+    /// 
+    /// Override to run validations on key/value pairs. Make sure to still
+    /// call the base version.
+    /// 
+    internal virtual void OnSettingValue(ref string key, ref object value)
+    {
+        if (key is null)
         {
-            if (key is null)
-            {
-                throw new ArgumentNullException(nameof(key));
-            }
+            throw new ArgumentNullException(nameof(key));
         }
+    }
 
-        /// 
-        /// PerformOperation is like setting a value at an index, but instead of
-        /// just taking a new value, it takes a ParseFieldOperation that modifies the value.
-        /// 
-        internal void PerformOperation(string key, IParseFieldOperation operation)
+    /// 
+    /// PerformOperation is like setting a value at an index, but instead of
+    /// just taking a new value, it takes a ParseFieldOperation that modifies the value.
+    /// 
+    internal void PerformOperation(string key, IParseFieldOperation operation)
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
-            {
-                EstimatedData.TryGetValue(key, out object oldValue);
-                object newValue = operation.Apply(oldValue, key);
-
-                if (newValue != ParseDeleteOperation.Token)
-                {
-                    EstimatedData[key] = newValue;
-                }
-                else
-                {
-                    EstimatedData.Remove(key);
-                }
-
-                bool wasDirty = CurrentOperations.Count > 0;
-                CurrentOperations.TryGetValue(key, out IParseFieldOperation oldOperation);
-                IParseFieldOperation newOperation = operation.MergeWithPrevious(oldOperation);
-                CurrentOperations[key] = newOperation;
+            EstimatedData.TryGetValue(key, out object oldValue);
+            object newValue = operation.Apply(oldValue, key);
 
-                if (!wasDirty)
-                {
-                    OnPropertyChanged(nameof(IsDirty));
-                }
-
-                OnFieldsChanged(new[] { key });
+            if (newValue != ParseDeleteOperation.Token)
+            {
+                EstimatedData[key] = newValue;
             }
-        }
-
-        /// 
-        /// Regenerates the estimatedData map from the serverData and operations.
-        /// 
-        internal void RebuildEstimatedData()
-        {
-            lock (Mutex)
+            else
             {
-                EstimatedData.Clear();
-
-                foreach (KeyValuePair item in State)
-                {
-                    EstimatedData.Add(item);
-                }
-                foreach (IDictionary operations in OperationSetQueue)
-                {
-                    ApplyOperations(operations, EstimatedData);
-                }
+                EstimatedData.Remove(key);
+            }
 
-                // We've just applied a bunch of operations to estimatedData which
-                // may have changed all of its keys. Notify of all keys and properties
-                // mapped to keys being changed.
+            bool wasDirty = CurrentOperations.Count > 0;
+            CurrentOperations.TryGetValue(key, out IParseFieldOperation oldOperation);
+            IParseFieldOperation newOperation = operation.MergeWithPrevious(oldOperation);
+            CurrentOperations[key] = newOperation;
 
-                OnFieldsChanged(default);
+            if (!wasDirty)
+            {
+                OnPropertyChanged(nameof(IsDirty));
             }
-        }
 
-        public IDictionary ServerDataToJSONObjectForSerialization()
-        {
-            return PointerOrLocalIdEncoder.Instance.Encode(State.ToDictionary(pair => pair.Key, pair => pair.Value), Services) as IDictionary;
+            OnFieldsChanged(new[] { key });
         }
+    }
 
-        /// 
-        /// Perform Set internally which is not gated by mutability check.
-        /// 
-        /// key for the object.
-        /// the value for the key.
-        public void Set(string key, object value)
+    /// 
+    /// Regenerates the estimatedData map from the serverData and operations.
+    /// 
+    internal void RebuildEstimatedData()
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
+            EstimatedData.Clear();
+
+            foreach (KeyValuePair item in State)
+            {
+                EstimatedData.Add(item);
+            }
+            foreach (IDictionary operations in OperationSetQueue)
             {
-                OnSettingValue(ref key, ref value);
+                ApplyOperations(operations, EstimatedData);
+            }
 
-                if (!ParseDataEncoder.Validate(value))
-                {
-                    throw new ArgumentException("Invalid type for value: " + value.GetType().ToString());
-                }
+            // We've just applied a bunch of operations to estimatedData which
+            // may have changed all of its keys. Notify of all keys and properties
+            // mapped to keys being changed.
 
-                PerformOperation(key, new ParseSetOperation(value));
-            }
+            OnFieldsChanged(default);
         }
+    }
 
-        /// 
-        /// Allows subclasses to set values for non-pointer construction.
-        /// 
-        internal virtual void SetDefaultValues() { }
+    public IDictionary ServerDataToJSONObjectForSerialization()
+    {
+        return PointerOrLocalIdEncoder.Instance.Encode(State.ToDictionary(pair => pair.Key, pair => pair.Value), Services) as IDictionary;
+    }
 
-        public void SetIfDifferent(string key, T value)
+    /// 
+    /// Perform Set internally which is not gated by mutability check.
+    /// 
+    /// key for the object.
+    /// the value for the key.
+    public void Set(string key, object value)
+    {
+        lock (Mutex)
         {
-            bool hasCurrent = TryGetValue(key, out T current);
+            OnSettingValue(ref key, ref value);
 
-            if (value == null)
+            if (!ParseDataEncoder.Validate(value))
             {
-                if (hasCurrent)
-                {
-                    PerformOperation(key, ParseDeleteOperation.Instance);
-                }
-                return;
+                throw new ArgumentException("Invalid type for value: " + value.GetType().ToString());
             }
 
-            if (!hasCurrent || !value.Equals(current))
-            {
-                Set(key, value);
-            }
+            PerformOperation(key, new ParseSetOperation(value));
         }
+    }
 
-        #endregion
+    /// 
+    /// Allows subclasses to set values for non-pointer construction.
+    /// 
+    internal virtual void SetDefaultValues() { }
 
-        #region Save Object(s)
+    public void SetIfDifferent(string key, T value)
+    {
+        bool hasCurrent = TryGetValue(key, out T current);
 
-        /// 
-        /// Pushes new operations onto the queue and returns the current set of operations.
-        /// 
-        internal IDictionary StartSave()
+        if (value == null)
         {
-            lock (Mutex)
+            if (hasCurrent)
             {
-                IDictionary currentOperations = CurrentOperations;
-                OperationSetQueue.AddLast(new Dictionary());
-                OnPropertyChanged(nameof(IsDirty));
-                return currentOperations;
+                PerformOperation(key, ParseDeleteOperation.Instance);
             }
+            return;
         }
 
-        #endregion
-
-        /// 
-        /// Gets the value of a property based upon its associated ParseFieldName attribute.
-        /// 
-        /// The value of the property.
-        /// The name of the property.
-        /// The return type of the property.
-        protected T GetProperty([CallerMemberName] string propertyName = null)
+        if (!hasCurrent || !value.Equals(current))
         {
-            return GetProperty(default(T), propertyName);
+            Set(key, value);
         }
+    }
+
+    #endregion
 
-        /// 
-        /// Gets the value of a property based upon its associated ParseFieldName attribute.
-        /// 
-        /// The value of the property.
-        /// The value to return if the property is not present on the ParseObject.
-        /// The name of the property.
-        /// The return type of the property.
-        protected T GetProperty(T defaultValue, [CallerMemberName] string propertyName = null)
+    #region Save Object(s)
+
+    /// 
+    /// Pushes new operations onto the queue and returns the current set of operations.
+    /// 
+    internal IDictionary StartSave()
+    {
+        lock (Mutex)
         {
-            return TryGetValue(Services.GetFieldForPropertyName(ClassName, propertyName), out T result) ? result : defaultValue;
+            IDictionary currentOperations = CurrentOperations;
+            OperationSetQueue.AddLast(new Dictionary());
+            OnPropertyChanged(nameof(IsDirty));
+            return currentOperations;
         }
+    }
+
+    #endregion
+
+    /// 
+    /// Gets the value of a property based upon its associated ParseFieldName attribute.
+    /// 
+    /// The value of the property.
+    /// The name of the property.
+    /// The return type of the property.
+    protected T GetProperty([CallerMemberName] string propertyName = null)
+    {
+        return GetProperty(default(T), propertyName);
+    }
+
+    /// 
+    /// Gets the value of a property based upon its associated ParseFieldName attribute.
+    /// 
+    /// The value of the property.
+    /// The value to return if the property is not present on the ParseObject.
+    /// The name of the property.
+    /// The return type of the property.
+    protected T GetProperty(T defaultValue, [CallerMemberName] string propertyName = null)
+    {
+        return TryGetValue(Services.GetFieldForPropertyName(ClassName, propertyName), out T result) ? result : defaultValue;
+    }
+
+    /// 
+    /// Gets a relation for a property based upon its associated ParseFieldName attribute.
+    /// 
+    /// The ParseRelation for the property.
+    /// The name of the property.
+    /// The ParseObject subclass type of the ParseRelation.
+    protected ParseRelation GetRelationProperty([CallerMemberName] string propertyName = null) where T : ParseObject
+    {
+        return GetRelation(Services.GetFieldForPropertyName(ClassName, propertyName));
+    }
+
+    protected virtual bool CheckKeyMutable(string key)
+    {
+        return true;
+    }
 
-        /// 
-        /// Gets a relation for a property based upon its associated ParseFieldName attribute.
-        /// 
-        /// The ParseRelation for the property.
-        /// The name of the property.
-        /// The ParseObject subclass type of the ParseRelation.
-        protected ParseRelation GetRelationProperty([CallerMemberName] string propertyName = null) where T : ParseObject
+    /// 
+    /// Raises change notifications for all properties associated with the given
+    /// field names. If fieldNames is null, this will notify for all known field-linked
+    /// properties (e.g. this happens when we recalculate all estimated data from scratch)
+    /// 
+    protected void OnFieldsChanged(IEnumerable fields)
+    {
+        IDictionary mappings = Services.ClassController.GetPropertyMappings(ClassName);
+
+        foreach (string property in mappings is { } ? fields is { } ? from mapping in mappings join field in fields on mapping.Value equals field select mapping.Key : mappings.Keys : Enumerable.Empty())
         {
-            return GetRelation(Services.GetFieldForPropertyName(ClassName, propertyName));
+            OnPropertyChanged(property);
         }
 
-        protected virtual bool CheckKeyMutable(string key)
+        OnPropertyChanged("Item[]");
+    }
+
+    /// 
+    /// Raises change notifications for a property. Passing null or the empty string
+    /// notifies the binding framework that all properties/indexes have changed.
+    /// Passing "Item[]" tells the binding framework that all indexed values
+    /// have changed (but not all properties)
+    /// 
+    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
+    {
+        PropertyChangedHandler.Invoke(this, new PropertyChangedEventArgs(propertyName));
+    }
+
+    protected virtual async Task SaveAsync(Task toAwait, CancellationToken cancellationToken)
+    {
+        if (!IsDirty)
         {
-            return true;
+            // No need to save if the object is not dirty
+            return;
         }
 
-        /// 
-        /// Raises change notifications for all properties associated with the given
-        /// field names. If fieldNames is null, this will notify for all known field-linked
-        /// properties (e.g. this happens when we recalculate all estimated data from scratch)
-        /// 
-        protected void OnFieldsChanged(IEnumerable fields)
+        // Get the session token and prepare the save operation
+        var currentOperations = StartSave();
+        var sessionToken = Services.GetCurrentSessionToken();
+
+        // Perform the deep save asynchronously
+        try
         {
-            IDictionary mappings = Services.ClassController.GetPropertyMappings(ClassName);
+            // Await the deep save operation
+            await Services.DeepSaveAsync(EstimatedData, sessionToken, cancellationToken).ConfigureAwait(false);
 
-            foreach (string property in mappings is { } ? fields is { } ? from mapping in mappings join field in fields on mapping.Value equals field select mapping.Key : mappings.Keys : Enumerable.Empty())
-            {
-                OnPropertyChanged(property);
-            }
+            // Proceed with the object save
+            await Services.ObjectController.SaveAsync(State, currentOperations, sessionToken, Services, cancellationToken).ConfigureAwait(false);
 
-            OnPropertyChanged("Item[]");
+            // Handle successful save
+            HandleSave(State);
         }
-
-        /// 
-        /// Raises change notifications for a property. Passing null or the empty string
-        /// notifies the binding framework that all properties/indexes have changed.
-        /// Passing "Item[]" tells the binding framework that all indexed values
-        /// have changed (but not all properties)
-        /// 
-        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
+        catch (OperationCanceledException)
         {
-            PropertyChangedHandler.Invoke(this, new PropertyChangedEventArgs(propertyName));
+            // Handle the cancellation case
+            HandleFailedSave(currentOperations);
         }
-
-        protected virtual Task SaveAsync(Task toAwait, CancellationToken cancellationToken)
+        catch (Exception ex)
         {
-            IDictionary currentOperations = null;
+            // Log or handle unexpected errors
+            HandleFailedSave(currentOperations);
+            Console.Error.WriteLine($"Error during save: {ex.Message}");
+        }
+    }
 
-            if (!IsDirty)
-            {
-                return Task.CompletedTask;
-            }
 
-            Task deepSaveTask;
-            string sessionToken;
+    /// 
+    /// Sets the value of a property based upon its associated ParseFieldName attribute.
+    /// 
+    /// The new value.
+    /// The name of the property.
+    /// The type for the property.
+    protected void SetProperty(T value, [CallerMemberName] string propertyName = null)
+    {
+        this[Services.GetFieldForPropertyName(ClassName, propertyName)] = value;
+    }
 
-            lock (Mutex)
+    void ApplyOperations(IDictionary operations, IDictionary map)
+    {
+        lock (Mutex)
+        {
+            foreach (KeyValuePair pair in operations)
             {
-                // Get the JSON representation of the object.
-
-                currentOperations = StartSave();
-                sessionToken = Services.GetCurrentSessionToken();
-                deepSaveTask = Services.DeepSaveAsync(EstimatedData, sessionToken, cancellationToken);
-            }
+                map.TryGetValue(pair.Key, out object oldValue);
+                object newValue = pair.Value.Apply(oldValue, pair.Key);
 
-            return deepSaveTask.OnSuccess(_ => toAwait).Unwrap().OnSuccess(_ => Services.ObjectController.SaveAsync(State, currentOperations, sessionToken, Services, cancellationToken)).Unwrap().ContinueWith(task =>
-            {
-                if (task.IsFaulted || task.IsCanceled)
+                if (newValue != ParseDeleteOperation.Token)
                 {
-                    HandleFailedSave(currentOperations);
+                    map[pair.Key] = newValue;
                 }
                 else
                 {
-                    HandleSave(task.Result);
-                }
-
-                return task;
-            }).Unwrap();
-        }
-
-        /// 
-        /// Sets the value of a property based upon its associated ParseFieldName attribute.
-        /// 
-        /// The new value.
-        /// The name of the property.
-        /// The type for the property.
-        protected void SetProperty(T value, [CallerMemberName] string propertyName = null)
-        {
-            this[Services.GetFieldForPropertyName(ClassName, propertyName)] = value;
-        }
-
-        void ApplyOperations(IDictionary operations, IDictionary map)
-        {
-            lock (Mutex)
-            {
-                foreach (KeyValuePair pair in operations)
-                {
-                    map.TryGetValue(pair.Key, out object oldValue);
-                    object newValue = pair.Value.Apply(oldValue, pair.Key);
-
-                    if (newValue != ParseDeleteOperation.Token)
-                    {
-                        map[pair.Key] = newValue;
-                    }
-                    else
-                    {
-                        map.Remove(pair.Key);
-                    }
+                    map.Remove(pair.Key);
                 }
             }
         }
-        void CheckGetAccess(string key)
+    }
+    void CheckGetAccess(string key)
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
+            if (!CheckIsDataAvailable(key))
             {
-                if (!CheckIsDataAvailable(key))
-                {
-                    Debug.WriteLine($"Warning: ParseObject has no data for key '{key}'. Ensure FetchIfNeededAsync() is called before accessing data.");
-                    // Optionally, set a flag or return early to signal the issue.
-                    return;
-                }
+                Debug.WriteLine($"Warning: ParseObject has no data for key '{key}'. Ensure FetchIfNeededAsync() is called before accessing data.");
+                // Optionally, set a flag or return early to signal the issue.
+                return;
             }
         }
+    }
 
 
-        bool CheckIsDataAvailable(string key)
+    bool CheckIsDataAvailable(string key)
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
-            {
-                return IsDataAvailable || EstimatedData.ContainsKey(key);
-            }
+            return IsDataAvailable || EstimatedData.ContainsKey(key);
         }
+    }
 
-        internal bool CheckIsDirty(bool considerChildren)
+    internal bool CheckIsDirty(bool considerChildren)
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
-            {
-                return Dirty || CurrentOperations.Count > 0 || considerChildren && HasDirtyChildren;
-            }
+            return Dirty || CurrentOperations.Count > 0 || considerChildren && HasDirtyChildren;
         }
+    }
 
-        void CheckKeyIsMutable(string key)
+    void CheckKeyIsMutable(string key)
+    {
+        if (!CheckKeyMutable(key))
         {
-            if (!CheckKeyMutable(key))
-            {
-                throw new InvalidOperationException($@"Cannot change the ""{key}"" property of a ""{ClassName}"" object.");
-            }
+            throw new InvalidOperationException($@"Cannot change the ""{key}"" property of a ""{ClassName}"" object.");
         }
+    }
 
-        /// 
-        /// Deep traversal of this object to grab a copy of any object referenced by this object.
-        /// These instances may have already been fetched, and we don't want to lose their data when
-        /// refreshing or saving.
-        /// 
-        /// Map of objectId to ParseObject which have been fetched.
-        IDictionary CollectFetchedObjects()
-        {
-            return Services.TraverseObjectDeep(EstimatedData).OfType().Where(o => o.ObjectId != null && o.IsDataAvailable).GroupBy(o => o.ObjectId).ToDictionary(group => group.Key, group => group.Last());
-        }
+    /// 
+    /// Deep traversal of this object to grab a copy of any object referenced by this object.
+    /// These instances may have already been fetched, and we don't want to lose their data when
+    /// refreshing or saving.
+    /// 
+    /// Map of objectId to ParseObject which have been fetched.
+    IDictionary CollectFetchedObjects()
+    {
+        return Services.TraverseObjectDeep(EstimatedData).OfType().Where(o => o.ObjectId != null && o.IsDataAvailable).GroupBy(o => o.ObjectId).ToDictionary(group => group.Key, group => group.Last());
+    }
 
-        IEnumerable FindUnsavedChildren()
-        {
-            return Services.TraverseObjectDeep(EstimatedData).OfType().Where(o => o.IsDirty);
-        }
+    IEnumerable FindUnsavedChildren()
+    {
+        return Services.TraverseObjectDeep(EstimatedData).OfType().Where(o => o.IsDirty);
+    }
 
-        IEnumerator> IEnumerable>.GetEnumerator()
+    IEnumerator> IEnumerable>.GetEnumerator()
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
-            {
-                return EstimatedData.GetEnumerator();
-            }
+            return EstimatedData.GetEnumerator();
         }
+    }
 
-        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
-            {
-                return ((IEnumerable>) this).GetEnumerator();
-            }
+            return ((IEnumerable>) this).GetEnumerator();
         }
-        /// 
-        /// Sets the objectId without marking dirty.
-        /// 
-        /// The new objectId
-        void SetObjectIdInternal(string objectId)
+    }
+    /// 
+    /// Sets the objectId without marking dirty.
+    /// 
+    /// The new objectId
+    void SetObjectIdInternal(string objectId)
+    {
+        lock (Mutex)
         {
-            lock (Mutex)
-            {
-                MutateState(mutableClone => mutableClone.ObjectId = objectId);
-                OnPropertyChanged(nameof(ObjectId));
-            }
+            MutateState(mutableClone => mutableClone.ObjectId = objectId);
+            OnPropertyChanged(nameof(ObjectId));
         }
     }
 }
diff --git a/Parse/Platform/Objects/ParseObjectController.cs b/Parse/Platform/Objects/ParseObjectController.cs
index e951101d..ce344294 100644
--- a/Parse/Platform/Objects/ParseObjectController.cs
+++ b/Parse/Platform/Objects/ParseObjectController.cs
@@ -14,142 +14,175 @@
 using Parse.Infrastructure.Execution;
 using Parse.Infrastructure.Data;
 
-namespace Parse.Platform.Objects
+namespace Parse.Platform.Objects;
+
+public class ParseObjectController : IParseObjectController
 {
-    public class ParseObjectController : IParseObjectController
-    {
-        IParseCommandRunner CommandRunner { get; }
+    IParseCommandRunner CommandRunner { get; }
 
-        IParseDataDecoder Decoder { get; }
+    IParseDataDecoder Decoder { get; }
 
-        IServerConnectionData ServerConnectionData { get; }
+    IServerConnectionData ServerConnectionData { get; }
 
-        public ParseObjectController(IParseCommandRunner commandRunner, IParseDataDecoder decoder, IServerConnectionData serverConnectionData) => (CommandRunner, Decoder, ServerConnectionData) = (commandRunner, decoder, serverConnectionData);
+    public ParseObjectController(IParseCommandRunner commandRunner, IParseDataDecoder decoder, IServerConnectionData serverConnectionData) => (CommandRunner, Decoder, ServerConnectionData) = (commandRunner, decoder, serverConnectionData);
 
-        public Task FetchAsync(IObjectState state, string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default)
-        {
-            ParseCommand command = new ParseCommand($"classes/{Uri.EscapeDataString(state.ClassName)}/{Uri.EscapeDataString(state.ObjectId)}", method: "GET", sessionToken: sessionToken, data: default);
-            return CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(task => ParseObjectCoder.Instance.Decode(task.Result.Item2, Decoder, serviceHub));
-        }
+    public async Task FetchAsync(IObjectState state, string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default)
+    {
+        var command = new ParseCommand($"classes/{Uri.EscapeDataString(state.ClassName)}/{Uri.EscapeDataString(state.ObjectId)}", method: "GET", sessionToken: sessionToken, data: null);
 
-        public Task SaveAsync(IObjectState state, IDictionary operations, string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default)
-        {
-            ParseCommand command = new ParseCommand(state.ObjectId == null ? $"classes/{Uri.EscapeDataString(state.ClassName)}" : $"classes/{Uri.EscapeDataString(state.ClassName)}/{state.ObjectId}", method: state.ObjectId is null ? "POST" : "PUT", sessionToken: sessionToken, data: serviceHub.GenerateJSONObjectForSaving(operations));
-            return CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(task => ParseObjectCoder.Instance.Decode(task.Result.Item2, Decoder, serviceHub).MutatedClone(mutableClone => mutableClone.IsNew = task.Result.Item1 == System.Net.HttpStatusCode.Created));
-        }
+        var result = await CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).ConfigureAwait(false);
+        return ParseObjectCoder.Instance.Decode(result.Item2, Decoder, serviceHub);
+    }
 
-        public IList> SaveAllAsync(IList states, IList> operationsList, string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default)
-        {
-            return ExecuteBatchRequests(states.Zip(operationsList, (item, operations) => new ParseCommand(item.ObjectId is null ? $"classes/{Uri.EscapeDataString(item.ClassName)}" : $"classes/{Uri.EscapeDataString(item.ClassName)}/{Uri.EscapeDataString(item.ObjectId)}", method: item.ObjectId is null ? "POST" : "PUT", data: serviceHub.GenerateJSONObjectForSaving(operations))).ToList(), sessionToken, cancellationToken).Select(task => task.OnSuccess(task => ParseObjectCoder.Instance.Decode(task.Result, Decoder, serviceHub))).ToList();
-        }
 
-        public Task DeleteAsync(IObjectState state, string sessionToken, CancellationToken cancellationToken = default)
+    public async Task SaveAsync(IObjectState state, IDictionary operations, string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default)
+    {
+        ParseCommand command;
+        if (state.ObjectId == null)
         {
-            return CommandRunner.RunCommandAsync(new ParseCommand($"classes/{state.ClassName}/{state.ObjectId}", method: "DELETE", sessionToken: sessionToken, data: null), cancellationToken: cancellationToken);
+            command = new ParseCommand($"classes/{Uri.EscapeDataString(state.ClassName)}", method: state.ObjectId == null ? "POST" : "PUT", sessionToken: sessionToken, data: serviceHub.GenerateJSONObjectForSaving(operations));
         }
-
-        public IList DeleteAllAsync(IList states, string sessionToken, CancellationToken cancellationToken = default)
+        else
         {
-            return ExecuteBatchRequests(states.Where(item => item.ObjectId is { }).Select(item => new ParseCommand($"classes/{Uri.EscapeDataString(item.ClassName)}/{Uri.EscapeDataString(item.ObjectId)}", method: "DELETE", data: default)).ToList(), sessionToken, cancellationToken).Cast().ToList();
+            command = new ParseCommand($"classes/{Uri.EscapeDataString(state.ClassName)}/{state.ObjectId}", method: state.ObjectId == null ? "POST" : "PUT", sessionToken: sessionToken, data: serviceHub.GenerateJSONObjectForSaving(operations));
         }
+        var result = await CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).ConfigureAwait(false);
+        var decodedState = ParseObjectCoder.Instance.Decode(result.Item2, Decoder, serviceHub);
 
-        int MaximumBatchSize { get; } = 50;
+        // Mutating the state and marking it as new if the status code is Created
+        decodedState.MutatedClone(mutableClone => mutableClone.IsNew = result.Item1 == System.Net.HttpStatusCode.Created);
 
-        // TODO (hallucinogen): move this out to a class to be used by Analytics
+        return decodedState;
+    }
 
-        internal IList>> ExecuteBatchRequests(IList requests, string sessionToken, CancellationToken cancellationToken = default)
-        {
-            List>> tasks = new List>>();
-            int batchSize = requests.Count;
 
-            IEnumerable remaining = requests;
+    public async Task>> SaveAllAsync(IList states,IList> operationsList,string sessionToken,IServiceHub serviceHub,CancellationToken cancellationToken = default)
+    {
+        // Create a list of tasks where each task represents a command to be executed
+        var tasks =
+            states.Zip(operationsList, (state, operations) => new ParseCommand(state.ObjectId == null? $"classes/{Uri.EscapeDataString(state.ClassName)}": $"classes/{Uri.EscapeDataString(state.ClassName)}/{Uri.EscapeDataString(state.ObjectId)}",
+            method: state.ObjectId == null ? "POST" : "PUT",sessionToken: sessionToken,data: serviceHub.GenerateJSONObjectForSaving(operations)))
+        .Select(command => CommandRunner.RunCommandAsync(command,null,null, cancellationToken)) // Run commands asynchronously
+        .ToList();
+
+        // Wait for all tasks to complete
+        var results = await Task.WhenAll(tasks).ConfigureAwait(false);
+        var decodedStates = results.Select(result => ParseObjectCoder.Instance.Decode(result.Item2, Decoder, serviceHub)).ToList();
+        // Decode results and return a list of tasks that resolve to IObjectState
+        return results.Select(result =>
+            Task.FromResult(ParseObjectCoder.Instance.Decode(result.Item2, Decoder, serviceHub)) // Return a task that resolves to IObjectState
+        ).ToList();
+    }
 
-            while (batchSize > MaximumBatchSize)
-            {
-                List process = remaining.Take(MaximumBatchSize).ToList();
 
-                remaining = remaining.Skip(MaximumBatchSize);
-                tasks.AddRange(ExecuteBatchRequest(process, sessionToken, cancellationToken));
-                batchSize = remaining.Count();
-            }
 
-            tasks.AddRange(ExecuteBatchRequest(remaining.ToList(), sessionToken, cancellationToken));
-            return tasks;
-        }
 
-        IList>> ExecuteBatchRequest(IList requests, string sessionToken, CancellationToken cancellationToken = default)
+    public Task DeleteAsync(IObjectState state, string sessionToken, CancellationToken cancellationToken = default)
+    {
+        return CommandRunner.RunCommandAsync(new ParseCommand($"classes/{state.ClassName}/{state.ObjectId}", method: "DELETE", sessionToken: sessionToken, data: null), cancellationToken: cancellationToken);
+    }
+
+    public IList DeleteAllAsync(IList states, string sessionToken, CancellationToken cancellationToken = default)
+    {
+        return ExecuteBatchRequests(states.Where(item => item.ObjectId is { }).Select(item => new ParseCommand($"classes/{Uri.EscapeDataString(item.ClassName)}/{Uri.EscapeDataString(item.ObjectId)}", method: "DELETE", data: default)).ToList(), sessionToken, cancellationToken).Cast().ToList();
+    }
+
+    int MaximumBatchSize { get; } = 50;
+
+    // TODO (hallucinogen): move this out to a class to be used by Analytics
+
+    internal IList>> ExecuteBatchRequests(IList requests, string sessionToken, CancellationToken cancellationToken = default)
+    {
+        List>> tasks = new List>>();
+        int batchSize = requests.Count;
+
+        IEnumerable remaining = requests;
+
+        while (batchSize > MaximumBatchSize)
         {
-            int batchSize = requests.Count;
+            List process = remaining.Take(MaximumBatchSize).ToList();
 
-            List>> tasks = new List>> { };
-            List>> completionSources = new List>> { };
+            remaining = remaining.Skip(MaximumBatchSize);
+            tasks.AddRange(ExecuteBatchRequest(process, sessionToken, cancellationToken));
+            batchSize = remaining.Count();
+        }
 
-            for (int i = 0; i < batchSize; ++i)
-            {
-                TaskCompletionSource> tcs = new TaskCompletionSource>();
+        tasks.AddRange(ExecuteBatchRequest(remaining.ToList(), sessionToken, cancellationToken));
+        return tasks;
+    }
 
-                completionSources.Add(tcs);
-                tasks.Add(tcs.Task);
-            }
+    IList>> ExecuteBatchRequest(IList requests, string sessionToken, CancellationToken cancellationToken = default)
+    {
+        int batchSize = requests.Count;
+
+        List>> tasks = new List>> { };
+        List>> completionSources = new List>> { };
+
+        for (int i = 0; i < batchSize; ++i)
+        {
+            TaskCompletionSource> tcs = new TaskCompletionSource>();
 
-            List encodedRequests = requests.Select(request =>
+            completionSources.Add(tcs);
+            tasks.Add(tcs.Task);
+        }
+
+        List encodedRequests = requests.Select(request =>
+        {
+            Dictionary results = new Dictionary
             {
-                Dictionary results = new Dictionary
-                {
-                    ["method"] = request.Method,
-                    ["path"] = request is { Path: { }, Resource: { } } ? request.Target.AbsolutePath : new Uri(new Uri(ServerConnectionData.ServerURI), request.Path).AbsolutePath,
-                };
+                ["method"] = request.Method,
+                ["path"] = request is { Path: { }, Resource: { } } ? request.Target.AbsolutePath : new Uri(new Uri(ServerConnectionData.ServerURI), request.Path).AbsolutePath,
+            };
 
-                if (request.DataObject != null)
-                    results["body"] = request.DataObject;
+            if (request.DataObject != null)
+                results["body"] = request.DataObject;
 
-                return results;
-            }).Cast().ToList();
+            return results;
+        }).Cast().ToList();
 
-            ParseCommand command = new ParseCommand("batch", method: "POST", sessionToken: sessionToken, data: new Dictionary { [nameof(requests)] = encodedRequests });
+        ParseCommand command = new ParseCommand("batch", method: "POST", sessionToken: sessionToken, data: new Dictionary { [nameof(requests)] = encodedRequests });
 
-            CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).ContinueWith(task =>
+        CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).ContinueWith(task =>
+        {
+            if (task.IsFaulted || task.IsCanceled)
             {
-                if (task.IsFaulted || task.IsCanceled)
-                {
-                    foreach (TaskCompletionSource> tcs in completionSources)
-                        if (task.IsFaulted)
-                            tcs.TrySetException(task.Exception);
-                        else if (task.IsCanceled)
-                            tcs.TrySetCanceled();
+                foreach (TaskCompletionSource> tcs in completionSources)
+                    if (task.IsFaulted)
+                        tcs.TrySetException(task.Exception);
+                    else if (task.IsCanceled)
+                        tcs.TrySetCanceled();
 
-                    return;
-                }
+                return;
+            }
 
-                IList resultsArray = Conversion.As>(task.Result.Item2["results"]);
-                int resultLength = resultsArray.Count;
+            IList resultsArray = Conversion.As>(task.Result.Item2["results"]);
+            int resultLength = resultsArray.Count;
 
-                if (resultLength != batchSize)
-                {
-                    foreach (TaskCompletionSource> completionSource in completionSources)
-                        completionSource.TrySetException(new InvalidOperationException($"Batch command result count expected: {batchSize} but was: {resultLength}."));
+            if (resultLength != batchSize)
+            {
+                foreach (TaskCompletionSource> completionSource in completionSources)
+                    completionSource.TrySetException(new InvalidOperationException($"Batch command result count expected: {batchSize} but was: {resultLength}."));
 
-                    return;
-                }
+                return;
+            }
 
-                for (int i = 0; i < batchSize; ++i)
+            for (int i = 0; i < batchSize; ++i)
+            {
+                Dictionary result = resultsArray[i] as Dictionary;
+                TaskCompletionSource> target = completionSources[i];
+
+                if (result.ContainsKey("success"))
+                    target.TrySetResult(result["success"] as IDictionary);
+                else if (result.ContainsKey("error"))
                 {
-                    Dictionary result = resultsArray[i] as Dictionary;
-                    TaskCompletionSource> target = completionSources[i];
-
-                    if (result.ContainsKey("success"))
-                        target.TrySetResult(result["success"] as IDictionary);
-                    else if (result.ContainsKey("error"))
-                    {
-                        IDictionary error = result["error"] as IDictionary;
-                        target.TrySetException(new ParseFailureException((ParseFailureException.ErrorCode) (long) error["code"], error[nameof(error)] as string));
-                    }
-                    else
-                        target.TrySetException(new InvalidOperationException("Invalid batch command response."));
+                    IDictionary error = result["error"] as IDictionary;
+                    target.TrySetException(new ParseFailureException((ParseFailureException.ErrorCode) (long) error["code"], error[nameof(error)] as string));
                 }
-            });
+                else
+                    target.TrySetException(new InvalidOperationException("Invalid batch command response."));
+            }
+        });
 
-            return tasks;
-        }
+        return tasks;
     }
 }
diff --git a/Parse/Platform/Push/ParsePushChannelsController.cs b/Parse/Platform/Push/ParsePushChannelsController.cs
index a9c312de..fa50aad2 100644
--- a/Parse/Platform/Push/ParsePushChannelsController.cs
+++ b/Parse/Platform/Push/ParsePushChannelsController.cs
@@ -7,30 +7,28 @@
 using Parse.Abstractions.Platform.Push;
 using Parse.Infrastructure.Utilities;
 
-namespace Parse.Platform.Push
+namespace Parse.Platform.Push;
+internal class ParsePushChannelsController : IParsePushChannelsController
 {
-    internal class ParsePushChannelsController : IParsePushChannelsController
-    {
-        IParseCurrentInstallationController CurrentInstallationController { get; }
+    private IParseCurrentInstallationController CurrentInstallationController { get; }
 
-        public ParsePushChannelsController(IParseCurrentInstallationController currentInstallationController) => CurrentInstallationController = currentInstallationController;
+    public ParsePushChannelsController(IParseCurrentInstallationController currentInstallationController)
+    {
+        CurrentInstallationController = currentInstallationController;
+    }
 
-        public Task SubscribeAsync(IEnumerable channels, IServiceHub serviceHub, CancellationToken cancellationToken = default)
-        {
-            return CurrentInstallationController.GetAsync(serviceHub, cancellationToken).OnSuccess(task =>
-        {
-            task.Result.AddRangeUniqueToList(nameof(channels), channels);
-            return task.Result.SaveAsync(cancellationToken);
-        }).Unwrap();
-        }
+    public async Task SubscribeAsync(IEnumerable channels, IServiceHub serviceHub, CancellationToken cancellationToken = default)
+    {
+        var installation = await CurrentInstallationController.GetAsync(serviceHub, cancellationToken).ConfigureAwait(false);
+        installation.AddRangeUniqueToList(nameof(channels), channels);
+        await installation.SaveAsync(cancellationToken).ConfigureAwait(false);
+    }
 
-        public Task UnsubscribeAsync(IEnumerable channels, IServiceHub serviceHub, CancellationToken cancellationToken = default)
-        {
-            return CurrentInstallationController.GetAsync(serviceHub, cancellationToken).OnSuccess(task =>
-        {
-            task.Result.RemoveAllFromList(nameof(channels), channels);
-            return task.Result.SaveAsync(cancellationToken);
-        }).Unwrap();
-        }
+    public async Task UnsubscribeAsync(IEnumerable channels, IServiceHub serviceHub, CancellationToken cancellationToken = default)
+    {
+        var installation = await CurrentInstallationController.GetAsync(serviceHub, cancellationToken).ConfigureAwait(false);
+        installation.RemoveAllFromList(nameof(channels), channels);
+        await installation.SaveAsync(cancellationToken).ConfigureAwait(false);
     }
 }
+
diff --git a/Parse/Platform/Push/ParsePushController.cs b/Parse/Platform/Push/ParsePushController.cs
index d9fb1c06..1877be8e 100644
--- a/Parse/Platform/Push/ParsePushController.cs
+++ b/Parse/Platform/Push/ParsePushController.cs
@@ -7,23 +7,30 @@
 using Parse.Infrastructure.Execution;
 using Parse.Infrastructure.Utilities;
 
-namespace Parse.Platform.Push
+namespace Parse.Platform.Push;
+internal class ParsePushController : IParsePushController
 {
-    internal class ParsePushController : IParsePushController
+    private IParseCommandRunner CommandRunner { get; }
+    private IParseCurrentUserController CurrentUserController { get; }
+
+    public ParsePushController(IParseCommandRunner commandRunner, IParseCurrentUserController currentUserController)
     {
-        IParseCommandRunner CommandRunner { get; }
+        CommandRunner = commandRunner;
+        CurrentUserController = currentUserController;
+    }
 
-        IParseCurrentUserController CurrentUserController { get; }
+    public async Task SendPushNotificationAsync(IPushState state, IServiceHub serviceHub, CancellationToken cancellationToken = default)
+    {
+        // Fetch the current session token
+        var sessionToken = await CurrentUserController.GetCurrentSessionTokenAsync(serviceHub, cancellationToken).ConfigureAwait(false);
 
-        public ParsePushController(IParseCommandRunner commandRunner, IParseCurrentUserController currentUserController)
-        {
-            CommandRunner = commandRunner;
-            CurrentUserController = currentUserController;
-        }
+        // Create the push command and execute it
+        var pushCommand = new ParseCommand(
+            "push",
+            method: "POST",
+            sessionToken: sessionToken,
+            data: ParsePushEncoder.Instance.Encode(state));
 
-        public Task SendPushNotificationAsync(IPushState state, IServiceHub serviceHub, CancellationToken cancellationToken = default)
-        {
-            return CurrentUserController.GetCurrentSessionTokenAsync(serviceHub, cancellationToken).OnSuccess(sessionTokenTask => CommandRunner.RunCommandAsync(new ParseCommand("push", method: "POST", sessionToken: sessionTokenTask.Result, data: ParsePushEncoder.Instance.Encode(state)), cancellationToken: cancellationToken)).Unwrap();
-        }
+        await CommandRunner.RunCommandAsync(pushCommand, cancellationToken: cancellationToken).ConfigureAwait(false);
     }
 }
diff --git a/Parse/Platform/Queries/ParseQuery.cs b/Parse/Platform/Queries/ParseQuery.cs
index ebeecb12..050082e1 100644
--- a/Parse/Platform/Queries/ParseQuery.cs
+++ b/Parse/Platform/Queries/ParseQuery.cs
@@ -716,16 +716,16 @@ public Task> FindAsync()
         {
             return FindAsync(CancellationToken.None);
         }
-
         /// 
         /// Retrieves a list of ParseObjects that satisfy this query from Parse.
         /// 
         /// The cancellation token.
         /// The list of ParseObjects that match this query.
-        public Task> FindAsync(CancellationToken cancellationToken)
+        public async Task> FindAsync(CancellationToken cancellationToken)
         {
             EnsureNotInstallationQuery();
-            return Services.QueryController.FindAsync(this, Services.GetCurrentUser(), cancellationToken).OnSuccess(task => from state in task.Result select Services.GenerateObjectFromState(state, ClassName));
+            var result = await Services.QueryController.FindAsync(this, Services.GetCurrentUser(), cancellationToken).ConfigureAwait(false);
+            return result.Select(state => Services.GenerateObjectFromState(state, ClassName));
         }
 
         /// 
@@ -742,12 +742,17 @@ public Task FirstOrDefaultAsync()
         /// 
         /// The cancellation token.
         /// A single ParseObject that satisfies this query, or else null.
-        public Task FirstOrDefaultAsync(CancellationToken cancellationToken)
+        public async Task FirstOrDefaultAsync(CancellationToken cancellationToken)
         {
             EnsureNotInstallationQuery();
-            return Services.QueryController.FirstAsync(this, Services.GetCurrentUser(), cancellationToken).OnSuccess(task => task.Result is IObjectState state && state is { } ? Services.GenerateObjectFromState(state, ClassName) : default);
+            var result = await Services.QueryController.FirstAsync(this, Services.GetCurrentUser(), cancellationToken).ConfigureAwait(false);
+
+            return result != null
+                ? Services.GenerateObjectFromState(result, ClassName)
+                : default; // Return default value (null for reference types) if result is null
         }
 
+
         /// 
         /// Retrieves at most one ParseObject that satisfies this query.
         /// 
@@ -764,9 +769,14 @@ public Task FirstAsync()
         /// The cancellation token.
         /// A single ParseObject that satisfies this query.
         /// If no results match the query.
-        public Task FirstAsync(CancellationToken cancellationToken)
+        public async Task FirstAsync(CancellationToken cancellationToken)
         {
-            return FirstOrDefaultAsync(cancellationToken).OnSuccess(task => task.Result ?? throw new ParseFailureException(ParseFailureException.ErrorCode.ObjectNotFound, "No results matched the query."));
+            var result = await FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
+            if (result == null)
+            {
+                throw new ParseFailureException(ParseFailureException.ErrorCode.ObjectNotFound, "No results matched the query.");
+            }
+            return result;
         }
 
         /// 
@@ -807,11 +817,14 @@ public Task GetAsync(string objectId)
         /// ObjectId of the ParseObject to fetch.
         /// The cancellation token.
         /// The ParseObject for the given objectId.
-        public Task GetAsync(string objectId, CancellationToken cancellationToken)
+        public async Task GetAsync(string objectId, CancellationToken cancellationToken)
         {
-            ParseQuery singleItemQuery = new ParseQuery(Services, ClassName).WhereEqualTo(nameof(objectId), objectId);
-            singleItemQuery = new ParseQuery(singleItemQuery, includes: Includes, selectedKeys: KeySelections, limit: 1);
-            return singleItemQuery.FindAsync(cancellationToken).OnSuccess(t => t.Result.FirstOrDefault() ?? throw new ParseFailureException(ParseFailureException.ErrorCode.ObjectNotFound, "Object with the given objectId not found."));
+            var query = new ParseQuery(Services, ClassName)
+                .WhereEqualTo(nameof(objectId), objectId)
+                .Limit(1);
+
+            var result = await query.FindAsync(cancellationToken).ConfigureAwait(false);
+            return result.FirstOrDefault() ?? throw new ParseFailureException(ParseFailureException.ErrorCode.ObjectNotFound, "Object with the given objectId not found.");
         }
 
         internal object GetConstraint(string key)
diff --git a/Parse/Platform/Queries/ParseQueryController.cs b/Parse/Platform/Queries/ParseQueryController.cs
index adb31b66..397d8eab 100644
--- a/Parse/Platform/Queries/ParseQueryController.cs
+++ b/Parse/Platform/Queries/ParseQueryController.cs
@@ -11,44 +11,64 @@
 using Parse.Infrastructure.Execution;
 using Parse.Infrastructure.Utilities;
 
-namespace Parse.Platform.Queries
+namespace Parse.Platform.Queries;
+
+/// 
+/// A straightforward implementation of  that uses  to decode raw server data when needed.
+/// 
+
+internal class ParseQueryController : IParseQueryController
 {
-    /// 
-    /// A straightforward implementation of  that uses  to decode raw server data when needed.
-    /// 
-    internal class ParseQueryController : IParseQueryController
+    private IParseCommandRunner CommandRunner { get; }
+    private IParseDataDecoder Decoder { get; }
+
+    public ParseQueryController(IParseCommandRunner commandRunner, IParseDataDecoder decoder)
+    {
+        CommandRunner = commandRunner;
+        Decoder = decoder;
+    }
+
+    public async Task> FindAsync(ParseQuery query, ParseUser user, CancellationToken cancellationToken = default) where T : ParseObject
     {
-        IParseCommandRunner CommandRunner { get; }
+        var result = await FindAsync(query.ClassName, query.BuildParameters(), user?.SessionToken, cancellationToken).ConfigureAwait(false);
+        var rawResults = result["results"] as IList ?? new List();
 
-        IParseDataDecoder Decoder { get; }
+        return rawResults
+            .Select(item => ParseObjectCoder.Instance.Decode(item as IDictionary, Decoder, user?.Services));
+    }
 
-        public ParseQueryController(IParseCommandRunner commandRunner, IParseDataDecoder decoder) => (CommandRunner, Decoder) = (commandRunner, decoder);
+    public async Task CountAsync(ParseQuery query, ParseUser user, CancellationToken cancellationToken = default) where T : ParseObject
+    {
+        var parameters = query.BuildParameters();
+        parameters["limit"] = 0;
+        parameters["count"] = 1;
 
-        public Task> FindAsync(ParseQuery query, ParseUser user, CancellationToken cancellationToken = default) where T : ParseObject
-        {
-            return FindAsync(query.ClassName, query.BuildParameters(), user?.SessionToken, cancellationToken).OnSuccess(t => (from item in t.Result["results"] as IList select ParseObjectCoder.Instance.Decode(item as IDictionary, Decoder, user?.Services)));
-        }
+        var result = await FindAsync(query.ClassName, parameters, user?.SessionToken, cancellationToken).ConfigureAwait(false);
+        return Convert.ToInt32(result["count"]);
+    }
 
-        public Task CountAsync(ParseQuery query, ParseUser user, CancellationToken cancellationToken = default) where T : ParseObject
-        {
-            IDictionary parameters = query.BuildParameters();
-            parameters["limit"] = 0;
-            parameters["count"] = 1;
+    public async Task FirstAsync(ParseQuery query, ParseUser user, CancellationToken cancellationToken = default) where T : ParseObject
+    {
+        var parameters = query.BuildParameters();
+        parameters["limit"] = 1;
 
-            return FindAsync(query.ClassName, parameters, user?.SessionToken, cancellationToken).OnSuccess(task => Convert.ToInt32(task.Result["count"]));
-        }
+        var result = await FindAsync(query.ClassName, parameters, user?.SessionToken, cancellationToken).ConfigureAwait(false);
+        var rawResults = result["results"] as IList ?? new List();
 
-        public Task FirstAsync(ParseQuery query, ParseUser user, CancellationToken cancellationToken = default) where T : ParseObject
-        {
-            IDictionary parameters = query.BuildParameters();
-            parameters["limit"] = 1;
+        var firstItem = rawResults.FirstOrDefault() as IDictionary;
+        return firstItem != null ? ParseObjectCoder.Instance.Decode(firstItem, Decoder, user?.Services) : null;
+    }
 
-            return FindAsync(query.ClassName, parameters, user?.SessionToken, cancellationToken).OnSuccess(task => (task.Result["results"] as IList).FirstOrDefault() as IDictionary is Dictionary item && item != null ? ParseObjectCoder.Instance.Decode(item, Decoder, user.Services) : null);
-        }
+    private async Task> FindAsync(string className, IDictionary parameters, string sessionToken, CancellationToken cancellationToken = default)
+    {
+        var command = new ParseCommand(
+            $"classes/{Uri.EscapeDataString(className)}?{ParseClient.BuildQueryString(parameters)}",
+            method: "GET",
+            sessionToken: sessionToken,
+            data: null
+        );
 
-        Task> FindAsync(string className, IDictionary parameters, string sessionToken, CancellationToken cancellationToken = default)
-        {
-            return CommandRunner.RunCommandAsync(new ParseCommand($"classes/{Uri.EscapeDataString(className)}?{ParseClient.BuildQueryString(parameters)}", method: "GET", sessionToken: sessionToken, data: null), cancellationToken: cancellationToken).OnSuccess(t => t.Result.Item2);
-        }
+        var response = await CommandRunner.RunCommandAsync(command, null,null,cancellationToken).ConfigureAwait(false);
+        return response.Item2;
     }
-}
+}
\ No newline at end of file
diff --git a/Parse/Platform/Sessions/ParseSessionController.cs b/Parse/Platform/Sessions/ParseSessionController.cs
index 982aeb33..e93323b9 100644
--- a/Parse/Platform/Sessions/ParseSessionController.cs
+++ b/Parse/Platform/Sessions/ParseSessionController.cs
@@ -10,34 +10,54 @@
 using Parse.Infrastructure.Execution;
 using Parse.Infrastructure.Data;
 
-namespace Parse.Platform.Sessions
+namespace Parse.Platform.Sessions;
+
+public class ParseSessionController : IParseSessionController
 {
-    public class ParseSessionController : IParseSessionController
+    IParseCommandRunner CommandRunner { get; }
+
+    IParseDataDecoder Decoder { get; }
+
+    public ParseSessionController(IParseCommandRunner commandRunner, IParseDataDecoder decoder) => (CommandRunner, Decoder) = (commandRunner, decoder);
+
+    public async Task GetSessionAsync(string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default)
     {
-        IParseCommandRunner CommandRunner { get; }
+        var result = await CommandRunner.RunCommandAsync(
+            new ParseCommand("sessions/me", method: "GET", sessionToken: sessionToken, data: null),
+            cancellationToken: cancellationToken
+        );
+
+        return ParseObjectCoder.Instance.Decode(result.Item2, Decoder, serviceHub);
+    }
 
-        IParseDataDecoder Decoder { get; }
 
-        public ParseSessionController(IParseCommandRunner commandRunner, IParseDataDecoder decoder) => (CommandRunner, Decoder) = (commandRunner, decoder);
+    public Task RevokeAsync(string sessionToken, CancellationToken cancellationToken = default)
+    {
+        return CommandRunner
+            .RunCommandAsync(new ParseCommand("logout", method: "POST", sessionToken: sessionToken, data: new Dictionary { }), cancellationToken: cancellationToken);
+    }
+
+    public async Task UpgradeToRevocableSessionAsync(
+       string sessionToken,
+       IServiceHub serviceHub,
+       CancellationToken cancellationToken = default)
+    {
+        var command = new ParseCommand(
+            "upgradeToRevocableSession",
+            method: "POST",
+            sessionToken: sessionToken,
+            data: new Dictionary()
+        );
 
-        public Task GetSessionAsync(string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default)
-        {
-            return CommandRunner.RunCommandAsync(new ParseCommand("sessions/me", method: "GET", sessionToken: sessionToken, data: null), cancellationToken: cancellationToken).OnSuccess(task => ParseObjectCoder.Instance.Decode(task.Result.Item2, Decoder, serviceHub));
-        }
+        var response = await CommandRunner.RunCommandAsync(command,null,null, cancellationToken).ConfigureAwait(false);
+        var decoded = ParseObjectCoder.Instance.Decode(response.Item2, Decoder, serviceHub);
 
-        public Task RevokeAsync(string sessionToken, CancellationToken cancellationToken = default)
-        {
-            return CommandRunner.RunCommandAsync(new ParseCommand("logout", method: "POST", sessionToken: sessionToken, data: new Dictionary { }), cancellationToken: cancellationToken);
-        }
+        return decoded;
+    }
 
-        public Task UpgradeToRevocableSessionAsync(string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default)
-        {
-            return CommandRunner.RunCommandAsync(new ParseCommand("upgradeToRevocableSession", method: "POST", sessionToken: sessionToken, data: new Dictionary()), cancellationToken: cancellationToken).OnSuccess(task => ParseObjectCoder.Instance.Decode(task.Result.Item2, Decoder, serviceHub));
-        }
 
-        public bool IsRevocableSessionToken(string sessionToken)
-        {
-            return sessionToken.Contains("r:");
-        }
+    public bool IsRevocableSessionToken(string sessionToken)
+    {
+        return sessionToken.Contains("r:");
     }
 }
diff --git a/Parse/Platform/Users/ParseCurrentUserController.cs b/Parse/Platform/Users/ParseCurrentUserController.cs
index becb2543..e4d84cdc 100644
--- a/Parse/Platform/Users/ParseCurrentUserController.cs
+++ b/Parse/Platform/Users/ParseCurrentUserController.cs
@@ -9,127 +9,137 @@
 using Parse.Abstractions.Platform.Users;
 using Parse.Infrastructure.Utilities;
 using Parse.Infrastructure.Data;
+using System;
+
+namespace Parse.Platform.Users;
 
-namespace Parse.Platform.Users
-{
 #pragma warning disable CS1030 // #warning directive
 #warning This class needs to be rewritten (PCuUsC).
 
-    public class ParseCurrentUserController : IParseCurrentUserController
-#pragma warning restore CS1030 // #warning directive
-    {
-        object Mutex { get; } = new object { };
-
-        TaskQueue TaskQueue { get; } = new TaskQueue { };
 
-        ICacheController StorageController { get; }
+public class ParseCurrentUserController : IParseCurrentUserController
+{
+    private readonly ICacheController StorageController;
+    private readonly IParseObjectClassController ClassController;
+    private readonly IParseDataDecoder Decoder;
 
-        IParseObjectClassController ClassController { get; }
+    private readonly TaskQueue TaskQueue = new();
+    private ParseUser? currentUser; // Nullable to explicitly handle absence of a user
 
-        IParseDataDecoder Decoder { get; }
+    public ParseCurrentUserController(ICacheController storageController, IParseObjectClassController classController, IParseDataDecoder decoder)
+    {
+        StorageController = storageController ?? throw new ArgumentNullException(nameof(storageController));
+        ClassController = classController ?? throw new ArgumentNullException(nameof(classController));
+        Decoder = decoder ?? throw new ArgumentNullException(nameof(decoder));
+    }
 
-        public ParseCurrentUserController(ICacheController storageController, IParseObjectClassController classController, IParseDataDecoder decoder) => (StorageController, ClassController, Decoder) = (storageController, classController, decoder);
+    public ParseUser? CurrentUser
+    {
+        get => currentUser;
+        private set => currentUser = value; // Setter is private to ensure controlled modification
+    }
+    private static string GenerateParseObjectId()
+    {
+        const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+        var random = new Random();
+        return new string(Enumerable.Repeat(chars, 10)
+            .Select(s => s[random.Next(s.Length)]).ToArray());
+    }
 
-        ParseUser currentUser;
-        public ParseUser CurrentUser
+    public async Task SetAsync(ParseUser? user, CancellationToken cancellationToken)
+    {
+        await TaskQueue.Enqueue>(async _ =>
         {
-            get
-            {
-                lock (Mutex)
-                    return currentUser;
-            }
-            set
+            if (user == null)
             {
-                lock (Mutex)
-                    currentUser = value;
+                var storage = await StorageController.LoadAsync().ConfigureAwait(false);
+                await storage.RemoveAsync(nameof(CurrentUser)).ConfigureAwait(false);
             }
-        }
-
-        public Task SetAsync(ParseUser user, CancellationToken cancellationToken)
-        {
-            return TaskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ =>
-        {
-            Task saveTask = default;
-
-            if (user is null)
-                saveTask = StorageController.LoadAsync().OnSuccess(task => task.Result.RemoveAsync(nameof(CurrentUser))).Unwrap();
             else
             {
-                // TODO (hallucinogen): we need to use ParseCurrentCoder instead of this janky encoding
+                // Use ParseCurrentCoder for encoding if available
+                var data = new Dictionary
+                {
+                    ["objectId"] = user.ObjectId ?? GenerateParseObjectId()
+                };
+
+                // Additional properties can be added to the dictionary as needed
 
-                IDictionary data = user.ServerDataToJSONObjectForSerialization();
-                data["objectId"] = user.ObjectId;
 
                 if (user.CreatedAt != null)
                     data["createdAt"] = user.CreatedAt.Value.ToString(ParseClient.DateFormatStrings.First(), CultureInfo.InvariantCulture);
+
                 if (user.UpdatedAt != null)
                     data["updatedAt"] = user.UpdatedAt.Value.ToString(ParseClient.DateFormatStrings.First(), CultureInfo.InvariantCulture);
 
-                saveTask = StorageController.LoadAsync().OnSuccess(task => task.Result.AddAsync(nameof(CurrentUser), JsonUtilities.Encode(data))).Unwrap();
+                var storage = await StorageController.LoadAsync().ConfigureAwait(false);
+                await storage.AddAsync(nameof(CurrentUser), JsonUtilities.Encode(data)).ConfigureAwait(false);
             }
 
             CurrentUser = user;
-            return saveTask;
-        }).Unwrap(), cancellationToken);
-        }
+            return true; // Enforce return type as `Task`
+        }, cancellationToken).ConfigureAwait(false);
+    }
 
-        public Task GetAsync(IServiceHub serviceHub, CancellationToken cancellationToken = default)
-        {
-            ParseUser cachedCurrent;
 
-            lock (Mutex)
-                cachedCurrent = CurrentUser;
+    public async Task GetAsync(IServiceHub serviceHub, CancellationToken cancellationToken = default)
+    {
+        if (CurrentUser is { ObjectId: { } })
+            return CurrentUser;
 
-            if (cachedCurrent is { } && (!string.IsNullOrEmpty(cachedCurrent.Email)) && !string.IsNullOrEmpty(cachedCurrent.ObjectId))
-                return  Task.FromResult(cachedCurrent);
+        return await TaskQueue.Enqueue>(async _ =>
+        {
+            var storage = await StorageController.LoadAsync().ConfigureAwait(false);
+            if (storage.TryGetValue(nameof(CurrentUser), out var serializedData) && serializedData is string serialization)
+            {
+                var state = ParseObjectCoder.Instance.Decode(JsonUtilities.Parse(serialization) as IDictionary, Decoder, serviceHub);
+                CurrentUser = ClassController.GenerateObjectFromState(state, "_User", serviceHub);
+            }
             else
-                return  TaskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ => StorageController.LoadAsync().OnSuccess(task =>
             {
-                task.Result.TryGetValue(nameof(CurrentUser), out object data);
-                ParseUser user = default;
-
-                if (data is string { } serialization)
-                {
-                    user = ClassController.GenerateObjectFromState(ParseObjectCoder.Instance.Decode(JsonUtilities.Parse(serialization) as IDictionary, Decoder, serviceHub), "_User", serviceHub);
-                }
-                return CurrentUser = user;
-            })).Unwrap(), cancellationToken);
-        }
+                CurrentUser = null;
+            }
 
-        public Task ExistsAsync(CancellationToken cancellationToken)
-        {
-            return CurrentUser is { } ? Task.FromResult(true) : TaskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ => StorageController.LoadAsync().OnSuccess(t => t.Result.ContainsKey(nameof(CurrentUser)))).Unwrap(), cancellationToken);
-        }
+            return CurrentUser; // Explicitly return the current user (or null)
+        }, cancellationToken).ConfigureAwait(false);
+    }
 
-        public bool IsCurrent(ParseUser user)
-        {
-            lock (Mutex)
-                return CurrentUser == user;
-        }
 
-        public void ClearFromMemory()
+    public async Task ExistsAsync(CancellationToken cancellationToken = default)
+    {
+        return CurrentUser != null || await TaskQueue.Enqueue(async _ =>
         {
-            CurrentUser = default;
-        }
+            var storage = await StorageController.LoadAsync().ConfigureAwait(false);
+            return storage.ContainsKey(nameof(CurrentUser));
+        }, cancellationToken).ConfigureAwait(false);
+    }
 
-        public void ClearFromDisk()
-        {
-            lock (Mutex)
-            {
-                ClearFromMemory();
+    public bool IsCurrent(ParseUser user) => CurrentUser == user;
 
-                TaskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ => StorageController.LoadAsync().OnSuccess(t => t.Result.RemoveAsync(nameof(CurrentUser)))).Unwrap().Unwrap(), CancellationToken.None);
-            }
-        }
+    public void ClearFromMemory() => CurrentUser = null;
 
-        public Task GetCurrentSessionTokenAsync(IServiceHub serviceHub, CancellationToken cancellationToken = default)
+    public async Task ClearFromDiskAsync()
+    {
+        ClearFromMemory();
+        await TaskQueue.Enqueue(async _ =>
         {
-            return GetAsync(serviceHub, cancellationToken).OnSuccess(task => task.Result?.SessionToken);
-        }
+            var storage = await StorageController.LoadAsync().ConfigureAwait(false);
+            await storage.RemoveAsync(nameof(CurrentUser)).ConfigureAwait(false);
+        }, CancellationToken.None).ConfigureAwait(false);
+    }
 
-        public Task LogOutAsync(IServiceHub serviceHub, CancellationToken cancellationToken = default)
+    public async Task GetCurrentSessionTokenAsync(IServiceHub serviceHub, CancellationToken cancellationToken = default)
+    {
+        var user = await GetAsync(serviceHub, cancellationToken).ConfigureAwait(false);
+        return user?.SessionToken;
+    }
+
+    public async Task LogOutAsync(IServiceHub serviceHub, CancellationToken cancellationToken = default)
+    {
+        await TaskQueue.Enqueue(async _ =>
         {
-            return TaskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ => GetAsync(serviceHub, cancellationToken)).Unwrap().OnSuccess(task => ClearFromDisk()), cancellationToken);
-        }
+            await GetAsync(serviceHub, cancellationToken).ConfigureAwait(false);
+            ClearFromDiskAsync();
+        }, cancellationToken).ConfigureAwait(false);
     }
-}
+}
\ No newline at end of file
diff --git a/Parse/Platform/Users/ParseUser.cs b/Parse/Platform/Users/ParseUser.cs
index 4e3b07c8..f394b8d5 100644
--- a/Parse/Platform/Users/ParseUser.cs
+++ b/Parse/Platform/Users/ParseUser.cs
@@ -6,109 +6,54 @@
 using Parse.Abstractions.Infrastructure.Control;
 using Parse.Abstractions.Platform.Authentication;
 using Parse.Abstractions.Platform.Objects;
-using Parse.Infrastructure.Utilities;
 
 namespace Parse
 {
-    /// 
-    /// Represents a user for a Parse application.
-    /// 
     [ParseClassName("_User")]
     public class ParseUser : ParseObject
     {
-        /// 
-        /// Indicates whether the current ParseUser has been authenticated on this device. 
-        /// Authentication means that the user has a valid session token and has been logged 
-        /// into the application.
-        /// 
         public bool IsAuthenticated
         {
             get
             {
-                // Synchronize access to the critical section to ensure thread safety
                 lock (Mutex)
                 {
-                    // Step 1: Check if the session token exists
-                    // The session token is generated when the user logs in successfully
-                    // or signs up. If it is null or empty, the user is not authenticated.
                     if (SessionToken == null)
-                    {
-                        return false; // No session token means the user is not authenticated
-                    }
+                        return false;
 
-                    // Step 2: Get the current user from the IServiceHub (via the Services instance)
-                    // This typically represents the currently logged-in user.
                     var currentUser = Services.GetCurrentUser();
-
-                    // Step 3: Ensure the current user is not null
-                    // If no user is retrieved from the service hub, the current ParseUser
-                    // cannot be considered authenticated.
-                    if (currentUser == null)
-                    {
-                        return false; // No current user means the user is not authenticated
-                    }
-
-                    // Step 4: Compare the current user's ObjectId with this user's ObjectId
-                    // If the ObjectIds match, it means this ParseUser is the currently
-                    // authenticated user.
-                    bool isSameUser = currentUser.ObjectId == ObjectId;
-                    if(isSameUser)
-                    {
-                        Debug.WriteLine("Ok");
-                    }
-                    // Return the final result of the comparison
-                    return isSameUser;
+                    return currentUser?.ObjectId == ObjectId;
                 }
             }
         }
 
-
-        /// 
-        /// Removes a key from the object's data if it exists.
-        /// 
-        /// The key to remove.
-        /// Cannot remove the username key.
         public override void Remove(string key)
         {
             if (key == "username")
-            {
                 throw new InvalidOperationException("Cannot remove the username key.");
-            }
 
             base.Remove(key);
         }
 
-        protected override bool CheckKeyMutable(string key)
-        {
-            return !ImmutableKeys.Contains(key);
-        }
+        protected override bool CheckKeyMutable(string key) => !ImmutableKeys.Contains(key);
 
         internal override void HandleSave(IObjectState serverState)
         {
             base.HandleSave(serverState);
-
             SynchronizeAllAuthData();
             CleanupAuthData();
-
             MutateState(mutableClone => mutableClone.ServerData.Remove("password"));
         }
 
         public string SessionToken => State.ContainsKey("sessionToken") ? State["sessionToken"] as string : null;
 
-        internal Task SetSessionTokenAsync(string newSessionToken)
-        {
-            return SetSessionTokenAsync(newSessionToken, CancellationToken.None);
-        }
 
-        internal Task SetSessionTokenAsync(string newSessionToken, CancellationToken cancellationToken)
+        internal async Task SetSessionTokenAsync(string newSessionToken, CancellationToken cancellationToken = default)
         {
             MutateState(mutableClone => mutableClone.ServerData["sessionToken"] = newSessionToken);
-            return Services.SaveCurrentUserAsync(this);
+            await Services.SaveCurrentUserAsync(this, cancellationToken).ConfigureAwait(false);
         }
 
-        /// 
-        /// Gets or sets the username.
-        /// 
         [ParseFieldName("username")]
         public string Username
         {
@@ -116,9 +61,6 @@ public string Username
             set => SetProperty(value, nameof(Username));
         }
 
-        /// 
-        /// Sets the password.
-        /// 
         [ParseFieldName("password")]
         public string Password
         {
@@ -126,9 +68,6 @@ public string Password
             set => SetProperty(value, nameof(Password));
         }
 
-        /// 
-        /// Sets the email address.
-        /// 
         [ParseFieldName("email")]
         public string Email
         {
@@ -136,256 +75,179 @@ public string Email
             set => SetProperty(value, nameof(Email));
         }
 
-        internal Task SignUpAsync(Task toAwait, CancellationToken cancellationToken)
+        internal async Task SignUpAsync(CancellationToken cancellationToken = default)
         {
-            if (AuthData == null)
+            if (string.IsNullOrWhiteSpace(Username))
+                throw new InvalidOperationException("Cannot sign up user with an empty name.");
+
+            if (string.IsNullOrWhiteSpace(Password))
+                throw new InvalidOperationException("Cannot sign up user with an empty password.");
+
+            if (!string.IsNullOrWhiteSpace(ObjectId))
+                throw new InvalidOperationException("Cannot sign up a user that already exists.");
+
+            var currentOperations = StartSave();
+
+            try
             {
-                // TODO (hallucinogen): make an Extension of Task to create Task with exception/canceled.
-                if (String.IsNullOrEmpty(Username))
-                {
-                    TaskCompletionSource tcs = new TaskCompletionSource();
-                    tcs.TrySetException(new InvalidOperationException("Cannot sign up user with an empty name."));
-                    return tcs.Task;
-                }
-                if (String.IsNullOrEmpty(Password))
-                {
-                    TaskCompletionSource tcs = new TaskCompletionSource();
-                    tcs.TrySetException(new InvalidOperationException("Cannot sign up user with an empty password."));
-                    return tcs.Task;
-                }
+                var result = await Services.UserController.SignUpAsync(State, currentOperations, Services, cancellationToken).ConfigureAwait(false);
+                HandleSave(result);
+                await Services.SaveCurrentUserAsync(this, cancellationToken).ConfigureAwait(false);
             }
-            if (!String.IsNullOrEmpty(ObjectId))
+            catch
             {
-                TaskCompletionSource tcs = new TaskCompletionSource();
-                tcs.TrySetException(new InvalidOperationException("Cannot sign up a user that already exists."));
-                return tcs.Task;
+                HandleFailedSave(currentOperations);
+                throw;
             }
-
-            IDictionary currentOperations = StartSave();
-
-            return toAwait.OnSuccess(_ => Services.UserController.SignUpAsync(State, currentOperations, Services, cancellationToken)).Unwrap().ContinueWith(t =>
-            {
-                if (t.IsFaulted || t.IsCanceled)
-                {
-                    HandleFailedSave(currentOperations);
-                }
-                else
-                {
-                    HandleSave(t.Result);
-                }
-                return t;
-            }).Unwrap().OnSuccess(_ => Services.SaveCurrentUserAsync(this)).Unwrap();
         }
 
-        /// 
-        /// Signs up a new user. This will create a new ParseUser on the server and will also persist the
-        /// session on disk so that you can access the user using . A username and
-        /// password must be set before calling SignUpAsync.
-        /// 
-        public Task SignUpAsync()
+        protected override async Task SaveAsync(Task toAwait, CancellationToken cancellationToken)
         {
-            return SignUpAsync(CancellationToken.None);
-        }
+            await toAwait.ConfigureAwait(false);
 
-        /// 
-        /// Signs up a new user. This will create a new ParseUser on the server and will also persist the
-        /// session on disk so that you can access the user using . A username and
-        /// password must be set before calling SignUpAsync.
-        /// 
-        /// The cancellation token.
-        public Task SignUpAsync(CancellationToken cancellationToken)
-        {
-            return TaskQueue.Enqueue(toAwait => SignUpAsync(toAwait, cancellationToken), cancellationToken);
-        }
+            if (ObjectId is null)
+                throw new InvalidOperationException("You must call SignUpAsync before calling SaveAsync.");
 
-        protected override Task SaveAsync(Task toAwait, CancellationToken cancellationToken)
-        {
-            lock (Mutex)
-            {
-                if (ObjectId is null)
-                {
-                    throw new InvalidOperationException("You must call SignUpAsync before calling SaveAsync.");
-                }
+            await base.SaveAsync(toAwait, cancellationToken).ConfigureAwait(false);
 
-                return base.SaveAsync(toAwait, cancellationToken).OnSuccess(_ => Services.CurrentUserController.IsCurrent(this) ? Services.SaveCurrentUserAsync(this) : Task.CompletedTask).Unwrap();
+            if (Services.CurrentUserController.IsCurrent(this))
+            {
+                await Services.SaveCurrentUserAsync(this, cancellationToken).ConfigureAwait(false);
             }
         }
 
-        // If this is already the current user, refresh its state on disk.
-        internal override Task FetchAsyncInternal(Task toAwait, CancellationToken cancellationToken)
+        internal override async Task FetchAsyncInternal(CancellationToken cancellationToken)
         {
-            return base.FetchAsyncInternal(toAwait, cancellationToken).OnSuccess(t => !Services.CurrentUserController.IsCurrent(this) ? Task.FromResult(t.Result) : Services.SaveCurrentUserAsync(this).OnSuccess(_ => t.Result)).Unwrap();
-        }
+            //await toAwait.ConfigureAwait(false);
 
-        internal Task LogOutAsync(Task toAwait, CancellationToken cancellationToken)
-        {
-            string oldSessionToken = SessionToken;
-            if (oldSessionToken == null)
+            var result = await base.FetchAsyncInternal(cancellationToken).ConfigureAwait(false);
+
+            if (Services.CurrentUserController.IsCurrent(this))
             {
-                return Task.FromResult(0);
+                await Services.SaveCurrentUserAsync(this, cancellationToken).ConfigureAwait(false);
             }
 
-            // Cleanup in-memory session.
-
-            MutateState(mutableClone => mutableClone.ServerData.Remove("sessionToken"));
-            Task revokeSessionTask = Services.RevokeSessionAsync(oldSessionToken, cancellationToken);
-            return Task.WhenAll(revokeSessionTask, Services.CurrentUserController.LogOutAsync(Services, cancellationToken));
+            return result;
         }
 
-        internal Task UpgradeToRevocableSessionAsync()
+        internal async Task LogOutAsync(CancellationToken cancellationToken)
         {
-            return UpgradeToRevocableSessionAsync(CancellationToken.None);
-        }
+            var oldSessionToken = SessionToken;
+            if (oldSessionToken == null)
+                return;
 
-        internal Task UpgradeToRevocableSessionAsync(CancellationToken cancellationToken)
-        {
-            return TaskQueue.Enqueue(toAwait => UpgradeToRevocableSessionAsync(toAwait, cancellationToken), cancellationToken);
+            MutateState(mutableClone => mutableClone.ServerData.Remove("sessionToken"));
+
+            await Task.WhenAll(
+                Services.RevokeSessionAsync(oldSessionToken, cancellationToken),
+                Services.CurrentUserController.LogOutAsync(Services, cancellationToken)
+            ).ConfigureAwait(false);
         }
 
-        internal Task UpgradeToRevocableSessionAsync(Task toAwait, CancellationToken cancellationToken)
+        internal async Task UpgradeToRevocableSessionAsync(CancellationToken cancellationToken = default)
         {
-            string sessionToken = SessionToken;
-
-            return toAwait.OnSuccess(_ => Services.UpgradeToRevocableSessionAsync(sessionToken, cancellationToken)).Unwrap().OnSuccess(task => SetSessionTokenAsync(task.Result)).Unwrap();
+            var sessionToken = SessionToken;
+            var newSessionToken = await Services.UpgradeToRevocableSessionAsync(sessionToken, cancellationToken).ConfigureAwait(false);
+            await SetSessionTokenAsync(newSessionToken, cancellationToken).ConfigureAwait(false);
         }
+        //public string SessionToken => State.ContainsKey("sessionToken") ? State["sessionToken"] as string : null;
 
-        /// 
-        /// Gets the authData for this user.
-        /// 
         public IDictionary> AuthData
         {
-            get => TryGetValue("authData", out IDictionary> authData) ? authData : null;
+
+            get => ContainsKey("authData") ? AuthData["authData"] as IDictionary> : null;
             set => this["authData"] = value;
         }
 
-        /// 
-        /// Removes null values from authData (which exist temporarily for unlinking)
-        /// 
         void CleanupAuthData()
         {
             lock (Mutex)
             {
                 if (!Services.CurrentUserController.IsCurrent(this))
-                {
                     return;
-                }
-
-                IDictionary> authData = AuthData;
 
+                var authData = AuthData;
                 if (authData == null)
-                {
                     return;
-                }
 
-                foreach (KeyValuePair> pair in new Dictionary>(authData))
+                foreach (var key in new List(authData.Keys))
                 {
-                    if (pair.Value == null)
+                    if (authData[key] == null)
                     {
-                        authData.Remove(pair.Key);
+                        authData.Remove(key);
                     }
                 }
             }
         }
 
-#pragma warning disable CS1030 // #warning directive
-#warning Check if the following properties should be injected via IServiceHub.UserController (except for ImmutableKeys).
-
-        internal static IParseAuthenticationProvider GetProvider(string providerName)
+        internal async Task LinkWithAsync(string authType, IDictionary data, CancellationToken cancellationToken)
         {
-            return Authenticators.TryGetValue(providerName, out IParseAuthenticationProvider provider) ? provider : null;
+            lock (Mutex)
+            {
+                AuthData ??= new Dictionary>();
+                AuthData[authType] = data;
+            }
+
+            await SaveAsync(cancellationToken).ConfigureAwait(false);
         }
-#pragma warning restore CS1030 // #warning directive
 
-        internal static IDictionary Authenticators { get; } = new Dictionary { };
+        internal async Task LinkWithAsync(string authType, CancellationToken cancellationToken)
+        {
+            var provider = GetProvider(authType);
+            if (provider != null)
+            {
+                var authData = await provider.AuthenticateAsync(cancellationToken).ConfigureAwait(false);
+                await LinkWithAsync(authType, authData, cancellationToken).ConfigureAwait(false);
+            }
+        }
 
-        internal static HashSet ImmutableKeys { get; } = new HashSet { "sessionToken", "isNew" };
+        internal Task UnlinkFromAsync(string authType, CancellationToken cancellationToken)
+        {
+            return LinkWithAsync(authType, null, cancellationToken);
+        }
 
-        /// 
-        /// Synchronizes authData for all providers.
-        /// 
-        internal void SynchronizeAllAuthData()
+        internal bool IsLinked(string authType)
         {
             lock (Mutex)
             {
-                IDictionary> authData = AuthData;
-
-                if (authData == null)
-                {
-                    return;
-                }
-
-                foreach (KeyValuePair> pair in authData)
-                {
-                    SynchronizeAuthData(GetProvider(pair.Key));
-                }
+                return AuthData != null && AuthData.TryGetValue(authType, out var data) && data != null;
             }
         }
 
-        internal void SynchronizeAuthData(IParseAuthenticationProvider provider)
+        internal static IParseAuthenticationProvider GetProvider(string providerName)
         {
-            bool restorationSuccess = false;
+            return Authenticators.TryGetValue(providerName, out var provider) ? provider : null;
+        }
+
+        internal static IDictionary Authenticators { get; } = new Dictionary();
+        internal static HashSet ImmutableKeys { get; } = new() { "sessionToken", "isNew" };
 
+        internal void SynchronizeAllAuthData()
+        {
             lock (Mutex)
             {
-                IDictionary> authData = AuthData;
-
-                if (authData == null || provider == null)
-                {
+                var authData = AuthData;
+                if (authData == null)
                     return;
-                }
 
-                if (authData.TryGetValue(provider.AuthType, out IDictionary data))
+                foreach (var provider in authData.Keys)
                 {
-                    restorationSuccess = provider.RestoreAuthentication(data);
+                    SynchronizeAuthData(GetProvider(provider));
                 }
             }
-
-            if (!restorationSuccess)
-            {
-                UnlinkFromAsync(provider.AuthType, CancellationToken.None);
-            }
         }
 
-        internal Task LinkWithAsync(string authType, IDictionary data, CancellationToken cancellationToken)
-        {
-            return TaskQueue.Enqueue(toAwait =>
-        {
-            IDictionary> authData = AuthData;
-
-            if (authData == null)
-            {
-                authData = AuthData = new Dictionary>();
-            }
-
-            authData[authType] = data;
-            AuthData = authData;
-
-            return SaveAsync(cancellationToken);
-        }, cancellationToken);
-        }
-
-        internal Task LinkWithAsync(string authType, CancellationToken cancellationToken)
+        internal void SynchronizeAuthData(IParseAuthenticationProvider provider)
         {
-            IParseAuthenticationProvider provider = GetProvider(authType);
-            return provider.AuthenticateAsync(cancellationToken).OnSuccess(t => LinkWithAsync(authType, t.Result, cancellationToken)).Unwrap();
-        }
+            if (provider == null || AuthData == null)
+                return;
 
-        /// 
-        /// Unlinks a user from a service.
-        /// 
-        internal Task UnlinkFromAsync(string authType, CancellationToken cancellationToken)
-        {
-            return LinkWithAsync(authType, null, cancellationToken);
-        }
+            if (!AuthData.TryGetValue(provider.AuthType, out var data))
+                return;
 
-        /// 
-        /// Checks whether a user is linked to a service.
-        /// 
-        internal bool IsLinked(string authType)
-        {
-            lock (Mutex)
+            if (!provider.RestoreAuthentication(data))
             {
-                return AuthData != null && AuthData.ContainsKey(authType) && AuthData[authType] != null;
+                UnlinkFromAsync(provider.AuthType, CancellationToken.None);
             }
         }
     }
diff --git a/Parse/Platform/Users/ParseUserController.cs b/Parse/Platform/Users/ParseUserController.cs
index 9b4af710..d5f89ec2 100644
--- a/Parse/Platform/Users/ParseUserController.cs
+++ b/Parse/Platform/Users/ParseUserController.cs
@@ -10,49 +10,122 @@
 using Parse.Abstractions.Platform.Objects;
 using Parse.Infrastructure.Execution;
 using Parse.Infrastructure.Data;
+using System.Net.Http;
+using System;
 
-namespace Parse.Platform.Users
+namespace Parse.Platform.Users;
+
+
+public class ParseUserController : IParseUserController
 {
-    public class ParseUserController : IParseUserController
+    private IParseCommandRunner CommandRunner { get; }
+    private IParseDataDecoder Decoder { get; }
+
+    public bool RevocableSessionEnabled { get; set; } = false; // Use a simple property
+
+    public ParseUserController(IParseCommandRunner commandRunner, IParseDataDecoder decoder)
     {
-        IParseCommandRunner CommandRunner { get; }
+        CommandRunner = commandRunner ?? throw new ArgumentNullException(nameof(commandRunner));
+        Decoder = decoder ?? throw new ArgumentNullException(nameof(decoder));
+    }
 
-        IParseDataDecoder Decoder { get; }
+    public async Task SignUpAsync(
+        IObjectState state,
+        IDictionary operations,
+        IServiceHub serviceHub,
+        CancellationToken cancellationToken = default)
+    {
+        if (state == null)
+            throw new ArgumentNullException(nameof(state));
+        if (operations == null)
+            throw new ArgumentNullException(nameof(operations));
+        if (serviceHub == null)
+            throw new ArgumentNullException(nameof(serviceHub));
 
-        public bool RevocableSessionEnabled { get; set; }
+        var command = new ParseCommand(
+            "classes/_User",
+            HttpMethod.Post.ToString(),
+            data: serviceHub.GenerateJSONObjectForSaving(operations));
 
-        public object RevocableSessionEnabledMutex { get; } = new object { };
+        var result = await CommandRunner.RunCommandAsync(command).ConfigureAwait(false);
+        return ParseObjectCoder.Instance
+            .Decode(result.Item2, Decoder, serviceHub)
+            .MutatedClone(mutableClone => mutableClone.IsNew = true);
+    }
 
-        public ParseUserController(IParseCommandRunner commandRunner, IParseDataDecoder decoder) => (CommandRunner, Decoder) = (commandRunner, decoder);
+    public async Task LogInAsync(
+        string username,
+        string password,
+        IServiceHub serviceHub,
+        CancellationToken cancellationToken = default)
+    {
+        if (string.IsNullOrWhiteSpace(username))
+            throw new ArgumentException("Username cannot be null or empty.", nameof(username));
+        if (string.IsNullOrWhiteSpace(password))
+            throw new ArgumentException("Password cannot be null or empty.", nameof(password));
+        if (serviceHub == null)
+            throw new ArgumentNullException(nameof(serviceHub));
 
-        public Task SignUpAsync(IObjectState state, IDictionary operations, IServiceHub serviceHub, CancellationToken cancellationToken = default)
-        {
-            return CommandRunner.RunCommandAsync(new ParseCommand("classes/_User", method: "POST", data: serviceHub.GenerateJSONObjectForSaving(operations)), cancellationToken: cancellationToken).OnSuccess(task => ParseObjectCoder.Instance.Decode(task.Result.Item2, Decoder, serviceHub).MutatedClone(mutableClone => mutableClone.IsNew = true));
-        }
+        // Use POST for login with credentials in the body to improve security
+        var command = new ParseCommand(
+            "login",
+            HttpMethod.Post.ToString(),
+            data: new Dictionary { ["username"] = username, ["password"] = password });
 
-        public Task LogInAsync(string username, string password, IServiceHub serviceHub, CancellationToken cancellationToken = default)
-        {
-            return CommandRunner.RunCommandAsync(new ParseCommand($"login?{ParseClient.BuildQueryString(new Dictionary { [nameof(username)] = username, [nameof(password)] = password })}", method: "GET", data: null), cancellationToken: cancellationToken).OnSuccess(task => ParseObjectCoder.Instance.Decode(task.Result.Item2, Decoder, serviceHub).MutatedClone(mutableClone => mutableClone.IsNew = task.Result.Item1 == System.Net.HttpStatusCode.Created));
-        }
+        var result = await CommandRunner.RunCommandAsync(command).ConfigureAwait(false);
+        return ParseObjectCoder.Instance
+            .Decode(result.Item2, Decoder, serviceHub)
+            .MutatedClone(mutableClone => mutableClone.IsNew = result.Item1 == System.Net.HttpStatusCode.Created);
+    }
 
-        public Task LogInAsync(string authType, IDictionary data, IServiceHub serviceHub, CancellationToken cancellationToken = default)
-        {
-            Dictionary authData = new Dictionary
-            {
-                [authType] = data
-            };
+    public async Task LogInAsync(
+        string authType,
+        IDictionary data,
+        IServiceHub serviceHub,
+        CancellationToken cancellationToken = default)
+    {
+        if (string.IsNullOrWhiteSpace(authType))
+            throw new ArgumentException("AuthType cannot be null or empty.", nameof(authType));
+        if (data == null)
+            throw new ArgumentNullException(nameof(data));
+        if (serviceHub == null)
+            throw new ArgumentNullException(nameof(serviceHub));
 
-            return CommandRunner.RunCommandAsync(new ParseCommand("users", method: "POST", data: new Dictionary { [nameof(authData)] = authData }), cancellationToken: cancellationToken).OnSuccess(task => ParseObjectCoder.Instance.Decode(task.Result.Item2, Decoder, serviceHub).MutatedClone(mutableClone => mutableClone.IsNew = task.Result.Item1 == System.Net.HttpStatusCode.Created));
-        }
+        var authData = new Dictionary { [authType] = data };
+
+        var command = new ParseCommand("users",HttpMethod.Post.ToString(),data: new Dictionary { ["authData"] = authData });
+
+        var result = await CommandRunner.RunCommandAsync(command).ConfigureAwait(false);
+        return ParseObjectCoder.Instance
+            .Decode(result.Item2, Decoder, serviceHub)
+            .MutatedClone(mutableClone => mutableClone.IsNew = result.Item1 == System.Net.HttpStatusCode.Created);
+    }
+
+    public async Task GetUserAsync(
+        string sessionToken,
+        IServiceHub serviceHub,
+        CancellationToken cancellationToken = default)
+    {
+        if (string.IsNullOrWhiteSpace(sessionToken))
+            throw new ArgumentException("Session token cannot be null or empty.", nameof(sessionToken));
+        if (serviceHub == null)
+            throw new ArgumentNullException(nameof(serviceHub));
+
+        var command = new ParseCommand("users/me",HttpMethod.Get.ToString(),sessionToken: sessionToken, null, null);
+        var result = await CommandRunner.RunCommandAsync(command).ConfigureAwait(false);
+        return ParseObjectCoder.Instance.Decode(result.Item2, Decoder, serviceHub);
+    }
+
+    public Task RequestPasswordResetAsync(string email, CancellationToken cancellationToken = default)
+    {
+        if (string.IsNullOrWhiteSpace(email))
+            throw new ArgumentException("Email cannot be null or empty.", nameof(email));
 
-        public Task GetUserAsync(string sessionToken, IServiceHub serviceHub, CancellationToken cancellationToken = default)
-        {
-            return CommandRunner.RunCommandAsync(new ParseCommand("users/me", method: "GET", sessionToken: sessionToken, data: default), cancellationToken: cancellationToken).OnSuccess(task => ParseObjectCoder.Instance.Decode(task.Result.Item2, Decoder, serviceHub));
-        }
+        var command = new ParseCommand(
+            "requestPasswordReset",
+            HttpMethod.Post.ToString(),
+            data: new Dictionary { ["email"] = email });
 
-        public Task RequestPasswordResetAsync(string email, CancellationToken cancellationToken = default)
-        {
-            return CommandRunner.RunCommandAsync(new ParseCommand("requestPasswordReset", method: "POST", data: new Dictionary { [nameof(email)] = email }), cancellationToken: cancellationToken);
-        }
+        return CommandRunner.RunCommandAsync(command);
     }
-}
+}
\ No newline at end of file
diff --git a/Parse/Utilities/AnalyticsServiceExtensions.cs b/Parse/Utilities/AnalyticsServiceExtensions.cs
index f13bf77c..a348a0d7 100644
--- a/Parse/Utilities/AnalyticsServiceExtensions.cs
+++ b/Parse/Utilities/AnalyticsServiceExtensions.cs
@@ -4,100 +4,102 @@
 using Parse.Abstractions.Infrastructure;
 using Parse.Infrastructure.Utilities;
 
-namespace Parse
+namespace Parse;
+
+/// 
+/// Provides an interface to Parse's logging and analytics backend.
+///
+/// Methods will return immediately and cache requests (along with timestamps)
+/// to be handled in the background.
+/// 
+public static class AnalyticsServiceExtensions
 {
     /// 
-    /// Provides an interface to Parse's logging and analytics backend.
+    /// Tracks this application being launched.
+    /// 
+    /// An Async Task that can be waited on or ignored.
+    public static Task TrackLaunchAsync(this IServiceHub serviceHub)
+    {
+        return TrackLaunchWithPushHashAsync(serviceHub);
+    }
+
+    /// 
+    /// Tracks the occurrence of a custom event with additional dimensions.
+    /// Parse will store a data point at the time of invocation with the
+    /// given event name.
+    ///
+    /// Dimensions will allow segmentation of the occurrences of this
+    /// custom event.
     ///
-    /// Methods will return immediately and cache requests (along with timestamps)
-    /// to be handled in the background.
+    /// To track a user signup along with additional metadata, consider the
+    /// following:
+    /// 
+    /// IDictionary<string, string> dims = new Dictionary<string, string> {
+    ///   { "gender", "m" },
+    ///   { "source", "web" },
+    ///   { "dayType", "weekend" }
+    /// };
+    /// ParseAnalytics.TrackEventAsync("signup", dims);
+    /// 
+    ///
+    /// There is a default limit of 8 dimensions per event tracked.
     /// 
-    public static class AnalyticsServiceExtensions
+    /// The name of the custom event to report to ParseClient
+    /// as having happened.
+    /// An Async Task that can be waited on or ignored.
+    public static Task TrackAnalyticsEventAsync(this IServiceHub serviceHub, string name)
     {
-        /// 
-        /// Tracks this application being launched.
-        /// 
-        /// An Async Task that can be waited on or ignored.
-        public static Task TrackLaunchAsync(this IServiceHub serviceHub)
-        {
-            return TrackLaunchWithPushHashAsync(serviceHub);
-        }
+        return TrackAnalyticsEventAsync(serviceHub, name, default);
+    }
 
-        /// 
-        /// Tracks the occurrence of a custom event with additional dimensions.
-        /// Parse will store a data point at the time of invocation with the
-        /// given event name.
-        ///
-        /// Dimensions will allow segmentation of the occurrences of this
-        /// custom event.
-        ///
-        /// To track a user signup along with additional metadata, consider the
-        /// following:
-        /// 
-        /// IDictionary<string, string> dims = new Dictionary<string, string> {
-        ///   { "gender", "m" },
-        ///   { "source", "web" },
-        ///   { "dayType", "weekend" }
-        /// };
-        /// ParseAnalytics.TrackEventAsync("signup", dims);
-        /// 
-        ///
-        /// There is a default limit of 8 dimensions per event tracked.
-        /// 
-        /// The name of the custom event to report to ParseClient
-        /// as having happened.
-        /// An Async Task that can be waited on or ignored.
-        public static Task TrackAnalyticsEventAsync(this IServiceHub serviceHub, string name)
+    /// 
+    /// Tracks the occurrence of a custom event with additional dimensions.
+    /// Parse will store a data point at the time of invocation with the
+    /// given event name.
+    ///
+    /// Dimensions will allow segmentation of the occurrences of this
+    /// custom event.
+    ///
+    /// To track a user signup along with additional metadata, consider the
+    /// following:
+    /// 
+    /// IDictionary<string, string> dims = new Dictionary<string, string> {
+    ///   { "gender", "m" },
+    ///   { "source", "web" },
+    ///   { "dayType", "weekend" }
+    /// };
+    /// ParseAnalytics.TrackEventAsync("signup", dims);
+    /// 
+    ///
+    /// There is a default limit of 8 dimensions per event tracked.
+    /// 
+    /// The name of the custom event to report to ParseClient
+    /// as having happened.
+    /// The dictionary of information by which to
+    /// segment this event.
+    /// An Async Task that can be awaited on or ignored.
+    public static async Task TrackAnalyticsEventAsync(this IServiceHub serviceHub, string name, IDictionary dimensions)
+    {
+        if (string.IsNullOrWhiteSpace(name))
         {
-            return TrackAnalyticsEventAsync(serviceHub, name, default);
+            throw new ArgumentException("A name for the custom event must be provided.", nameof(name));
         }
 
-        /// 
-        /// Tracks the occurrence of a custom event with additional dimensions.
-        /// Parse will store a data point at the time of invocation with the
-        /// given event name.
-        ///
-        /// Dimensions will allow segmentation of the occurrences of this
-        /// custom event.
-        ///
-        /// To track a user signup along with additional metadata, consider the
-        /// following:
-        /// 
-        /// IDictionary<string, string> dims = new Dictionary<string, string> {
-        ///   { "gender", "m" },
-        ///   { "source", "web" },
-        ///   { "dayType", "weekend" }
-        /// };
-        /// ParseAnalytics.TrackEventAsync("signup", dims);
-        /// 
-        ///
-        /// There is a default limit of 8 dimensions per event tracked.
-        /// 
-        /// The name of the custom event to report to ParseClient
-        /// as having happened.
-        /// The dictionary of information by which to
-        /// segment this event.
-        /// An Async Task that can be waited on or ignored.
-        public static Task TrackAnalyticsEventAsync(this IServiceHub serviceHub, string name, IDictionary dimensions)
-        {
-            if (name is null || name.Trim().Length == 0)
-            {
-                throw new ArgumentException("A name for the custom event must be provided.");
-            }
-
-            return serviceHub.CurrentUserController.GetCurrentSessionTokenAsync(serviceHub).OnSuccess(task => serviceHub.AnalyticsController.TrackEventAsync(name, dimensions, task.Result, serviceHub)).Unwrap();
-        }
+        var sessionToken = await serviceHub.CurrentUserController.GetCurrentSessionTokenAsync(serviceHub).ConfigureAwait(false);
+        await serviceHub.AnalyticsController.TrackEventAsync(name, dimensions, sessionToken, serviceHub).ConfigureAwait(false);
+    }
 
-        /// 
-        /// Private method, used by platform-specific extensions to report an app-open
-        /// to the server.
-        /// 
-        /// An identifying hash for a given push notification,
-        /// passed down from the server.
-        /// An Async Task that can be waited on or ignored.
-        static Task TrackLaunchWithPushHashAsync(this IServiceHub serviceHub, string pushHash = null)
-        {
-            return serviceHub.CurrentUserController.GetCurrentSessionTokenAsync(serviceHub).OnSuccess(task => serviceHub.AnalyticsController.TrackAppOpenedAsync(pushHash, task.Result, serviceHub)).Unwrap();
-        }
+    /// 
+    /// Private method, used by platform-specific extensions to report an app-open
+    /// to the server.
+    /// 
+    /// An identifying hash for a given push notification,
+    /// passed down from the server.
+    /// An Async Task that can be waited on or ignored.
+    static async Task TrackLaunchWithPushHashAsync(this IServiceHub serviceHub, string pushHash = null)
+    {
+        var sessionToken = await serviceHub.CurrentUserController.GetCurrentSessionTokenAsync(serviceHub).ConfigureAwait(false);
+        await serviceHub.AnalyticsController.TrackAppOpenedAsync(pushHash, sessionToken, serviceHub).ConfigureAwait(false);
     }
+
 }
diff --git a/Parse/Utilities/ObjectServiceExtensions.cs b/Parse/Utilities/ObjectServiceExtensions.cs
index 3317d044..50a4b2e4 100644
--- a/Parse/Utilities/ObjectServiceExtensions.cs
+++ b/Parse/Utilities/ObjectServiceExtensions.cs
@@ -11,622 +11,676 @@
 using Parse.Infrastructure.Data;
 using System.Diagnostics;
 
-namespace Parse
+namespace Parse;
+
+public static class ObjectServiceExtensions
 {
-    public static class ObjectServiceExtensions
-    {
-        /// 
-        /// Registers a custom subclass type with the Parse SDK, enabling strong-typing of those ParseObjects whenever
-        /// they appear. Subclasses must specify the ParseClassName attribute, have a default constructor, and properties
-        /// backed by ParseObject fields should have ParseFieldName attributes supplied.
-        /// 
-        /// The target  instance.
-        /// The ParseObject subclass type to register.
-        public static void AddValidClass(this IServiceHub serviceHub) where T : ParseObject, new()
-        {
-            serviceHub.ClassController.AddValid(typeof(T));
-        }
+    /// 
+    /// Registers a custom subclass type with the Parse SDK, enabling strong-typing of those ParseObjects whenever
+    /// they appear. Subclasses must specify the ParseClassName attribute, have a default constructor, and properties
+    /// backed by ParseObject fields should have ParseFieldName attributes supplied.
+    /// 
+    /// The target  instance.
+    /// The ParseObject subclass type to register.
+    public static void AddValidClass(this IServiceHub serviceHub) where T : ParseObject, new()
+    {
+        serviceHub.ClassController.AddValid(typeof(T));
+    }
 
-        /// 
-        /// Registers a custom subclass type with the Parse SDK, enabling strong-typing of those ParseObjects whenever
-        /// they appear. Subclasses must specify the ParseClassName attribute, have a default constructor, and properties
-        /// backed by ParseObject fields should have ParseFieldName attributes supplied.
-        /// 
-        /// The ParseObject subclass type to register.
-        /// The target  instance.
-        public static void RegisterSubclass(this IServiceHub serviceHub, Type type)
+    /// 
+    /// Registers a custom subclass type with the Parse SDK, enabling strong-typing of those ParseObjects whenever
+    /// they appear. Subclasses must specify the ParseClassName attribute, have a default constructor, and properties
+    /// backed by ParseObject fields should have ParseFieldName attributes supplied.
+    /// 
+    /// The ParseObject subclass type to register.
+    /// The target  instance.
+    public static void RegisterSubclass(this IServiceHub serviceHub, Type type)
+    {
+        if (typeof(ParseObject).IsAssignableFrom(type))
         {
-            if (typeof(ParseObject).IsAssignableFrom(type))
-            {
-                serviceHub.ClassController.AddValid(type);
-            }
+            serviceHub.ClassController.AddValid(type);
         }
+    }
 
-        /// 
-        /// Unregisters a previously-registered sub-class of  with the subclassing controller.
-        /// 
-        /// 
-        /// 
-        public static void RemoveClass(this IServiceHub serviceHub) where T : ParseObject, new()
-        {
-            serviceHub.ClassController.RemoveClass(typeof(T));
-        }
+    /// 
+    /// Unregisters a previously-registered sub-class of  with the subclassing controller.
+    /// 
+    /// 
+    /// 
+    public static void RemoveClass(this IServiceHub serviceHub) where T : ParseObject, new()
+    {
+        serviceHub.ClassController.RemoveClass(typeof(T));
+    }
 
-        /// 
-        /// Unregisters a previously-registered sub-class of  with the subclassing controller.
-        /// 
-        /// 
-        /// 
-        public static void RemoveClass(this IParseObjectClassController subclassingController, Type type)
+    /// 
+    /// Unregisters a previously-registered sub-class of  with the subclassing controller.
+    /// 
+    /// 
+    /// 
+    public static void RemoveClass(this IParseObjectClassController subclassingController, Type type)
+    {
+        if (typeof(ParseObject).IsAssignableFrom(type))
         {
-            if (typeof(ParseObject).IsAssignableFrom(type))
-            {
-                subclassingController.RemoveClass(type);
-            }
+            subclassingController.RemoveClass(type);
         }
+    }
+
+    /// 
+    /// Creates a new ParseObject based upon a class name. If the class name is a special type (e.g.
+    /// for ), then the appropriate type of ParseObject is returned.
+    /// 
+    /// The class of object to create.
+    /// A new ParseObject for the given class name.
+    public static ParseObject CreateObject(this IServiceHub serviceHub, string className)
+    {
+        return serviceHub.ClassController.Instantiate(className, serviceHub);
+    }
+
+    /// 
+    /// Creates a new ParseObject based upon a given subclass type.
+    /// 
+    /// A new ParseObject for the given class name.
+    public static T CreateObject(this IServiceHub serviceHub) where T : ParseObject
+    {
+        return (T) serviceHub.ClassController.CreateObject(serviceHub);
+    }
 
-        /// 
-        /// Creates a new ParseObject based upon a class name. If the class name is a special type (e.g.
-        /// for ), then the appropriate type of ParseObject is returned.
-        /// 
-        /// The class of object to create.
-        /// A new ParseObject for the given class name.
-        public static ParseObject CreateObject(this IServiceHub serviceHub, string className)
+    /// 
+    /// Creates a new ParseObject based upon a given subclass type.
+    /// 
+    /// A new ParseObject for the given class name.
+    public static T CreateObject(this IParseObjectClassController classController, IServiceHub serviceHub) where T : ParseObject
+    {
+        return (T) classController.Instantiate(classController.GetClassName(typeof(T)), serviceHub);
+    }
+
+    /// 
+    /// Creates a reference to an existing ParseObject for use in creating associations between
+    /// ParseObjects. Calling  on this object will return
+    /// false until  has been called.
+    /// No network request will be made.
+    /// 
+    /// The object's class.
+    /// The object id for the referenced object.
+    /// A ParseObject without data.
+    public static ParseObject CreateObjectWithoutData(this IServiceHub serviceHub, string className, string objectId)
+    {
+        return serviceHub.ClassController.CreateObjectWithoutData(className, objectId, serviceHub);
+    }
+
+    /// 
+    /// Creates a reference to an existing ParseObject for use in creating associations between
+    /// ParseObjects. Calling  on this object will return
+    /// false until  has been called.
+    /// No network request will be made.
+    /// 
+    /// The object's class.
+    /// The object id for the referenced object.
+    /// A ParseObject without data.
+    public static ParseObject CreateObjectWithoutData(this IParseObjectClassController classController, string className, string objectId, IServiceHub serviceHub)
+    {
+        ParseObject.CreatingPointer.Value = true;
+        try
         {
-            return serviceHub.ClassController.Instantiate(className, serviceHub);
-        }
+            ParseObject result = classController.Instantiate(className, serviceHub);
+            result.ObjectId = objectId;
+
+            // Left in because the property setter might be doing something funky.
 
-        /// 
-        /// Creates a new ParseObject based upon a given subclass type.
-        /// 
-        /// A new ParseObject for the given class name.
-        public static T CreateObject(this IServiceHub serviceHub) where T : ParseObject
+            result.IsDirty = false;
+            if (result.IsDirty)
+                throw new InvalidOperationException("A ParseObject subclass default constructor must not make changes to the object that cause it to be dirty.");
+            else
+                return  result;
+        }
+        finally
         {
-            return (T) serviceHub.ClassController.CreateObject(serviceHub);
+            ParseObject.CreatingPointer.Value = false;
         }
+    }
 
-        /// 
-        /// Creates a new ParseObject based upon a given subclass type.
-        /// 
-        /// A new ParseObject for the given class name.
-        public static T CreateObject(this IParseObjectClassController classController, IServiceHub serviceHub) where T : ParseObject
+    /// 
+    /// Creates a reference to an existing ParseObject for use in creating associations between
+    /// ParseObjects. Calling  on this object will return
+    /// false until  has been called.
+    /// No network request will be made.
+    /// 
+    /// The object id for the referenced object.
+    /// A ParseObject without data.
+    public static T CreateObjectWithoutData(this IServiceHub serviceHub, string objectId) where T : ParseObject
+    {
+        return (T) serviceHub.CreateObjectWithoutData(serviceHub.ClassController.GetClassName(typeof(T)), objectId);
+    }
+    /// 
+    /// Creates a reference to a new ParseObject with the specified initial data.
+    /// This can be used for creating new objects with predefined values.
+    /// No network request will be made until the object is saved.
+    /// 
+    /// A dictionary containing the initial key-value pairs for the object.
+    /// A new ParseObject with the specified initial data.
+    public static T CreateObjectWithData(this IServiceHub serviceHub, IDictionary initialData) where T : ParseObject
+    {
+        if (initialData == null)
         {
-            return (T) classController.Instantiate(classController.GetClassName(typeof(T)), serviceHub);
+            throw new ArgumentNullException(nameof(initialData), "Initial data cannot be null.");
         }
 
-        /// 
-        /// Creates a reference to an existing ParseObject for use in creating associations between
-        /// ParseObjects. Calling  on this object will return
-        /// false until  has been called.
-        /// No network request will be made.
-        /// 
-        /// The object's class.
-        /// The object id for the referenced object.
-        /// A ParseObject without data.
-        public static ParseObject CreateObjectWithoutData(this IServiceHub serviceHub, string className, string objectId)
+        // Create a new instance of the specified ParseObject type
+        var parseObject = (T) serviceHub.CreateObject(serviceHub.ClassController.GetClassName(typeof(T)));
+
+        // Set initial data properties
+        foreach (var kvp in initialData)
         {
-            return serviceHub.ClassController.CreateObjectWithoutData(className, objectId, serviceHub);
+            parseObject[kvp.Key] = kvp.Value;
         }
 
-        /// 
-        /// Creates a reference to an existing ParseObject for use in creating associations between
-        /// ParseObjects. Calling  on this object will return
-        /// false until  has been called.
-        /// No network request will be made.
-        /// 
-        /// The object's class.
-        /// The object id for the referenced object.
-        /// A ParseObject without data.
-        public static ParseObject CreateObjectWithoutData(this IParseObjectClassController classController, string className, string objectId, IServiceHub serviceHub)
+        return parseObject;
+    }
+
+    /// 
+    /// Deletes each object in the provided list.
+    /// 
+    /// The objects to delete.
+    public static Task DeleteObjectsAsync(this IServiceHub serviceHub, IEnumerable objects) where T : ParseObject
+    {
+        return DeleteObjectsAsync(serviceHub, objects, CancellationToken.None);
+    }
+
+    /// 
+    /// Deletes each object in the provided list.
+    /// 
+    /// The objects to delete.
+    /// The cancellation token.
+    public static async Task DeleteObjectsAsync(this IServiceHub serviceHub, IEnumerable objects, CancellationToken cancellationToken) where T : ParseObject
+    {
+        // Get a unique set of ParseObjects
+        var uniqueObjects = new HashSet(objects.OfType(), new IdentityEqualityComparer());
+
+        await EnqueueForAll(uniqueObjects, async toAwait =>
         {
-            ParseObject.CreatingPointer.Value = true;
-            try
-            {
-                ParseObject result = classController.Instantiate(className, serviceHub);
-                result.ObjectId = objectId;
+            // Wait for the preceding task (toAwait) to complete
+            await toAwait.ConfigureAwait(false);
 
-                // Left in because the property setter might be doing something funky.
+            // Perform the delete operation for all objects
+            await Task.WhenAll(
+                serviceHub.ObjectController.DeleteAllAsync(
+                    uniqueObjects.Select(obj => obj.State).ToList(),
+                    serviceHub.GetCurrentSessionToken(),
+                    cancellationToken)
+            ).ConfigureAwait(false);
 
-                result.IsDirty = false;
-                if (result.IsDirty)
-                    throw new InvalidOperationException("A ParseObject subclass default constructor must not make changes to the object that cause it to be dirty.");
-                else
-                    return  result;
-            }
-            finally
+            // Mark all objects as dirty
+            foreach (var obj in uniqueObjects)
             {
-                ParseObject.CreatingPointer.Value = false;
+                obj.IsDirty = true;
             }
-        }
 
-        /// 
-        /// Creates a reference to an existing ParseObject for use in creating associations between
-        /// ParseObjects. Calling  on this object will return
-        /// false until  has been called.
-        /// No network request will be made.
-        /// 
-        /// The object id for the referenced object.
-        /// A ParseObject without data.
-        public static T CreateObjectWithoutData(this IServiceHub serviceHub, string objectId) where T : ParseObject
-        {
-            return (T) serviceHub.CreateObjectWithoutData(serviceHub.ClassController.GetClassName(typeof(T)), objectId);
-        }
+            return true; // Return a meaningful result if needed
+        }, cancellationToken).ConfigureAwait(false);
+    }
 
-        /// 
-        /// Deletes each object in the provided list.
-        /// 
-        /// The objects to delete.
-        public static Task DeleteObjectsAsync(this IServiceHub serviceHub, IEnumerable objects) where T : ParseObject
-        {
-            return DeleteObjectsAsync(serviceHub, objects, CancellationToken.None);
-        }
 
-        /// 
-        /// Deletes each object in the provided list.
-        /// 
-        /// The objects to delete.
-        /// The cancellation token.
-        public static Task DeleteObjectsAsync(this IServiceHub serviceHub, IEnumerable objects, CancellationToken cancellationToken) where T : ParseObject
-        {
-            HashSet unique = new HashSet(objects.OfType().ToList(), new IdentityEqualityComparer { });
 
-            return EnqueueForAll(unique, toAwait => toAwait.OnSuccess(_ => Task.WhenAll(serviceHub.ObjectController.DeleteAllAsync(unique.Select(task => task.State).ToList(), serviceHub.GetCurrentSessionToken(), cancellationToken))).Unwrap().OnSuccess(task =>
-            {
-                // Dirty all objects in memory.
 
-                foreach (ParseObject obj in unique)
-                {
-                    obj.IsDirty = true;
-                }
 
-                return default(object);
-            }), cancellationToken);
-        }
+    /// 
+    /// Fetches all of the objects in the provided list.
+    /// 
+    /// The objects to fetch.
+    /// The list passed in for convenience.
+    public static Task> FetchObjectsAsync(this IServiceHub serviceHub, IEnumerable objects) where T : ParseObject
+    {
+        return FetchObjectsAsync(serviceHub, objects, CancellationToken.None);
+    }
 
-        /// 
-        /// Fetches all of the objects in the provided list.
-        /// 
-        /// The objects to fetch.
-        /// The list passed in for convenience.
-        public static Task> FetchObjectsAsync(this IServiceHub serviceHub, IEnumerable objects) where T : ParseObject
-        {
-            return FetchObjectsAsync(serviceHub, objects, CancellationToken.None);
-        }
+    /// 
+    /// Fetches all of the objects in the provided list.
+    /// 
+    /// The objects to fetch.
+    /// The cancellation token.
+    /// The list passed in for convenience.
+    public static Task> FetchObjectsAsync(this IServiceHub serviceHub, IEnumerable objects, CancellationToken cancellationToken) where T : ParseObject
+    {
+        return EnqueueForAll(objects.Cast(), (Task toAwait) => serviceHub.FetchAllInternalAsync(objects, true, toAwait, cancellationToken), cancellationToken);
+    }
 
-        /// 
-        /// Fetches all of the objects in the provided list.
-        /// 
-        /// The objects to fetch.
-        /// The cancellation token.
-        /// The list passed in for convenience.
-        public static Task> FetchObjectsAsync(this IServiceHub serviceHub, IEnumerable objects, CancellationToken cancellationToken) where T : ParseObject
-        {
-            return EnqueueForAll(objects.Cast(), (Task toAwait) => serviceHub.FetchAllInternalAsync(objects, true, toAwait, cancellationToken), cancellationToken);
-        }
+    /// 
+    /// Fetches all of the objects that don't have data in the provided list.
+    /// 
+    /// todo: describe objects parameter on FetchAllIfNeededAsync
+    /// The list passed in for convenience.
+    public static Task> FetchObjectsIfNeededAsync(this IServiceHub serviceHub, IEnumerable objects) where T : ParseObject
+    {
+        return FetchObjectsIfNeededAsync(serviceHub, objects, CancellationToken.None);
+    }
 
-        /// 
-        /// Fetches all of the objects that don't have data in the provided list.
-        /// 
-        /// todo: describe objects parameter on FetchAllIfNeededAsync
-        /// The list passed in for convenience.
-        public static Task> FetchObjectsIfNeededAsync(this IServiceHub serviceHub, IEnumerable objects) where T : ParseObject
-        {
-            return FetchObjectsIfNeededAsync(serviceHub, objects, CancellationToken.None);
-        }
+    /// 
+    /// Fetches all of the objects that don't have data in the provided list.
+    /// 
+    /// The objects to fetch.
+    /// The cancellation token.
+    /// The list passed in for convenience.
+    public static Task> FetchObjectsIfNeededAsync(this IServiceHub serviceHub, IEnumerable objects, CancellationToken cancellationToken) where T : ParseObject
+    {
+        return EnqueueForAll(objects.Cast(), (Task toAwait) => serviceHub.FetchAllInternalAsync(objects, false, toAwait, cancellationToken), cancellationToken);
+    }
 
-        /// 
-        /// Fetches all of the objects that don't have data in the provided list.
-        /// 
-        /// The objects to fetch.
-        /// The cancellation token.
-        /// The list passed in for convenience.
-        public static Task> FetchObjectsIfNeededAsync(this IServiceHub serviceHub, IEnumerable objects, CancellationToken cancellationToken) where T : ParseObject
+    /// 
+    /// Gets a  for the type of object specified by
+    /// 
+    /// 
+    /// The class name of the object.
+    /// A new .
+    public static ParseQuery GetQuery(this IServiceHub serviceHub, string className)
+    {
+        // Since we can't return a ParseQuery (due to strong-typing with
+        // generics), we'll require you to go through subclasses. This is a better
+        // experience anyway, especially with LINQ integration, since you'll get
+        // strongly-typed queries and compile-time checking of property names and
+        // types.
+
+        if (serviceHub.ClassController.GetType(className) is { })
         {
-            return EnqueueForAll(objects.Cast(), (Task toAwait) => serviceHub.FetchAllInternalAsync(objects, false, toAwait, cancellationToken), cancellationToken);
+            throw new ArgumentException($"Use the class-specific query properties for class {className}", nameof(className));
         }
+        return new ParseQuery(serviceHub, className);
+    }
 
-        /// 
-        /// Gets a  for the type of object specified by
-        /// 
-        /// 
-        /// The class name of the object.
-        /// A new .
-        public static ParseQuery GetQuery(this IServiceHub serviceHub, string className)
-        {
-            // Since we can't return a ParseQuery (due to strong-typing with
-            // generics), we'll require you to go through subclasses. This is a better
-            // experience anyway, especially with LINQ integration, since you'll get
-            // strongly-typed queries and compile-time checking of property names and
-            // types.
+    /// 
+    /// Saves each object in the provided list.
+    /// 
+    /// The objects to save.
+    public static Task SaveObjectsAsync(this IServiceHub serviceHub, IEnumerable objects) where T : ParseObject
+    {
+        return SaveObjectsAsync(serviceHub, objects, CancellationToken.None);
+    }
 
-            if (serviceHub.ClassController.GetType(className) is { })
-            {
-                throw new ArgumentException($"Use the class-specific query properties for class {className}", nameof(className));
-            }
-            return new ParseQuery(serviceHub, className);
-        }
+    /// 
+    /// Saves each object in the provided list.
+    /// 
+    /// The objects to save.
+    /// The cancellation token.
+    public static Task SaveObjectsAsync(this IServiceHub serviceHub, IEnumerable objects, CancellationToken cancellationToken) where T : ParseObject
+    {
+        return DeepSaveAsync(serviceHub, objects.ToList(), serviceHub.GetCurrentSessionToken(), cancellationToken);
+    }
 
-        /// 
-        /// Saves each object in the provided list.
-        /// 
-        /// The objects to save.
-        public static Task SaveObjectsAsync(this IServiceHub serviceHub, IEnumerable objects) where T : ParseObject
-        {
-            return SaveObjectsAsync(serviceHub, objects, CancellationToken.None);
-        }
+    /// 
+    /// Flattens dictionaries and lists into a single enumerable of all contained objects
+    /// that can then be queried over.
+    /// 
+    /// The root of the traversal
+    /// Whether to traverse into ParseObjects' children
+    /// Whether to include the root in the result
+    /// 
+    internal static IEnumerable TraverseObjectDeep(this IServiceHub serviceHub, object root, bool traverseParseObjects = false, bool yieldRoot = false)
+    {
+        IEnumerable items = DeepTraversalInternal(serviceHub, root, traverseParseObjects, new HashSet(new IdentityEqualityComparer()));
+        return yieldRoot ? new[] { root }.Concat(items) : items;
+    }
 
-        /// 
-        /// Saves each object in the provided list.
-        /// 
-        /// The objects to save.
-        /// The cancellation token.
-        public static Task SaveObjectsAsync(this IServiceHub serviceHub, IEnumerable objects, CancellationToken cancellationToken) where T : ParseObject
-        {
-            return DeepSaveAsync(serviceHub, objects.ToList(), serviceHub.GetCurrentSessionToken(), cancellationToken);
-        }
+    // TODO (hallucinogen): add unit test
+    internal static T GenerateObjectFromState(this IServiceHub serviceHub, IObjectState state, string defaultClassName) where T : ParseObject
+    {
+        var obj = serviceHub.ClassController.GenerateObjectFromState(state, defaultClassName, serviceHub);
+        return obj;
+    }
 
-        /// 
-        /// Flattens dictionaries and lists into a single enumerable of all contained objects
-        /// that can then be queried over.
-        /// 
-        /// The root of the traversal
-        /// Whether to traverse into ParseObjects' children
-        /// Whether to include the root in the result
-        /// 
-        internal static IEnumerable TraverseObjectDeep(this IServiceHub serviceHub, object root, bool traverseParseObjects = false, bool yieldRoot = false)
+    internal static T GenerateObjectFromState(
+this IParseObjectClassController classController,
+IObjectState state,
+string defaultClassName,
+IServiceHub serviceHub
+) where T : ParseObject
+    {
+        if (state == null)
         {
-            IEnumerable items = DeepTraversalInternal(serviceHub, root, traverseParseObjects, new HashSet(new IdentityEqualityComparer()));
-            return yieldRoot ? new[] { root }.Concat(items) : items;
+            throw new ArgumentNullException(nameof(state), "The state cannot be null.");
         }
 
-        // TODO (hallucinogen): add unit test
-        internal static T GenerateObjectFromState(this IServiceHub serviceHub, IObjectState state, string defaultClassName) where T : ParseObject
+        if (string.IsNullOrEmpty(state.ClassName) && string.IsNullOrEmpty(defaultClassName))
         {
-            var obj = serviceHub.ClassController.GenerateObjectFromState(state, defaultClassName, serviceHub);
-            return obj;
+            throw new InvalidOperationException("Both state.ClassName and defaultClassName are null or empty. Unable to determine class name.");
         }
 
-        internal static T GenerateObjectFromState(
-    this IParseObjectClassController classController,
-    IObjectState state,
-    string defaultClassName,
-    IServiceHub serviceHub
-) where T : ParseObject
-        {
-            if (state == null)
-            {
-                throw new ArgumentNullException(nameof(state), "The state cannot be null.");
-            }
+        // Use the provided class name from the state, or fall back to the default class name
+        string className = state.ClassName ?? defaultClassName;
+        state.ClassName = className;    //to make it so that user cl
+        var obj = (T) ParseClient.Instance.CreateObject(className);
+        
+        
+        obj.HandleFetchResult(state);
 
-            if (string.IsNullOrEmpty(state.ClassName) && string.IsNullOrEmpty(defaultClassName))
-            {
-                throw new InvalidOperationException("Both state.ClassName and defaultClassName are null or empty. Unable to determine class name.");
-            }
+        return obj;
+    }
 
-            // Use the provided class name from the state, or fall back to the default class name
-            string className = state.ClassName ?? defaultClassName;
-            state.ClassName = className;    //to make it so that user cl
-            var obj = (T) ParseClient.Instance.CreateObject(className);
-            
-            
-            obj.HandleFetchResult(state);
 
-            return obj;
+    internal static IDictionary GenerateJSONObjectForSaving(this IServiceHub serviceHub, IDictionary operations)
+    {
+        Dictionary result = new Dictionary();
+
+        foreach (KeyValuePair pair in operations)
+        {
+            result[pair.Key] = PointerOrLocalIdEncoder.Instance.Encode(pair.Value, serviceHub);
         }
 
+        return result;
+    }
 
-        internal static IDictionary GenerateJSONObjectForSaving(this IServiceHub serviceHub, IDictionary operations)
+    /// 
+    /// Returns true if the given object can be serialized for saving as a value
+    /// that is pointed to by a ParseObject.
+    /// 
+    internal static bool CanBeSerializedAsValue(this IServiceHub serviceHub, object value)
+    {
+        return TraverseObjectDeep(serviceHub, value, yieldRoot: true).OfType().All(entity => entity.ObjectId is { });
+    }
+
+    static void CollectDirtyChildren(this IServiceHub serviceHub, object node, IList dirtyChildren, ICollection seen, ICollection seenNew)
+    {
+        foreach (ParseObject target in TraverseObjectDeep(serviceHub, node).OfType())
         {
-            Dictionary result = new Dictionary();
+            ICollection scopedSeenNew;
 
-            foreach (KeyValuePair pair in operations)
+            // Check for cycles of new objects. Any such cycle means it will be impossible to save
+            // this collection of objects, so throw an exception.
+
+            if (target.ObjectId != null)
             {
-                result[pair.Key] = PointerOrLocalIdEncoder.Instance.Encode(pair.Value, serviceHub);
+                scopedSeenNew = new HashSet(new IdentityEqualityComparer());
             }
+            else
+            {
+                if (seenNew.Contains(target))
+                {
+                    throw new InvalidOperationException("Found a circular dependency while saving");
+                }
 
-            return result;
-        }
+                scopedSeenNew = new HashSet(seenNew, new IdentityEqualityComparer()) { target };
+            }
 
-        /// 
-        /// Returns true if the given object can be serialized for saving as a value
-        /// that is pointed to by a ParseObject.
-        /// 
-        internal static bool CanBeSerializedAsValue(this IServiceHub serviceHub, object value)
-        {
-            return TraverseObjectDeep(serviceHub, value, yieldRoot: true).OfType().All(entity => entity.ObjectId is { });
-        }
+            // Check for cycles of any object. If this occurs, then there's no problem, but
+            // we shouldn't recurse any deeper, because it would be an infinite recursion.
 
-        static void CollectDirtyChildren(this IServiceHub serviceHub, object node, IList dirtyChildren, ICollection seen, ICollection seenNew)
-        {
-            foreach (ParseObject target in TraverseObjectDeep(serviceHub, node).OfType())
+            if (seen.Contains(target))
             {
-                ICollection scopedSeenNew;
+                return;
+            }
 
-                // Check for cycles of new objects. Any such cycle means it will be impossible to save
-                // this collection of objects, so throw an exception.
+            seen.Add(target);
 
-                if (target.ObjectId != null)
-                {
-                    scopedSeenNew = new HashSet(new IdentityEqualityComparer());
-                }
-                else
-                {
-                    if (seenNew.Contains(target))
-                    {
-                        throw new InvalidOperationException("Found a circular dependency while saving");
-                    }
+            // Recurse into this object's children looking for dirty children.
+            // We only need to look at the child object's current estimated data,
+            // because that's the only data that might need to be saved now.
 
-                    scopedSeenNew = new HashSet(seenNew, new IdentityEqualityComparer()) { target };
-                }
+            CollectDirtyChildren(serviceHub, target.EstimatedData, dirtyChildren, seen, scopedSeenNew);
 
-                // Check for cycles of any object. If this occurs, then there's no problem, but
-                // we shouldn't recurse any deeper, because it would be an infinite recursion.
+            if (target.CheckIsDirty(false))
+            {
+                dirtyChildren.Add(target);
+            }
+        }
+    }
 
-                if (seen.Contains(target))
-                {
-                    return;
-                }
+    /// 
+    /// Helper version of CollectDirtyChildren so that callers don't have to add the internally
+    /// used parameters.
+    /// 
+    static void CollectDirtyChildren(this IServiceHub serviceHub, object node, IList dirtyChildren)
+    {
+        CollectDirtyChildren(serviceHub, node, dirtyChildren, new HashSet(new IdentityEqualityComparer()), new HashSet(new IdentityEqualityComparer()));
+    }
 
-                seen.Add(target);
+    internal static async Task DeepSaveAsync(this IServiceHub serviceHub, object target, string sessionToken, CancellationToken cancellationToken)
+    {
+        // Collect dirty objects
+        var objects = new List();
+        CollectDirtyChildren(serviceHub, target, objects);
 
-                // Recurse into this object's children looking for dirty children.
-                // We only need to look at the child object's current estimated data,
-                // because that's the only data that might need to be saved now.
+        var uniqueObjects = new HashSet(objects, new IdentityEqualityComparer());
 
-                CollectDirtyChildren(serviceHub, target.EstimatedData, dirtyChildren, seen, scopedSeenNew);
+        // Save all dirty files
+        var saveDirtyFileTasks = TraverseObjectDeep(serviceHub, target, true)
+            .OfType()
+            .Where(file => file.IsDirty)
+            .Select(file => file.SaveAsync(serviceHub, cancellationToken))
+            .ToList();
 
-                if (target.CheckIsDirty(false))
-                {
-                    dirtyChildren.Add(target);
-                }
-            }
-        }
+        await Task.WhenAll(saveDirtyFileTasks).ConfigureAwait(false);
 
-        /// 
-        /// Helper version of CollectDirtyChildren so that callers don't have to add the internally
-        /// used parameters.
-        /// 
-        static void CollectDirtyChildren(this IServiceHub serviceHub, object node, IList dirtyChildren)
+        // Save remaining objects in batches
+        var remaining = new List(uniqueObjects);
+        while (remaining.Any())
         {
-            CollectDirtyChildren(serviceHub, node, dirtyChildren, new HashSet(new IdentityEqualityComparer()), new HashSet(new IdentityEqualityComparer()));
-        }
+            // Partition objects into those that can be saved immediately and those that cannot
+            var current = remaining.Where(item => item.CanBeSerialized).ToList();
+            var nextBatch = remaining.Where(item => !item.CanBeSerialized).ToList();
+            remaining = nextBatch;
 
-        internal static Task DeepSaveAsync(this IServiceHub serviceHub, object target, string sessionToken, CancellationToken cancellationToken)
-        {
-            List objects = new List();
-            CollectDirtyChildren(serviceHub, target, objects);
+            if (!current.Any())
+            {
+                throw new InvalidOperationException("Unable to save a ParseObject with a relation to a cycle.");
+            }
 
-            HashSet uniqueObjects = new HashSet(objects, new IdentityEqualityComparer());
-            List saveDirtyFileTasks = TraverseObjectDeep(serviceHub, target, true).OfType().Where(file => file.IsDirty).Select(file => file.SaveAsync(serviceHub, cancellationToken)).ToList();
+            // Save all objects in the current batch
+            var states = current.Select(item => item.State).ToList();
+            var operationsList = current.Select(item => item.StartSave()).ToList();
 
-            return Task.WhenAll(saveDirtyFileTasks).OnSuccess(_ =>
+            try
             {
-                IEnumerable remaining = new List(uniqueObjects);
-                return InternalExtensions.WhileAsync(() => Task.FromResult(remaining.Any()), () =>
-                {
-                    // Partition the objects into two sets: those that can be saved immediately,
-                    // and those that rely on other objects to be created first.
+                // Await SaveAllAsync to get the collection of Task
+                var saveTasks = await serviceHub.ObjectController.SaveAllAsync(states, operationsList, sessionToken, serviceHub, cancellationToken).ConfigureAwait(false);
 
-                    List current = (from item in remaining where item.CanBeSerialized select item).ToList(), nextBatch = (from item in remaining where !item.CanBeSerialized select item).ToList();
-                    remaining = nextBatch;
-
-                    if (current.Count == 0)
-                    {
-                        // We do cycle-detection when building the list of objects passed to this
-                        // function, so this should never get called. But we should check for it
-                        // anyway, so that we get an exception instead of an infinite loop.
+                // Await individual tasks in the result
+                foreach (var (item, stateTask) in current.Zip(saveTasks, (item, stateTask) => (item, stateTask)))
+                {
+                    var state = await stateTask.ConfigureAwait(false); // Await the Task
+                    item.HandleSave(state); // Now state is IObjectState
+                }
+            }
+            catch (Exception ex) when (ex is TaskCanceledException || ex is OperationCanceledException)
+            {
+                foreach (var (item, ops) in current.Zip(operationsList, (item, ops) => (item, ops)))
+                {
+                    item.HandleFailedSave(ops);
+                }
 
-                        throw new InvalidOperationException("Unable to save a ParseObject with a relation to a cycle.");
-                    }
+                throw; // Re-throw cancellation exceptions
+            }
+        }
+    }
 
-                    // Save all of the objects in current.
+    static IEnumerable DeepTraversalInternal(this IServiceHub serviceHub, object root, bool traverseParseObjects, ICollection seen)
+    {
+        seen.Add(root);
+        System.Collections.IEnumerable targets = ParseClient.IL2CPPCompiled ? null : null as IEnumerable;
 
-                    return EnqueueForAll(current, toAwait => toAwait.OnSuccess(__ =>
-                    {
-                        List states = (from item in current select item.State).ToList();
-                        List> operationsList = (from item in current select item.StartSave()).ToList();
-
-                        IList> saveTasks = serviceHub.ObjectController.SaveAllAsync(states, operationsList, sessionToken, serviceHub, cancellationToken);
-
-                        return Task.WhenAll(saveTasks).ContinueWith(task =>
-                        {
-                            if (task.IsFaulted || task.IsCanceled)
-                            {
-                                foreach ((ParseObject item, IDictionary ops) pair in current.Zip(operationsList, (item, ops) => (item, ops)))
-                                {
-                                    pair.item.HandleFailedSave(pair.ops);
-                                }
-                            }
-                            else
-                            {
-                                foreach ((ParseObject item, IObjectState state) pair in current.Zip(task.Result, (item, state) => (item, state)))
-                                {
-                                    pair.item.HandleSave(pair.state);
-                                }
-                            }
-
-                            cancellationToken.ThrowIfCancellationRequested();
-                            return task;
-                        }).Unwrap();
-                    }).Unwrap().OnSuccess(t => (object) null), cancellationToken);
-                });
-            }).Unwrap();
+        if (Conversion.As>(root) is { } rootDictionary)
+        {
+            targets = rootDictionary.Values;
         }
-
-        static IEnumerable DeepTraversalInternal(this IServiceHub serviceHub, object root, bool traverseParseObjects, ICollection seen)
+        else
         {
-            seen.Add(root);
-            System.Collections.IEnumerable targets = ParseClient.IL2CPPCompiled ? null : null as IEnumerable;
-
-            if (Conversion.As>(root) is { } rootDictionary)
+            if (Conversion.As>(root) is { } rootList)
             {
-                targets = rootDictionary.Values;
+                targets = rootList;
             }
-            else
+            else if (traverseParseObjects)
             {
-                if (Conversion.As>(root) is { } rootList)
+                if (root is ParseObject entity)
                 {
-                    targets = rootList;
-                }
-                else if (traverseParseObjects)
-                {
-                    if (root is ParseObject entity)
-                    {
-                        targets = entity.Keys.ToList().Select(key => entity[key]);
-                    }
+                    targets = entity.Keys.ToList().Select(key => entity[key]);
                 }
             }
+        }
 
-            if (targets is { })
+        if (targets is { })
+        {
+            foreach (object item in targets)
             {
-                foreach (object item in targets)
+                if (!seen.Contains(item))
                 {
-                    if (!seen.Contains(item))
-                    {
-                        yield return item;
+                    yield return item;
 
-                        foreach (object child in DeepTraversalInternal(serviceHub, item, traverseParseObjects, seen))
-                        {
-                            yield return child;
-                        }
+                    foreach (object child in DeepTraversalInternal(serviceHub, item, traverseParseObjects, seen))
+                    {
+                        yield return child;
                     }
                 }
             }
         }
+    }
 
-        /// 
-        /// Adds a task to the queue for all of the given objects.
-        /// 
-        static Task EnqueueForAll(IEnumerable objects, Func> taskStart, CancellationToken cancellationToken)
-        {
-            // The task that will be complete when all of the child queues indicate they're ready to start.
-
-            TaskCompletionSource readyToStart = new TaskCompletionSource();
+    /// 
+    /// Adds a task to the queue for all of the given objects.
+    /// 
+    static Task EnqueueForAll(IEnumerable objects, Func> taskStart, CancellationToken cancellationToken)
+    {
+        // The task that will be complete when all of the child queues indicate they're ready to start.
 
-            // First, we need to lock the mutex for the queue for every object. We have to hold this
-            // from at least when taskStart() is called to when obj.taskQueue enqueue is called, so
-            // that saves actually get executed in the order they were setup by taskStart().
-            // The locks have to be sorted so that we always acquire them in the same order.
-            // Otherwise, there's some risk of deadlock.
+        TaskCompletionSource readyToStart = new TaskCompletionSource();
 
-            LockSet lockSet = new LockSet(objects.Select(o => o.TaskQueue.Mutex));
+        // First, we need to lock the mutex for the queue for every object. We have to hold this
+        // from at least when taskStart() is called to when obj.taskQueue enqueue is called, so
+        // that saves actually get executed in the order they were setup by taskStart().
+        // The locks have to be sorted so that we always acquire them in the same order.
+        // Otherwise, there's some risk of deadlock.
 
-            lockSet.Enter();
-            try
-            {
-                // The task produced by taskStart. By running this immediately, we allow everything prior
-                // to toAwait to run before waiting for all of the queues on all of the objects.
+        LockSet lockSet = new LockSet(objects.Select(o => o.TaskQueue.Mutex));
 
-                Task fullTask = taskStart(readyToStart.Task);
+        lockSet.Enter();
+        try
+        {
+            // The task produced by taskStart. By running this immediately, we allow everything prior
+            // to toAwait to run before waiting for all of the queues on all of the objects.
 
-                // Add fullTask to each of the objects' queues.
+            Task fullTask = taskStart(readyToStart.Task);
 
-                List childTasks = new List();
-                foreach (ParseObject obj in objects)
-                {
-                    obj.TaskQueue.Enqueue((Task task) =>
-                    {
-                        childTasks.Add(task);
-                        return fullTask;
-                    }, cancellationToken);
-                }
+            // Add fullTask to each of the objects' queues.
 
-                // When all of the objects' queues are ready, signal fullTask that it's ready to go on.
-                Task.WhenAll(childTasks.ToArray()).ContinueWith((Task task) => readyToStart.SetResult(default));
-                return fullTask;
-            }
-            finally
+            List childTasks = new List();
+            foreach (ParseObject obj in objects)
             {
-                lockSet.Exit();
+                obj.TaskQueue.Enqueue((Task task) =>
+                {
+                    childTasks.Add(task);
+                    return fullTask;
+                }, cancellationToken);
             }
-        }
 
-        /// 
-        /// Fetches all of the objects in the list.
-        /// 
-        /// The objects to fetch.
-        /// If false, only objects without data will be fetched.
-        /// A task to await before starting.
-        /// The cancellation token.
-        /// The list passed in for convenience.
-        static Task> FetchAllInternalAsync(this IServiceHub serviceHub, IEnumerable objects, bool force, Task toAwait, CancellationToken cancellationToken) where T : ParseObject
+            // When all of the objects' queues are ready, signal fullTask that it's ready to go on.
+            Task.WhenAll(childTasks.ToArray()).ContinueWith((Task task) => readyToStart.SetResult(default));
+            return fullTask;
+        }
+        finally
         {
-            return toAwait.OnSuccess(_ =>
+            lockSet.Exit();
+        }
+    }
+
+    /// 
+    /// Fetches all of the objects in the list.
+    /// 
+    /// The objects to fetch.
+    /// If false, only objects without data will be fetched.
+    /// A task to await before starting.
+    /// The cancellation token.
+    /// The list passed in for convenience.
+    static async Task> FetchAllInternalAsync(this IServiceHub serviceHub, IEnumerable objects, bool force, Task toAwait, CancellationToken cancellationToken) where T : ParseObject
+    {
+        // Wait for the preceding task (toAwait) to complete
+        await toAwait.ConfigureAwait(false);
+
+        // Ensure all objects have an ObjectId
+        if (objects.Any(obj => obj.State.ObjectId == null))
         {
-            if (objects.Any(obj => obj.State.ObjectId == null))
-            {
-                throw new InvalidOperationException("You cannot fetch objects that haven't already been saved.");
-            }
+            throw new InvalidOperationException("You cannot fetch objects that haven't already been saved.");
+        }
 
-            List objectsToFetch = (from obj in objects where force || !obj.IsDataAvailable select obj).ToList();
+        // Filter objects to fetch based on the force flag and data availability
+        var objectsToFetch = objects.Where(obj => force || !obj.IsDataAvailable).ToList();
 
-            if (objectsToFetch.Count == 0)
-            {
-                return Task.FromResult(objects);
-            }
+        if (objectsToFetch.Count == 0)
+        {
+            return objects; // No objects need to be fetched
+        }
 
-            // Do one Find for each class.
+        // Group objects by ClassName and prepare queries
+        var findsByClass = objectsToFetch
+            .GroupBy(obj => obj.ClassName)
+            .Where(group => group.Any())
+            .ToDictionary(
+                group => group.Key,
+                group => new ParseQuery(serviceHub, group.Key)
+                            .WhereContainedIn("objectId", group.Select(obj => obj.State.ObjectId))
+                            .FindAsync(cancellationToken)
+            );
 
-            Dictionary>> findsByClass = (from obj in objectsToFetch group obj.ObjectId by obj.ClassName into classGroup where classGroup.Count() > 0 select (ClassName: classGroup.Key, FindTask: new ParseQuery(serviceHub, classGroup.Key).WhereContainedIn("objectId", classGroup).FindAsync(cancellationToken))).ToDictionary(pair => pair.ClassName, pair => pair.FindTask);
+        // Execute all queries in parallel
+        var findResults = await Task.WhenAll(findsByClass.Values).ConfigureAwait(false);
 
-            // Wait for all the Finds to complete.
+        // If the operation was canceled, return the original list
+        if (cancellationToken.IsCancellationRequested)
+        {
+            return objects;
+        }
 
-            return Task.WhenAll(findsByClass.Values.ToList()).OnSuccess(__ =>
+        // Merge fetched data into the original objects
+        foreach (var obj in objectsToFetch)
+        {
+            if (findsByClass.TryGetValue(obj.ClassName, out var resultsTask))
             {
-                if (cancellationToken.IsCancellationRequested)
-                {
-                    return objects;
-                }
+                var results = await resultsTask.ConfigureAwait(false);
+                var match = results.FirstOrDefault(result => result.ObjectId == obj.ObjectId);
 
-                // Merge the data from the Finds into the input objects.
-                foreach ((T obj, ParseObject result) in from obj in objectsToFetch from result in findsByClass[obj.ClassName].Result where result.ObjectId == obj.ObjectId select (obj, result))
+                if (match != null)
                 {
-                    obj.MergeFromObject(result);
+                    obj.MergeFromObject(match);
                     obj.Fetched = true;
                 }
-
-                return objects;
-            });
-        }).Unwrap();
+            }
         }
 
-        internal static string GetFieldForPropertyName(this IServiceHub serviceHub, string className, string propertyName)
-        {
-            if (serviceHub == null)
-            {
-                Debug.WriteLine("ServiceHub is null.");
-                return null;
-            }
+        return objects;
+    }
 
-            if (string.IsNullOrEmpty(className))
-            {
-                throw new ArgumentException("ClassName cannot be null or empty.", nameof(className));
-            }
 
-            if (string.IsNullOrEmpty(propertyName))
-            {
-                throw new ArgumentException("PropertyName cannot be null or empty.", nameof(propertyName));
-            }
+    internal static string GetFieldForPropertyName(this IServiceHub serviceHub, string className, string propertyName)
+    {
+        if (serviceHub == null)
+        {
+            Debug.WriteLine("ServiceHub is null.");
+            return null;
+        }
 
-            var classController = serviceHub.ClassController;
-            if (classController == null)
-            {
-                throw new InvalidOperationException("ClassController is null.");
-            }
+        if (string.IsNullOrEmpty(className))
+        {
+            throw new ArgumentException("ClassName cannot be null or empty.", nameof(className));
+        }
 
-            var propertyMappings = classController.GetPropertyMappings(className);
-            if (propertyMappings == null)
-            {
-                throw new InvalidOperationException($"Property mappings for class '{className}' are null.");
-            }
+        if (string.IsNullOrEmpty(propertyName))
+        {
+            throw new ArgumentException("PropertyName cannot be null or empty.", nameof(propertyName));
+        }
 
-            if (!propertyMappings.TryGetValue(propertyName, out string fieldName))
-            {
-                throw new KeyNotFoundException($"Property '{propertyName}' not found in class '{className}'.");
-            }
+        var classController = serviceHub.ClassController;
+        if (classController == null)
+        {
+            throw new InvalidOperationException("ClassController is null.");
+        }
 
-            return fieldName;
+        var propertyMappings = classController.GetPropertyMappings(className);
+        if (propertyMappings == null)
+        {
+            throw new InvalidOperationException($"Property mappings for class '{className}' are null.");
         }
 
+        if (!propertyMappings.TryGetValue(propertyName, out string fieldName))
+        {
+            throw new KeyNotFoundException($"Property '{propertyName}' not found in class '{className}'.");
+        }
+
+        return fieldName;
     }
+
 }
diff --git a/Parse/Utilities/ParseExtensions.cs b/Parse/Utilities/ParseExtensions.cs
index f85ffc31..94f379c7 100644
--- a/Parse/Utilities/ParseExtensions.cs
+++ b/Parse/Utilities/ParseExtensions.cs
@@ -2,51 +2,38 @@
 using System.Threading.Tasks;
 using Parse.Infrastructure.Utilities;
 
-namespace Parse
+namespace Parse;
+
+/// 
+/// Provides convenience extension methods for working with collections
+/// of ParseObjects so that you can easily save and fetch them in batches.
+/// 
+/// 
+/// Provides convenience extension methods for working with collections
+/// of ParseObjects so that you can easily save and fetch them in batches.
+/// 
+public static class ParseExtensions
 {
     /// 
-    /// Provides convenience extension methods for working with collections
-    /// of ParseObjects so that you can easily save and fetch them in batches.
+    /// Fetches this object with the data from the server.
     /// 
-    public static class ParseExtensions
+    /// The ParseObject to fetch.
+    /// The cancellation token (optional).
+    public static async Task FetchAsync(this T obj, CancellationToken cancellationToken = default) where T : ParseObject
     {
-        /// 
-        /// Fetches this object with the data from the server.
-        /// 
-        public static Task FetchAsync(this T obj) where T : ParseObject
-        {
-            return obj.FetchAsyncInternal(CancellationToken.None).OnSuccess(t => (T) t.Result);
-        }
-
-        /// 
-        /// Fetches this object with the data from the server.
-        /// 
-        /// The ParseObject to fetch.
-        /// The cancellation token.
-        public static Task FetchAsync(this T target, CancellationToken cancellationToken) where T : ParseObject
-        {
-            return target.FetchAsyncInternal(cancellationToken).OnSuccess(task => (T) task.Result);
-        }
-
-        /// 
-        /// If this ParseObject has not been fetched (i.e.  returns
-        /// false), fetches this object with the data from the server.
-        /// 
-        /// The ParseObject to fetch.
-        public static Task FetchIfNeededAsync(this T obj) where T : ParseObject
-        {
-            return obj.FetchIfNeededAsyncInternal(CancellationToken.None).OnSuccess(t => (T) t.Result);
-        }
+        var result = await obj.FetchAsyncInternal(cancellationToken).ConfigureAwait(false);
+        return (T) result;
+    }
 
-        /// 
-        /// If this ParseObject has not been fetched (i.e.  returns
-        /// false), fetches this object with the data from the server.
-        /// 
-        /// The ParseObject to fetch.
-        /// The cancellation token.
-        public static Task FetchIfNeededAsync(this T obj, CancellationToken cancellationToken) where T : ParseObject
-        {
-            return obj.FetchIfNeededAsyncInternal(cancellationToken).OnSuccess(t => (T) t.Result);
-        }
+    /// 
+    /// If this ParseObject has not been fetched (i.e.  returns
+    /// false), fetches this object with the data from the server.
+    /// 
+    /// The ParseObject to fetch.
+    /// The cancellation token (optional).
+    public static async Task FetchIfNeededAsync(this T obj, CancellationToken cancellationToken = default) where T : ParseObject
+    {
+        var result = await obj.FetchIfNeededAsyncInternal(cancellationToken).ConfigureAwait(false);
+        return (T) result;
     }
 }
diff --git a/Parse/Utilities/SessionsServiceExtensions.cs b/Parse/Utilities/SessionsServiceExtensions.cs
index c28d2851..90f82eaf 100644
--- a/Parse/Utilities/SessionsServiceExtensions.cs
+++ b/Parse/Utilities/SessionsServiceExtensions.cs
@@ -3,48 +3,68 @@
 using Parse.Abstractions.Infrastructure;
 using Parse.Infrastructure.Utilities;
 
-namespace Parse
+namespace Parse;
+
+public static class SessionsServiceExtensions
 {
-    public static class SessionsServiceExtensions
+    /// 
+    /// Constructs a  for ParseSession.
+    /// 
+    public static ParseQuery GetSessionQuery(this IServiceHub serviceHub)
     {
-        /// 
-        /// Constructs a  for ParseSession.
-        /// 
-        public static ParseQuery GetSessionQuery(this IServiceHub serviceHub)
-        {
-            return serviceHub.GetQuery();
-        }
+        return serviceHub.GetQuery();
+    }
 
-        /// 
-        /// Gets the current  object related to the current user.
-        /// 
-        public static Task GetCurrentSessionAsync(this IServiceHub serviceHub)
-        {
-            return GetCurrentSessionAsync(serviceHub, CancellationToken.None);
-        }
+    /// 
+    /// Gets the current  object related to the current user.
+    /// 
+    public static Task GetCurrentSessionAsync(this IServiceHub serviceHub)
+    {
+        return GetCurrentSessionAsync(serviceHub, CancellationToken.None);
+    }
 
-        /// 
-        /// Gets the current  object related to the current user.
-        /// 
-        /// The cancellation token
-        public static Task GetCurrentSessionAsync(this IServiceHub serviceHub, CancellationToken cancellationToken)
-        {
-            return serviceHub.GetCurrentUserAsync().OnSuccess(task => task.Result switch
-        {
-            null => Task.FromResult(default),
-            { SessionToken: null } => Task.FromResult(default),
-            { SessionToken: { } sessionToken } => serviceHub.SessionController.GetSessionAsync(sessionToken, serviceHub, cancellationToken).OnSuccess(successTask => serviceHub.GenerateObjectFromState(successTask.Result, "_Session"))
-        }).Unwrap();
-        }
+    /// 
+    /// Gets the current  object related to the current user.
+    /// 
+    /// The cancellation token
+    public static async Task GetCurrentSessionAsync(this IServiceHub serviceHub, CancellationToken cancellationToken)
+    {
+        var currentUser = await serviceHub.GetCurrentUserAsync().ConfigureAwait(false);
 
-        public static Task RevokeSessionAsync(this IServiceHub serviceHub, string sessionToken, CancellationToken cancellationToken)
+        if (currentUser == null || currentUser.SessionToken == null)
         {
-            return sessionToken is null || !serviceHub.SessionController.IsRevocableSessionToken(sessionToken) ? Task.CompletedTask : serviceHub.SessionController.RevokeAsync(sessionToken, cancellationToken);
+            // Return null if there is no current user or session token
+            return null;
         }
 
-        public static Task UpgradeToRevocableSessionAsync(this IServiceHub serviceHub, string sessionToken, CancellationToken cancellationToken)
+        // Fetch the session using the session token
+        var sessionState = await serviceHub.SessionController
+            .GetSessionAsync(currentUser.SessionToken, serviceHub, cancellationToken)
+            .ConfigureAwait(false);
+
+        // Generate and return the ParseSession object
+        return serviceHub.GenerateObjectFromState(sessionState, "_Session");
+    }
+
+
+    public static Task RevokeSessionAsync(this IServiceHub serviceHub, string sessionToken, CancellationToken cancellationToken)
+    {
+        return sessionToken is null || !serviceHub.SessionController.IsRevocableSessionToken(sessionToken) ? Task.CompletedTask : serviceHub.SessionController.RevokeAsync(sessionToken, cancellationToken);
+    }
+
+    public static async Task UpgradeToRevocableSessionAsync(this IServiceHub serviceHub, string sessionToken, CancellationToken cancellationToken)
+    {
+        if (sessionToken is null || serviceHub.SessionController.IsRevocableSessionToken(sessionToken))
         {
-            return sessionToken is null || serviceHub.SessionController.IsRevocableSessionToken(sessionToken) ? Task.FromResult(sessionToken) : serviceHub.SessionController.UpgradeToRevocableSessionAsync(sessionToken, serviceHub, cancellationToken).OnSuccess(task => serviceHub.GenerateObjectFromState(task.Result, "_Session").SessionToken);
+            return sessionToken;
         }
+
+        // Perform the upgrade asynchronously
+        var upgradeResult = await serviceHub.SessionController.UpgradeToRevocableSessionAsync(sessionToken, serviceHub, cancellationToken).ConfigureAwait(false);
+
+        // Generate the session object from the result and return the session token
+        var session = serviceHub.GenerateObjectFromState(upgradeResult, "_Session");
+        return session.SessionToken;
     }
+
 }
diff --git a/Parse/Utilities/UserServiceExtensions.cs b/Parse/Utilities/UserServiceExtensions.cs
index 8e8c0505..5db0f57f 100644
--- a/Parse/Utilities/UserServiceExtensions.cs
+++ b/Parse/Utilities/UserServiceExtensions.cs
@@ -50,28 +50,30 @@ public static Task SignUpAsync(this IServiceHub serviceHub, ParseUser user, Canc
         }
 
         /// 
-        /// Logs in a user with a username and password. On success, this saves the session to disk or to memory so you can retrieve the currently logged in user using .
+        /// Logs in a user with a username and password. On success, this saves the session to disk or to memory so you can retrieve the currently logged-in user using .
         /// 
         /// The  instance to target when logging in.
         /// The username to log in with.
         /// The password to log in with.
         /// The cancellation token.
         /// The newly logged-in user.
-        public static Task LogInAsync(this IServiceHub serviceHub, string username, string password, CancellationToken cancellationToken = default)
+        public static async Task LogInAsync(this IServiceHub serviceHub, string username, string password, CancellationToken cancellationToken = default)
         {
-            return serviceHub.UserController.LogInAsync(username, password, serviceHub, cancellationToken).OnSuccess(task =>
-        {
-            ParseUser user = serviceHub.GenerateObjectFromState(task.Result, "_User");
+            // Log in the user and get the user state
+            var userState = await serviceHub.UserController
+                .LogInAsync(username, password, serviceHub, cancellationToken)
+                .ConfigureAwait(false);
+
+            // Generate the ParseUser object from the returned state
+            var user = serviceHub.GenerateObjectFromState(userState, "_User");
+
+            // Save the user locally
+            await SaveCurrentUserAsync(serviceHub, user).ConfigureAwait(false);
+
+            // Set the authenticated user as the current instance
             InstanceUser = user;
-            var s = user.IsAuthenticated;
-            //user.IsAuthenticated
-            return SaveCurrentUserAsync(serviceHub, user)
-            .OnSuccess(_ =>
-            {
-                InstanceUser = user;
-                return user;
-            });
-        }).Unwrap();
+
+            return user;
         }
 
         public static ParseUser InstanceUser { get; set; }
@@ -79,32 +81,30 @@ public static Task LogInAsync(this IServiceHub serviceHub, string use
 
         /// 
         /// Logs in a user with a username and password. On success, this saves the session to disk so you
-        /// can retrieve the currently logged in user using .
+        /// can retrieve the currently logged-in user using .
         /// 
         /// The session token to authorize with
         /// The cancellation token.
         /// The user if authorization was successful
-        public static Task BecomeAsync(this IServiceHub serviceHub, string sessionToken, CancellationToken cancellationToken = default)
+        public static async Task BecomeAsync(this IServiceHub serviceHub, string sessionToken, CancellationToken cancellationToken = default)
         {
-            return serviceHub.UserController.GetUserAsync(sessionToken, serviceHub, cancellationToken)
-                .OnSuccess(t =>
-                {
-                    // Generate the ParseUser object from the returned state
-                    ParseUser user = serviceHub.GenerateObjectFromState(t.Result, "_User");
-
-                    // Save the user locally
-                    return SaveCurrentUserAsync(serviceHub, user)
-                        .OnSuccess(_ =>
-                        {
-                            // Set the authenticated user as the current instance only after successful save
-                            InstanceUser = user;
-                            return user;
-                        });
-                })
-                .Unwrap();
+            // Fetch the user state using the session token
+            var userState = await serviceHub.UserController.GetUserAsync(sessionToken, serviceHub, cancellationToken).ConfigureAwait(false);
+
+            // Generate the ParseUser object from the returned state
+            var user = serviceHub.GenerateObjectFromState(userState, "_User");
+
+            // Save the user locally
+            await SaveCurrentUserAsync(serviceHub, user).ConfigureAwait(false);
+
+            // Set the authenticated user as the current instance only after successful save
+            InstanceUser = user;
+
+            return user;
         }
 
 
+
         /// 
         /// Logs out the currently logged in user session. This will remove the session from disk, log out of
         /// linked services, and future calls to  will return null.
@@ -137,15 +137,22 @@ public static Task LogOutAsync(this IServiceHub serviceHub)
         /// This is preferable to using , unless your code is already running from a
         /// background thread.
         /// 
-        public static Task LogOutAsync(this IServiceHub serviceHub, CancellationToken cancellationToken)
-        {
-            return GetCurrentUserAsync(serviceHub).OnSuccess(task =>
+        public static async Task LogOutAsync(this IServiceHub serviceHub, CancellationToken cancellationToken)
         {
+            // Fetch the current user
+            var user = await GetCurrentUserAsync(serviceHub).ConfigureAwait(false);
+
+            // Log out with providers
             LogOutWithProviders();
-            return task.Result is { } user ? user.TaskQueue.Enqueue(toAwait => user.LogOutAsync(toAwait, cancellationToken), cancellationToken) : Task.CompletedTask;
-        }).Unwrap();
+
+            // If a user is logged in, log them out and return the result, otherwise, complete immediately
+            if (user != null)
+            {
+                await user.TaskQueue.Enqueue(toAwait => user.LogOutAsync(cancellationToken), cancellationToken).ConfigureAwait(false);
+            }
         }
 
+
         static void LogOutWithProviders()
         {
             foreach (IParseAuthenticationProvider provider in ParseUser.Authenticators.Values)
@@ -203,31 +210,31 @@ public static ParseQuery GetUserQuery(this IServiceHub serviceHub)
         /// migrate the sessionToken on disk to revocable session.
         /// 
         /// The Task that upgrades the session.
-        public static Task EnableRevocableSessionAsync(this IServiceHub serviceHub, CancellationToken cancellationToken = default)
-        {
-            lock (serviceHub.UserController.RevocableSessionEnabledMutex)
-            {
-                serviceHub.UserController.RevocableSessionEnabled = true;
-            }
-
-            return GetCurrentUserAsync(serviceHub, cancellationToken).OnSuccess(task => task.Result.UpgradeToRevocableSessionAsync(cancellationToken));
-        }
-
-        internal static void DisableRevocableSession(this IServiceHub serviceHub)
-        {
-            lock (serviceHub.UserController.RevocableSessionEnabledMutex)
-            {
-                serviceHub.UserController.RevocableSessionEnabled = false;
-            }
-        }
-
-        internal static bool GetIsRevocableSessionEnabled(this IServiceHub serviceHub)
-        {
-            lock (serviceHub.UserController.RevocableSessionEnabledMutex)
-            {
-                return serviceHub.UserController.RevocableSessionEnabled;
-            }
-        }
+        //public static Task EnableRevocableSessionAsync(this IServiceHub serviceHub, CancellationToken cancellationToken = default)
+        //{
+        //    lock (serviceHub.UserController.RevocableSessionEnabledMutex)
+        //    {
+        //        serviceHub.UserController.RevocableSessionEnabled = true;
+        //    }
+
+        //    return GetCurrentUserAsync(serviceHub, cancellationToken).OnSuccess(task => task.Result.UpgradeToRevocableSessionAsync(cancellationToken));
+        //}
+
+        //internal static void DisableRevocableSession(this IServiceHub serviceHub)
+        //{
+        //    lock (serviceHub.UserController.RevocableSessionEnabledMutex)
+        //    {
+        //        serviceHub.UserController.RevocableSessionEnabled = false;
+        //    }
+        //}
+
+        //internal static bool GetIsRevocableSessionEnabled(this IServiceHub serviceHub)
+        //{
+        //    lock (serviceHub.UserController.RevocableSessionEnabledMutex)
+        //    {
+        //        return serviceHub.UserController.RevocableSessionEnabled;
+        //    }
+        //}
 
         #endregion
 
@@ -252,40 +259,46 @@ public static Task RequestPasswordResetAsync(this IServiceHub serviceHub, string
             return serviceHub.UserController.RequestPasswordResetAsync(email, cancellationToken);
         }
 
-        public static Task LogInWithAsync(this IServiceHub serviceHub, string authType, IDictionary data, CancellationToken cancellationToken)
+        public static async Task LogInWithAsync(this IServiceHub serviceHub, string authType, IDictionary data, CancellationToken cancellationToken)
         {
-            ParseUser user = null;
+            // Log in the user with the provided authType and data
+            var userState = await serviceHub.UserController
+                .LogInAsync(authType, data, serviceHub, cancellationToken)
+                .ConfigureAwait(false);
 
-            return serviceHub.UserController.LogInAsync(authType, data, serviceHub, cancellationToken).OnSuccess(task =>
-            {
-                user = serviceHub.GenerateObjectFromState(task.Result, "_User");
+            // Generate the ParseUser object from the user state
+            var user = serviceHub.GenerateObjectFromState(userState, "_User");
 
-                lock (user.Mutex)
-                {
-                    if (user.AuthData == null)
-                    {
-                        user.AuthData = new Dictionary>();
-                    }
+            // Synchronize the user data in a thread-safe way
+            lock (user.Mutex)
+            {
+                user.AuthData ??= new Dictionary>();
 
-                    user.AuthData[authType] = data;
+                user.AuthData[authType] = data;
 
-#pragma warning disable CS1030 // #warning directive
-#warning Check if SynchronizeAllAuthData should accept an IServiceHub for consistency on which actions take place on which IServiceHub implementation instance.
+                // Synchronize authentication data for all providers
+                user.SynchronizeAllAuthData();
+            }
 
-                    user.SynchronizeAllAuthData();
-#pragma warning restore CS1030 // #warning directive
-                }
+            // Save the current user locally
+            await SaveCurrentUserAsync(serviceHub, user).ConfigureAwait(false);
 
-                return SaveCurrentUserAsync(serviceHub, user);
-            }).Unwrap().OnSuccess(t => user);
+            return user;
         }
 
-        public static Task LogInWithAsync(this IServiceHub serviceHub, string authType, CancellationToken cancellationToken)
+        public static async Task LogInWithAsync(this IServiceHub serviceHub, string authType, CancellationToken cancellationToken)
         {
+            // Get the authentication provider based on the provided authType
             IParseAuthenticationProvider provider = ParseUser.GetProvider(authType);
-            return provider.AuthenticateAsync(cancellationToken).OnSuccess(authData => LogInWithAsync(serviceHub, authType, authData.Result, cancellationToken)).Unwrap();
+
+            // Authenticate using the provider
+            var authData = await provider.AuthenticateAsync(cancellationToken).ConfigureAwait(false);
+
+            // Log in using the authenticated data
+            return await LogInWithAsync(serviceHub, authType, authData, cancellationToken).ConfigureAwait(false);
         }
 
+
         internal static void RegisterProvider(this IServiceHub serviceHub, IParseAuthenticationProvider provider)
         {
             ParseUser.Authenticators[provider.AuthType] = provider;