/// <summary> /// Verifies that the given type has constructors to migrate from all ancestor types. /// </summary> private static void VerifyConstructors(VersionedType type) { ConstructorInfo[] publicConstructors = type.ModelType.GetDeclaredConstructors(); for (int i = 0; i < type.Ancestors.Length; ++i) { Type requiredConstructorType = type.Ancestors[i].ModelType; bool found = false; for (int j = 0; j < publicConstructors.Length; ++j) { var parameters = publicConstructors[j].GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType == requiredConstructorType) { found = true; break; } } if (found == false) { throw new MissingVersionConstructorException(type.ModelType, requiredConstructorType); } } }
/// <summary> /// Verifies that the given version graph contains only unique versions. /// </summary> private static void VerifyUniqueVersionStrings(VersionedType type) { // simple tree traversal var found = new Dictionary <string, Type>(); var remaining = new Queue <VersionedType>(); remaining.Enqueue(type); while (remaining.Count > 0) { VersionedType item = remaining.Dequeue(); // Verify we do not already have the version string. Take into account that we're not just // comparing the same model twice, since we can have a valid import graph that has the same // model multiple times. if (found.ContainsKey(item.VersionString) && found[item.VersionString] != item.ModelType) { throw new DuplicateVersionNameException(found[item.VersionString], item.ModelType, item.VersionString); } found[item.VersionString] = item.ModelType; // scan the ancestors as well foreach (var ancestor in item.Ancestors) { remaining.Enqueue(ancestor); } } }
public static Result GetVersionImportPath(string currentVersion, VersionedType targetVersion, out List <VersionedType> path) { path = new List <VersionedType>(); if (GetVersionImportPathRecursive(path, currentVersion, targetVersion) == false) { return(Result.Fail("There is no migration path from \"" + currentVersion + "\" to \"" + targetVersion.VersionString + "\"")); } path.Add(targetVersion); return(Result.Success); }
public static Option <VersionedType> GetVersionedType(Type type) { Option <VersionedType> optionalVersionedType; if (_cache.TryGetValue(type, out optionalVersionedType) == false) { var attr = PortableReflection.GetAttribute <ObjectAttribute>(type); if (attr != null) { if (string.IsNullOrEmpty(attr.VersionString) == false || attr.PreviousModels != null) { // Version string must be provided if (attr.PreviousModels != null && string.IsNullOrEmpty(attr.VersionString)) { throw new Exception("Object attribute on " + type + " contains a PreviousModels specifier - it must also include a VersionString modifier"); } // Map the ancestor types into versioned types VersionedType[] ancestors = new VersionedType[attr.PreviousModels != null ? attr.PreviousModels.Length : 0]; for (int i = 0; i < ancestors.Length; ++i) { Option <VersionedType> ancestorType = GetVersionedType(attr.PreviousModels[i]); if (ancestorType.IsEmpty) { throw new Exception("Unable to create versioned type for ancestor " + ancestorType + "; please add an [Object(VersionString=\"...\")] attribute"); } ancestors[i] = ancestorType.Value; } // construct the actual versioned type instance VersionedType versionedType = new VersionedType { Ancestors = ancestors, VersionString = attr.VersionString, ModelType = type }; // finally, verify that the versioned type passes some sanity checks VerifyUniqueVersionStrings(versionedType); VerifyConstructors(versionedType); optionalVersionedType = Option.Just(versionedType); } } _cache[type] = optionalVersionedType; } return(optionalVersionedType); }
private static bool GetVersionImportPathRecursive(List <VersionedType> path, string currentVersion, VersionedType current) { for (int i = 0; i < current.Ancestors.Length; ++i) { VersionedType ancestor = current.Ancestors[i]; if (ancestor.VersionString == currentVersion || GetVersionImportPathRecursive(path, currentVersion, ancestor)) { path.Add(ancestor); return(true); } } return(false); }