private GenericTypeLookupGraph BuildGenericTypeResolutionGraph(EntityModelSchema model) { var graph = new GenericTypeLookupGraph(); foreach (var unmappedBaseType in model.EntityTypes.Where(e => (e.Annotations.OfType <ObjectLayerOnlyAnnotation>().Any() || e.Annotations.OfType <GenericTypeAnnotation>().Any()) && e.BaseType == null)) { var genericTypeAnnotation = unmappedBaseType.Annotations.OfType <GenericTypeAnnotation>().FirstOrDefault(); if (genericTypeAnnotation == null) { continue; } int count = genericTypeAnnotation.TypeParameters.Count; for (int i = 0; i < count; ++i) { var lookups = new Dictionary <EntityType, GenericArgument>(); var typeParameterName = genericTypeAnnotation.TypeParameters[i]; graph.Add(new GenericTypeParameter(unmappedBaseType, typeParameterName), lookups); this.WalkChildren(graph, model, unmappedBaseType, typeParameterName, lookups); } } return(graph); }
private MemberProperty ResolveProperty(GenericTypeLookupGraph graph, MemberProperty property, EntityType newOwner) { GenericPropertyTypeAnnotation propertyTypeAnnotation = property.Annotations.OfType <GenericPropertyTypeAnnotation>().FirstOrDefault(); if (propertyTypeAnnotation != null) { // Have to find where the property type comes from var parameter = propertyTypeAnnotation.GenericTypeParameterName; EntityType oldOwner = null; for (oldOwner = newOwner.BaseType; oldOwner != null; oldOwner = oldOwner.BaseType) { if (oldOwner.Properties.Contains(property)) { break; } } Dictionary <EntityType, GenericArgument> lookups = graph[new GenericTypeParameter(oldOwner, parameter)]; GenericArgument genericArgument = null; EntityType resolvingType = null; for (resolvingType = newOwner; resolvingType != null && !lookups.TryGetValue(resolvingType, out genericArgument); resolvingType = resolvingType.BaseType) { } // Means we found a concrete generic argument for this property type. if (resolvingType != null) { return(new MemberProperty(property.Name, genericArgument.DataType) { IsPrimaryKey = property.IsPrimaryKey }); } return(new MemberProperty(property.Name) { IsPrimaryKey = property.IsPrimaryKey, Annotations = { new GenericPropertyTypeAnnotation(parameter) } }); } return(new MemberProperty(property.Name, property.PropertyType) { IsPrimaryKey = property.IsPrimaryKey }); }
private void WalkChildren(GenericTypeLookupGraph graph, EntityModelSchema model, EntityType baseType, string typeParameterName, Dictionary <EntityType, GenericArgument> lookups) { foreach (var childType in model.EntityTypes.Where(e => e.BaseType == baseType)) { var genericTypeAnnotation = childType.Annotations.OfType <GenericTypeAnnotation>().FirstOrDefault(); var genericArgumentsAnnotation = childType.Annotations.OfType <GenericArgumentsAnnotation>().FirstOrDefault(); GenericArgument argument = null; bool argumentMissing = genericArgumentsAnnotation == null || (argument = genericArgumentsAnnotation.GenericArguments.SingleOrDefault(ga => ga.TypeParameterName == typeParameterName)) == null; if (argumentMissing) { var exception = new TaupoArgumentException( string.Format( CultureInfo.InvariantCulture, "Entity type {0} cannot derive from entity type {1} because {0} does not have a type parameter {2} specified by a {3}.", childType.Name, baseType.Name, typeParameterName, typeof(GenericTypeAnnotation).Name)); if (genericTypeAnnotation == null) { throw exception; } var childTypeParameterPosition = genericTypeAnnotation.TypeParameters.IndexOf(typeParameterName); if (childTypeParameterPosition == -1) { throw exception; } var childLookups = new Dictionary <EntityType, GenericArgument>(); graph.Add(new GenericTypeParameter(childType, typeParameterName), childLookups); this.WalkChildren(graph, model, childType, typeParameterName, childLookups); // Merge the child lookups back into lookups foreach (var kvp in childLookups) { lookups[kvp.Key] = kvp.Value; } } else { lookups[childType] = argument; } } }
private void PushPropertiesToDerivedType(EntityModelSchema model, GenericTypeLookupGraph graph, EntityType unmappedBaseType, List <AssociationSet> oldAssociationSetsToRemove, EntityType derivedType, Dictionary <EntitySet, EntitySet> oldNewMap) { // Push primitive properties into derived classes. foreach (var property in unmappedBaseType.AllProperties) { var newProperty = this.ResolveProperty(graph, property, derivedType); derivedType.Properties.Add(newProperty); } // Push navigation properties into derived classes and add a new AssociationType. foreach (var navigationProperty in unmappedBaseType.AllNavigationProperties) { var oldAssociationType = navigationProperty.Association; var oldThisEnd = navigationProperty.FromAssociationEnd; var oldOtherEnd = navigationProperty.ToAssociationEnd; var associationTypeEndLookup = new Dictionary <AssociationEnd, AssociationEnd>(); var thisEnd = associationTypeEndLookup[oldThisEnd] = new AssociationEnd(derivedType.Name, derivedType, oldThisEnd.Multiplicity, oldThisEnd.DeleteBehavior); var otherEnd = associationTypeEndLookup[oldOtherEnd] = new AssociationEnd(oldOtherEnd.RoleName, oldOtherEnd.EntityType, oldOtherEnd.Multiplicity, oldOtherEnd.DeleteBehavior); // Assumes combination of the derived type name and the old association type name is unique. var associationType = new AssociationType(oldAssociationType.NamespaceName, derivedType.Name + "_" + oldAssociationType.Name) { associationTypeEndLookup[oldThisEnd], associationTypeEndLookup[oldOtherEnd] }; if (oldAssociationType.ReferentialConstraint != null) { associationType.ReferentialConstraint = new ReferentialConstraint() .WithDependentProperties(associationTypeEndLookup[oldAssociationType.ReferentialConstraint.DependentAssociationEnd], oldAssociationType.ReferentialConstraint.DependentProperties.ToArray()) .ReferencesPrincipalProperties(associationTypeEndLookup[oldAssociationType.ReferentialConstraint.PrincipalAssociationEnd], oldAssociationType.ReferentialConstraint.PrincipalProperties.ToArray()); } var newNavigationProperty = new NavigationProperty(navigationProperty.Name, associationType, thisEnd, otherEnd); derivedType.NavigationProperties.Add(newNavigationProperty); model.Add(associationType); // Update the association sets for this association type. var oldAssociationSets = model.EntityContainers.SelectMany(e => e.AssociationSets).Where(a => a.AssociationType == oldAssociationType).ToArray(); foreach (var oldAssociationSet in oldAssociationSets) { EntitySet firstEntitySet; if (!oldNewMap.TryGetValue(oldAssociationSet.Ends[0].EntitySet, out firstEntitySet)) { firstEntitySet = oldAssociationSet.Ends[0].EntitySet; } EntitySet secondEntitySet; if (!oldNewMap.TryGetValue(oldAssociationSet.Ends[1].EntitySet, out secondEntitySet)) { secondEntitySet = oldAssociationSet.Ends[1].EntitySet; } var associationSet = new AssociationSet(associationType.Name + "_" + oldAssociationSet.Name, associationType) { new AssociationSetEnd(associationTypeEndLookup[oldAssociationSet.Ends[0].AssociationEnd], firstEntitySet), new AssociationSetEnd(associationTypeEndLookup[oldAssociationSet.Ends[1].AssociationEnd], secondEntitySet), }; oldAssociationSet.Container.Add(associationSet); } // Can't remove these yet, because we have to wait until the original has been copied for all derived entity types. oldAssociationSetsToRemove.AddRange(oldAssociationSets); } }