/// <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); }