private HierarchyInfo BuildHierarchyInfo(TypeInfo root, HierarchyDef hierarchyDef) { var key = BuildKeyInfo(root, hierarchyDef); var schema = hierarchyDef.Schema; // Optimization. It there is the only class in hierarchy then ConcreteTable schema is applied if (schema != InheritanceSchema.ConcreteTable) { var node = context.DependencyGraph.TryGetNode(hierarchyDef.Root); // No dependencies => no descendants if (node == null || node.IncomingEdges.Count(e => e.Kind == EdgeKind.Inheritance) == 0) { schema = InheritanceSchema.ConcreteTable; } } var typeDiscriminatorField = hierarchyDef.Root.Fields.FirstOrDefault(f => f.IsTypeDiscriminator); var typeDiscriminatorMap = typeDiscriminatorField != null ? new TypeDiscriminatorMap() : null; var hierarchy = new HierarchyInfo(root, key, schema, typeDiscriminatorMap) { Name = root.Name, }; key.Hierarchy = hierarchy; // Setting backreference context.Model.Hierarchies.Add(hierarchy); return(hierarchy); }
public void Process(HierarchyDef hierarchyDef, FieldDef fieldDef, KeyAttribute attribute) { ArgumentValidator.EnsureArgumentIsInRange(attribute.Position, 0, WellKnown.MaxKeyFieldNumber - 1, "attribute.Position"); var keyField = new KeyField(fieldDef.Name, attribute.Direction); if (hierarchyDef.KeyFields.Count > attribute.Position) { var current = hierarchyDef.KeyFields[attribute.Position]; if (current != null) { throw new DomainBuilderException(string.Format(Strings.ExKeyFieldsXAndXHaveTheSamePositionX, current.Name, fieldDef.Name, attribute.Position)); } hierarchyDef.KeyFields[attribute.Position] = keyField; } else { // Adding null stubs for not yet processed key fields while (hierarchyDef.KeyFields.Count < attribute.Position) { hierarchyDef.KeyFields.Add(null); } // Finally adding target key field at the specified position hierarchyDef.KeyFields.Add(keyField); } }
public void ValidateHierarchyEquality(TypeDef @interface, HierarchyDef first, HierarchyDef second) { // TypeId mode must match if (first.IncludeTypeId != second.IncludeTypeId) { throw new DomainBuilderException(string.Format( Strings.ExImplementorsOfXInterfaceBelongToHierarchiesOneOfWhichIncludesTypeIdButAnotherDoesntYZ, @interface.Name, first.Root.Name, second.Root.Name)); } // Number of key fields must match if (first.KeyFields.Count != second.KeyFields.Count) { throw new DomainBuilderException(string.Format( Strings.ExImplementorsOfXInterfaceBelongToHierarchiesWithDifferentKeyStructureYZ, @interface.Name, first.Root.Name, second.Root.Name)); } // Type of each key field must match for (int i = 0; i < first.KeyFields.Count; i++) { var masterField = first.Root.Fields[first.KeyFields[i].Name]; var candidateField = second.Root.Fields[second.KeyFields[i].Name]; if (masterField.ValueType != candidateField.ValueType) { throw new DomainBuilderException(string.Format( Strings.ExImplementorsOfXInterfaceBelongToHierarchiesWithDifferentKeyStructureYZ, @interface.Name, first.Root.Name, second.Root.Name)); } } }
private SequenceInfo BuildSequence(HierarchyDef hierarchyDef, KeyInfo key) { var seed = 0L; var cacheSize = (long)context.Configuration.KeyGeneratorCacheSize; var generatorName = key.GeneratorName; KeyGeneratorConfiguration configuration; if (keyGeneratorConfigurations.TryGetValue(generatorName, out configuration)) { seed = configuration.Seed; cacheSize = configuration.CacheSize; } var sequence = new SequenceInfo(generatorName) { Seed = seed + cacheSize, // Preallocate keys for the first access Increment = cacheSize, MappingDatabase = hierarchyDef.Root.MappingDatabase, MappingSchema = context.Configuration.DefaultSchema, MappingName = context.NameBuilder.BuildSequenceName(key), }; return(sequence); }
public void Process(HierarchyDef hierarchy, KeyGeneratorAttribute attribute) { hierarchy.KeyGeneratorKind = attribute.Kind; if (!string.IsNullOrEmpty(attribute.Name)) { hierarchy.KeyGeneratorName = attribute.Name; } }
public TypeDef ProcessType(Type type) { var modelDef = context.ModelDef; var typeDef = modelDef.Types.TryGetValue(type); if (typeDef != null) { return(typeDef); } using (BuildLog.InfoRegion(Strings.LogDefiningX, type.GetFullName())) { typeDef = DefineType(type); if (modelDef.Types.Contains(typeDef.Name)) { throw new DomainBuilderException(string.Format(Strings.ExTypeWithNameXIsAlreadyDefined, typeDef.Name)); } HierarchyDef hierarchyDef = null; if (typeDef.IsEntity) { // HierarchyRootAttribute is required for hierarchy root var hra = type.GetAttribute <HierarchyRootAttribute>(AttributeSearchOptions.Default); if (hra != null) { hierarchyDef = DefineHierarchy(typeDef, hra); } } ProcessProperties(typeDef, hierarchyDef); if (typeDef.IsEntity || typeDef.IsInterface) { ProcessIndexes(typeDef); } if (hierarchyDef != null) { BuildLog.Info(Strings.LogHierarchyX, typeDef.Name); modelDef.Hierarchies.Add(hierarchyDef); } modelDef.Types.Add(typeDef); ProcessFullTextIndexes(typeDef); var validators = type.GetCustomAttributes(false).OfType <IObjectValidator>(); foreach (var validator in validators) { typeDef.Validators.Add(validator); } return(typeDef); } }
public void ProcessProperties(TypeDef typeDef, HierarchyDef hierarchyDef) { var properties = typeDef.UnderlyingType.GetProperties( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (var propertyInfo in properties) { // Domain builder stage-related filter if (!IsFieldAvailable(propertyInfo)) { continue; } // FieldAttribute presence is required var reversedFieldAttributes = GetReversedFieldAttributes <FieldAttribute>(propertyInfo); if (reversedFieldAttributes.Count == 0) { continue; } var field = DefineField(propertyInfo, reversedFieldAttributes); // Declared & inherited fields must be processed for hierarchy root if (hierarchyDef != null) { typeDef.Fields.Add(field); BuildLog.Info(Strings.LogFieldX, field.Name); var keyAttributes = propertyInfo.GetAttribute <KeyAttribute>(AttributeSearchOptions.InheritAll); if (keyAttributes != null) { attributeProcessor.Process(hierarchyDef, field, keyAttributes); } } // Only declared properties must be processed in other cases else if (propertyInfo.DeclaringType == propertyInfo.ReflectedType) { typeDef.Fields.Add(field); BuildLog.Info(Strings.LogFieldX, field.Name); } // Checking whether property type is registered in model var propertyType = field.UnderlyingProperty.PropertyType; if (propertyType.IsGenericParameter) { continue; } if (propertyType.IsSubclassOf(WellKnownOrmTypes.Persistent) && !context.ModelDef.Types.Contains(propertyType)) { types.Enqueue(propertyType); } } }
/// <summary> /// Builds name for key generator. /// </summary> /// <param name="key">Key to build key generator name for.</param> /// <param name="hierarchyDef">Hierarchy definition.</param> /// <returns>Key generator name</returns> public string BuildKeyGeneratorName(KeyInfo key, HierarchyDef hierarchyDef) { var mappingDatabase = hierarchyDef.Root.MappingDatabase; var databaseSuffixRequired = key.GeneratorKind == KeyGeneratorKind.Default && KeyGeneratorFactory.IsSequenceBacked(key.SingleColumnType) && !string.IsNullOrEmpty(mappingDatabase); var baseName = key.GeneratorBaseName; return(databaseSuffixRequired ? FormatKeyGeneratorName(mappingDatabase, baseName) : baseName); }
public void ValidateHierarchy(HierarchyDef hierarchyDef) { var keyFields = hierarchyDef.KeyFields; if (keyFields.Count == 0) { throw new DomainBuilderException(string.Format( Strings.ExHierarchyXDoesntContainAnyKeyFields, hierarchyDef.Root.Name)); } var root = hierarchyDef.Root; if (hierarchyDef.KeyGeneratorKind == KeyGeneratorKind.Default) { // Default key generator can't produce values with 2 or more fields if (keyFields.Count > 2) { throw new DomainBuilderException(Strings.ExDefaultGeneratorCanServeHierarchyWithExactlyOneKeyField); } // if one of key fields is TypeId field and number of fields == 2 then it is OK if (keyFields.Count == 2 && keyFields.Find(f => f.Name == WellKnown.TypeIdFieldName) != null) { throw new DomainBuilderException(Strings.ExDefaultGeneratorCanServeHierarchyWithExactlyOneKeyField); } var field = keyFields.FirstOrDefault(f => f.Name != WellKnown.TypeIdFieldName); } foreach (var keyField in keyFields) { if (keyField == null) { throw new DomainBuilderException(String.Format(Strings.ExKeyStructureForXContainsNULLValue, root.Name)); } FieldDef fieldDef = root.Fields[keyField.Name]; if (fieldDef == null) { throw new DomainBuilderException(string.Format(Strings.ExKeyFieldXXIsNotFound, root.Name, keyField.Name)); } ValidateFieldType(root, fieldDef.ValueType, true); if (fieldDef.IsLazyLoad) { throw new DomainBuilderException(string.Format( Strings.ExFieldXCannotBeLazyLoadAsItIsIncludedInPrimaryKey, fieldDef.Name)); } } }
public HierarchyDef DefineHierarchy(TypeDef typeDef) { // Prefer standard hierarchy definition flow var hra = typeDef.UnderlyingType.GetAttribute <HierarchyRootAttribute>(AttributeSearchOptions.Default); if (hra != null) { return(DefineHierarchy(typeDef, hra)); } context.Validator.ValidateHierarchyRoot(context.ModelDef, typeDef); var result = new HierarchyDef(typeDef); return(result); }
public HierarchyDef DefineHierarchy(TypeDef typeDef, HierarchyRootAttribute attribute) { context.Validator.ValidateHierarchyRoot(context.ModelDef, typeDef); var hierarchyDef = new HierarchyDef(typeDef); attributeProcessor.Process(hierarchyDef, attribute); // KeyGeneratorAttribute is optional var kga = typeDef.UnderlyingType.GetAttribute <KeyGeneratorAttribute>(AttributeSearchOptions.InheritAll); if (kga != null) { attributeProcessor.Process(hierarchyDef, kga); } return(hierarchyDef); }
public static void Inspect(BuildingContext context, HierarchyDef hierarchyDef) { var root = hierarchyDef.Root; BuildLog.Info(Strings.LogInspectingHierarchyX, root.Name); context.Validator.ValidateHierarchy(hierarchyDef); // Skip open generic hierarchies if (root.IsGenericTypeDefinition) { return; } // Check the presence of TypeId field FieldDef typeIdField; if (!root.Fields.TryGetValue(WellKnown.TypeIdFieldName, out typeIdField)) { context.ModelInspectionResult.Actions.Enqueue(new AddTypeIdFieldAction(root)); } else { context.ModelInspectionResult.Actions.Enqueue(new MarkFieldAsSystemAction(root, typeIdField)); } // Should TypeId field be added to key fields? if (hierarchyDef.IncludeTypeId && hierarchyDef.KeyFields.Find(f => f.Name == WellKnown.TypeIdFieldName) == null) { context.ModelInspectionResult.Register(new AddTypeIdToKeyFieldsAction(hierarchyDef)); } context.ModelInspectionResult.Actions.Enqueue(new ReorderFieldsAction(hierarchyDef)); foreach (var keyField in hierarchyDef.KeyFields) { var field = root.Fields[keyField.Name]; InspectField(context, root, field, true); } context.ModelInspectionResult.Actions.Enqueue(new AddPrimaryIndexAction(root)); }
/// <summary> /// Builds base name for key generator. /// </summary> /// <param name="key">Key to build base key generator name for.</param> /// <param name="hierarchyDef">Hierarchy definition.</param> /// <returns>Base key generator name.</returns> public string BuildKeyGeneratorBaseName(KeyInfo key, HierarchyDef hierarchyDef) { if (key.GeneratorKind == KeyGeneratorKind.None) { throw new ArgumentOutOfRangeException("key.GeneratorKind"); } if (!string.IsNullOrEmpty(hierarchyDef.KeyGeneratorName)) { return(hierarchyDef.KeyGeneratorName); } if (key.GeneratorKind == KeyGeneratorKind.Custom) { throw new DomainBuilderException(string.Format( Strings.ExKeyGeneratorAttributeOnTypeXRequiresNameToBeSet, hierarchyDef.Root.UnderlyingType.GetShortName())); } // KeyGeneratorKind.Default: return(key.SingleColumnType.GetShortName()); }
// Constructors public CopyKeyFieldsAction(TypeDef target, HierarchyDef source) : base(target) { Source = source; }
internal void ValidateType(TypeDef typeDef, HierarchyDef hierarchyDef) { }
public void Process(HierarchyDef hierarchyDef, HierarchyRootAttribute attribute) { hierarchyDef.Schema = attribute.InheritanceSchema; hierarchyDef.IncludeTypeId = attribute.IncludeTypeId; hierarchyDef.IsClustered = attribute.Clustered; }
private static void InspectInterfaces(BuildingContext context) { foreach (var interfaceDef in context.ModelDef.Types.Where(t => t.IsInterface)) { var interfaceNode = context.DependencyGraph.TryGetNode(interfaceDef); // Are there any dependencies at all? if (interfaceNode == null) { context.ModelInspectionResult.Register(new RemoveTypeAction(interfaceDef)); continue; } // Are there any implementors? var implementorEdges = interfaceNode.IncomingEdges.Where(e => e.Kind == EdgeKind.Implementation).ToList(); // There is no implementors. If there are no references to the interface, it could be safely removed if (implementorEdges.Count == 0 && interfaceNode.IncomingEdges.All(e => e.Kind != EdgeKind.Reference)) { context.ModelInspectionResult.Register(new RemoveTypeAction(interfaceDef)); continue; } HierarchyDef hierarchyDef; // There is only one implementor. Nothing else to do here if (implementorEdges.Count == 1) { hierarchyDef = context.ModelDef.FindHierarchy(implementorEdges[0].Tail.Value); if (hierarchyDef == null) { throw new DomainBuilderException(string.Format( Strings.ExXImplementorsDontBelongToAnyHierarchy, interfaceDef.Name)); } context.ModelInspectionResult.SingleHierarchyInterfaces.Add(interfaceDef); } else { // Cleaning implementation edges. We need only direct implementors of interface var directImplementorEdges = new HashSet <Edge <TypeDef> >(); foreach (var implementorEdge in implementorEdges) { if (context.ModelInspectionResult.RemovedTypes.Contains(implementorEdge.Tail.Value)) { continue; } var implementorType = implementorEdge.Tail.Value.UnderlyingType; var interfaceType = implementorEdge.Head.Value.UnderlyingType; // if not explicit implementation var interfaceMap = implementorType.GetInterfaceMap(interfaceType); if (!interfaceMap.TargetMethods.Any(tm => tm.DeclaringType == implementorType && tm.IsPrivate)) { // Checking for ancestor-descendant connection foreach (var directImplementorEdge in directImplementorEdges) { var directImplementorType = directImplementorEdge.Tail.Value.UnderlyingType; // Implementor is a descendant of one of direct implementors if (implementorType.IsSubclassOf(directImplementorType)) { interfaceNode.IncomingEdges.Remove(implementorEdge); implementorEdge.Tail.OutgoingEdges.Remove(implementorEdge); goto Next; } // Implementor is an ancestor of one of direct implementors if (directImplementorType.IsSubclassOf(implementorType)) { directImplementorEdges.Remove(directImplementorEdge); directImplementorEdges.Add(implementorEdge); interfaceNode.IncomingEdges.Remove(directImplementorEdge); directImplementorEdge.Tail.OutgoingEdges.Remove(directImplementorEdge); goto Next; } } } // None of direct implementors is in the same hierarchy as implementor directImplementorEdges.Add(implementorEdge); Next :; } // Find hierarchies for all direct implementors var hierarchies = new HashSet <HierarchyDef>(); foreach (var edge in directImplementorEdges) { var implementor = edge.Tail.Value; var hierarchy = context.ModelDef.FindHierarchy(implementor); if (hierarchy != null) { hierarchies.Add(hierarchy); } else { context.ModelInspectionResult.Register(new RemoveTypeAction(implementor)); } } // TODO: what if hierarchies.Count == 0? var count = hierarchies.Count; if (count == 0) { throw new DomainBuilderException(string.Format( Strings.ExXImplementorsDontBelongToAnyHierarchy, interfaceDef.Name)); } if (count == 1) { context.ModelInspectionResult.SingleHierarchyInterfaces.Add(interfaceDef); } else { HierarchyDef master = null; foreach (var candidate in hierarchies) { if (master == null) { master = candidate; continue; } context.Validator.ValidateHierarchyEquality(interfaceDef, master, candidate); } } hierarchyDef = hierarchies.First(); } context.ModelInspectionResult.Register(new CopyKeyFieldsAction(interfaceDef, hierarchyDef)); context.ModelInspectionResult.Register(new ReorderFieldsAction(interfaceDef, hierarchyDef)); context.ModelInspectionResult.Register(new BuildImplementorListAction(interfaceDef)); context.ModelInspectionResult.Register(new AddPrimaryIndexAction(interfaceDef)); } }
// Constructors public ReorderFieldsAction(HierarchyDef hierarchy) : this(hierarchy.Root, hierarchy) { }
private KeyInfo BuildKeyInfo(TypeInfo root, HierarchyDef hierarchyDef) { var keyFields = root.Fields .Where(field => field.IsPrimaryKey) .OrderBy(field => field.MappingInfo.Offset) .ToList(); var keyColumns = keyFields .Where(field => field.Column != null) .Select(field => field.Column) .ToList(); var keyTupleDescriptor = TupleDescriptor.Create( keyColumns.Select(c => c.ValueType).ToArray(keyColumns.Count)); var typeIdColumnIndex = -1; if (hierarchyDef.IncludeTypeId) { for (var i = 0; i < keyColumns.Count; i++) { if (keyColumns[i].Field.IsTypeId) { typeIdColumnIndex = i; } } } var key = new KeyInfo(root.Name, keyFields, keyColumns, keyTupleDescriptor, typeIdColumnIndex); var generatorKind = hierarchyDef.KeyGeneratorKind; // Force absence of key generator if key is a reference. if (key.ContainsForeignKeys) { generatorKind = KeyGeneratorKind.None; } if (generatorKind == KeyGeneratorKind.Default) { var canBeHandled = key.SingleColumnType != null && KeyGeneratorFactory.IsSupported(key.SingleColumnType); // Force absence of key generator if key can not be handled by standard keygen. if (!canBeHandled) { generatorKind = KeyGeneratorKind.None; } } if (generatorKind == KeyGeneratorKind.None) { // No key generator is attached. // Each hierarchy has it's own equality identifier. key.IsFirstAmongSimilarKeys = true; key.EqualityIdentifier = new object(); return(key); } // Hierarchy has key generator. // Setup key generator name. key.GeneratorKind = generatorKind; key.GeneratorBaseName = context.NameBuilder.BuildKeyGeneratorBaseName(key, hierarchyDef); key.GeneratorName = context.NameBuilder.BuildKeyGeneratorName(key, hierarchyDef); var generatorIdentity = context.Configuration.MultidatabaseKeys ? key.GeneratorBaseName : key.GeneratorName; // Equality identifier is the same if and only if key generator names match. key.IsFirstAmongSimilarKeys = !processedKeyGenerators.Contains(key.GeneratorName); if (key.IsFirstAmongSimilarKeys) { _ = processedKeyGenerators.Add(key.GeneratorName); } if (keyEqualityIdentifiers.TryGetValue(generatorIdentity, out var equalityIdentifier)) { key.EqualityIdentifier = equalityIdentifier; } else { key.EqualityIdentifier = new object(); keyEqualityIdentifiers.Add(generatorIdentity, key.EqualityIdentifier); } // Don't create sequences for user key generators // and for key generators that are not sequence-backed (such as GuidGenerator). if (key.GeneratorKind == KeyGeneratorKind.Custom || !IsSequenceBacked(key)) { return(key); } // Generate backing sequence. if (sequences.TryGetValue(key.GeneratorName, out var sequence)) { key.Sequence = sequence; } else { var newSequence = BuildSequence(hierarchyDef, key); if (context.Configuration.MultidatabaseKeys) { EnsureSequenceSeedIsUnique(newSequence); } key.Sequence = newSequence; sequences.Add(key.GeneratorName, key.Sequence); } return(key); }
// Constructors public AddTypeIdToKeyFieldsAction(HierarchyDef hierarchy) : base(hierarchy) { }
public ReorderFieldsAction(TypeDef target, HierarchyDef hierarchy) : base(hierarchy) { Target = target; }
protected HierarchyAction(HierarchyDef hierarchy) { Hierarchy = hierarchy; }