/// <summary> /// Loads the configuration root entity from the durable cache. /// </summary> /// <returns>The root node and its children, or null if it cannot be found.</returns> protected ConfigRoot LoadConfigFromCache() { IJsonEntity <ConfigRoot> configRoot = _cacheProvider.Entities.FirstOrDefault(y => y.Contents.ComponentName == _componentName); if (configRoot != null) { return(configRoot.Contents); } return(null); }
internal void SetPendingDelete(IJsonEntity entity, bool value) { if (value && !_pendingDeleteEntities.Contains(entity)) { _pendingDeleteEntities.Add(entity); } else if (!value && _pendingDeleteEntities.Contains(entity)) { _pendingDeleteEntities.Remove(entity); } }
/// <summary> /// Saves the configuration root entity to the durable cache. /// </summary> /// <param name="config">The node to store</param> protected void SaveConfigToCache(ConfigRoot config) { IJsonEntity <ConfigRoot> configEntity = _cacheProvider.Entities.FirstOrDefault(y => y.Contents.ComponentName == _componentName); if (configEntity == null) { configEntity = _cacheProvider.Create(); } configEntity.Contents = config; _cacheProvider.Update(configEntity); _cacheProvider.Save(); }
/// <summary> /// Performs an update on an existing Config resource and returns a copy of the saved data if successful. /// </summary> /// <param name="componentName">The name of the resource to update. Not case-sensitive.</param> /// <param name="config">The new config data to store - overwrites any exsiting data</param> /// <returns> /// 200 - If update is successful /// 409 - If an attempt is made to change the name of an existing resource and that name change would cause it to overwrite another resource (see remarks) /// 404 - If the requested resource does not exist /// </returns> /// <remarks> /// This operation allows resource renaming. If you change the component name in the config JSON, when the operation completes the resource will have a /// new Location returned in the headers, provided that no other resource exists with the same name. /// </remarks> public OperationResult Put(string componentName, ConfigRoot config) { _log.Debug("Entering ConfigHandler.Put()"); try { var requestedEntity = _repository.Entities.FirstOrDefault( x => x.Contents.ComponentName.ToUpper() == componentName.ToUpper()); if (requestedEntity == null) { _log.Debug(string.Format("Returning 404 Not Found for resource {0}", componentName)); return(new OperationResult.NotFound()); } if (config.ComponentName == null || Regex.IsMatch(config.ComponentName, @"\W")) { return(new OperationResult.BadRequest()); } IJsonEntity <ConfigRoot> existingEntity = _repository.Entities.FirstOrDefault( x => x.Contents.ComponentName.ToUpper() == config.ComponentName.ToUpper()); if (existingEntity != null && config.ComponentName.ToUpper() != componentName.ToUpper()) { _log.Debug( string.Format( "Returning 409 Conflict for resource {0} - attempted to change name to existing resource {1}", componentName, config.ComponentName)); return(new ConflictOperationResult()); } requestedEntity.Contents = config; _repository.Update(requestedEntity); _repository.Save(); var location = HttpHelper.GetLocation(_request.Uri, config.ComponentName); _response.Headers[HttpHelper.LocationHeader] = location.ToString(); return(new OperationResult.OK { ResponseResource = requestedEntity.Contents }); } catch (Exception exception) { _log.Error(string.Format("Error in Put for component '{0}': {1}", componentName, exception)); throw; } }
/// <summary> /// Operation to update a resource in the repository /// </summary> /// <param name="item">The item to update</param> public void Update(IJsonEntity <T> item) { if (item == null) { throw new ArgumentNullException("item"); } _readWriteLock.EnterWriteLock(); try { _context[item.Id] = item; } finally { _readWriteLock.ExitWriteLock(); } }
/// <summary> /// Operation to delete a resource from the repository /// </summary> /// <param name="item">The item to delete</param> public void Delete(IJsonEntity <T> item) { if (item == null) { throw new ArgumentNullException("item"); } _readWriteLock.EnterWriteLock(); try { _deleteList.Add(item); _context.Remove(item.Id); } finally { _readWriteLock.ExitWriteLock(); } }
private static void FillJsonEntity(BaseDbEntity entity, IJsonEntity newJsonEntity) { List<MapToField> mappedFields = GetMappedFields(entity); Type jsonEntityType = newJsonEntity.GetType(); Type dbEntityType = entity.GetType(); mappedFields.ForEach(mappedAttr => { var jsonEntityProp = jsonEntityType.GetProperty(mappedAttr.JsonModelField); var dbEntityProp = dbEntityType.GetProperty(mappedAttr.JsonModelField); if (jsonEntityProp != null && dbEntityProp != null) { var dbValue = dbEntityProp.GetValue(entity); jsonEntityProp.SetValue(newJsonEntity, dbValue); } }); }
public void AddEntityThenDeleteFromMemory() { using (var testRepository = new DurableMemoryRepository <EntityTest>(MockDataPath, _mockProvider.MockFileSystem.Object)) { IJsonEntity <EntityTest> entity = testRepository.Create(); entity.Contents = new EntityTest { Name = "foo", Number = 1 }; testRepository.Add(entity); IJsonEntity <EntityTest> entityFromRepo = testRepository.Entities.FirstOrDefault(x => x.Contents.Name == "foo"); Assert.IsNotNull(entityFromRepo); testRepository.Delete(entity); entityFromRepo = testRepository.Entities.FirstOrDefault(x => x.Contents.Name == "foo"); Assert.IsNull(entityFromRepo); } }
/// <summary> /// Operation to add a new resource in the repository /// </summary> /// <param name="item">The item to create</param> public void Add(IJsonEntity <T> item) { if (item == null) { throw new ArgumentNullException("item"); } _readWriteLock.EnterWriteLock(); try { if (!_context.ContainsKey(item.Id)) { _context[item.Id] = item; } } finally { _readWriteLock.ExitWriteLock(); } }
/// <summary> /// Handles POST for creating new resource. /// </summary> /// <param name="config">Config root deserialized from JSON in request body</param> /// <returns> /// 409 Conflict - If config exists for component of same name already in repository /// 201 Created - If adding new config succeeds /// 500 Internal Server Error - If error occurs /// </returns> public OperationResult Post(ConfigRoot config) { _log.Debug("Entering ConfigHandler.Post()"); try { IJsonEntity <ConfigRoot> configEntity = _repository.Entities.FirstOrDefault( x => x.Contents.ComponentName.ToLower() == config.ComponentName.ToLower()); if (configEntity != null) { _log.Debug(string.Format("Returning 400 Bad Request for POST to component {0}", config.ComponentName)); return(new OperationResult.BadRequest()); } if (config.ComponentName == null || Regex.IsMatch(config.ComponentName, @"\W")) { _log.Debug(string.Format("Returning 400 Bad Request for POST to component {0}", config.ComponentName)); return(new OperationResult.BadRequest()); } configEntity = _repository.Create(); configEntity.Contents = config; _repository.Add(configEntity); _repository.Save(); var location = HttpHelper.GetLocation(_request.Uri, config.ComponentName); _response.Headers[HttpHelper.LocationHeader] = location.ToString(); return(new OperationResult.Created { ResponseResource = configEntity.Contents }); } catch (Exception exception) { _log.Error(string.Format("Error in Post for component '{0}': {1}", config != null ? config.ComponentName : null, exception)); throw; } }
internal void Initialize(IJsonEntity entity) { var type = entity.GetType(); var id = entity.Id; if (!id.HasValue) { throw new InvalidOperationException("Cannot initialize entity of type '" + type.Name + "' with no id."); } Dictionary <string, object> instanceData; var typeData = GetTypeData(type); if (!typeData.TryGetValue(id.Value.ToString(CultureInfo.InvariantCulture), out instanceData)) { throw new InvalidOperationException("Cannot initialize entity: " + type.Name + "|" + id + "."); } InitializeInstance(type, entity, instanceData); }
internal bool IsModified(IJsonEntity entity) { var type = entity.GetType(); var id = entity.Id; if (_deletedEntities.Contains(entity)) { if (!id.HasValue) { throw new Exception("Accessed deleted entity with no id: " + type.Name + "|."); } throw new Exception("Accessed deleted entity: " + type.Name + "|" + id + "."); } if (_pendingDeleteEntities.Contains(entity)) { return(true); } if (_newEntities.Contains(entity)) { return(true); } if (!id.HasValue) { throw new Exception("Found existing entity with no id: " + type.Name + "|."); } var typeData = GetTypeData(type); Dictionary <string, object> instanceData; if (typeData == null || !typeData.TryGetValue(id.Value.ToString(CultureInfo.InvariantCulture), out instanceData)) { throw new Exception("Couldn't find data for existing entity: " + type.Name + "|."); } foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (property.Name == "Id") { continue; } var value = property.GetValue(entity, null); var propertyType = property.PropertyType; object propertyData; var hasPropertyData = instanceData.TryGetValue(property.Name, out propertyData); Type referenceType; bool isReferenceList; if (JsonEntity.TryGetReferenceType(propertyType, supportedTypes, out referenceType, out isReferenceList)) { if (isReferenceList) { var referenceIds = new List <int>(); foreach (IJsonEntity reference in (IEnumerable)value) { var referenceId = reference.Id; if (!referenceId.HasValue) { throw new InvalidOperationException(); } referenceIds.Add(referenceId.Value); } if (!hasPropertyData || IsReferenceListModified(referenceIds.ToArray(), propertyData)) { return(true); } } else { var referenceId = ((IJsonEntity)value).Id; if (!referenceId.HasValue) { throw new InvalidOperationException(); } if (!hasPropertyData || IsReferenceModified(referenceId, propertyData)) { return(true); } } } else if (!hasPropertyData || IsValueModified(value, propertyData)) { return(true); } } return(false); }
private void UpdateData(Type type, IJsonEntity entity, bool allowDelete, out Type[] affectedTypes, out object[] addedEntities, out object[] removedEntities) { var affected = new List <Type>(); var added = new List <object>(); var removed = new List <object>(); var typeData = GetTypeData(type, true); if (_deletedEntities.Contains(entity)) { var id = entity.Id; if (!id.HasValue) { throw new Exception("Found deleted entity with no id: " + type.Name + "|."); } throw new Exception("Found deleted entity: " + type.Name + "|" + id + "."); } if (_pendingDeleteEntities.Contains(entity)) { var id = entity.Id; if (!allowDelete) { throw new Exception("Cannot delete entity: " + type.Name + "|" + id + "."); } // Only need to delete Entities that were actually saved previously. if (id.HasValue) { if (typeData.ContainsKey(id.Value.ToString(CultureInfo.InvariantCulture))) { typeData.Remove(id.Value.ToString(CultureInfo.InvariantCulture)); } removed.Add(entity); affected.Add(type); if (!_deletedEntities.Contains(entity)) { _deletedEntities.Add(entity); } } _pendingDeleteEntities.Remove(entity); } else { var updated = false; Dictionary <string, object> instanceData; if (_newEntities.Contains(entity)) { int newId; if (typeData.Count == 0) { newId = 1; } else { var largestId = typeData.Keys.Max(k => int.Parse(k)); newId = largestId + 1; } entity.Id = newId; typeData[newId.ToString(CultureInfo.InvariantCulture)] = instanceData = new Dictionary <string, object>(); _newEntities.Remove(entity); added.Add(entity); Dictionary <int, IJsonEntity> instanceCache; if (!_existingEntities.TryGetValue(type, out instanceCache)) { _existingEntities[type] = instanceCache = new Dictionary <int, IJsonEntity>(); } instanceCache[newId] = entity; updated = true; } else { var id = entity.Id; if (!id.HasValue) { throw new Exception("Found existing entity with no id: " + type.Name + "|."); } if (!typeData.TryGetValue(id.Value.ToString(CultureInfo.InvariantCulture), out instanceData)) { throw new Exception("Couldn't find data for existing entity: " + type.Name + "|."); } } foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (property.Name == "Id") { continue; } var value = property.GetValue(entity, null); object serializedValue; var propertyType = property.PropertyType; object propertyData; var hasPropertyData = instanceData.TryGetValue(property.Name, out propertyData); Type referenceType; bool isReferenceList; if (JsonEntity.TryGetReferenceType(propertyType, supportedTypes, out referenceType, out isReferenceList)) { if (isReferenceList) { var references = ((IEnumerable)value).Cast <IJsonEntity>().ToArray(); var referenceIds = new List <int>(); foreach (var reference in references) { Type[] refAffectedTypes; object[] refAdded; object[] refRemoved; UpdateData(referenceType, reference, false, out refAffectedTypes, out refAdded, out refRemoved); affected.AddRange(refAffectedTypes); added.AddRange(refAdded); removed.AddRange(refRemoved); var referenceId = reference.Id; if (!referenceId.HasValue) { throw new InvalidOperationException(); } referenceIds.Add(referenceId.Value); } serializedValue = referenceIds.ToArray(); if (!hasPropertyData) { updated = true; } else { if (IsReferenceListModified(referenceIds.ToArray(), propertyData)) { updated = true; } var persistedReferences = ((IEnumerable)propertyData).Cast <object>().Select(i => (int)Convert.ChangeType(i, typeof(int))).ToArray(); foreach (var priorReference in persistedReferences.Select(id => Fetch(referenceType, id)).Where(r => !references.Contains(r))) { Type[] refAffectedTypes; object[] refAdded; object[] refRemoved; UpdateData(referenceType, (IJsonEntity)priorReference, true, out refAffectedTypes, out refAdded, out refRemoved); affected.AddRange(refAffectedTypes); added.AddRange(refAdded); removed.AddRange(refRemoved); } } } else { int?referenceId; if (value == null) { referenceId = null; } else { Type[] refAffectedTypes; object[] refAdded; object[] refRemoved; UpdateData(referenceType, (IJsonEntity)value, false, out refAffectedTypes, out refAdded, out refRemoved); affected.AddRange(refAffectedTypes); added.AddRange(refAdded); removed.AddRange(refRemoved); referenceId = ((IJsonEntity)value).Id; if (!referenceId.HasValue) { throw new InvalidOperationException(); } } serializedValue = referenceId; if (!hasPropertyData) { updated = true; } else { if (IsReferenceModified(referenceId, propertyData)) { updated = true; } if (propertyData != null) { var priorReferenceId = (int?)propertyData; if (priorReferenceId != referenceId) { var priorReference = (IJsonEntity)Fetch(referenceType, priorReferenceId.Value); Type[] refAffectedTypes; object[] refAdded; object[] refRemoved; UpdateData(referenceType, priorReference, true, out refAffectedTypes, out refAdded, out refRemoved); affected.AddRange(refAffectedTypes); added.AddRange(refAdded); removed.AddRange(refRemoved); } } } } } else { serializedValue = value; if (!hasPropertyData || IsValueModified(value, propertyData)) { updated = true; } } instanceData[property.Name] = serializedValue; } if (updated) { affected.Add(type); } } affectedTypes = affected.ToArray(); addedEntities = added.ToArray(); removedEntities = removed.ToArray(); }
internal static void OnInitExisting(IJsonEntity entity) { JsonEntityContext.GetContextForEntity(entity).Initialize(entity); }
private void InitializeInstance(IReflect type, IJsonEntity entity, Dictionary <string, object> data) { var jsonProperties = data.Keys.ToArray(); foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { var propertyType = property.PropertyType; Type referenceType; bool isReferenceList; object propData; if (jsonProperties.Contains(property.Name)) { propData = data[property.Name]; } else { propData = null; if (JsonEntity.TryGetReferenceType(propertyType, supportedTypes, out referenceType, out isReferenceList)) { if (isReferenceList) { var list = JsonEntityAdapter <JsonEntity> .CreateList(referenceType); property.SetValue(entity, list, null); } } } if (propData == null) { continue; } object value; if (JsonEntity.TryGetReferenceType(propertyType, supportedTypes, out referenceType, out isReferenceList)) { if (isReferenceList) { var list = JsonEntityAdapter <JsonEntity> .CreateList(referenceType); var addMethod = list.GetType().GetMethod("Add", BindingFlags.Instance | BindingFlags.Public, null, new[] { referenceType }, null); foreach (var referenceId in (IEnumerable)propData) { var reference = Fetch(referenceType, (int)Convert.ChangeType(referenceId, typeof(int))); if (reference == null) { throw new InvalidOperationException(); } addMethod.Invoke(list, new[] { reference }); } value = list; } else { var referenceId = (int)Convert.ChangeType(propData, typeof(int)); value = Fetch(referenceType, referenceId); } } else { Type targetType; if (!TryGetNullableType(propertyType, out targetType)) { targetType = propertyType; } if (targetType == typeof(DateTime) && propData is string) { value = DateTime.Parse((string)propData); } else if (propData.GetType() != targetType) { value = Convert.ChangeType(propData, targetType); } else { value = propData; } } property.SetValue(entity, value, null); } }
internal static void OnInitNew(IJsonEntity entity) { JsonEntityContext.GetContextForEntity(entity).Add(entity); }
/// <summary> /// Given an entity, constructs the full path to the data file that should be used by that entity /// </summary> /// <param name="jsonEntity">The entity for which to construct a path</param> /// <returns>The path</returns> private string GetDataFilePath(IJsonEntity <T> jsonEntity) { var dataFileName = string.Format("{0:d10}_" + typeof(T) + ".json", jsonEntity.Id); return(System.IO.Path.Combine(_path, dataFileName)); }
internal bool IsPendingDelete(IJsonEntity entity) { return(_pendingDeleteEntities.Contains(entity)); }
internal bool IsDeleted(IJsonEntity entity) { return(_deletedEntities.Contains(entity)); }
public bool Equals(IJsonEntity obj) => Id == obj?.Id;
/// <summary> /// Returns a copy of this <see cref="IJsonEntity{T}"/> object with the same ID and version, but replaces the /// body with a new one. /// </summary> public static IJsonEntity <T> Modify <T>(this IJsonEntity <T> entity, T newValue) where T : class? { return(new JsonEntity <T>(entity.Id, newValue, entity.Version));