/// <summary>
        /// Updates the provided entity using a json string, and saves it to the registered persistence context
        /// </summary>
        /// <param name="json">The json string containing the new property values</param>
        /// <param name="toSave">The target entity</param>
        /// <param name="t">An optional type override to use when finding the correct context</param>
        public void SaveJsonObject(string json, Entity toSave, DynamicSaveCache cache, Type?t = null)
        {
            if (t is null)
            {
                t = TypeFactory.GetType(toSave);
            }
            ;

            if (this.ServiceProvider.GetRepositoryForType(t) is IRepository typeRepository)
            {
                toSave = this.UpdateJsonObject(json, toSave, cache, t);
                typeRepository.AddOrUpdate(toSave);
            }
            else
            {
                throw new Exception($"Typed repository not found for type {t}");
            }
        }
        /// <summary>
        /// Uses the provided Json to update the given KeyedObject, and saves it in the repository for the type
        /// </summary>
        /// <param name="json">The json containing the new properties</param>
        /// <param name="toSave">The KeyedObject used as a target for the Json Update</param>
        /// <param name="t">An optional type used as an override for the requested object type when requesting the repository</param>
        /// <returns>An updated version of the object being saved</returns>
        public KeyedObject UpdateJsonObject(string json, KeyedObject toSave, DynamicSaveCache cache, Type?t = null, IKeyedObjectRepository repository = null, KeyedObject repositoryObject = null)
        {
            if (json is null)
            {
                throw new ArgumentNullException(nameof(json));
            }

            if (toSave is null)
            {
                throw new ArgumentNullException(nameof(toSave));
            }

            if (cache is null)
            {
                throw new ArgumentNullException(nameof(cache));
            }

            repository ??= this.ServiceProvider.GetRepositoryForType <IKeyedObjectRepository>(t ?? toSave.GetType());

            if (repository != null)
            {
                if (toSave._Id != 0)
                {
                    toSave = repositoryObject ?? repository.Find(toSave._Id) ?? toSave;
                }

                this.UpdateProperties(json, toSave, cache, t);

                if (toSave._Id == 0)
                {
                    repository.Add(toSave);
                }

                return(toSave);
            }
            else
            {
                throw new Exception($"Repository not found for type {t}");
            }
        }
        /// <summary>
        /// Updates the properties of the given object using the provided json
        /// </summary>
        /// <param name="json">The json containing the new property values to apply to the target</param>
        /// <param name="toSave">The target object to update using the provided json</param>
        /// <param name="t">An optional override type to use in place of the target object type</param>
        public void UpdateProperties(string json, KeyedObject toSave, DynamicSaveCache cache, Type?t = null)
        {
            if (toSave is null)
            {
                throw new ArgumentNullException(nameof(toSave));
            }

            if (cache is null)
            {
                throw new ArgumentNullException(nameof(cache));
            }

            t ??= toSave.GetType();

            if (t is null)
            {
                throw new Exception("What the f**k?");
            }

            JObject jObject = JObject.Parse(json);

            foreach (PropertyInfo thisProperty in t.GetProperties())
            {
                if (thisProperty.GetGetMethod() == null)
                {
                    continue;
                }

                if (thisProperty.PropertyType == typeof(string))
                {
                    continue;
                }

                if (thisProperty.PropertyType.GetInterface("IEnumerable") != null)
                {
                    //Create an array of the values we're going to use from the source JSON
                    JArray sourceArray = jObject.Property(thisProperty).Value as JArray;

                    //Remove the array source from the JSON so we dont "populate" it later, we're managing that now.
                    //Also, turn that array into a concrete collection so we dont have to do that manually
                    //Were going to treat it as an IEnumerable because we dont actually know what the implementation is on the target. This is the safes way
                    IEnumerable <KeyedObject> sourceEnumerable = jObject.Remove <IEnumerable <KeyedObject> >(thisProperty);

                    //Figure out the base type for the target collection
                    Type[] GenericArguments = thisProperty.PropertyType.GetGenericArguments();

                    //No base type? We cant do anything. Move on;
                    if (!GenericArguments.Any())
                    {
                        continue;
                    }

                    //Now we have the collection base type
                    Type listType = GenericArguments[0];

                    //Turn out input json collection into a list
                    IList tempCollection = sourceEnumerable.ToList();

                    //Create a list to bind to the return object once we've updated everything
                    IList newCollection = (IList)Activator.CreateInstance(typeof(List <>).MakeGenericType(listType));

                    //Grab the collection thats on the object we already have, since it might already have DB instances so we dont have to search

                    //Create a dictionary to hold these existing instances to avoid lookup overhead
                    Dictionary <int, KeyedObject> ExistingDictionary = new Dictionary <int, KeyedObject>();

                    //If we managed to grab the existing object enumerable
                    if (!(!(thisProperty.GetValue(toSave) is IEnumerable existingCollection)))
                    {
                        //Loop through it
                        foreach (KeyedObject ko in existingCollection)
                        {
                            //we cant use null entries
                            if (ko is null)
                            {
                                continue;
                            }

                            //And index the values
                            ExistingDictionary.Add(ko._Id, ko);
                        }
                    }

                    if (listType.IsSubclassOf(typeof(Entity)))
                    {
                        this.UpdateEntityList(tempCollection !);

                        foreach (object o in tempCollection)
                        {
                            newCollection.Add(o);
                        }
                    }
                    //If this collection is KeyedObjects
                    else if (listType.IsSubclassOf(typeof(KeyedObject)))
                    {
                        //Grab an instance of the repository we need now, instead of on every iteration
                        IKeyedObjectRepository repository = this.ServiceProvider.GetRepositoryForType <IKeyedObjectRepository>(listType);

                        //Now loop through the temporary object list from the source json
                        for (int i = 0; i < tempCollection.Count; i++)
                        {
                            //Grab the instance
                            KeyedObject toPopulate = (KeyedObject)tempCollection[i];

                            //Set up to grab any existing instance
                            KeyedObject existingObject = null;

                            //If the new json claims the object already exists
                            if (toPopulate._Id != 0)
                            {
                                //We're going to check the object holding the original list to see if it contains that value
                                //If we cant find it here, UpdateJsonObject will check again.
                                ExistingDictionary.Remove(toPopulate._Id, out existingObject);
                            }

                            //We're going to call further down the stack to get an instance attached to the DB and containing all the new values
                            KeyedObject updatedObject = this.UpdateJsonObject(sourceArray[i].ToString(), (KeyedObject)tempCollection[i], cache, listType, repository, existingObject);

                            //Add that proper instance to the list we're using as our new collection value
                            newCollection.Add(updatedObject);
                        }

                        if (!(repository is null))
                        {
                            //Now loop through any of the values that werent found in the new list and remove them from the repository.
                            //Keyed objects are only referenced by one owner so once removed, they have no use.
                            foreach (KeyValuePair <int, KeyedObject> kvp in ExistingDictionary)
                            {
                                KeyedObject toRemove = kvp.Value;

                                repository.Delete(toRemove);
                            }
                        }
                    }

                    //Assign the new collection to the object that was passed in as our target
                    thisProperty.SetValue(toSave, newCollection);
                }
                else if (typeof(Entity).IsAssignableFrom(thisProperty.PropertyType))
                {
                    Entity newValue = jObject.Remove <Entity>(thisProperty);

                    if (!(newValue is null))
                    {
                        Entity cachedEntity = cache.GetObject(newValue);

                        if (cachedEntity is null)
                        {
                            newValue = this.RetrieveSavedEntity(newValue);
                            cache.AddObject(newValue);
                        }
                        else
                        {
                            newValue = cachedEntity;
                        }
                    }

                    thisProperty.SetValue(toSave, newValue);
                }
                else if (thisProperty.PropertyType.IsSubclassOf(typeof(KeyedObject)))
                {
                    string jProp = JObject.Parse(json)[thisProperty.Name]?.ToString();

                    //Remove the value attached to the post since we're going to handle it here
                    jObject.Remove(thisProperty.Name);

                    //Dont f**k with this property if it wasn't sent over by the client!
                    //Missing string, dont touch.
                    if (!(jProp is null))
                    {
                        //EMPTY string is manually set to null
                        if (string.IsNullOrWhiteSpace(jProp))
                        {
                            if (!(!(thisProperty.GetValue(toSave) is KeyedObject existing)))
                            {
                                IKeyedObjectRepository repository = this.ServiceProvider.GetRepositoryForType <IKeyedObjectRepository>(thisProperty.PropertyType);

                                repository.Delete(existing);

                                thisProperty.SetValue(toSave, null);
                            }
                        }
                        //Non empty string needs update
                        else
                        {
                            //Check for existing prop value

                            //if existing val is null, try and create a new one so we have something to set
                            if (!(thisProperty.GetValue(toSave) is KeyedObject val))
                            {
                                val = JsonConvert.DeserializeObject(jProp, thisProperty.PropertyType) as KeyedObject;
                            }

                            //If all that works out we're going to add it to the object
                            if (!(val is null))
                            {
                                KeyedObject thisObject = this.UpdateJsonObject(jProp, val, cache, thisProperty.PropertyType);

                                thisProperty.SetValue(toSave, thisObject);
                            }
                        }
                    }
                }
            }

            JsonConvert.PopulateObject(jObject.ToString(), toSave);
        }
        public Entity UpdateJsonObject(string json, Entity toSave, DynamicSaveCache cache, Type?t = null)
        {
            this.UpdateProperties(json, toSave, cache, t);

            return(toSave);
        }