/// <summary> /// Adds an entity to the queue list. This provides circular reference checking as /// well as ensuring that proper order is maintained for all entities. /// </summary> /// <param name="entity">The entity that is to be included in the export.</param> /// <param name="path">The entity path that lead to this entity being encoded.</param> /// <param name="entityIsCritical">True if the entity is critical, that is referenced directly.</param> /// <param name="exporter">The exporter that will handle processing for this entity.</param> protected void EnqueueEntity(IEntity entity, EntityPath path, bool entityIsCritical, IExporter exporter) { // // These are system generated rows, we should never try to backup or restore them. // if (entity.TypeName == "Rock.Model.EntityType" || entity.TypeName == "Rock.Model.FieldType") { return; } // // If the entity is already in our path that means we are beginning a circular // reference so we can just ignore this one. // if (path.Where(e => e.Entity.Guid == entity.Guid).Any()) { return; } // // Find the entities that this entity references, in other words entities that must // exist before this one can be created, and queue them up. // var referencedEntities = FindReferencedEntities(entity, path, exporter); foreach (var r in referencedEntities) { if (r.Value != null) { var refPath = path + new EntityPathComponent(entity, r.Key); EnqueueEntity(r.Value, refPath, true, exporter); } } // // If we already know about the entity, add a reference to it and return. // var queuedEntity = Entities.Where(e => e.Entity.Guid == entity.Guid).FirstOrDefault(); if (queuedEntity == null) { queuedEntity = new QueuedEntity(entity, path.Clone()); Entities.Add(queuedEntity); } else { // // We have already visited this entity from the same parent. Not sure why we are here. // if (path.Any() && queuedEntity.ReferencePaths.Where(r => r.Any() && r.Last().Entity.Guid == path.Last().Entity.Guid).Any()) { return; } queuedEntity.AddReferencePath(path.Clone()); } // // Add any new referenced properties/entities that may have been supplied for this // entity, as it's possible that has changed based on the path we took to get here. // foreach (var r in referencedEntities) { if (!queuedEntity.ReferencedEntities.Any(e => e.Key == r.Key && e.Value == r.Value)) { queuedEntity.ReferencedEntities.Add(new KeyValuePair <string, IEntity>(r.Key, r.Value)); } } // // Mark the entity as critical if it's a root entity or is otherwise specified as critical. // if (path.Count == 0 || entityIsCritical || exporter.IsPathCritical(path)) { queuedEntity.IsCritical = true; } // // Mark the entity as requiring a new Guid value if so indicated. // if (exporter.DoesPathNeedNewGuid(path)) { queuedEntity.RequiresNewGuid = true; } // // Find the entities that this entity has as children. This is usually the many side // of a one-to-many reference (such as a Workflow has many WorkflowActions, this would // get a list of the WorkflowActions). // var children = FindChildEntities(entity, path, exporter); children.ForEach(e => EnqueueEntity(e.Value, path + new EntityPathComponent(entity, e.Key), false, exporter)); // // Allow the exporter a chance to add custom reference values. // var userReferences = exporter.GetUserReferencesForPath(entity, path); if (userReferences != null && userReferences.Any()) { foreach (var r in userReferences) { queuedEntity.UserReferences.AddOrReplace(r.Property, r); } } }
/// <summary> /// Export the given entity into an EncodedEntity object. This can be used later to /// reconstruct the entity. /// </summary> /// <param name="entity">The entity to be exported.</param> /// <returns>The exported data that can be imported.</returns> protected EncodedEntity Export(QueuedEntity queuedEntity) { EncodedEntity encodedEntity = new EncodedEntity(); Type entityType = GetEntityType(queuedEntity.Entity); encodedEntity.Guid = queuedEntity.Entity.Guid; encodedEntity.EntityType = entityType.FullName; // // Generate the standard properties and references. // foreach (var property in GetEntityProperties(queuedEntity.Entity)) { // // Don't encode IEntity properties, we should have the Id encoded instead. // if (typeof(IEntity).IsAssignableFrom(property.PropertyType)) { continue; } // // Don't encode IEnumerable properties. Those should be included as // their own entities to be encoded later. // if (property.PropertyType.GetInterface("IEnumerable") != null && property.PropertyType.GetGenericArguments().Length == 1 && typeof(IEntity).IsAssignableFrom(property.PropertyType.GetGenericArguments()[0])) { continue; } encodedEntity.Properties.Add(property.Name, property.GetValue(queuedEntity.Entity)); } // // Run any post-process transforms. // foreach (var processor in FindEntityProcessors(entityType)) { var data = processor.ProcessExportedEntity(queuedEntity.Entity, encodedEntity, this); if (data != null) { encodedEntity.AddTransformData(processor.Identifier.ToString(), data); } } // // Generate the references to other entities. // foreach (var x in queuedEntity.ReferencedEntities) { encodedEntity.MakePropertyIntoReference(x.Key, x.Value); } // // Add in the user references. // encodedEntity.References.AddRange(queuedEntity.UserReferences.Values); return(encodedEntity); }