/// <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> /// Build a ContentModelSchema from the current state of the content types /// </summary> /// <returns>A ContentModelSchema storing the current state</returns> public static ContentModelSchema Build() { var schema = new ContentModelSchema(); var contentPersistenceTypes = ContentTypeHierarchy.AllContentTypes .Where(ct => Collator.Instance.Registered(ct) is ContentCollator); foreach (Type t in contentPersistenceTypes) schema.AddContentType(t); foreach (var kvp in ContentTypeHierarchy.SummaryTypes) schema.AddSummaryType(kvp.Key, kvp.Value); return schema; }
public ContentModelSchema Copy() { var copy = new ContentModelSchema { ContentTypes = this.ContentTypes.ToList(), SummaryMap = this.SummaryMap.ToDictionary(kvp => kvp.Key, kvp => kvp.Value), SummaryTypes = this.SummaryTypes.ToDictionary(kvp => kvp.Key, kvp => kvp.Value), TypeProperties = this.TypeProperties.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToDictionary(kvp2 => kvp2.Key, kvp2 => kvp2.Value)), }; return(copy); }
/// <summary> /// Build a ContentModelSchema from the current state of the content types /// </summary> /// <returns>A ContentModelSchema storing the current state</returns> public static ContentModelSchema Build() { var schema = new ContentModelSchema(); var contentPersistenceTypes = ContentTypeHierarchy.AllContentTypes .Where(ct => Collator.Instance.Registered(ct) is ContentCollator); foreach (Type t in contentPersistenceTypes) { schema.AddContentType(t); } foreach (var kvp in ContentTypeHierarchy.SummaryTypes) { schema.AddSummaryType(kvp.Key, kvp.Value); } return(schema); }
/// <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) { switch (problem.ProblemType) { case ChangeProblemType.DeletionNeeded: // copy the deleted type to the new schema TypeProperties.Add(problem.TypeName, oldSchema.TypeProperties[problem.TypeName]); break; case ChangeProblemType.PropertyDropped: case ChangeProblemType.PropertyDroppedFromSummary: // copy the dropped property to the new schema TypeProperties[problem.TypeName].Add(problem.PropertyName, oldSchema.TypeProperties[problem.TypeName][problem.PropertyName]); break; // Problems not requiring data modification case ChangeProblemType.NotBinarySerializable: case ChangeProblemType.NullObjectValue: break; } }
public override bool Initialise() { LastSchema = null; Load(); CurrentSchema = ContentModelSchema.Build(); if (LastSchema != null) { ContentRepository.ChangeProblems = LastSchema.FindProblems(CurrentSchema); if (ContentRepository.ChangeProblems.Any()) LyniconUi.Instance.ShowProblemAlert = true; } return true; }
public void Load() { FileInfo fi = new FileInfo(Paths.AppDataPath + "\\ContentSchema\\LastSchema.json"); if (fi.Exists) { try { var sz = new JsonSerializer(); using (var stream = fi.OpenRead()) using (var reader = new StreamReader(stream)) using (var jsonTextReader = new JsonTextReader(reader)) { LastSchema = sz.Deserialize<ContentModelSchema>(jsonTextReader); } } catch (Exception ex) { log.Error(ex); } } }
/// <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); }
/// <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 typeName in codeSchema.TypeProperties.Keys) { // Check its binary serializable Type type = ContentTypeHierarchy.GetAnyType(typeName); if (type == null) { type = ReflectionX.GetLoadedType(typeName); otherTypes.Add(typeName, type); } if (type != null && type.GetCustomAttribute<SerializableAttribute>() == null) changeProblems.Add(new ChangeProblem(typeName, null, ChangeProblemType.NotBinarySerializable)); } 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(nullProperty.ReflectedType.FullName, nullProperty.Name, ChangeProblemType.NullObjectValue)); } // Types existing in both code and data schemas foreach (string typeName in codeSchema.TypeProperties.Keys.Intersect(TypeProperties.Keys)) { bool isSummary = ContentTypeHierarchy.GetSummaryType(typeName) != null; if (isSummary && !this.SummaryMap.ContainsKey(typeName)) { log.Warn("Unused summary type: " + typeName); continue; } foreach (string deletedProp in TypeProperties[typeName].Keys.Except(codeSchema.TypeProperties[typeName].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[kvp.Key].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[typeName].Keys.Except(TypeProperties[typeName].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[kvp.Key].ContainsKey(newContentProp)); if (anyContentTypeContainedProp) changeProblems.Add(new ChangeProblem(typeName, addedProp, ChangeProblemType.PropertyAddedToSummary)); } } } } return changeProblems; }