Exemple #1
0
        private void WriteOnModelCreateEFCore(ModelRoot modelRoot, List <string> segments, ModelClass[] classesWithTables)
        {
            Output("partial void OnModelCreatingImpl(ModelBuilder modelBuilder);");
            Output("partial void OnModelCreatedImpl(ModelBuilder modelBuilder);");
            NL();

            Output("/// <inheritdoc />");
            Output("protected override void OnModelCreating(ModelBuilder modelBuilder)");
            Output("{");
            Output("base.OnModelCreating(modelBuilder);");
            Output("OnModelCreatingImpl(modelBuilder);");
            NL();

            Output($"modelBuilder.HasDefaultSchema(\"{modelRoot.DatabaseSchema}\");");

            List <Association> visited           = new List <Association>();
            List <string>      foreignKeyColumns = new List <string>();

            foreach (ModelClass modelClass in modelRoot.Classes.OrderBy(x => x.Name))
            {
                segments.Clear();
                foreignKeyColumns.Clear();
                NL();

                // class level
                segments.Add($"modelBuilder.{(modelClass.IsDependentType ? "Owned" : "Entity")}<{modelClass.FullName}>()");

                foreach (ModelAttribute transient in modelClass.Attributes.Where(x => !x.Persistent))
                {
                    segments.Add($"Ignore(t => t.{transient.Name})");
                }

                if (!modelClass.IsDependentType)
                {
                    // note: this must come before the 'ToTable' call or there's a runtime error
                    if (modelRoot.InheritanceStrategy == CodeStrategy.TablePerConcreteType && modelClass.Superclass != null)
                    {
                        segments.Add("Map(x => x.MapInheritedProperties())");
                    }

                    if (classesWithTables.Contains(modelClass))
                    {
                        segments.Add(modelClass.DatabaseSchema == modelClass.ModelRoot.DatabaseSchema
                                    ? $"ToTable(\"{modelClass.TableName}\")"
                                    : $"ToTable(\"{modelClass.TableName}\", \"{modelClass.DatabaseSchema}\")");

                        // primary key code segments must be output last, since HasKey returns a different type
                        List <ModelAttribute> identityAttributes = modelClass.IdentityAttributes.ToList();

                        if (identityAttributes.Count == 1)
                        {
                            segments.Add($"HasKey(t => t.{identityAttributes[0].Name})");
                        }
                        else if (identityAttributes.Count > 1)
                        {
                            segments.Add($"HasKey(t => new {{ t.{string.Join(", t.", identityAttributes.Select(ia => ia.Name))} }})");
                        }
                    }
                }

                if (segments.Count > 1 || modelClass.IsDependentType)
                {
                    if (modelRoot.ChopMethodChains)
                    {
                        OutputChopped(segments);
                    }
                    else
                    {
                        Output(string.Join(".", segments) + ";");
                    }
                }

                if (modelClass.IsDependentType)
                {
                    continue;
                }

                // attribute level
                foreach (ModelAttribute modelAttribute in modelClass.Attributes.Where(x => x.Persistent && !SpatialTypes.Contains(x.Type)))
                {
                    segments.Clear();

                    if ((modelAttribute.MaxLength ?? 0) > 0)
                    {
                        segments.Add($"HasMaxLength({modelAttribute.MaxLength.Value})");
                    }

                    if (modelAttribute.Required)
                    {
                        segments.Add("IsRequired()");
                    }

                    if (modelAttribute.ColumnName != modelAttribute.Name && !string.IsNullOrEmpty(modelAttribute.ColumnName))
                    {
                        segments.Add($"HasColumnName(\"{modelAttribute.ColumnName}\")");
                    }

                    if (!modelAttribute.AutoProperty)
                    {
                        segments.Add($"HasField(\"_{modelAttribute.Name}\")");
                        segments.Add($"UsePropertyAccessMode(PropertyAccessMode.{(modelAttribute.PersistencePoint == PersistencePointType.Field ? "Field" : "Property")})");
                    }

                    if (!string.IsNullOrEmpty(modelAttribute.ColumnType) && modelAttribute.ColumnType.ToLowerInvariant() != "default")
                    {
                        if (modelAttribute.ColumnType.ToLowerInvariant() == "varchar" || modelAttribute.ColumnType.ToLowerInvariant() == "nvarchar" || modelAttribute.ColumnType.ToLowerInvariant() == "char")
                        {
                            segments.Add($"HasColumnType(\"{modelAttribute.ColumnType}({(modelAttribute.MaxLength > 0 ? modelAttribute.MaxLength.ToString() : "max")})\")");
                        }
                        else
                        {
                            segments.Add($"HasColumnType(\"{modelAttribute.ColumnType}\")");
                        }
                    }

                    if (modelAttribute.IsConcurrencyToken)
                    {
                        segments.Add("IsRowVersion()");
                    }

                    if (modelAttribute.IsIdentity)
                    {
                        segments.Add(modelAttribute.IdentityType == IdentityType.AutoGenerated
                                    ? "ValueGeneratedOnAdd()"
                                    : "ValueGeneratedNever()");
                    }

                    if (segments.Any())
                    {
                        segments.Insert(0, $"modelBuilder.{(modelClass.IsDependentType ? "Owned" : "Entity")}<{modelClass.FullName}>()");
                        segments.Insert(1, $"Property(t => t.{modelAttribute.Name})");

                        if (modelRoot.ChopMethodChains)
                        {
                            OutputChopped(segments);
                        }
                        else
                        {
                            Output(string.Join(".", segments) + ";");
                        }
                    }

                    if (modelAttribute.Indexed && !modelAttribute.IsIdentity)
                    {
                        segments.Clear();

                        segments.Add($"modelBuilder.Entity<{modelClass.FullName}>().HasIndex(t => t.{modelAttribute.Name})");

                        if (modelAttribute.IndexedUnique)
                        {
                            segments.Add("IsUnique()");
                        }

                        if (modelRoot.ChopMethodChains)
                        {
                            OutputChopped(segments);
                        }
                        else
                        {
                            Output(string.Join(".", segments) + ";");
                        }
                    }
                }

                bool hasDefinedConcurrencyToken = modelClass.AllAttributes.Any(x => x.IsConcurrencyToken);

                if (!hasDefinedConcurrencyToken && modelClass.EffectiveConcurrency == ConcurrencyOverride.Optimistic)
                {
                    Output($@"modelBuilder.Entity<{modelClass.FullName}>().Property<byte[]>(""Timestamp"").IsConcurrencyToken();");
                }

                // Navigation endpoints are distingished as Source and Target. They are also distinguished as Principal
                // and Dependent. How do these map?
                // In the case of one-to-one or zero-to-one-to-zero-to-one, it's model dependent and the user has to tell us
                // In all other cases, we can tell by the cardinalities of the associations
                // What matters is the Principal and Dependent classifications, so we look at those.
                // Source and Target are accidents of where the user started drawing the association.

                // navigation properties
                // ReSharper disable once LoopCanBePartlyConvertedToQuery
                foreach (UnidirectionalAssociation association in Association.GetLinksToTargets(modelClass)
                         .OfType <UnidirectionalAssociation>()
                         .Where(x => x.Persistent && !x.Target.IsDependentType))
                {
                    if (visited.Contains(association))
                    {
                        continue;
                    }

                    visited.Add(association);

                    segments.Clear();
                    segments.Add($"modelBuilder.Entity<{modelClass.FullName}>()");
                    bool required = false;

                    switch (association.TargetMultiplicity) // realized by property on source
                    {
                    case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany:
                        // TODO: Implement many-to-many
                        if (association.SourceMultiplicity != Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany)
                        {
                            segments.Add($"HasMany(x => x.{association.TargetPropertyName})");
                        }
                        else
                        {
                            continue;
                        }

                        break;

                    case Sawczyn.EFDesigner.EFModel.Multiplicity.One:
                        segments.Add($"HasOne(x => x.{association.TargetPropertyName})");

                        break;

                    case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne:
                        segments.Add($"HasOne(x => x.{association.TargetPropertyName})");

                        break;

                        //case Sawczyn.EFDesigner.EFModel.Multiplicity.OneMany:
                        //   segments.Add($"HasMany(x => x.{association.TargetPropertyName})");
                        //   break;
                    }

                    string columnPrefix = association.SourceRole == EndpointRole.Dependent
                                    ? ""
                                    : association.Target.Name + "_";

                    switch (association.SourceMultiplicity) // realized by shadow property on target
                    {
                    case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany:
                        // TODO: Implement many-to-many
                        if (association.TargetMultiplicity != Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany)
                        {
                            segments.Add("WithMany()");
                            segments.Add($"HasForeignKey(\"{columnPrefix}{association.TargetPropertyName}_Id\")");
                        }
                        else
                        {
                            continue;
                        }

                        break;

                    case Sawczyn.EFDesigner.EFModel.Multiplicity.One:
                        segments.Add("WithOne()");

                        segments.Add(association.TargetMultiplicity != Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany
                                       ? $"HasForeignKey<{association.Source.FullName}>(\"{columnPrefix}{association.TargetPropertyName}_Id\")"
                                       : $"HasForeignKey(\"{columnPrefix}{association.TargetPropertyName}_Id\")");

                        required = true;

                        break;

                    case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne:
                        segments.Add("WithOne()");

                        segments.Add(association.TargetMultiplicity != Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany
                                       ? $"HasForeignKey<{association.Source.FullName}>(\"{columnPrefix}{association.TargetPropertyName}_Id\")"
                                       : $"HasForeignKey(\"{columnPrefix}{association.TargetPropertyName}_Id\")");

                        break;

                        //case Sawczyn.EFDesigner.EFModel.Multiplicity.OneMany:
                        //   segments.Add("HasMany()");
                        //   break;
                    }

                    if (required)
                    {
                        segments.Add("IsRequired()");
                    }

                    if (association.TargetRole == EndpointRole.Principal || association.SourceRole == EndpointRole.Principal)
                    {
                        DeleteAction deleteAction = association.SourceRole == EndpointRole.Principal
                                             ? association.SourceDeleteAction
                                             : association.TargetDeleteAction;

                        switch (deleteAction)
                        {
                        case DeleteAction.None:
                            segments.Add("OnDelete(DeleteBehavior.Restrict)");

                            break;

                        case DeleteAction.Cascade:
                            segments.Add("OnDelete(DeleteBehavior.Cascade)");

                            break;
                        }
                    }

                    if (modelRoot.ChopMethodChains)
                    {
                        OutputChopped(segments);
                    }
                    else
                    {
                        Output(string.Join(".", segments) + ";");
                    }
                }

                foreach (UnidirectionalAssociation association in Association.GetLinksToTargets(modelClass)
                         .OfType <UnidirectionalAssociation>()
                         .Where(x => x.Persistent && x.Target.IsDependentType))
                {
                    if (association.TargetMultiplicity == Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne || association.TargetMultiplicity == Sawczyn.EFDesigner.EFModel.Multiplicity.One)
                    {
                        Output($"modelBuilder.Entity<{modelClass.FullName}>().OwnsOne(x => x.{association.TargetPropertyName});");
                    }
                    else
                    {
                        Output($"// Dependent 1-many association seen ({association.TargetPropertyName}). Code generation still unsupported in designer.");
                    }
                }

                // ReSharper disable once LoopCanBePartlyConvertedToQuery
                foreach (BidirectionalAssociation association in Association.GetLinksToSources(modelClass)
                         .OfType <BidirectionalAssociation>()
                         .Where(x => x.Persistent))
                {
                    if (visited.Contains(association))
                    {
                        continue;
                    }

                    visited.Add(association);

                    // TODO: fix cascade delete
                    bool required = false;

                    segments.Clear();
                    segments.Add($"modelBuilder.Entity<{modelClass.FullName}>()");

                    switch (association.SourceMultiplicity) // realized by property on target
                    {
                    case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany:
                        // TODO: Implement many-to-many
                        if (association.TargetMultiplicity != Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany)
                        {
                            segments.Add($"HasMany(x => x.{association.SourcePropertyName})");
                        }
                        else
                        {
                            continue;
                        }

                        break;

                    case Sawczyn.EFDesigner.EFModel.Multiplicity.One:
                        segments.Add($"HasOne(x => x.{association.SourcePropertyName})");

                        break;

                    case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne:
                        segments.Add($"HasOne(x => x.{association.SourcePropertyName})");

                        break;

                        //case Sawczyn.EFDesigner.EFModel.Multiplicity.OneMany:
                        //   segments.Add($"HasMany(x => x.{association.SourcePropertyName})");
                        //   break;
                    }

                    switch (association.TargetMultiplicity) // realized by property on source
                    {
                    case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany:
                        // TODO: Implement many-to-many
                        if (association.SourceMultiplicity != Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany)
                        {
                            segments.Add($"WithMany(x => x.{association.TargetPropertyName})");
                        }
                        else
                        {
                            continue;
                        }

                        break;

                    case Sawczyn.EFDesigner.EFModel.Multiplicity.One:
                        segments.Add($"WithOne(x => x.{association.TargetPropertyName})");
                        required = true;

                        break;

                    case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne:
                        segments.Add($"WithOne(x => x.{association.TargetPropertyName})");

                        break;

                        //case Sawczyn.EFDesigner.EFModel.Multiplicity.OneMany:
                        //   segments.Add($"HasMany(x => x.{association.TargetPropertyName})");
                        //   break;
                    }

                    string foreignKeySegment = CreateForeignKeySegmentEFCore(association, foreignKeyColumns);

                    if (foreignKeySegment != null)
                    {
                        segments.Add(foreignKeySegment);
                    }

                    if (required)
                    {
                        segments.Add("IsRequired()");
                    }

                    if (association.TargetRole == EndpointRole.Principal || association.SourceRole == EndpointRole.Principal)
                    {
                        DeleteAction deleteAction = association.SourceRole == EndpointRole.Principal
                                             ? association.SourceDeleteAction
                                             : association.TargetDeleteAction;

                        switch (deleteAction)
                        {
                        case DeleteAction.None:
                            segments.Add("OnDelete(DeleteBehavior.Restrict)");

                            break;

                        case DeleteAction.Cascade:
                            segments.Add("OnDelete(DeleteBehavior.Cascade)");

                            break;
                        }
                    }

                    if (modelRoot.ChopMethodChains)
                    {
                        OutputChopped(segments);
                    }
                    else
                    {
                        Output(string.Join(".", segments) + ";");
                    }
                }
            }

            NL();

            Output("OnModelCreatedImpl(modelBuilder);");
            Output("}");
        }
Exemple #2
0
        void WriteDbContextEF6(ModelRoot modelRoot)
        {
            Output("using System;");
            Output("using System.Collections.Generic;");
            Output("using System.Linq;");
            Output("using System.ComponentModel.DataAnnotations.Schema;");
            Output("using System.Data.Entity;");
            Output("using System.Data.Entity.Infrastructure.Annotations;");
            NL();

            BeginNamespace(modelRoot.Namespace);

            if (!string.IsNullOrEmpty(modelRoot.Summary))
            {
                Output("/// <summary>");
                WriteCommentBody(modelRoot.Summary);
                Output("/// </summary>");

                if (!string.IsNullOrEmpty(modelRoot.Description))
                {
                    Output("/// <remarks>");
                    WriteCommentBody(modelRoot.Description);
                    Output("/// </remarks>");
                }
            }
            else
            {
                Output("/// <inheritdoc/>");
            }

            Output($"{modelRoot.EntityContainerAccess.ToString().ToLower()} partial class {modelRoot.EntityContainerName} : System.Data.Entity.DbContext");
            Output("{");

            PluralizationService pluralizationService = ModelRoot.PluralizationService;

            /***********************************************************************/
            // generate DBSets
            /***********************************************************************/

            ModelClass[] classesWithTables = null;

            switch (modelRoot.InheritanceStrategy)
            {
            case CodeStrategy.TablePerType:
                classesWithTables = modelRoot.Classes.Where(mc => !mc.IsDependentType).OrderBy(x => x.Name).ToArray();

                break;

            case CodeStrategy.TablePerConcreteType:
                classesWithTables = modelRoot.Classes.Where(mc => !mc.IsDependentType && !mc.IsAbstract).OrderBy(x => x.Name).ToArray();

                break;

            case CodeStrategy.TablePerHierarchy:
                classesWithTables = modelRoot.Classes.Where(mc => !mc.IsDependentType && mc.Superclass == null).OrderBy(x => x.Name).ToArray();

                break;
            }

            if (classesWithTables?.Any() == true)
            {
                Output("#region DbSets");

                foreach (ModelClass modelClass in modelRoot.Classes.Where(x => !x.IsDependentType).OrderBy(x => x.Name))
                {
                    string dbSetName;

                    if (!string.IsNullOrEmpty(modelClass.DbSetName))
                    {
                        dbSetName = modelClass.DbSetName;
                    }
                    else
                    {
                        dbSetName = pluralizationService?.IsSingular(modelClass.Name) == true
                                 ? pluralizationService.Pluralize(modelClass.Name)
                                 : modelClass.Name;
                    }

                    if (!string.IsNullOrEmpty(modelClass.Summary))
                    {
                        NL();
                        Output("/// <summary>");
                        WriteCommentBody($"Repository for {modelClass.FullName} - {modelClass.Summary}");
                        Output("/// </summary>");
                    }

                    Output($"{modelRoot.DbSetAccess.ToString().ToLower()} virtual System.Data.Entity.DbSet<{modelClass.FullName}> {dbSetName} {{ get; set; }}");
                }

                Output("#endregion DbSets");
                NL();
            }

            Output("#region Constructors");
            NL();
            Output("partial void CustomInit();");
            NL();

            /***********************************************************************/
            // constructors
            /***********************************************************************/

            if (!string.IsNullOrEmpty(modelRoot.ConnectionString) || !string.IsNullOrEmpty(modelRoot.ConnectionStringName))
            {
                string connectionString = string.IsNullOrEmpty(modelRoot.ConnectionString)
                                         ? $"Name={modelRoot.ConnectionStringName}"
                                         : modelRoot.ConnectionString;

                Output("/// <summary>");
                Output("/// Default connection string");
                Output("/// </summary>");
                Output($"public static string ConnectionString {{ get; set; }} = @\"{connectionString}\";");

                Output("/// <inheritdoc />");
                Output($"public {modelRoot.EntityContainerName}() : base(ConnectionString)");
                Output("{");
                Output($"Configuration.LazyLoadingEnabled = {modelRoot.LazyLoadingEnabled.ToString().ToLower()};");
                Output($"Configuration.ProxyCreationEnabled = {modelRoot.ProxyGenerationEnabled.ToString().ToLower()};");

                Output(modelRoot.DatabaseInitializerType == DatabaseInitializerKind.None
                      ? $"System.Data.Entity.Database.SetInitializer<{modelRoot.EntityContainerName}>(null);"
                      : $"System.Data.Entity.Database.SetInitializer<{modelRoot.EntityContainerName}>(new {modelRoot.EntityContainerName}DatabaseInitializer());");

                Output("CustomInit();");
                Output("}");
                NL();
            }
            else
            {
                Output($"#warning Default constructor not generated for {modelRoot.EntityContainerName} since no default connection string was specified in the model");
                NL();
            }

            Output("/// <inheritdoc />");
            Output($"public {modelRoot.EntityContainerName}(string connectionString) : base(connectionString)");
            Output("{");
            Output($"Configuration.LazyLoadingEnabled = {modelRoot.LazyLoadingEnabled.ToString().ToLower()};");
            Output($"Configuration.ProxyCreationEnabled = {modelRoot.ProxyGenerationEnabled.ToString().ToLower()};");

            Output(modelRoot.DatabaseInitializerType == DatabaseInitializerKind.None
                   ? $"System.Data.Entity.Database.SetInitializer<{modelRoot.EntityContainerName}>(null);"
                   : $"System.Data.Entity.Database.SetInitializer<{modelRoot.EntityContainerName}>(new {modelRoot.EntityContainerName}DatabaseInitializer());");

            Output("CustomInit();");
            Output("}");
            NL();

            Output("/// <inheritdoc />");
            Output($"public {modelRoot.EntityContainerName}(string connectionString, System.Data.Entity.Infrastructure.DbCompiledModel model) : base(connectionString, model)");
            Output("{");
            Output($"Configuration.LazyLoadingEnabled = {modelRoot.LazyLoadingEnabled.ToString().ToLower()};");
            Output($"Configuration.ProxyCreationEnabled = {modelRoot.ProxyGenerationEnabled.ToString().ToLower()};");

            Output(modelRoot.DatabaseInitializerType == DatabaseInitializerKind.None
                   ? $"System.Data.Entity.Database.SetInitializer<{modelRoot.EntityContainerName}>(null);"
                   : $"System.Data.Entity.Database.SetInitializer<{modelRoot.EntityContainerName}>(new {modelRoot.EntityContainerName}DatabaseInitializer());");

            Output("CustomInit();");
            Output("}");
            NL();

            Output("/// <inheritdoc />");
            Output($"public {modelRoot.EntityContainerName}(System.Data.Common.DbConnection existingConnection, bool contextOwnsConnection) : base(existingConnection, contextOwnsConnection)");
            Output("{");
            Output($"Configuration.LazyLoadingEnabled = {modelRoot.LazyLoadingEnabled.ToString().ToLower()};");
            Output($"Configuration.ProxyCreationEnabled = {modelRoot.ProxyGenerationEnabled.ToString().ToLower()};");

            Output(modelRoot.DatabaseInitializerType == DatabaseInitializerKind.None
                   ? $"System.Data.Entity.Database.SetInitializer<{modelRoot.EntityContainerName}>(null);"
                   : $"System.Data.Entity.Database.SetInitializer<{modelRoot.EntityContainerName}>(new {modelRoot.EntityContainerName}DatabaseInitializer());");

            Output("CustomInit();");
            Output("}");
            NL();

            Output("/// <inheritdoc />");
            Output($"public {modelRoot.EntityContainerName}(System.Data.Common.DbConnection existingConnection, System.Data.Entity.Infrastructure.DbCompiledModel model, bool contextOwnsConnection) : base(existingConnection, model, contextOwnsConnection)");
            Output("{");
            Output($"Configuration.LazyLoadingEnabled = {modelRoot.LazyLoadingEnabled.ToString().ToLower()};");
            Output($"Configuration.ProxyCreationEnabled = {modelRoot.ProxyGenerationEnabled.ToString().ToLower()};");

            Output(modelRoot.DatabaseInitializerType == DatabaseInitializerKind.None
                   ? $"System.Data.Entity.Database.SetInitializer<{modelRoot.EntityContainerName}>(null);"
                   : $"System.Data.Entity.Database.SetInitializer<{modelRoot.EntityContainerName}>(new {modelRoot.EntityContainerName}DatabaseInitializer());");

            Output("CustomInit();");
            Output("}");
            NL();

            Output("/// <inheritdoc />");
            Output($"public {modelRoot.EntityContainerName}(System.Data.Entity.Infrastructure.DbCompiledModel model) : base(model)");
            Output("{");
            Output($"Configuration.LazyLoadingEnabled = {modelRoot.LazyLoadingEnabled.ToString().ToLower()};");
            Output($"Configuration.ProxyCreationEnabled = {modelRoot.ProxyGenerationEnabled.ToString().ToLower()};");

            Output(modelRoot.DatabaseInitializerType == DatabaseInitializerKind.None
                   ? $"System.Data.Entity.Database.SetInitializer<{modelRoot.EntityContainerName}>(null);"
                   : $"System.Data.Entity.Database.SetInitializer<{modelRoot.EntityContainerName}>(new {modelRoot.EntityContainerName}DatabaseInitializer());");

            Output("CustomInit();");
            Output("}");
            NL();

            Output("/// <inheritdoc />");
            Output($"public {modelRoot.EntityContainerName}(System.Data.Entity.Core.Objects.ObjectContext objectContext, bool dbContextOwnsObjectContext) : base(objectContext, dbContextOwnsObjectContext)");
            Output("{");
            Output($"Configuration.LazyLoadingEnabled = {modelRoot.LazyLoadingEnabled.ToString().ToLower()};");
            Output($"Configuration.ProxyCreationEnabled = {modelRoot.ProxyGenerationEnabled.ToString().ToLower()};");

            Output(modelRoot.DatabaseInitializerType == DatabaseInitializerKind.None
                   ? $"System.Data.Entity.Database.SetInitializer<{modelRoot.EntityContainerName}>(null);"
                   : $"System.Data.Entity.Database.SetInitializer<{modelRoot.EntityContainerName}>(new {modelRoot.EntityContainerName}DatabaseInitializer());");

            Output("CustomInit();");
            Output("}");
            NL();
            Output("#endregion Constructors");
            NL();

            /***********************************************************************/
            // OnModelCreating
            /***********************************************************************/
            Output("partial void OnModelCreatingImpl(System.Data.Entity.DbModelBuilder modelBuilder);");
            Output("partial void OnModelCreatedImpl(System.Data.Entity.DbModelBuilder modelBuilder);");
            NL();

            Output("/// <inheritdoc />");
            Output("protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)");
            Output("{");
            Output("base.OnModelCreating(modelBuilder);");
            Output("OnModelCreatingImpl(modelBuilder);");
            NL();

            Output($"modelBuilder.HasDefaultSchema(\"{modelRoot.DatabaseSchema}\");");

            List <string> segments = new List <string>();

            List <Association> visited           = new List <Association>();
            List <string>      foreignKeyColumns = new List <string>();

            foreach (ModelClass modelClass in modelRoot.Classes.OrderBy(x => x.Name))
            {
                segments.Clear();
                foreignKeyColumns.Clear();
                NL();

                // class level
                bool isDependent = modelClass.IsDependentType;
                segments.Add($"modelBuilder.{(isDependent ? "ComplexType" : "Entity")}<{modelClass.FullName}>()");

                foreach (ModelAttribute transient in modelClass.Attributes.Where(x => !x.Persistent))
                {
                    segments.Add($"Ignore(t => t.{transient.Name})");
                }

                // note: this must come before the 'ToTable' call or there's a runtime error
                if (modelRoot.InheritanceStrategy == CodeStrategy.TablePerConcreteType && modelClass.Superclass != null)
                {
                    segments.Add("Map(x => x.MapInheritedProperties())");
                }

                if (classesWithTables.Contains(modelClass))
                {
                    segments.Add(modelClass.DatabaseSchema == modelClass.ModelRoot.DatabaseSchema
                               ? $"ToTable(\"{modelClass.TableName}\")"
                               : $"ToTable(\"{modelClass.TableName}\", \"{modelClass.DatabaseSchema}\")");

                    // primary key code segments must be output last, since HasKey returns a different type
                    List <ModelAttribute> identityAttributes = modelClass.IdentityAttributes.ToList();

                    if (identityAttributes.Count == 1)
                    {
                        segments.Add($"HasKey(t => t.{identityAttributes[0].Name})");
                    }
                    else if (identityAttributes.Count > 1)
                    {
                        segments.Add($"HasKey(t => new {{ t.{string.Join(", t.", identityAttributes.Select(ia => ia.Name))} }})");
                    }
                }

                if (segments.Count > 1)
                {
                    if (modelRoot.ChopMethodChains)
                    {
                        OutputChopped(segments);
                    }
                    else
                    {
                        Output(string.Join(".", segments) + ";");
                    }
                }

                if (modelClass.IsDependentType)
                {
                    continue;
                }

                // indexed properties
                foreach (ModelAttribute indexed in modelClass.Attributes.Where(x => x.Indexed && !x.IsIdentity))
                {
                    segments.Clear();

                    segments.Add(indexed.AutoProperty
                               ? $"modelBuilder.Entity<{modelClass.FullName}>().HasIndex(t => t.{indexed.Name})"
                               : $"modelBuilder.Entity<{modelClass.FullName}>().HasIndex(\"_{indexed.Name}\")");

                    if (indexed.IndexedUnique)
                    {
                        segments.Add("IsUnique()");
                    }

                    if (segments.Count > 1)
                    {
                        if (modelRoot.ChopMethodChains)
                        {
                            OutputChopped(segments);
                        }
                        else
                        {
                            Output(string.Join(".", segments) + ";");
                        }
                    }
                }

                // attribute level
                foreach (ModelAttribute modelAttribute in modelClass.Attributes.Where(x => x.Persistent && !SpatialTypes.Contains(x.Type)))
                {
                    segments.Clear();

                    if (modelAttribute.MaxLength > 0)
                    {
                        segments.Add($"HasMaxLength({modelAttribute.MaxLength})");
                    }

                    if (modelAttribute.Required)
                    {
                        segments.Add("IsRequired()");
                    }

                    if (modelAttribute.ColumnName != modelAttribute.Name && !string.IsNullOrEmpty(modelAttribute.ColumnName))
                    {
                        segments.Add($"HasColumnName(\"{modelAttribute.ColumnName}\")");
                    }

                    if (!string.IsNullOrEmpty(modelAttribute.ColumnType) && modelAttribute.ColumnType.ToLowerInvariant() != "default")
                    {
                        segments.Add($"HasColumnType(\"{modelAttribute.ColumnType}\")");
                    }

                    if (modelAttribute.Indexed && !modelAttribute.IsIdentity)
                    {
                        segments.Add("HasColumnAnnotation(\"Index\", new IndexAnnotation(new IndexAttribute()))");
                    }

                    if (modelAttribute.IsConcurrencyToken)
                    {
                        segments.Add("IsRowVersion()");
                    }

                    if (modelAttribute.IsIdentity)
                    {
                        segments.Add(modelAttribute.IdentityType == IdentityType.AutoGenerated
                                  ? "HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)"
                                  : "HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)");
                    }

                    if (segments.Any())
                    {
                        segments.Insert(0, $"modelBuilder.{(isDependent ? "ComplexType" : "Entity")}<{modelClass.FullName}>()");
                        segments.Insert(1, $"Property(t => t.{modelAttribute.Name})");

                        if (modelRoot.ChopMethodChains)
                        {
                            OutputChopped(segments);
                        }
                        else
                        {
                            Output(string.Join(".", segments) + ";");
                        }
                    }
                }

                if (!isDependent)
                {
                    // Navigation endpoints are distingished as Source and Target. They are also distinguished as Principal
                    // and Dependent. How do these map?
                    // In the case of one-to-one or zero-to-one-to-zero-to-one, it's model dependent and the user has to tell us
                    // In all other cases, we can tell by the cardinalities of the associations
                    // What matters is the Principal and Dependent classifications, so we look at those.
                    // Source and Target are accidents of where the user started drawing the association.

                    // navigation properties
                    // ReSharper disable once LoopCanBePartlyConvertedToQuery
                    foreach (UnidirectionalAssociation association in Association.GetLinksToTargets(modelClass)
                             .OfType <UnidirectionalAssociation>()
                             .Where(x => x.Persistent && !x.Target.IsDependentType))
                    {
                        if (visited.Contains(association))
                        {
                            continue;
                        }

                        visited.Add(association);

                        segments.Clear();
                        segments.Add($"modelBuilder.Entity<{modelClass.FullName}>()");

                        switch (association.TargetMultiplicity) // realized by property on source
                        {
                        case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany:
                            segments.Add($"HasMany(x => x.{association.TargetPropertyName})");

                            break;

                        case Sawczyn.EFDesigner.EFModel.Multiplicity.One:
                            segments.Add($"HasRequired(x => x.{association.TargetPropertyName})");

                            break;

                        case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne:
                            segments.Add($"HasOptional(x => x.{association.TargetPropertyName})");

                            break;

                            //case Sawczyn.EFDesigner.EFModel.Multiplicity.OneMany:
                            //   segments.Add($"HasMany(x => x.{association.TargetPropertyName})");
                            //   break;
                        }

                        switch (association.SourceMultiplicity) // realized by property on target, but no property on target
                        {
                        case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany:
                            segments.Add("WithMany()");

                            if (association.TargetMultiplicity == Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany)
                            {
                                if (modelClass == association.Source)
                                {
                                    segments.Add("Map(x => { "
                                                 + $@"x.ToTable(""{association.Source.Name}_x_{association.TargetPropertyName}""); "
                                                 + $@"x.MapLeftKey(""{association.Source.Name}_{association.Source.AllAttributes.FirstOrDefault(a => a.IsIdentity)?.Name}""); "
                                                 + $@"x.MapRightKey(""{association.Target.Name}_{association.Target.AllAttributes.FirstOrDefault(a => a.IsIdentity)?.Name}""); "
                                                 + "})");
                                }
                                else
                                {
                                    segments.Add("Map(x => { "
                                                 + $@"x.ToTable(""{association.Source.Name}_x_{association.TargetPropertyName}""); "
                                                 + $@"x.MapRightKey(""{association.Source.Name}_{association.Source.AllAttributes.FirstOrDefault(a => a.IsIdentity)?.Name}""); "
                                                 + $@"x.MapLeftKey(""{association.Target.Name}_{association.Target.AllAttributes.FirstOrDefault(a => a.IsIdentity)?.Name}""); "
                                                 + "})");
                                }
                            }

                            break;

                        case Sawczyn.EFDesigner.EFModel.Multiplicity.One:
                            if (association.TargetMultiplicity == Sawczyn.EFDesigner.EFModel.Multiplicity.One)
                            {
                                segments.Add(association.TargetRole == EndpointRole.Dependent
                                           ? "WithRequiredDependent()"
                                           : "WithRequiredPrincipal()");
                            }
                            else
                            {
                                segments.Add("WithRequired()");
                            }

                            break;

                        case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne:
                            if (association.TargetMultiplicity == Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne)
                            {
                                segments.Add(association.TargetRole == EndpointRole.Dependent
                                           ? "WithOptionalDependent()"
                                           : "WithOptionalPrincipal()");
                            }
                            else
                            {
                                segments.Add("WithOptional()");
                            }

                            break;

                            //case Sawczyn.EFDesigner.EFModel.Multiplicity.OneMany:
                            //   segments.Add("HasMany()");
                            //   break;
                        }

                        string foreignKeySegment = CreateForeignKeyColumnSegmentEF6(association, foreignKeyColumns);

                        if (foreignKeySegment != null)
                        {
                            segments.Add(foreignKeySegment);
                        }

                        // Certain associations cascade delete automatically. Also, the user may ask for it.
                        // We only generate a cascade delete call if the user asks for it.
                        if ((association.TargetDeleteAction != DeleteAction.Default && association.TargetRole == EndpointRole.Principal) ||
                            (association.SourceDeleteAction != DeleteAction.Default && association.SourceRole == EndpointRole.Principal))
                        {
                            string willCascadeOnDelete = association.TargetDeleteAction != DeleteAction.Default && association.TargetRole == EndpointRole.Principal
                                                     ? (association.TargetDeleteAction == DeleteAction.Cascade).ToString().ToLowerInvariant()
                                                     : (association.SourceDeleteAction == DeleteAction.Cascade).ToString().ToLowerInvariant();

                            segments.Add($"WillCascadeOnDelete({willCascadeOnDelete})");
                        }

                        if (modelRoot.ChopMethodChains)
                        {
                            OutputChopped(segments);
                        }
                        else
                        {
                            Output(string.Join(".", segments) + ";");
                        }
                    }

                    // ReSharper disable once LoopCanBePartlyConvertedToQuery
                    foreach (BidirectionalAssociation association in Association.GetLinksToSources(modelClass)
                             .OfType <BidirectionalAssociation>()
                             .Where(x => x.Persistent))
                    {
                        if (visited.Contains(association))
                        {
                            continue;
                        }

                        visited.Add(association);

                        segments.Clear();
                        segments.Add($"modelBuilder.Entity<{modelClass.FullName}>()");

                        switch (association.SourceMultiplicity) // realized by property on target
                        {
                        case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany:
                            segments.Add($"HasMany(x => x.{association.SourcePropertyName})");

                            break;

                        case Sawczyn.EFDesigner.EFModel.Multiplicity.One:
                            segments.Add($"HasRequired(x => x.{association.SourcePropertyName})");

                            break;

                        case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne:
                            segments.Add($"HasOptional(x => x.{association.SourcePropertyName})");

                            break;

                            //one or more constraint not supported in EF. TODO: make this possible ... later
                            //case Sawczyn.EFDesigner.EFModel.Multiplicity.OneMany:
                            //   segments.Add($"HasMany(x => x.{association.SourcePropertyName})");
                            //   break;
                        }

                        switch (association.TargetMultiplicity) // realized by property on source
                        {
                        case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany:
                            segments.Add($"WithMany(x => x.{association.TargetPropertyName})");

                            if (association.SourceMultiplicity == Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany)
                            {
                                if (modelClass == association.Source)
                                {
                                    segments.Add("Map(x => { "
                                                 + $@"x.ToTable(""{association.SourcePropertyName}_x_{association.TargetPropertyName}""); "
                                                 + $@"x.MapLeftKey(""{association.Source.Name}_{association.Source.AllAttributes.FirstOrDefault(a => a.IsIdentity)?.Name}""); "
                                                 + $@"x.MapRightKey(""{association.Target.Name}_{association.Target.AllAttributes.FirstOrDefault(a => a.IsIdentity)?.Name}""); "
                                                 + "})");
                                }
                                else
                                {
                                    segments.Add("Map(x => { "
                                                 + $@"x.ToTable(""{association.SourcePropertyName}_x_{association.TargetPropertyName}""); "
                                                 + $@"x.MapRightKey(""{association.Source.Name}_{association.Source.AllAttributes.FirstOrDefault(a => a.IsIdentity)?.Name}""); "
                                                 + $@"x.MapLeftKey(""{association.Target.Name}_{association.Target.AllAttributes.FirstOrDefault(a => a.IsIdentity)?.Name}""); "
                                                 + "})");
                                }
                            }

                            break;

                        case Sawczyn.EFDesigner.EFModel.Multiplicity.One:
                            if (association.SourceMultiplicity == Sawczyn.EFDesigner.EFModel.Multiplicity.One)
                            {
                                segments.Add(association.SourceRole == EndpointRole.Dependent
                                           ? $"WithRequiredDependent(x => x.{association.TargetPropertyName})"
                                           : $"WithRequiredPrincipal(x => x.{association.TargetPropertyName})");
                            }
                            else
                            {
                                segments.Add($"WithRequired(x => x.{association.TargetPropertyName})");
                            }

                            break;

                        case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne:
                            if (association.SourceMultiplicity == Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne)
                            {
                                segments.Add(association.SourceRole == EndpointRole.Dependent
                                           ? $"WithOptionalDependent(x => x.{association.TargetPropertyName})"
                                           : $"WithOptionalPrincipal(x => x.{association.TargetPropertyName})");
                            }
                            else
                            {
                                segments.Add($"WithOptional(x => x.{association.TargetPropertyName})");
                            }

                            break;

                            //one or more constraint not supported in EF. TODO: make this possible ... later
                            //case Sawczyn.EFDesigner.EFModel.Multiplicity.OneMany:
                            //   segments.Add($"HasMany(x => x.{association.TargetPropertyName})");
                            //   break;
                        }

                        string foreignKeySegment = CreateForeignKeyColumnSegmentEF6(association, foreignKeyColumns);

                        if (foreignKeySegment != null)
                        {
                            segments.Add(foreignKeySegment);
                        }

                        if ((association.TargetDeleteAction != DeleteAction.Default && association.TargetRole == EndpointRole.Principal) ||
                            (association.SourceDeleteAction != DeleteAction.Default && association.SourceRole == EndpointRole.Principal))
                        {
                            string willCascadeOnDelete = association.TargetDeleteAction != DeleteAction.Default && association.TargetRole == EndpointRole.Principal
                                                     ? (association.TargetDeleteAction == DeleteAction.Cascade).ToString().ToLowerInvariant()
                                                     : (association.SourceDeleteAction == DeleteAction.Cascade).ToString().ToLowerInvariant();

                            segments.Add($"WillCascadeOnDelete({willCascadeOnDelete})");
                        }

                        if (modelRoot.ChopMethodChains)
                        {
                            OutputChopped(segments);
                        }
                        else
                        {
                            Output(string.Join(".", segments) + ";");
                        }
                    }
                }
            }

            NL();

            Output("OnModelCreatedImpl(modelBuilder);");
            Output("}");

            Output("}");

            EndNamespace(modelRoot.Namespace);
        }
Exemple #3
0
        private void WriteModelCreateEF6(ModelRoot modelRoot, ModelClass[] classesWithTables)
        {
            Output("partial void OnModelCreatingImpl(System.Data.Entity.DbModelBuilder modelBuilder);");
            Output("partial void OnModelCreatedImpl(System.Data.Entity.DbModelBuilder modelBuilder);");
            NL();

            Output("/// <inheritdoc />");
            Output("protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)");
            Output("{");
            Output("base.OnModelCreating(modelBuilder);");
            Output("OnModelCreatingImpl(modelBuilder);");
            NL();

            Output($"modelBuilder.HasDefaultSchema(\"{modelRoot.DatabaseSchema}\");");

            List <string> segments = new List <string>();

            List <Association> visited                  = new List <Association>();
            List <string>      foreignKeyColumns        = new List <string>();
            List <string>      declaredShadowProperties = new List <string>();

            foreach (ModelClass modelClass in modelRoot.Classes.OrderBy(x => x.Name))
            {
                segments.Clear();
                foreignKeyColumns.Clear();
                declaredShadowProperties.Clear();
                NL();

                // class level
                bool isDependent = modelClass.IsDependentType;
                segments.Add($"modelBuilder.{(isDependent ? "ComplexType" : "Entity")}<{modelClass.FullName}>()");

                foreach (ModelAttribute transient in modelClass.Attributes.Where(x => !x.Persistent))
                {
                    segments.Add($"Ignore(t => t.{transient.Name})");
                }

                // note: this must come before the 'ToTable' call or there's a runtime error
                if (modelRoot.InheritanceStrategy == CodeStrategy.TablePerConcreteType && modelClass.Superclass != null)
                {
                    segments.Add("Map(x => x.MapInheritedProperties())");
                }

                if (classesWithTables.Contains(modelClass))
                {
                    segments.Add(modelClass.DatabaseSchema == modelClass.ModelRoot.DatabaseSchema
                                 ? $"ToTable(\"{modelClass.TableName}\")"
                                 : $"ToTable(\"{modelClass.TableName}\", \"{modelClass.DatabaseSchema}\")");

                    // primary key code segments must be output last, since HasKey returns a different type
                    List <ModelAttribute> identityAttributes = modelClass.IdentityAttributes.ToList();

                    if (identityAttributes.Count == 1)
                    {
                        segments.Add($"HasKey(t => t.{identityAttributes[0].Name})");
                    }
                    else if (identityAttributes.Count > 1)
                    {
                        segments.Add($"HasKey(t => new {{ t.{string.Join(", t.", identityAttributes.Select(ia => ia.Name))} }})");
                    }
                }

                if (segments.Count > 1)
                {
                    if (modelRoot.ChopMethodChains)
                    {
                        OutputChopped(segments);
                    }
                    else
                    {
                        Output(string.Join(".", segments) + ";");
                    }
                }

                if (modelClass.IsDependentType)
                {
                    continue;
                }

                // attribute level
                foreach (ModelAttribute modelAttribute in modelClass.Attributes.Where(x => x.Persistent && !SpatialTypes.Contains(x.Type)))
                {
                    segments.Clear();

                    if ((modelAttribute.MaxLength ?? 0) > 0)
                    {
                        segments.Add($"HasMaxLength({modelAttribute.MaxLength})");
                    }

                    if (modelAttribute.Required)
                    {
                        segments.Add("IsRequired()");
                    }

                    if (modelAttribute.ColumnName != modelAttribute.Name && !string.IsNullOrEmpty(modelAttribute.ColumnName))
                    {
                        segments.Add($"HasColumnName(\"{modelAttribute.ColumnName}\")");
                    }

                    if (!string.IsNullOrEmpty(modelAttribute.ColumnType) && modelAttribute.ColumnType.ToLowerInvariant() != "default")
                    {
                        segments.Add($"HasColumnType(\"{modelAttribute.ColumnType}\")");
                    }

                    if (modelAttribute.Indexed && !modelAttribute.IsIdentity)
                    {
                        segments.Add("HasColumnAnnotation(\"Index\", new IndexAnnotation(new IndexAttribute()))");
                    }

                    if (modelAttribute.IsConcurrencyToken)
                    {
                        segments.Add("IsRowVersion()");
                    }

                    if (modelAttribute.IsIdentity)
                    {
                        segments.Add(modelAttribute.IdentityType == IdentityType.AutoGenerated
                                    ? "HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)"
                                    : "HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)");
                    }

                    if (segments.Any())
                    {
                        segments.Insert(0, $"modelBuilder.{(isDependent ? "ComplexType" : "Entity")}<{modelClass.FullName}>()");
                        segments.Insert(1, $"Property(t => t.{modelAttribute.Name})");

                        if (modelRoot.ChopMethodChains)
                        {
                            OutputChopped(segments);
                        }
                        else
                        {
                            Output(string.Join(".", segments) + ";");
                        }
                    }

                    if (modelAttribute.Indexed && !modelAttribute.IsIdentity)
                    {
                        segments.Clear();

                        segments.Add($"modelBuilder.Entity<{modelClass.FullName}>().HasIndex(t => t.{modelAttribute.Name})");

                        if (modelAttribute.IndexedUnique)
                        {
                            segments.Add("IsUnique()");
                        }

                        if (segments.Count > 1)
                        {
                            if (modelRoot.ChopMethodChains)
                            {
                                OutputChopped(segments);
                            }
                            else
                            {
                                Output(string.Join(".", segments) + ";");
                            }
                        }
                    }
                }

                if (!isDependent)
                {
                    // Navigation endpoints are distingished as Source and Target. They are also distinguished as Principal
                    // and Dependent. How do these map?
                    // In the case of one-to-one or zero-to-one-to-zero-to-one, it's model dependent and the user has to tell us
                    // In all other cases, we can tell by the cardinalities of the associations
                    // What matters is the Principal and Dependent classifications, so we look at those.
                    // Source and Target are accidents of where the user started drawing the association.

                    // navigation properties

                    // ReSharper disable once LoopCanBePartlyConvertedToQuery
                    foreach (UnidirectionalAssociation association in Association.GetLinksToTargets(modelClass)
                             .OfType <UnidirectionalAssociation>()
                             .Where(x => x.Persistent && !x.Target.IsDependentType))
                    {
                        if (visited.Contains(association))
                        {
                            continue;
                        }

                        visited.Add(association);

                        segments.Clear();
                        segments.Add($"modelBuilder.Entity<{modelClass.FullName}>()");

                        switch (association.TargetMultiplicity) // realized by property on source
                        {
                        case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany:
                            segments.Add($"HasMany(x => x.{association.TargetPropertyName})");

                            break;

                        case Sawczyn.EFDesigner.EFModel.Multiplicity.One:
                            segments.Add($"HasRequired(x => x.{association.TargetPropertyName})");
                            break;

                        case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne:
                            segments.Add($"HasOptional(x => x.{association.TargetPropertyName})");

                            break;

                            //case Sawczyn.EFDesigner.EFModel.Multiplicity.OneMany:
                            //   segments.Add($"HasMany(x => x.{association.TargetPropertyName})");
                            //   break;
                        }

                        switch (association.SourceMultiplicity) // realized by property on target, but no property on target
                        {
                        case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany:
                            segments.Add("WithMany()");

                            if (association.TargetMultiplicity == Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany)
                            {
                                string tableMap  = $"{association.Source.Name}_x_{association.TargetPropertyName}";
                                string sourceMap = string.Join(", ", association.Source.AllIdentityAttributeNames.Select(n => $@"""{association.Source.Name}_{n}""").ToList());
                                string targetMap = string.Join(", ", association.Target.AllIdentityAttributeNames.Select(n => $@"""{association.Target.Name}_{n}""").ToList());

                                segments.Add(modelClass == association.Source
                                             ? $@"Map(x => {{ x.ToTable(""{tableMap}""); x.MapLeftKey({sourceMap}); x.MapRightKey({targetMap}); }})"
                                             : $@"Map(x => {{ x.ToTable(""{tableMap}""); x.MapLeftKey({targetMap}); x.MapRightKey({sourceMap}); }})");
                            }

                            break;

                        case Sawczyn.EFDesigner.EFModel.Multiplicity.One:
                            if (association.TargetMultiplicity == Sawczyn.EFDesigner.EFModel.Multiplicity.One)
                            {
                                segments.Add(association.TargetRole == EndpointRole.Dependent
                                             ? "WithRequiredDependent()"
                                             : "WithRequiredPrincipal()");
                            }
                            else
                            {
                                segments.Add("WithRequired()");
                            }

                            break;

                        case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne:
                            if (association.TargetMultiplicity == Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne)
                            {
                                segments.Add(association.TargetRole == EndpointRole.Dependent
                                             ? "WithOptionalDependent()"
                                             : "WithOptionalPrincipal()");
                            }
                            else
                            {
                                segments.Add("WithOptional()");
                            }

                            break;

                            //case Sawczyn.EFDesigner.EFModel.Multiplicity.OneMany:
                            //   segments.Add("HasMany()");
                            //   break;
                        }

                        string foreignKeySegment = CreateForeignKeySegmentEF6(association, foreignKeyColumns);

                        // can't shadow properties twice
                        if (foreignKeySegment != null)
                        {
                            if (!foreignKeySegment.Contains("MapKey"))
                            {
                                segments.Add(foreignKeySegment);
                            }
                            else if (!declaredShadowProperties.Contains(foreignKeySegment))
                            {
                                declaredShadowProperties.Add(foreignKeySegment);
                                segments.Add(foreignKeySegment);
                            }
                        }

                        // Certain associations cascade delete automatically. Also, the user may ask for it.
                        // We only generate a cascade delete call if the user asks for it.
                        if ((association.TargetDeleteAction != DeleteAction.Default && association.TargetRole == EndpointRole.Principal) || (association.SourceDeleteAction != DeleteAction.Default && association.SourceRole == EndpointRole.Principal))
                        {
                            string willCascadeOnDelete = association.TargetDeleteAction != DeleteAction.Default && association.TargetRole == EndpointRole.Principal
                                                ? (association.TargetDeleteAction == DeleteAction.Cascade).ToString().ToLowerInvariant()
                                                : (association.SourceDeleteAction == DeleteAction.Cascade).ToString().ToLowerInvariant();

                            segments.Add($"WillCascadeOnDelete({willCascadeOnDelete})");
                        }

                        if (modelRoot.ChopMethodChains)
                        {
                            OutputChopped(segments);
                        }
                        else
                        {
                            Output(string.Join(".", segments) + ";");
                        }
                    }

                    // ReSharper disable once LoopCanBePartlyConvertedToQuery
                    foreach (BidirectionalAssociation association in Association.GetLinksToSources(modelClass)
                             .OfType <BidirectionalAssociation>()
                             .Where(x => x.Persistent))
                    {
                        if (visited.Contains(association))
                        {
                            continue;
                        }

                        visited.Add(association);

                        segments.Clear();
                        segments.Add($"modelBuilder.Entity<{modelClass.FullName}>()");

                        switch (association.SourceMultiplicity) // realized by property on target
                        {
                        case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany:
                            segments.Add($"HasMany(x => x.{association.SourcePropertyName})");

                            break;

                        case Sawczyn.EFDesigner.EFModel.Multiplicity.One:
                            segments.Add($"HasRequired(x => x.{association.SourcePropertyName})");
                            break;

                        case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne:
                            segments.Add($"HasOptional(x => x.{association.SourcePropertyName})");

                            break;

                            //one or more constraint not supported in EF.
                            // TODO: make this possible ... later
                            //case Sawczyn.EFDesigner.EFModel.Multiplicity.OneMany:
                            //   segments.Add($"HasMany(x => x.{association.SourcePropertyName})");
                            //   break;
                        }

                        switch (association.TargetMultiplicity) // realized by property on source
                        {
                        case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany:
                            segments.Add($"WithMany(x => x.{association.TargetPropertyName})");

                            if (association.SourceMultiplicity == Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany)
                            {
                                string tableMap  = $"{association.SourcePropertyName}_x_{association.TargetPropertyName}";
                                string sourceMap = string.Join(", ", association.Source.AllIdentityAttributeNames.Select(n => $@"""{association.Source.Name}_{n}""").ToList());
                                string targetMap = string.Join(", ", association.Target.AllIdentityAttributeNames.Select(n => $@"""{association.Target.Name}_{n}""").ToList());

                                segments.Add(modelClass == association.Source
                                             ? $@"Map(x => {{ x.ToTable(""{tableMap}""); x.MapLeftKey({sourceMap}); x.MapRightKey({targetMap}); }})"
                                             : $@"Map(x => {{ x.ToTable(""{tableMap}""); x.MapLeftKey({targetMap}); x.MapRightKey({sourceMap}); }})");
                            }

                            break;

                        case Sawczyn.EFDesigner.EFModel.Multiplicity.One:
                            if (association.SourceMultiplicity == Sawczyn.EFDesigner.EFModel.Multiplicity.One)
                            {
                                segments.Add(association.SourceRole == EndpointRole.Dependent
                                             ? $"WithRequiredDependent(x => x.{association.TargetPropertyName})"
                                             : $"WithRequiredPrincipal(x => x.{association.TargetPropertyName})");
                            }
                            else
                            {
                                segments.Add($"WithRequired(x => x.{association.TargetPropertyName})");
                            }

                            break;

                        case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne:
                            if (association.SourceMultiplicity == Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne)
                            {
                                segments.Add(association.SourceRole == EndpointRole.Dependent
                                             ? $"WithOptionalDependent(x => x.{association.TargetPropertyName})"
                                             : $"WithOptionalPrincipal(x => x.{association.TargetPropertyName})");
                            }
                            else
                            {
                                segments.Add($"WithOptional(x => x.{association.TargetPropertyName})");
                            }

                            break;

                            //one or more constraint not supported in EF. TODO: make this possible ... later
                            //case Sawczyn.EFDesigner.EFModel.Multiplicity.OneMany:
                            //   segments.Add($"HasMany(x => x.{association.TargetPropertyName})");
                            //   break;
                        }

                        string foreignKeySegment = CreateForeignKeySegmentEF6(association, foreignKeyColumns);

                        // can't shadow properties twice
                        if (foreignKeySegment != null)
                        {
                            if (!foreignKeySegment.Contains("MapKey"))
                            {
                                segments.Add(foreignKeySegment);
                            }
                            else if (!declaredShadowProperties.Contains(foreignKeySegment))
                            {
                                declaredShadowProperties.Add(foreignKeySegment);
                                segments.Add(foreignKeySegment);
                            }
                        }

                        if ((association.TargetDeleteAction != DeleteAction.Default && association.TargetRole == EndpointRole.Principal) || (association.SourceDeleteAction != DeleteAction.Default && association.SourceRole == EndpointRole.Principal))
                        {
                            string willCascadeOnDelete = association.TargetDeleteAction != DeleteAction.Default && association.TargetRole == EndpointRole.Principal
                                                ? (association.TargetDeleteAction == DeleteAction.Cascade).ToString().ToLowerInvariant()
                                                : (association.SourceDeleteAction == DeleteAction.Cascade).ToString().ToLowerInvariant();

                            segments.Add($"WillCascadeOnDelete({willCascadeOnDelete})");
                        }

                        if (modelRoot.ChopMethodChains)
                        {
                            OutputChopped(segments);
                        }
                        else
                        {
                            Output(string.Join(".", segments) + ";");
                        }
                    }
                }
            }

            NL();

            Output("OnModelCreatedImpl(modelBuilder);");
            Output("}");
        }
Exemple #4
0
// Revisit if/when supported in EFCore

// void WriteDatabaseInitializerEFCore(ModelRoot modelRoot)
// {
//    Output("using System.Data.Entity;");
//    NL();
//
//    BeginNamespace(modelRoot.Namespace);
//
//    if (modelRoot.DatabaseInitializerType == DatabaseInitializerKind.MigrateDatabaseToLatestVersion)
//       Output($"public partial class {modelRoot.EntityContainerName}DatabaseInitializer : MigrateDatabaseToLatestVersion<{modelRoot.EntityContainerName}, {modelRoot.EntityContainerName}DbMigrationConfiguration>");
//    else
//       Output($"public partial class {modelRoot.EntityContainerName}DatabaseInitializer : {modelRoot.DatabaseInitializerType}<{modelRoot.EntityContainerName}>");
//
//    Output("{");
//    Output("}");
//    EndNamespace(modelRoot.Namespace);
// }
//
// void WriteMigrationConfigurationEFCore(ModelRoot modelRoot)
// {
//    //if (modelRoot.DatabaseInitializerType != DatabaseInitializerKind.MigrateDatabaseToLatestVersion)
//    //   return;
//
//    Output("using System.Data.Entity.Migrations;");
//    NL();
//
//    BeginNamespace(modelRoot.Namespace);
//    Output("public sealed partial class {0}DbMigrationConfiguration : DbMigrationsConfiguration<{0}>", modelRoot.EntityContainerName);
//
//    Output("{");
//    Output("partial void Init();");
//    NL();
//
//    Output("public {0}DbMigrationConfiguration()", modelRoot.EntityContainerName);
//    Output("{");
//    Output("AutomaticMigrationsEnabled = {0};", modelRoot.AutomaticMigrationsEnabled.ToString().ToLower());
//    Output("AutomaticMigrationDataLossAllowed = false;");
//    Output("Init();");
//    Output("}");
//
//    Output("}");
//    EndNamespace(modelRoot.Namespace);
// }

        void WriteDbContextEFCore(ModelRoot modelRoot)
        {
            List <string> segments = new List <string>();

            Output("using System;");
            Output("using System.Collections.Generic;");
            Output("using System.Linq;");
            Output("using System.ComponentModel.DataAnnotations.Schema;");
            Output("using Microsoft.EntityFrameworkCore;");
            NL();

            BeginNamespace(modelRoot.Namespace);

            if (!string.IsNullOrEmpty(modelRoot.Summary))
            {
                Output("/// <summary>");
                WriteCommentBody(modelRoot.Summary);
                Output("/// </summary>");

                if (!string.IsNullOrEmpty(modelRoot.Description))
                {
                    Output("/// <remarks>");
                    WriteCommentBody(modelRoot.Description);
                    Output("/// </remarks>");
                }
            }
            else
            {
                Output("/// <inheritdoc/>");
            }

            Output($"{modelRoot.EntityContainerAccess.ToString().ToLower()} partial class {modelRoot.EntityContainerName} : Microsoft.EntityFrameworkCore.DbContext");
            Output("{");

            PluralizationService pluralizationService = ModelRoot.PluralizationService;

            /***********************************************************************/
            // generate DBSets
            /***********************************************************************/

            ModelClass[] classesWithTables = null;

            switch (modelRoot.InheritanceStrategy)
            {
            case CodeStrategy.TablePerType:
                classesWithTables = modelRoot.Classes.Where(mc => !mc.IsDependentType).OrderBy(x => x.Name).ToArray();

                break;

            case CodeStrategy.TablePerConcreteType:
                classesWithTables = modelRoot.Classes.Where(mc => !mc.IsDependentType && !mc.IsAbstract).OrderBy(x => x.Name).ToArray();

                break;

            case CodeStrategy.TablePerHierarchy:
                classesWithTables = modelRoot.Classes.Where(mc => !mc.IsDependentType && mc.Superclass == null).OrderBy(x => x.Name).ToArray();

                break;
            }

            if (classesWithTables != null)
            {
                Output("#region DbSets");

                foreach (ModelClass modelClass in modelRoot.Classes.Where(x => !x.IsDependentType).OrderBy(x => x.Name))
                {
                    string dbSetName;

                    if (!string.IsNullOrEmpty(modelClass.DbSetName))
                    {
                        dbSetName = modelClass.DbSetName;
                    }
                    else
                    {
                        dbSetName = pluralizationService?.IsSingular(modelClass.Name) == true
                                 ? pluralizationService.Pluralize(modelClass.Name)
                                 : modelClass.Name;
                    }

                    if (!string.IsNullOrEmpty(modelClass.Summary))
                    {
                        NL();
                        Output("/// <summary>");
                        WriteCommentBody($"Repository for {modelClass.FullName} - {modelClass.Summary}");
                        Output("/// </summary>");
                    }

                    Output($"{modelRoot.DbSetAccess.ToString().ToLower()} virtual Microsoft.EntityFrameworkCore.DbSet<{modelClass.FullName}> {dbSetName} {{ get; set; }}");
                }

                Output("#endregion DbSets");
                NL();
            }

            /***********************************************************************/
            // constructors
            /***********************************************************************/

            if (!string.IsNullOrEmpty(modelRoot.ConnectionString) || !string.IsNullOrEmpty(modelRoot.ConnectionStringName))
            {
                string connectionString = string.IsNullOrEmpty(modelRoot.ConnectionString)
                                         ? $"Name={modelRoot.ConnectionStringName}"
                                         : modelRoot.ConnectionString;

                Output("/// <summary>");
                Output("/// Default connection string");
                Output("/// </summary>");
                Output($"public static string ConnectionString {{ get; set; }} = @\"{connectionString}\";");
                NL();
            }

            //Output("/// <inheritdoc />");
            //Output($"public {modelRoot.EntityContainerName}() : base()");
            //Output("{");
            //Output("}");
            //NL();
            Output("/// <inheritdoc />");
            Output($"public {modelRoot.EntityContainerName}(DbContextOptions<{modelRoot.EntityContainerName}> options) : base(options)");
            Output("{");
            Output("}");
            NL();

            Output("partial void CustomInit(DbContextOptionsBuilder optionsBuilder);");
            NL();

            Output("/// <inheritdoc />");
            Output("protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)");
            Output("{");

            segments.Clear();

            if (modelRoot.GetEntityFrameworkPackageVersionNum() >= 2.1 && modelRoot.LazyLoadingEnabled)
            {
                segments.Add("UseLazyLoadingProxies()");
            }

            if (segments.Any())
            {
                segments.Insert(0, "optionsBuilder");

                if (modelRoot.ChopMethodChains)
                {
                    OutputChopped(segments);
                }
                else
                {
                    Output(string.Join(".", segments) + ";");
                }

                NL();
            }

            Output("CustomInit(optionsBuilder);");
            Output("}");
            NL();

            Output("partial void OnModelCreatingImpl(ModelBuilder modelBuilder);");
            Output("partial void OnModelCreatedImpl(ModelBuilder modelBuilder);");
            NL();

            /***********************************************************************/
            // OnModelCreating
            /***********************************************************************/
            Output("/// <inheritdoc />");
            Output("protected override void OnModelCreating(ModelBuilder modelBuilder)");
            Output("{");
            Output("base.OnModelCreating(modelBuilder);");
            Output("OnModelCreatingImpl(modelBuilder);");
            NL();

            Output($"modelBuilder.HasDefaultSchema(\"{modelRoot.DatabaseSchema}\");");

            List <Association> visited           = new List <Association>();
            List <string>      foreignKeyColumns = new List <string>();

            foreach (ModelClass modelClass in modelRoot.Classes.OrderBy(x => x.Name))
            {
                segments.Clear();
                foreignKeyColumns.Clear();
                NL();

                // class level
                segments.Add($"modelBuilder.{(modelClass.IsDependentType ? "Owned" : "Entity")}<{modelClass.FullName}>()");

                foreach (ModelAttribute transient in modelClass.Attributes.Where(x => !x.Persistent))
                {
                    segments.Add($"Ignore(t => t.{transient.Name})");
                }

                if (!modelClass.IsDependentType)
                {
                    // note: this must come before the 'ToTable' call or there's a runtime error
                    if (modelRoot.InheritanceStrategy == CodeStrategy.TablePerConcreteType && modelClass.Superclass != null)
                    {
                        segments.Add("Map(x => x.MapInheritedProperties())");
                    }

                    if (classesWithTables.Contains(modelClass))
                    {
                        segments.Add(modelClass.DatabaseSchema == modelClass.ModelRoot.DatabaseSchema
                                  ? $"ToTable(\"{modelClass.TableName}\")"
                                  : $"ToTable(\"{modelClass.TableName}\", \"{modelClass.DatabaseSchema}\")");

                        // primary key code segments must be output last, since HasKey returns a different type
                        List <ModelAttribute> identityAttributes = modelClass.IdentityAttributes.ToList();

                        if (identityAttributes.Count == 1)
                        {
                            segments.Add($"HasKey(t => t.{identityAttributes[0].Name})");
                        }
                        else if (identityAttributes.Count > 1)
                        {
                            segments.Add($"HasKey(t => new {{ t.{string.Join(", t.", identityAttributes.Select(ia => ia.Name))} }})");
                        }
                    }
                }

                if (segments.Count > 1 || modelClass.IsDependentType)
                {
                    if (modelRoot.ChopMethodChains)
                    {
                        OutputChopped(segments);
                    }
                    else
                    {
                        Output(string.Join(".", segments) + ";");
                    }
                }

                if (modelClass.IsDependentType)
                {
                    continue;
                }

                // indexed properties
                foreach (ModelAttribute indexed in modelClass.Attributes.Where(x => x.Indexed && !x.IsIdentity))
                {
                    segments.Clear();
                    segments.Add($"modelBuilder.Entity<{modelClass.FullName}>().HasIndex(t => t.{indexed.Name})");

                    if (indexed.IndexedUnique)
                    {
                        segments.Add("IsUnique()");
                    }

                    if (modelRoot.ChopMethodChains)
                    {
                        OutputChopped(segments);
                    }
                    else
                    {
                        Output(string.Join(".", segments) + ";");
                    }
                }

                // attribute level
                foreach (ModelAttribute modelAttribute in modelClass.Attributes.Where(x => x.Persistent && !SpatialTypes.Contains(x.Type)))
                {
                    segments.Clear();

                    if (modelAttribute.MaxLength > 0)
                    {
                        segments.Add($"HasMaxLength({((int?)modelAttribute.MaxLength).Value})");
                    }

                    if (modelAttribute.Required)
                    {
                        segments.Add("IsRequired()");
                    }

                    if (modelAttribute.ColumnName != modelAttribute.Name && !string.IsNullOrEmpty(modelAttribute.ColumnName))
                    {
                        segments.Add($"HasColumnName(\"{modelAttribute.ColumnName}\")");
                    }

                    if (!modelAttribute.AutoProperty)
                    {
                        segments.Add($"HasField(\"_{modelAttribute.Name}\")");
                        segments.Add($"UsePropertyAccessMode(PropertyAccessMode.{(modelAttribute.PersistencePoint == PersistencePointType.Field ? "Field" : "Property")})");
                    }

                    if (!string.IsNullOrEmpty(modelAttribute.ColumnType) && modelAttribute.ColumnType.ToLowerInvariant() != "default")
                    {
                        if (modelAttribute.ColumnType.ToLowerInvariant() == "varchar" || modelAttribute.ColumnType.ToLowerInvariant() == "nvarchar" || modelAttribute.ColumnType.ToLowerInvariant() == "char")
                        {
                            segments.Add($"HasColumnType(\"{modelAttribute.ColumnType}({(modelAttribute.MaxLength > 0 ? modelAttribute.MaxLength.ToString() : "max")})\")");
                        }
                        else
                        {
                            segments.Add($"HasColumnType(\"{modelAttribute.ColumnType}\")");
                        }
                    }

                    if (modelAttribute.IsConcurrencyToken)
                    {
                        segments.Add("IsRowVersion()");
                    }

                    if (modelAttribute.IsIdentity)
                    {
                        segments.Add(modelAttribute.IdentityType == IdentityType.AutoGenerated
                                  ? "ValueGeneratedOnAdd()"
                                  : "ValueGeneratedNever()");
                    }

                    if (segments.Any())
                    {
                        segments.Insert(0, $"modelBuilder.{(modelClass.IsDependentType ? "Owned" : "Entity")}<{modelClass.FullName}>()");
                        segments.Insert(1, $"Property(t => t.{modelAttribute.Name})");

                        if (modelRoot.ChopMethodChains)
                        {
                            OutputChopped(segments);
                        }
                        else
                        {
                            Output(string.Join(".", segments) + ";");
                        }
                    }
                }

                bool hasDefinedConcurrencyToken = modelClass.AllAttributes.Any(x => x.IsConcurrencyToken);

                if (!hasDefinedConcurrencyToken && modelClass.EffectiveConcurrency == ConcurrencyOverride.Optimistic)
                {
                    Output($@"modelBuilder.Entity<{modelClass.FullName}>().Property<byte[]>(""Timestamp"").IsConcurrencyToken();");
                }

                // Navigation endpoints are distingished as Source and Target. They are also distinguished as Principal
                // and Dependent. How do these map?
                // In the case of one-to-one or zero-to-one-to-zero-to-one, it's model dependent and the user has to tell us
                // In all other cases, we can tell by the cardinalities of the associations
                // What matters is the Principal and Dependent classifications, so we look at those.
                // Source and Target are accidents of where the user started drawing the association.

                // navigation properties
                // ReSharper disable once LoopCanBePartlyConvertedToQuery
                foreach (UnidirectionalAssociation association in Association.GetLinksToTargets(modelClass)
                         .OfType <UnidirectionalAssociation>()
                         .Where(x => x.Persistent && !x.Target.IsDependentType))
                {
                    if (visited.Contains(association))
                    {
                        continue;
                    }

                    visited.Add(association);

                    segments.Clear();
                    segments.Add($"modelBuilder.Entity<{modelClass.FullName}>()");
                    bool required = false;

                    switch (association.TargetMultiplicity) // realized by property on source
                    {
                    case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany:
                        // TODO: Implement many-to-many
                        if (association.SourceMultiplicity != Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany)
                        {
                            segments.Add($"HasMany(x => x.{association.TargetPropertyName})");
                        }
                        else
                        {
                            continue;
                        }

                        break;

                    case Sawczyn.EFDesigner.EFModel.Multiplicity.One:
                        segments.Add($"HasOne(x => x.{association.TargetPropertyName})");

                        break;

                    case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne:
                        segments.Add($"HasOne(x => x.{association.TargetPropertyName})");

                        break;

                        //case Sawczyn.EFDesigner.EFModel.Multiplicity.OneMany:
                        //   segments.Add($"HasMany(x => x.{association.TargetPropertyName})");
                        //   break;
                    }

                    string columnPrefix = association.SourceRole == EndpointRole.Dependent
                                        ? ""
                                        : association.Target.Name + "_";

                    switch (association.SourceMultiplicity) // realized by shadow property on target
                    {
                    case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany:
                        // TODO: Implement many-to-many
                        if (association.TargetMultiplicity != Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany)
                        {
                            segments.Add("WithMany()");
                            segments.Add($"HasForeignKey(\"{columnPrefix}{association.TargetPropertyName}_Id\")");
                        }
                        else
                        {
                            continue;
                        }

                        break;

                    case Sawczyn.EFDesigner.EFModel.Multiplicity.One:
                        segments.Add("WithOne()");

                        segments.Add(association.TargetMultiplicity != Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany
                                     ? $"HasForeignKey<{association.Source.FullName}>(\"{columnPrefix}{association.TargetPropertyName}_Id\")"
                                     : $"HasForeignKey(\"{columnPrefix}{association.TargetPropertyName}_Id\")");

                        required = true;

                        break;

                    case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne:
                        segments.Add("WithOne()");

                        segments.Add(association.TargetMultiplicity != Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany
                                     ? $"HasForeignKey<{association.Source.FullName}>(\"{columnPrefix}{association.TargetPropertyName}_Id\")"
                                     : $"HasForeignKey(\"{columnPrefix}{association.TargetPropertyName}_Id\")");

                        break;

                        //case Sawczyn.EFDesigner.EFModel.Multiplicity.OneMany:
                        //   segments.Add("HasMany()");
                        //   break;
                    }

                    if (required)
                    {
                        segments.Add("IsRequired()");
                    }

                    if (association.TargetRole == EndpointRole.Principal || association.SourceRole == EndpointRole.Principal)
                    {
                        DeleteAction deleteAction = association.SourceRole == EndpointRole.Principal
                                                 ? association.SourceDeleteAction
                                                 : association.TargetDeleteAction;

                        switch (deleteAction)
                        {
                        case DeleteAction.None:
                            segments.Add("OnDelete(DeleteBehavior.Restrict)");

                            break;

                        case DeleteAction.Cascade:
                            segments.Add("OnDelete(DeleteBehavior.Cascade)");

                            break;
                        }
                    }

                    if (modelRoot.ChopMethodChains)
                    {
                        OutputChopped(segments);
                    }
                    else
                    {
                        Output(string.Join(".", segments) + ";");
                    }
                }

                foreach (UnidirectionalAssociation association in Association.GetLinksToTargets(modelClass)
                         .OfType <UnidirectionalAssociation>()
                         .Where(x => x.Persistent && x.Target.IsDependentType))
                {
                    if (association.TargetMultiplicity == Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne ||
                        association.TargetMultiplicity == Sawczyn.EFDesigner.EFModel.Multiplicity.One)
                    {
                        Output($"modelBuilder.Entity<{modelClass.FullName}>().OwnsOne(x => x.{association.TargetPropertyName});");
                    }
                    else
                    {
                        Output($"// Dependent 1-many association seen ({association.TargetPropertyName}). Code generation still unsupported in designer.");
                    }
                }

                // ReSharper disable once LoopCanBePartlyConvertedToQuery
                foreach (BidirectionalAssociation association in Association.GetLinksToSources(modelClass)
                         .OfType <BidirectionalAssociation>()
                         .Where(x => x.Persistent))
                {
                    if (visited.Contains(association))
                    {
                        continue;
                    }

                    visited.Add(association);

                    // TODO: fix cascade delete
                    bool required = false;

                    segments.Clear();
                    segments.Add($"modelBuilder.Entity<{modelClass.FullName}>()");

                    switch (association.SourceMultiplicity) // realized by property on target
                    {
                    case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany:
                        // TODO: Implement many-to-many
                        if (association.TargetMultiplicity != Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany)
                        {
                            segments.Add($"HasMany(x => x.{association.SourcePropertyName})");
                        }
                        else
                        {
                            continue;
                        }

                        break;

                    case Sawczyn.EFDesigner.EFModel.Multiplicity.One:
                        segments.Add($"HasOne(x => x.{association.SourcePropertyName})");

                        break;

                    case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne:
                        segments.Add($"HasOne(x => x.{association.SourcePropertyName})");

                        break;

                        //case Sawczyn.EFDesigner.EFModel.Multiplicity.OneMany:
                        //   segments.Add($"HasMany(x => x.{association.SourcePropertyName})");
                        //   break;
                    }

                    switch (association.TargetMultiplicity) // realized by property on source
                    {
                    case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany:
                        // TODO: Implement many-to-many
                        if (association.SourceMultiplicity != Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany)
                        {
                            segments.Add($"WithMany(x => x.{association.TargetPropertyName})");
                        }
                        else
                        {
                            continue;
                        }

                        break;

                    case Sawczyn.EFDesigner.EFModel.Multiplicity.One:
                        segments.Add($"WithOne(x => x.{association.TargetPropertyName})");
                        required = true;

                        break;

                    case Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroOne:
                        segments.Add($"WithOne(x => x.{association.TargetPropertyName})");

                        break;

                        //case Sawczyn.EFDesigner.EFModel.Multiplicity.OneMany:
                        //   segments.Add($"HasMany(x => x.{association.TargetPropertyName})");
                        //   break;
                    }

                    string foreignKeySegment = CreateForeignKeyColumnSegmentEFCore(association
                                                                                   , foreignKeyColumns
                                                                                   , association.SourceMultiplicity != Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany &&
                                                                                   association.TargetMultiplicity != Sawczyn.EFDesigner.EFModel.Multiplicity.ZeroMany);

                    if (foreignKeySegment != null)
                    {
                        segments.Add(foreignKeySegment);
                    }

                    if (required)
                    {
                        segments.Add("IsRequired()");
                    }

                    if (association.TargetRole == EndpointRole.Principal || association.SourceRole == EndpointRole.Principal)
                    {
                        DeleteAction deleteAction = association.SourceRole == EndpointRole.Principal
                                                 ? association.SourceDeleteAction
                                                 : association.TargetDeleteAction;

                        switch (deleteAction)
                        {
                        case DeleteAction.None:
                            segments.Add("OnDelete(DeleteBehavior.Restrict)");

                            break;

                        case DeleteAction.Cascade:
                            segments.Add("OnDelete(DeleteBehavior.Cascade)");

                            break;
                        }
                    }

                    if (modelRoot.ChopMethodChains)
                    {
                        OutputChopped(segments);
                    }
                    else
                    {
                        Output(string.Join(".", segments) + ";");
                    }
                }
            }

            NL();

            Output("OnModelCreatedImpl(modelBuilder);");
            Output("}");

            Output("}");

            EndNamespace(modelRoot.Namespace);
        }