/// <inheritdoc/> public IEnumerable <IDbOperation> GenerateObjectRecord(string prefix, object obj, TypeMetadata typeMetadata, bool generateRefForProxy = true) { // Traverse the property tree by using DFS. // This implemention is for better performance in case some object have too much layers. var states = new Stack <(Type type, string name, object val, string prefix, uint depth)>(); var path = new Dictionary <uint, object>(); // Initialize the searching with the properties of the object. states.Push((typeMetadata.Type, null, obj, prefix, 0u)); // Searching and build the database record. var visitedEntities = new HashSet <string>(); while (states.Count != 0) { var(curType, curName, curValue, curPrefix, depth) = states.Pop(); var curTypeMetadata = this.typeRepo.GetOrRegister(curType); path[depth] = curValue; // Process current property. curPrefix += curName; switch (curTypeMetadata) { case EntityMetadata entityType: var entityKey = EntityKeyGenerator.GetDbKey(entityType, curValue); var entityKeyValue = EntityKeyGenerator.GetEntityKey(entityType, curValue); // For added entity, just record the reference. if (curName != null) { yield return(new DbStringRecord(curPrefix, entityKeyValue)); } if (!visitedEntities.Contains(entityKey) && !(curValue is IProxy && generateRefForProxy)) { // For new entity, add it to records. visitedEntities.Add(entityKey); yield return(new DbStringRecord(entityKey, bool.TrueString)); ExpandProperties( states, entityKey, curValue, this.GetNewPropertyInfos(entityType), depth + 1); } break; case ObjectMetadata objType: for (var i = 0u; i < depth; i++) { if (path[i] == curValue) { throw new NotSupportedException("Circular reference is not support Object type, consider use Entity instead."); } } yield return(new DbStringRecord(curPrefix, bool.TrueString)); ExpandProperties(states, curPrefix, curValue, objType.Properties, depth + 1); break; case ListMetadata listType: var list = curValue as IList; yield return(new DbStringRecord(curPrefix, list.Count.ToString())); for (int i = 0; i < list.Count; i++) { states.Push((listType.InnerType, i.ToString(), list[i], curPrefix, depth + 1)); } break; case TypeMetadata basicType when basicType.ValueType == ObjectValueType.Primitive || basicType.ValueType == ObjectValueType.String: yield return(new DbStringRecord(curPrefix, curValue.ToString())); break; default: throw new NotSupportedException(); } } }