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