static TableGenerator()
 {
     CreateDateFieldNames = TemplateConstants.CreateDateFieldNames
                            .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                            .ToDictionary(a => a);
     DestNavPropNames = new Dictionary <string, string>();
     TemplateConstants.DestinationNavigationPropertyNames
     .Split(',')
     .ToList()
     .ForEach(a =>
     {
         var parts = a.Split(':');
         DestNavPropNames.Add(
             parts[0].Trim(),
             parts[1].Trim());
     });
     EditableColumns = TemplateConstants.EditableColumns
                       .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                       .ToList()
                       .Select(a => a.Trim())
                       .ToDictionary(a => a, a => a);
     EndDateFieldNames = TemplateConstants.EndDateFieldNames
                         .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                         .ToDictionary(a => a);
     ExcludePopulateDatesInterface = TemplateConstants.ExcludePopulateDatesInterface
                                     .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                     .ToDictionary(a => a);
     ExcludeFieldsFromModelTable = TemplateConstants.ExcludeFieldsFromModelTable
                                   .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                   .ToList()
                                   .Select(a => a.Trim())
                                   .ToDictionary(a => a, a => a);
     ModelsSubclassEffDateExcludedColumns = TemplateConstants.ModelsSubclassEffDateExcludedColumns
                                            .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                            .ToList()
                                            .Select(a => a.Trim())
                                            .ToDictionary(a => a, a => a);
     ModifiedDateFieldNames = TemplateConstants.ModifiedDateFieldNames
                              .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                              .ToDictionary(a => a);
     NavPropDisplayNames = new Dictionary <string, string>();
     TemplateConstants.NavigationPropertyDisplayNames
     .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
     .ToList()
     .ForEach(a =>
     {
         var parts = a.Split(':');
         NavPropDisplayNames.Add(
             parts[0].Trim(),
             parts.Length < 2 ||
             string.IsNullOrWhiteSpace(parts[1])
                     ? NamingUtil.GetDisplayName(parts[0].Trim().Split('.')[1])
                     : parts[1].Trim());
     });
     NavPropsToSkip = TemplateConstants.NavigationPropertiesToSkip
                      .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                      .Select(a => a.Trim())
                      .ToDictionary(a => a);
     NewKeywordColumns = TemplateConstants.NewKeywordColumns
                         .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                         .Select(a => a.Trim())
                         .ToDictionary(a => a);
     OverrideColumns = TemplateConstants.OverrideColumns
                       .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                       .Select(a => a.Trim())
                       .ToDictionary(a => a);
     SourceNavPropNames = new Dictionary <string, string>();
     TemplateConstants.SourceNavigationPropertyNames
     .Split(',')
     .ToList()
     .ForEach(a =>
     {
         var parts = a.Split(':');
         SourceNavPropNames.Add(
             parts[0].Trim(),
             parts[1].Trim());
     });
     StartDateFieldNames = TemplateConstants.StartDateFieldNames
                           .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                           .ToDictionary(a => a);
     IsActiveFieldNames = TemplateConstants.IsActiveFieldNames
                          .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                          .ToDictionary(a => a);
     DeactivatedDateFieldNames = TemplateConstants.DeactivatedDateFieldNames
                                 .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                 .ToDictionary(a => a);
     TablesToOptOutOfSubClass = TemplateConstants.TablesToOptOutOfSubClass
                                .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                .ToList()
                                .Select(a => a.Trim())
                                .ToDictionary(a => a);
 }
        private static IEnumerable <NavigationProperty> GenerateNavigationProperties(
            DataTable columnsTable,
            DataTable foreignKeyColumnsTable,
            DataTable foreignKeysTable,
            DataTable tablesTable,
            Table table,
            IReadOnlyCollection <Table> tables)
        {
            var props = new List <NavigationProperty>();
            // get foreign keys
            var foreignKeys = DbUtil.GetForeignKeysByTableId(
                columnsTable,
                foreignKeysTable,
                foreignKeyColumnsTable,
                tablesTable,
                table.ObjectId,
                table.TableName);

            // for each foreign key,
            foreignKeys.ForEach(a =>
            {
                var isDestinationForeignKeyAllNonNullable =
                    IsDestinationNullable(a, columnsTable);
                var isSourceForeignKeyAlsoPrimaryKey =
                    IsSourcePrimaryKey(table, a);
                var type = EntityDesignUtil.Singularize(
                    NamingUtil.GetTableName(a.DestinationTableName));
                var srcCol = a.Columns[0].SourceColumn;
                var prop   = new NavigationProperty
                {
                    DestinationMultiplicity =
                        // Zero: if any source foreign key columns are nullable
                        !isDestinationForeignKeyAllNonNullable
                        ? Multiplicity.Zero
                        // One: not Zero
                        : Multiplicity.One,
                    ForeignKey = a,
                    // *-1, 1-0/1
                    Name = SourceNavPropNames.TryGetValue(
                        a.SourceTableName + "." + string.Join("|", a.Columns.Select(b => b.SourceColumn)),
                        out var sourcePropName)
                        ? sourcePropName
                        : srcCol.ToLower().EndsWith("id") && srcCol.Length > 2
                        ? srcCol.Substring(0, srcCol.Length - 2)
                        : type,
                    SourceMultiplicity = isSourceForeignKeyAlsoPrimaryKey
                        ? Multiplicity.One
                        : Multiplicity.Many,
                    Type = type
                };
                // only add if not in NavigationPropertiesToSkip
                if (!NavPropsToSkip.ContainsKey(table.GeneratedName + "." + prop.Name))
                {
                    props.Add(prop);
                }
                // get destination table
                if (tables == null)
                {
                    return;
                }
                {
                    var destTable = tables
                                    .FirstOrDefault(b => b.TableName == a.DestinationTableName);
                    type = EntityDesignUtil.Singularize(
                        NamingUtil.GetTableName(a.SourceTableName));
                    var destProp = new NavigationProperty
                    {
                        DestinationMultiplicity = prop.SourceMultiplicity,
                        ForeignKey      = a,
                        InverseProperty = prop.Name,
                        Name            = DestNavPropNames.TryGetValue(
                            a.SourceTableName + "." + a.Columns[0].SourceColumn,
                            out var destPropName)
                            ? destPropName
                            : prop.SourceMultiplicity == Multiplicity.Many
                                ? EntityDesignUtil.Pluralize(type)
                                : type,
                        SourceMultiplicity = prop.DestinationMultiplicity,
                        Type = prop.SourceMultiplicity == Multiplicity.Many
                            ? "ICollection<" + type + ">"
                            : type
                    };
                    if (destTable != null &&
                        !NavPropsToSkip.ContainsKey(destTable.GeneratedName + "." + destProp.Name))
                    {
                        destTable.NavigationProperties.Add(destProp);
                    }
                }
            });
            // var isManyToMany = AreAllColumnsForeignKeys
            // The relationship is many-to-many only if the
            // relationship table only contains primary keys
            // of both entities, and no other fields.
            //  0, 1, *: 0-1, 1-0, 1->1, 1<-1, 0-*, 1-*, *-0, *-1, *-*
            //  if 0-1,
            //      add single, non-nullable navigation property
            //      of foreign key destination table class,
            //      and ensure foreign key destination table class
            //      has non-required navigation property to current table class
            //  if 1-0,
            //      do nothing, as non-required navigation property will be added
            //      when processing other side of foreign key relationship
            //  if 1->1,
            //      add single, non-nullable navigation property
            //      of foreign key destination table class,
            //      and ensure foreign key destination table class
            //      has required navigation property to current table class
            //  if 1<-1,
            //      required navigation property will be added
            //      when processing other side of foreign key relationship
            //  if 0-*,
            //      add public virtual ICollection of
            //      foreign key destination table class,
            //      and ensure foreign key destination table class
            //      has non-required navigation property to current table class
            //  if 1-*,
            //      add public virtual ICollection
            //      of foreign key destination table class,
            //      and ensure foreign key destination table class
            //      has required navigation property to current table class
            //  if *-0,
            //      do nothing, as non-required navigation property will be added
            //      when processing other side of foreign key relationship
            //  if *-1,
            //      do nothing, as required navigation property will be added
            //      when processing other side of foreign key relationship
            //  if *-*,
            //      well, there are no such tables, according to the
            //      Entity Framework definition, which is that all columns
            //      in the join table being both in the join table primary key,
            //      and a foreign key pointing from the join table to one of the
            //      joined tables.
            return(props);
        }