/// <summary> /// Reverts part of the schema relevant to a problem to the state which when compared to its current /// state, regenerates the problem. The inverse of ResolveProblem. /// </summary> /// <param name="oldSchema">the old schema which generated the problem when compared to the current schema</param> /// <param name="problem">the problem it generated</param> public void ApplyProblem(ContentModelSchema oldSchema, ChangeProblem problem) { string aqTypeName = problem.GetAssemblyQualifiedTypeName(); switch (problem.ProblemType) { case ChangeProblemType.DeletionNeeded: // copy the deleted type to the new schema TypeProperties.Add(aqTypeName, oldSchema.TypeProperties[aqTypeName]); break; case ChangeProblemType.PropertyDropped: // copy the dropped property to the new schema TypeProperties[aqTypeName].Add(problem.PropertyName, oldSchema.TypeProperties[aqTypeName][problem.PropertyName]); break; case ChangeProblemType.PropertyDroppedFromSummary: Type contentType = ContentTypeHierarchy.GetContentType(problem.TypeName); string aqSummaryTypeName = ContentTypeHierarchy.SummaryTypes[contentType].AssemblyQualifiedName; TypeProperties[aqTypeName].Add(problem.PropertyName, oldSchema.TypeProperties[aqTypeName][problem.PropertyName]); TypeProperties[aqSummaryTypeName].Add(problem.PropertyName, oldSchema.TypeProperties[aqSummaryTypeName][problem.PropertyName]); break; // Problems not requiring data modification case ChangeProblemType.NullObjectValue: break; } }
/// <summary> /// Find problems in the changes between two schemas by comparing this /// recorded schema to a given schema (usually the current schema) /// </summary> /// <param name="codeSchema">The current schema</param> /// <returns>List of potential problems arising from the differences between the schemas</returns> public List <ChangeProblem> FindProblems(ContentModelSchema codeSchema) { var changeProblems = new List <ChangeProblem>(); foreach (string typeName in this.ContentTypes.Except(codeSchema.ContentTypes)) { changeProblems.Add(new ChangeProblem(typeName, null, ChangeProblemType.DeletionNeeded)); } var otherTypes = new Dictionary <string, Type>(); foreach (string aqTypeName in codeSchema.TypeProperties.Keys) { string typeName = aqTypeName.UpTo(","); Type type = ContentTypeHierarchy.GetAnyType(typeName); if (type == null) { type = Type.GetType(aqTypeName); otherTypes.Add(aqTypeName, type); } } foreach (string typeName in codeSchema.ContentTypes) { // Check it doesn't initialise with a null value for an object or list property Type type = ContentTypeHierarchy.GetAnyType(typeName); if (type == null) { type = otherTypes[typeName]; } PropertyInfo nullProperty = new NoNullObjectCheck().Run(type); // Exception for UniqueId of Summary if (nullProperty != null && !(nullProperty.DeclaringType == typeof(Summary) && nullProperty.Name == "UniqueId")) { changeProblems.Add(new ChangeProblem(type.FullName, nullProperty.Name, ChangeProblemType.NullObjectValue)); } } // Types existing in both code and data schemas foreach (string aqTypeName in codeSchema.TypeProperties.Keys.Intersect(TypeProperties.Keys)) { string typeName = aqTypeName.UpTo(","); bool isSummary = ContentTypeHierarchy.GetSummaryType(typeName) != null; if (isSummary && !this.SummaryMap.ContainsKey(typeName)) { log.Warn("Unused summary type: " + typeName); continue; } foreach (string deletedProp in TypeProperties[aqTypeName].Keys.Except(codeSchema.TypeProperties[aqTypeName].Keys)) { if (isSummary) { // Test for when a property is dropped from the summary which was mapped to the content type // In this case the property data needs copied from the Summary db field to the Content one if (this.SummaryMap[typeName].ContainsValue(deletedProp) && this.SummaryTypes.ContainsValue(typeName)) { var oldContentProp = this.SummaryMap[typeName].Single(kvp => kvp.Value == deletedProp).Key; bool anyContentTypeHadProperty = this.SummaryTypes .Where(kvp => kvp.Value == typeName) .Any(kvp => TypeProperties[ContentTypeHierarchy.GetContentType(kvp.Key).AssemblyQualifiedName].ContainsKey(oldContentProp)); if (anyContentTypeHadProperty) { changeProblems.Add(new ChangeProblem(typeName, deletedProp, ChangeProblemType.PropertyDroppedFromSummary)); } } } else { changeProblems.Add(new ChangeProblem(typeName, deletedProp, ChangeProblemType.PropertyDropped)); } } foreach (string addedProp in codeSchema.TypeProperties[aqTypeName].Keys.Except(TypeProperties[aqTypeName].Keys)) { if (isSummary) { // Test for when a property is added to the summary which is mapped from the content type // In this case the data needs copied from the Content db field to the Summary if (codeSchema.SummaryMap[typeName].ContainsValue(addedProp) && codeSchema.SummaryTypes.ContainsValue(typeName)) { var newContentProp = codeSchema.SummaryMap[typeName].Single(kvp => kvp.Value == addedProp).Key; var anyContentTypeContainedProp = codeSchema.SummaryTypes .Where(kvp => kvp.Value == typeName) .Any(kvp => TypeProperties[ContentTypeHierarchy.GetContentType(kvp.Key).AssemblyQualifiedName].ContainsKey(newContentProp)); if (anyContentTypeContainedProp) { changeProblems.Add(new ChangeProblem(typeName, addedProp, ChangeProblemType.PropertyAddedToSummary)); } } } } } return(changeProblems); }