/// <summary> /// Generate a proxy for the given entity type. /// </summary> /// <typeparam name="T">The entity type.</typeparam> /// <param name="entityKey">The entity key.</param> /// <returns>The proxy.</returns> public T GenerateForEntity <T>(string entityKey) where T : class { var type = typeof(T); var typeMetadata = this.typeRepo.GetOrRegister(type) as EntityMetadata; if (typeMetadata == null) { throw new ArgumentException("The given type must be an entity type."); } var proxyTypeBuilder = new ProxyTypeBuilder(type); var dbKey = EntityKeyGenerator.GetDbKey(typeMetadata, entityKey); if (!this.dbClient.KeyExists(dbKey)) { return(default(T)); } // Generate proxy for key property. var keyPropTypeMetadata = this.typeRepo.Get(typeMetadata.KeyProperty.PropertyType); proxyTypeBuilder.InjectStub().CreateCtor().OverrideProperty(typeMetadata.KeyProperty, keyPropTypeMetadata, string.Concat(dbKey, typeMetadata.KeyProperty.Name), true); // Generate proxies for other properties. return(this.GenerateForObjectInternal <T>(proxyTypeBuilder, typeMetadata, dbKey)); }
/// <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, entityType.Properties.Append(entityType.KeyProperty), 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(); } } }