protected override object Build() { var domainModel = TemplateContext.DomainModelProvider.GetDomainModel(); var orderedAggregates = domainModel .Aggregates.Where( a => a.Members.Any( e => _shouldRenderEntityForSchema(e) || e.Extensions.Any(ex => _shouldRenderEntityForSchema(ex)))) .OrderBy(x => x.FullName.Name) .ToList(); //Initialize. _classMappingsForEntities = new Dictionary <Entity, List <ClassMappingContext> >(); var distinctOrderedAggregates = orderedAggregates .SelectMany( x => x.Members.Where(e => _shouldRenderEntityForSchema(e)) .Concat( x.Members.SelectMany(m => m.Extensions) .Where(ex => _shouldRenderEntityForSchema(ex)))) .Distinct(); foreach (Entity entity in distinctOrderedAggregates) { _classMappingsForEntities[entity] = CreateContextsForGeneratingMultipleClassMappings(entity, GenerateQueryModel); } return (new OrmMapping { Assembly = TemplateContext.IsExtension ? PathHelper.GetProjectNameFromProjectPath(TemplateContext.ProjectPath) : Namespaces.Standard.BaseNamespace, Namespace = GenerateQueryModel ? Namespaces.Entities.NHibernate.QueryModels.BaseNamespace : Namespaces.Entities.NHibernate.BaseNamespace, Aggregates = orderedAggregates .Select( aggregate => new OrmAggregate { Classes = aggregate .Members // Derived classes are mapped within their base entity mapping, so exclude top level mappings for these classes .Where(c => !c.IsDerived && _shouldRenderEntityForSchema(c)) .Concat( aggregate.Members.SelectMany(m => m.Extensions) .Where(ex => _shouldRenderEntityForSchema(ex))) .SelectMany(GetClassMappings) .ToList() }) .ToList() }); List <ClassMappingContext> CreateContextsForGeneratingMultipleClassMappings(Entity entity, bool isQueryModel) { var contexts = new List <ClassMappingContext> { // Add the default context new ClassMappingContext { IsQueryModel = isQueryModel } }; if (entity.IsBase && !entity.IsAbstract) { // Concrete base classes need an additional specialized mapping created contexts.Add( new ClassMappingContext { IsConcreteEntityBaseMapping = true, IsQueryModel = isQueryModel }); } if (entity.Parent != null && entity.Parent.IsBase && !entity.Parent.IsAbstract && !entity.IsEntityExtension) { // Children of concrete base classes need an additional specialized mapping created contexts.Add( new ClassMappingContext { IsConcreteEntityChildMappingForBase = true, IsQueryModel = isQueryModel }); } return(contexts); } IEnumerable <OrmClass> GetClassMappings(Entity entity) { var contexts = _classMappingsForEntities[entity]; bool hasDiscriminator = entity.HasDiscriminator(); foreach (var classMappingContext in contexts) { string fullyQualifiedClassName = GetEntityFullNameForContext(entity, classMappingContext); yield return(new OrmClass { ClassName = fullyQualifiedClassName, ReferenceClassName = fullyQualifiedClassName + "ReferenceData", TableName = entity.TableName(TemplateContext.TemplateSet.DatabaseEngine, entity.Name), SchemaName = EdFiConventions.IsEdFiPhysicalSchemaName(entity.Schema) ? null : entity.Schema, IsAggregateRoot = entity.IsAggregateRoot, IsAbstract = entity.IsAbstract, Id = !HasKeyRequiringUseOfCompositeId(entity) ? CreateId() : null, CompositeId = HasKeyRequiringUseOfCompositeId(entity) ? CreateCompositeId(classMappingContext) : null, Properties = GetOrderedNonIdentifyingProperties(entity, classMappingContext) .ToList(), HasOneToOneChildMappings = entity.NavigableOneToOnes.Any(), OneToOneChildMappings = entity.NavigableOneToOnes .Where(a => _shouldRenderEntityForSchema(a.OtherEntity)) .Select(ch => CreateOrmOneToOne(ch, classMappingContext)) .ToList(), HasBackReferences = classMappingContext.IsQueryModel && entity.NonNavigableParents.Any(), BackReferences = classMappingContext.IsQueryModel ? entity.NonNavigableParents.OrderBy(p => p.Name) .Where(p => _shouldRenderEntityForSchema(p.OtherEntity)) .Select(CreateOrmBackReference) .ToList() : null, Collections = entity.NavigableChildren .Concat( classMappingContext.IsQueryModel ? entity.NonNavigableChildren : new AssociationView[0]) .Where( ch => !(classMappingContext.IsQueryModel && ch.IsSelfReferencing) && _shouldRenderEntityForSchema(ch.OtherEntity)) .OrderBy(ch => ch.Name) .Select(ch => CreateNHibernateCollectionMapping(ch, classMappingContext)) .ToList(), IsConcreteEntityBaseMapping = classMappingContext.IsConcreteEntityBaseMapping, HasDiscriminator = hasDiscriminator, IsReferenceable = entity.IsReferenceable(), HasDerivedEntities = entity.IsBase, HasDiscriminatorWhereClause = hasDiscriminator && entity.IsBase, DerivedEntities = CreateDerivedEntities() .ToList(), IsQueryModelContext = classMappingContext.IsQueryModel, AggregateReferences = !classMappingContext.IsQueryModel ? entity.GetAssociationsToReferenceableAggregateRoots() .OrderBy(a => a.Name) .Select(CreateOrmAggregateReference) .ToList() : null }); } OrmCompositeIdentity CreateCompositeId(ClassMappingContext classMappingContext) { return(new OrmCompositeIdentity { KeyProperties = entity.Identifier.Properties .Where(p => !p.IsFromParent) .OrderBy(p => p.PropertyName) .Select( p => new OrmProperty { PropertyName = p.PropertyName.MakeSafeForCSharpClass(entity.Name), ColumnName = p.ColumnName(TemplateContext.TemplateSet.DatabaseEngine, p.PropertyName), NHibernateTypeName = p.PropertyType.ToNHibernateType(), MaxLength = GetMaxLength(p) }) .ToList(), KeyManyToOne = entity.ParentAssociation == null ? null : new OrmManyToOne { Name = entity.ParentAssociation.Name, HasClassName = classMappingContext.IsConcreteEntityChildMappingForBase, ClassName = classMappingContext.IsConcreteEntityChildMappingForBase ? GetEntityFullNameForContext( entity.Parent, _classMappingsForEntities[entity.Parent] .FirstOrDefault(x => x.IsConcreteEntityBaseMapping) ?? _classMappingsForEntities[entity.Parent] .First()) : null, OrderedParentColumns = entity.ParentAssociation.Inverse.GetOrderedAssociationTargetColumns() .Select( p => new OrmColumn { Name = p.ColumnName(TemplateContext.TemplateSet.DatabaseEngine, p.PropertyName) }) .ToList() } }); } OrmProperty CreateId() { return(new OrmProperty { PropertyName = entity.Identifier.Properties.Single() .PropertyName, ColumnName = entity.Identifier.Properties.Single() .ColumnName( TemplateContext.TemplateSet.DatabaseEngine, entity.Identifier.Properties.Single() .PropertyName), NHibernateTypeName = entity.Identifier.Properties.Single() .PropertyType.ToNHibernateType(), MaxLength = GetMaxLength(entity.Identifier.Properties.Single()), GeneratorClass = entity.Identifier.Properties.Single() .IsServerAssigned ? "identity" : "assigned" }); } OrmOneToOne CreateOrmOneToOne(AssociationView ch, ClassMappingContext classMappingContext) { return(new OrmOneToOne { Name = ch.OtherEntity.Name, Access = _propertyAccessor, IsQueryModelMapping = classMappingContext.IsQueryModel, ClassName = GetEntityFullNameForContext( ch.OtherEntity, _classMappingsForEntities[ch.OtherEntity] .First()), Columns = ch.GetOrderedAssociationTargetColumns() .Select( ep => new OrmColumn { Name = ep.ColumnName(TemplateContext.TemplateSet.DatabaseEngine, ep.PropertyName) }) .ToList() }); } OrmBackReference CreateOrmBackReference(AssociationView p) { return(new OrmBackReference { BagName = p.Name, ParentClassName = GetEntityFullNameForContext( p.OtherEntity, _classMappingsForEntities[p.OtherEntity] .First()), Columns = p.Inverse.GetOrderedAssociationTargetColumns() .Select( ep => new OrmColumn { Name = ep.ColumnName(TemplateContext.TemplateSet.DatabaseEngine, ep.PropertyName) }) .ToList() }); } OrmAggregateReference CreateOrmAggregateReference(AssociationView a) { return(new OrmAggregateReference { BagName = a.Name + "ReferenceData", AggregateReferenceClassName = GetEntityFullNameForContext( a.OtherEntity.TypeHierarchyRootEntity, new ClassMappingContext { IsReference = true }), Columns = a.Inverse.GetOrderedAssociationTargetColumns() .Select( p => new OrmColumn { Name = p.ColumnName(TemplateContext.TemplateSet.DatabaseEngine, p.PropertyName) }) .ToList() }); } IEnumerable <OrmDerivedEntity> CreateDerivedEntities() { return(entity .DerivedEntities .Where( e => _shouldRenderEntityForSchema(e)) .Select( e => new EntityAndContext { Entity = e, Contexts = _classMappingsForEntities[e] }) .SelectMany( entityAndContexts => entityAndContexts .Contexts .Select(context => CreateOrmDerivedEntity(entityAndContexts, context)))); OrmDerivedEntity CreateOrmDerivedEntity(EntityAndContext entityAndContexts, ClassMappingContext context) { var e = entityAndContexts.Entity; var derivedEntityClassMappingContext = context; string className = GetEntityFullNameForContext(e, derivedEntityClassMappingContext); return (new OrmDerivedEntity { IsJoinedSubclass = e.IsDescriptorEntity, ClassName = className, ReferenceClassName = className + "ReferenceData", TableName = e.Name, SchemaName = EdFiConventions.IsEdFiPhysicalSchemaName(e.Schema) ? null : e.Schema, DiscriminatorValue = $"{e.Schema}.{e.Name}", BaseTableName = e.BaseEntity.Name, BaseTableSchemaName = EdFiConventions.IsEdFiPhysicalSchemaName(e.BaseEntity.Schema) ? null : e.BaseEntity.Schema, KeyColumns = e.Identifier.Properties .OrderBy(p => p.PropertyName) .Select(p => new OrmColumn { Name = p.PropertyName }) .ToList(), KeyProperties = e.Identifier.Properties .OrderBy(p => p.PropertyName) .Select( p => new OrmProperty { PropertyName = p.PropertyName.MakeSafeForCSharpClass(entity.Name), ColumnName = p.ColumnName(TemplateContext.TemplateSet.DatabaseEngine, p.PropertyName), NHibernateTypeName = p.PropertyType.ToNHibernateType(), MaxLength = GetMaxLength(p) }) .ToList(), Properties = GetOrderedNonIdentifyingProperties(e, derivedEntityClassMappingContext) .ToList(), AggregateReferences = !derivedEntityClassMappingContext.IsQueryModel ? e.GetAssociationsToReferenceableAggregateRoots() .OrderBy(a => a.Name) .Select( a => new OrmAggregateReference { BagName = a.Name + "ReferenceData", AggregateReferenceClassName = GetEntityFullNameForContext( a.OtherEntity.TypeHierarchyRootEntity, new ClassMappingContext { IsReference = true }), Columns = a.Inverse .GetOrderedAssociationTargetColumns() .Select( ep => new OrmColumn { Name = ep.ColumnName( TemplateContext.TemplateSet.DatabaseEngine, ep.PropertyName) }) .ToList() }) .ToList() : null, HasBackReferences = derivedEntityClassMappingContext.IsQueryModel && e.NonNavigableParents.Any( r => _shouldRenderEntityForSchema(r.OtherEntity)), BackReferences = derivedEntityClassMappingContext.IsQueryModel ? e.NonNavigableParents.OrderBy(p => p.Name) .Where(p => _shouldRenderEntityForSchema(p.OtherEntity)) .Select( p => new OrmBackReference { BagName = p.Name, ParentClassName = GetEntityFullNameForContext( p.OtherEntity, _classMappingsForEntities[p.OtherEntity] .First()), Columns = p.Inverse.GetOrderedAssociationTargetColumns() .Select( ep => new OrmColumn { Name = ep.ColumnName( TemplateContext.TemplateSet.DatabaseEngine, ep.PropertyName) }) .ToList() }) .ToList() : null, Collections = e.NavigableChildren.Concat( derivedEntityClassMappingContext.IsQueryModel ? e.NonNavigableChildren : new AssociationView[0]) .Where( ch => !(derivedEntityClassMappingContext.IsQueryModel && ch.IsSelfReferencing) && _shouldRenderEntityForSchema(ch.OtherEntity)) .Select( ch => new OrmCollection { BagName = ch.Name, IsEmbeddedCollection = !derivedEntityClassMappingContext.IsQueryModel, Columns = ch.GetOrderedAssociationTargetColumns() .Select( ep => new OrmColumn { Name = ep.ColumnName( TemplateContext.TemplateSet.DatabaseEngine, ep.PropertyName) }) .ToList(), ClassName = GetEntityFullNameForContext( ch.OtherEntity, _classMappingsForEntities[ch.OtherEntity] .First()) }) .ToList() }); } } } OrmCollection CreateNHibernateCollectionMapping( AssociationView childAssociation, ClassMappingContext classMappingContext) { return(new OrmCollection { //Can't use classMappingContext.IsHierarchical here because this method is called 2x in that case. BagName = childAssociation.Name, IsEmbeddedCollection = !classMappingContext.IsQueryModel && childAssociation.IsNavigable, Columns = childAssociation.GetOrderedAssociationTargetColumns() .Select( ep => new OrmColumn { Name = ep.ColumnName(TemplateContext.TemplateSet.DatabaseEngine, ep.PropertyName) }) .ToList(), ClassName = GetEntityFullNameForContext( childAssociation.OtherEntity, classMappingContext.IsConcreteEntityBaseMapping ? _classMappingsForEntities[childAssociation.OtherEntity] .FirstOrDefault(x => x.IsConcreteEntityChildMappingForBase) ?? _classMappingsForEntities[childAssociation.OtherEntity] .First() : _classMappingsForEntities[childAssociation.OtherEntity] .First()) }); } IEnumerable <NHibernatePropertyDefinition> GetOrderedNonIdentifyingProperties( Entity entity, ClassMappingContext classMappingContext) { return(entity.NonIdentifyingProperties .Where(p => !p.IsPredefinedProperty()) .Concat(classMappingContext.ViewProperties) // Add in properties found in the view in context, if any .OrderBy(p => p.PropertyName) .Select( p => new NHibernatePropertyDefinition { PropertyName = p.PropertyName, ColumnName = p.ColumnName(TemplateContext.TemplateSet.DatabaseEngine, p.PropertyName), NHibernateTypeName = p.PropertyType.ToNHibernateType(), MaxLength = GetMaxLength(p), IsNullable = p.PropertyType.IsNullable })); } string GetMaxLength(EntityProperty p) { return(p.PropertyType.ToNHibernateType() == "string" ? p.PropertyType.MaxLength.ToString() : null); } bool HasKeyRequiringUseOfCompositeId(Entity m) { return // It has a composite key structure (m.Identifier.Properties.Count > 1 // ... or it happens to have a single PK column in a one-to-one relationship || m.Identifier.Properties.Single() .IsFromParent); } }