/// <summary> /// Create a duplicate copy of this entity path and return it. /// </summary> /// <returns>A duplicate of this entity path.</returns> public EntityPath Clone() { EntityPath path = new EntityPath(); path.AddRange(this); return(path); }
/// <summary> /// Implements the operator +. /// </summary> /// <param name="a">The instance of this object that will is the left side of the operator.</param> /// <param name="b">The EntityPathComponent instance that is the right side of the operator.</param> /// <returns> /// The result of the operator. /// </returns> public static EntityPath operator +(EntityPath a, EntityPathComponent b) { EntityPath path = a.Clone(); path.Add(b); return(path); }
/// <summary> /// Determines if the entity at the given path requires a new Guid value when it's imported /// onto the target system. On import, if an entity of that type and Guid already exists then /// it is not imported and a reference to the existing entity is used instead. /// </summary> /// <param name="path">The path to the queued entity object that is being checked.</param> /// <returns> /// <c>true</c> if the path requires a new Guid value; otherwise, <c>false</c> /// </returns> public bool DoesPathNeedNewGuid(EntityPath path) { return(path == "" || path == "AttributeTypes" || path == "AttributeTypes.AttributeQualifiers" || path == "ActivityTypes" || path == "ActivityTypes.AttributeTypes" || path == "ActivityTypes.AttributeTypes.AttributeQualifiers" || path == "ActivityTypes.ActionTypes" || path == "ActivityTypes.ActionTypes.AttributeValues" || path == "ActivityTypes.ActionTypes.WorkflowFormId" || path == "ActivityTypes.ActionTypes.WorkflowFormId.FormAttributes"); }
/// <summary> /// Determines if the property at the given path should be followed to it's referenced entity. /// This is called for both referenced entities and child entities. /// </summary> /// <param name="path">The path.</param> /// <returns></returns> public bool ShouldFollowPathProperty(EntityPath path) { if (path == "CategoryId") { return(false); } if (path.Count > 0) { var lastComponent = path.Last(); if (lastComponent.Entity.TypeName == "Rock.Model.DefinedType" && lastComponent.PropertyName == "DefinedValues") { return(false); } } return(true); }
/// <summary> /// Determines whether the path to an entity should be considered critical. A critical /// entity is one that MUST exist on the target system in order for the export/import to /// succeed, as such a critical entity is always included. /// </summary> /// <param name="path">The path to the queued entity object that is being checked.</param> /// <returns> /// <c>true</c> if the path is critical; otherwise, <c>false</c>. /// </returns> public bool IsPathCritical(EntityPath path) { return(DoesPathNeedNewGuid(path)); }
/// <summary> /// Gets any custom references for the entity at the given path. /// </summary> /// <param name="parentEntity">The entity that will later be encoded.</param> /// <param name="path">The path to the parent entity.</param> /// <returns> /// A collection of references that should be applied to the encoded entity. /// </returns> public ICollection <Reference> GetUserReferencesForPath(IEntity parentEntity, EntityPath path) { if (path == "") { return(new List <Reference> { Reference.UserDefinedReference("CategoryId", "WorkflowCategory") }); } return(null); }
/// <summary> /// Generate the list of entities that reference this parent entity. These are entities that /// must be created after this entity has been created. /// </summary> /// <param name="parentEntity">The parent entity to find reverse-references to.</param> /// <param name="path">The property path that led us to this final property.</param> /// <param name="exporter">The object that handles filtering during an export process.</param> /// <returns>A list of KeyValuePairs that identify the property names and entites to be followed.</returns> protected List <KeyValuePair <string, IEntity> > FindChildEntities(IEntity parentEntity, EntityPath path, IExporter exporter) { List <KeyValuePair <string, IEntity> > children = new List <KeyValuePair <string, IEntity> >(); var properties = GetEntityProperties(parentEntity); // // Take a stab at any properties that are an ICollection<IEntity> and treat those // as child entities. // foreach (var property in properties) { if (property.PropertyType.GetInterface("IEnumerable") != null && property.PropertyType.GetGenericArguments().Length == 1) { if (typeof(IEntity).IsAssignableFrom(property.PropertyType.GetGenericArguments()[0]) && exporter.ShouldFollowPathProperty(path + new EntityPathComponent(parentEntity, property.Name))) { IEnumerable childEntities = ( IEnumerable )property.GetValue(parentEntity); foreach (IEntity childEntity in childEntities) { children.Add(new KeyValuePair <string, IEntity>(property.Name, childEntity)); } } } } // // We also need to pull in any attribute values. We have to pull attributes as well // since we might not have an actual value for that attribute yet and would need // it to pull the default value and definition. // if (parentEntity is IHasAttributes attributedEntity) { if (attributedEntity.Attributes == null) { attributedEntity.LoadAttributes(RockContext); } foreach (var item in attributedEntity.Attributes) { var attrib = new AttributeService(RockContext).Get(item.Value.Guid); children.Add(new KeyValuePair <string, IEntity>("Attributes", attrib)); var value = new AttributeValueService(RockContext).Queryable() .Where(v => v.AttributeId == attrib.Id && v.EntityId == attributedEntity.Id) .FirstOrDefault(); if (value != null) { children.Add(new KeyValuePair <string, IEntity>("AttributeValues", value)); } } } // // Allow for processors to adjust the list of children. // foreach (var processor in FindEntityProcessors(GetEntityType(parentEntity))) { processor.EvaluateChildEntities(parentEntity, children, this); } return(children); }
/// <summary> /// Find entities that this object references directly. These are entities that must be /// created before this entity can be re-created. /// </summary> /// <param name="parentEntity">The parent entity whose references we need to find.</param> /// <param name="path">The property path that led us to this final property.</param> /// <param name="exporter">The object that handles filtering during an export process.</param> /// <returns>A dictionary that identify the property names and entites to be followed.</returns> protected List <KeyValuePair <string, IEntity> > FindReferencedEntities(IEntity parentEntity, EntityPath path, IExporter exporter) { var references = new List <KeyValuePair <string, IEntity> >(); var properties = GetEntityProperties(parentEntity); // // Take a stab at any properties that end in "Id" and likely reference another // entity, such as a property called "WorkflowId" probably references the Workflow // entity and should be linked by Guid. // foreach (var property in properties) { if (property.Name.EndsWith("Id") && (property.PropertyType == typeof(int) || property.PropertyType == typeof(Nullable <int>))) { var entityProperty = parentEntity.GetType().GetProperty(property.Name.Substring(0, property.Name.Length - 2)); if (entityProperty == null || !typeof(IEntity).IsAssignableFrom(entityProperty.PropertyType)) { continue; } var value = ( IEntity )entityProperty.GetValue(parentEntity); if (exporter.ShouldFollowPathProperty(path + new EntityPathComponent(parentEntity, property.Name))) { references.Add(new KeyValuePair <string, IEntity>(property.Name, value)); } else { references.Add(new KeyValuePair <string, IEntity>(property.Name, null)); } } } // // Allow for processors to adjust the list of children. // foreach (var processor in FindEntityProcessors(GetEntityType(parentEntity))) { processor.EvaluateReferencedEntities(parentEntity, references, this); } return(references); }
/// <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); } } }
public bool DoesPathNeedNewGuid(EntityPath path) { return(path == "" || path == "AttributeTypes" || path == "AttributeTypes.AttributeQualifiers"); }