├── .gitignore ├── EFCoreColumnOrder.sln ├── EFCoreColumnOrder ├── CustomAnnotationNames.cs ├── CustomAnnotationProvider.cs ├── CustomPropertyBuilderExtensions.cs ├── CustomSqlServerMigrationsSqlGenerator.cs └── EFCoreColumnOrder.csproj ├── Images ├── migration1.png └── migration2.png ├── Readme.md └── Sample ├── ConsoleSample ├── ConsoleSample.csproj ├── Program.cs └── appsettings.json └── RepoSample ├── AppDbContext.cs ├── Configurations ├── ConfigurationBase.cs ├── OrderConfiguration.cs └── ProductConfiguration.cs ├── Constants.cs ├── Entities ├── EntityBase.cs ├── Order.cs └── Product.cs ├── Migrations ├── 20200214114303_First-Migration.Designer.cs ├── 20200214114303_First-Migration.cs ├── AppDbContextModelSnapshot.cs └── Infrastructure │ ├── AppDbContextFactory.cs │ ├── DesignTimeConfigurationProvider.cs │ └── DesignTimeDbContextFactoryBase.cs ├── ModelBuilderExtensions.cs └── RepoSample.csproj /.gitignore: -------------------------------------------------------------------------------- 1 | # Folders 2 | artifacts/ 3 | bin/ 4 | obj/ 5 | .dotnet/ 6 | .nuget/ 7 | .packages/ 8 | .tools/ 9 | .vs/ 10 | .vscode/ 11 | node_modules/ 12 | BenchmarkDotNet.Artifacts/ 13 | .gradle/ 14 | src/SignalR/clients/**/dist/ 15 | modules/ 16 | 17 | # File extensions 18 | *.aps 19 | *.binlog 20 | *.dll 21 | *.DS_Store 22 | *.exe 23 | *.idb 24 | *.lib 25 | *.log 26 | *.pch 27 | *.pdb 28 | *.pidb 29 | *.psess 30 | *.res 31 | *.snk 32 | *.suo 33 | *.tlog 34 | *.user 35 | *.userprefs 36 | *.vspx 37 | 38 | # Specific files, typically generated by tools 39 | launchSettings.json 40 | msbuild.ProjectImports.zip 41 | StyleCop.Cache 42 | UpgradeLog.htm 43 | .idea -------------------------------------------------------------------------------- /EFCoreColumnOrder.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29806.167 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFCoreColumnOrder", "EFCoreColumnOrder\EFCoreColumnOrder.csproj", "{A656B7B4-8D0F-488B-9781-DB67F7ED6B00}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sample", "Sample", "{2CEE9354-6766-4458-92E7-204901458CDA}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RepoSample", "Sample\RepoSample\RepoSample.csproj", "{AC2AB46D-A971-476D-8FBC-36740E207A69}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleSample", "Sample\ConsoleSample\ConsoleSample.csproj", "{96561F27-749A-4F34-A81E-DA1E53A83AE9}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {A656B7B4-8D0F-488B-9781-DB67F7ED6B00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {A656B7B4-8D0F-488B-9781-DB67F7ED6B00}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {A656B7B4-8D0F-488B-9781-DB67F7ED6B00}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {A656B7B4-8D0F-488B-9781-DB67F7ED6B00}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {AC2AB46D-A971-476D-8FBC-36740E207A69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {AC2AB46D-A971-476D-8FBC-36740E207A69}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {AC2AB46D-A971-476D-8FBC-36740E207A69}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {AC2AB46D-A971-476D-8FBC-36740E207A69}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {96561F27-749A-4F34-A81E-DA1E53A83AE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {96561F27-749A-4F34-A81E-DA1E53A83AE9}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {96561F27-749A-4F34-A81E-DA1E53A83AE9}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {96561F27-749A-4F34-A81E-DA1E53A83AE9}.Release|Any CPU.Build.0 = Release|Any CPU 32 | EndGlobalSection 33 | GlobalSection(SolutionProperties) = preSolution 34 | HideSolutionNode = FALSE 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {AC2AB46D-A971-476D-8FBC-36740E207A69} = {2CEE9354-6766-4458-92E7-204901458CDA} 38 | {96561F27-749A-4F34-A81E-DA1E53A83AE9} = {2CEE9354-6766-4458-92E7-204901458CDA} 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {76F0C728-6813-4AE7-950C-5D8197BA4DA6} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /EFCoreColumnOrder/CustomAnnotationNames.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Microsoft.EntityFrameworkCore.Metadata 7 | { 8 | public static class CustomAnnotationNames 9 | { 10 | public const string Collation = RelationalAnnotationNames.Prefix + "Collation"; 11 | public const string AlwaysEncrypt = RelationalAnnotationNames.Prefix + "AlwaysEncrypt"; 12 | public const string ColumnOrder = "ColumnOrder"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /EFCoreColumnOrder/CustomAnnotationProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Infrastructure; 2 | using Microsoft.EntityFrameworkCore.Metadata; 3 | using Microsoft.EntityFrameworkCore.Migrations; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | namespace Microsoft.EntityFrameworkCore.SqlServer.Migrations.Internal 8 | { 9 | public class CustomAnnotationProvider : SqlServerMigrationsAnnotationProvider 10 | { 11 | public CustomAnnotationProvider(MigrationsAnnotationProviderDependencies dependencies) 12 | : base(dependencies) 13 | { 14 | } 15 | 16 | public override IEnumerable For(IProperty property) 17 | { 18 | var baseAnnotations = base.For(property); 19 | 20 | var orderAnnotation = property.FindAnnotation(CustomAnnotationNames.ColumnOrder); 21 | 22 | return orderAnnotation == null 23 | ? baseAnnotations 24 | : baseAnnotations.Concat(new[] { orderAnnotation }); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /EFCoreColumnOrder/CustomPropertyBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace Microsoft.EntityFrameworkCore 8 | { 9 | public static class CustomPropertyBuilderExtensions 10 | { 11 | public static PropertyBuilder HasColumnOrder(this PropertyBuilder propertyBuilder, int order) 12 | { 13 | propertyBuilder.HasAnnotation(CustomAnnotationNames.ColumnOrder, order); 14 | return propertyBuilder; 15 | } 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /EFCoreColumnOrder/CustomSqlServerMigrationsSqlGenerator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata; 2 | using Microsoft.EntityFrameworkCore.Migrations.Operations; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Microsoft.EntityFrameworkCore.Migrations 7 | { 8 | public class CustomSqlServerMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator 9 | { 10 | public CustomSqlServerMigrationsSqlGenerator( 11 | MigrationsSqlGeneratorDependencies dependencies, 12 | IMigrationsAnnotationProvider migrationsAnnotations) 13 | : base(dependencies, migrationsAnnotations) 14 | { 15 | } 16 | 17 | protected override void CreateTableColumns(CreateTableOperation operation, IModel model, MigrationCommandListBuilder builder) 18 | { 19 | operation.Columns.Sort(new ColumnOrderComparision()); 20 | base.CreateTableColumns(operation, model, builder); 21 | } 22 | 23 | internal class ColumnOrderComparision : IComparer 24 | { 25 | public int Compare(AddColumnOperation x, AddColumnOperation y) 26 | { 27 | var orderX = Convert.ToInt32(x.FindAnnotation(CustomAnnotationNames.ColumnOrder)?.Value ?? 0); 28 | var orderY = Convert.ToInt32(y.FindAnnotation(CustomAnnotationNames.ColumnOrder)?.Value ?? 0); 29 | return orderX.CompareTo(orderY); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /EFCoreColumnOrder/EFCoreColumnOrder.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Images/migration1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/premchandrasingh/EFCoreColumnOrder/3426d134e482b5ab3c3fac88206d75294e1f15fa/Images/migration1.png -------------------------------------------------------------------------------- /Images/migration2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/premchandrasingh/EFCoreColumnOrder/3426d134e482b5ab3c3fac88206d75294e1f15fa/Images/migration2.png -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | Till EF Core 3.1, Database column ordering for migration is not available out of the box. Many people are trying to figure out their custom one. This is one custom ordering which I think much cleaner. 3 | 4 | Ref:- 5 | - https://github.com/dotnet/efcore/issues/11314 6 | - https://github.com/dotnet/efcore/issues/2272 7 | - https://github.com/bricelam/EFCore/commit/ed629d65089bc7b1bbd6853c335e541df5a5ae7e 8 | 9 | ## How to use 10 | - Copy files from `EFCoreColumnOrder` projects and paste in your project. Don't worry about namespace, all classes included in their original namespaces 11 | - Simply use `HasColumnOrder()` in your model configuration 12 | 13 | ### Output 14 | ![Generated C# migration script](/Images/migration1.png) 15 | 16 | ![Final DB](/Images/migration2.png) 17 | 18 | ## Sample 19 | 20 | ### Entities 21 | ```C# 22 | public class EntityBase 23 | { 24 | public DateTime CreatedDateUTC { get; set; } 25 | 26 | public string CreatedBy { get; set; } 27 | 28 | public DateTime ModifiedDateUTC { get; set; } 29 | 30 | public string ModifiedBy { get; set; } 31 | } 32 | 33 | public class Order : EntityBase 34 | { 35 | public long OrderId { get; set; } 36 | 37 | public long ProductId { get; set; } 38 | 39 | public int Units { get; set; } 40 | 41 | public decimal UnitPrice { get; set; } 42 | 43 | } 44 | 45 | ``` 46 | 47 | ### Configurations 48 | ```C# 49 | public abstract class ConfigurationBase : IEntityTypeConfiguration where TEntity : EntityBase 50 | { 51 | public virtual string SchemaName { get; } = "dbo"; 52 | 53 | public abstract string TableName { get; } 54 | 55 | public abstract void ConfigureEntity(EntityTypeBuilder builder); 56 | 57 | public virtual void Configure(EntityTypeBuilder builder) 58 | { 59 | builder.ToTable(TableName, SchemaName); 60 | 61 | ConfigureEntity(builder); 62 | 63 | builder.Property(e => e.CreatedDateUTC) 64 | .IsRequired() 65 | .HasDefaultValueSql("(getUTCDate())") 66 | .HasColumnType("datetime") 67 | .HasColumnOrder(101); //<============== 68 | 69 | builder.Property(e => e.CreatedBy) 70 | .IsRequired() 71 | .HasDefaultValueSql("(CURRENT_USER)") 72 | .HasColumnType("nvarchar(100)") 73 | .HasColumnOrder(102); //<============== 74 | 75 | builder.Property(e => e.ModifiedDateUTC) 76 | .IsRequired() 77 | .HasDefaultValueSql("(getUTCDate())") 78 | .HasColumnType("datetime") 79 | .HasColumnOrder(103); //<============== 80 | 81 | builder.Property(e => e.ModifiedBy) 82 | .IsRequired() 83 | .HasDefaultValueSql("(CURRENT_USER)") 84 | .HasColumnType("nvarchar(100)") 85 | .HasColumnOrder(104); //<============== 86 | } 87 | } 88 | 89 | 90 | public class OrderConfiguration : ConfigurationBase 91 | { 92 | public override string TableName => nameof(Order); 93 | 94 | public override void ConfigureEntity(EntityTypeBuilder builder) 95 | { 96 | //ConfigureCommonProperties(builder); 97 | 98 | builder.HasKey(e => e.OrderId); 99 | builder.Property(e => e.OrderId) 100 | //.HasDefaultValueSql($"NEXT VALUE FOR SEQ_Order_OrderId"); // use sequence 101 | .ValueGeneratedOnAdd(); // set identity column 102 | 103 | builder.Property(e => e.ProductId) 104 | .IsRequired() 105 | .HasColumnType("bigint"); 106 | 107 | builder.Property(e => e.Units) 108 | .IsRequired() 109 | .HasColumnType("int"); 110 | 111 | builder.Property(e => e.UnitPrice) 112 | .IsRequired() 113 | .HasColumnType("decimal(9,2)"); 114 | } 115 | } 116 | ``` 117 | 118 | -------------------------------------------------------------------------------- /Sample/ConsoleSample/ConsoleSample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | 10 | all 11 | runtime; build; native; contentfiles; analyzers; buildtransitive 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Sample/ConsoleSample/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ConsoleSample 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | Console.WriteLine("Hello World!"); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sample/ConsoleSample/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "EFCore_Conn": "Server=(local);Database=EFCore;Trusted_Connection=True;MultipleActiveResultSets=true" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /Sample/RepoSample/AppDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | using Microsoft.EntityFrameworkCore.SqlServer.Migrations.Internal; 4 | using RepoSample.Entities; 5 | 6 | namespace RepoSample 7 | { 8 | public class AppDbContext : DbContext 9 | { 10 | public AppDbContext(DbContextOptions options) 11 | : base(options) 12 | { 13 | 14 | } 15 | protected override void OnModelCreating(ModelBuilder modelBuilder) 16 | { 17 | modelBuilder.ApplyAllConfigurations(); 18 | modelBuilder.CascadeAllRelationsOnDelete(); 19 | } 20 | 21 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 22 | { 23 | base.OnConfiguring(optionsBuilder); 24 | optionsBuilder.ReplaceService(); 25 | optionsBuilder.ReplaceService(); 26 | } 27 | 28 | public DbSet Orders { get; set; } 29 | 30 | public DbSet Products { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sample/RepoSample/Configurations/ConfigurationBase.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using RepoSample.Entities; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace RepoSample.Configurations 9 | { 10 | public abstract class ConfigurationBase : IEntityTypeConfiguration where TEntity : EntityBase 11 | { 12 | public virtual string SchemaName { get; } = "dbo"; 13 | 14 | public abstract string TableName { get; } 15 | 16 | public abstract void ConfigureEntity(EntityTypeBuilder builder); 17 | 18 | public virtual void Configure(EntityTypeBuilder builder) 19 | { 20 | builder.ToTable(TableName, SchemaName); 21 | 22 | ConfigureEntity(builder); 23 | 24 | builder.Property(e => e.CreatedDateUTC) 25 | .IsRequired() 26 | .HasDefaultValueSql("(getUTCDate())") 27 | .HasColumnType("datetime") 28 | .HasColumnOrder(101); 29 | 30 | builder.Property(e => e.CreatedBy) 31 | .IsRequired() 32 | .HasDefaultValueSql("(CURRENT_USER)") 33 | .HasColumnType("nvarchar(100)") 34 | .HasColumnOrder(102); 35 | 36 | builder.Property(e => e.ModifiedDateUTC) 37 | .IsRequired() 38 | .HasDefaultValueSql("(getUTCDate())") 39 | .HasColumnType("datetime") 40 | .HasColumnOrder(103); 41 | 42 | builder.Property(e => e.ModifiedBy) 43 | .IsRequired() 44 | .HasDefaultValueSql("(CURRENT_USER)") 45 | .HasColumnType("nvarchar(100)") 46 | .HasColumnOrder(104); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sample/RepoSample/Configurations/OrderConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using RepoSample.Entities; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace RepoSample.Configurations 9 | { 10 | public class OrderConfiguration : ConfigurationBase 11 | { 12 | public override string TableName => nameof(Order); 13 | 14 | public override void ConfigureEntity(EntityTypeBuilder builder) 15 | { 16 | //ConfigureCommonProperties(builder); 17 | 18 | builder.HasKey(e => e.OrderId); 19 | builder.Property(e => e.OrderId) 20 | //.HasDefaultValueSql($"NEXT VALUE FOR SEQ_Order_OrderId"); // use sequence 21 | .ValueGeneratedOnAdd(); // set identity column 22 | 23 | builder.Property(e => e.ProductId) 24 | .IsRequired() 25 | .HasColumnType("bigint"); 26 | 27 | builder.Property(e => e.Units) 28 | .IsRequired() 29 | .HasColumnType("int"); 30 | 31 | builder.Property(e => e.UnitPrice) 32 | .IsRequired() 33 | .HasColumnType("decimal(9,2)"); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sample/RepoSample/Configurations/ProductConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using RepoSample.Entities; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace RepoSample.Configurations 9 | { 10 | public class ProductConfiguration : ConfigurationBase 11 | { 12 | public override string TableName => "Product"; 13 | 14 | public override void ConfigureEntity(EntityTypeBuilder builder) 15 | { 16 | //ConfigureCommonProperties(builder); 17 | 18 | builder.HasKey(e => e.ProductId); 19 | builder.Property(e => e.ProductId) 20 | //.HasDefaultValueSql($"NEXT VALUE FOR SEQ_Product_ProductId"); // use sequence 21 | .ValueGeneratedOnAdd(); // set identity column 22 | 23 | builder.Property(e => e.ProductCode) 24 | .IsRequired() 25 | .HasColumnType("nvarchar(10)") 26 | .HasMaxLength(10); 27 | 28 | builder.Property(e => e.Name) 29 | .IsRequired() 30 | .HasColumnType("nvarchar(256)") 31 | .HasMaxLength(256); 32 | 33 | 34 | builder.Property(e => e.UnitPrice) 35 | .IsRequired() 36 | .HasColumnType("decimal(9,2)"); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sample/RepoSample/Constants.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace RepoSample 6 | { 7 | public static class Constants 8 | { 9 | public const string CONNECTION_NAME = "EFCore_Conn"; 10 | public const string ASPNETCORE_ENVIRONMENT = "ASPNETCORE_ENVIRONMENT"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sample/RepoSample/Entities/EntityBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace RepoSample.Entities 6 | { 7 | public class EntityBase 8 | { 9 | public DateTime CreatedDateUTC { get; set; } 10 | 11 | public string CreatedBy { get; set; } 12 | 13 | public DateTime ModifiedDateUTC { get; set; } 14 | 15 | public string ModifiedBy { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sample/RepoSample/Entities/Order.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace RepoSample.Entities 6 | { 7 | public class Order : EntityBase 8 | { 9 | public long OrderId { get; set; } 10 | 11 | public long ProductId { get; set; } 12 | 13 | public int Units { get; set; } 14 | 15 | public decimal UnitPrice { get; set; } 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sample/RepoSample/Entities/Product.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace RepoSample.Entities 6 | { 7 | public class Product : EntityBase 8 | { 9 | public long ProductId { get; set; } 10 | 11 | public string ProductCode { get; set; } 12 | 13 | public string Name { get; set; } 14 | 15 | public decimal UnitPrice { get; set; } 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sample/RepoSample/Migrations/20200214114303_First-Migration.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | using RepoSample; 9 | 10 | namespace RepoSample.Migrations 11 | { 12 | [DbContext(typeof(AppDbContext))] 13 | [Migration("20200214114303_First-Migration")] 14 | partial class FirstMigration 15 | { 16 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 17 | { 18 | #pragma warning disable 612, 618 19 | modelBuilder 20 | .HasAnnotation("ProductVersion", "3.1.0") 21 | .HasAnnotation("Relational:MaxIdentifierLength", 128) 22 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 23 | 24 | modelBuilder.Entity("RepoSample.Entities.Order", b => 25 | { 26 | b.Property("OrderId") 27 | .ValueGeneratedOnAdd() 28 | .HasColumnType("bigint") 29 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 30 | 31 | b.Property("CreatedBy") 32 | .IsRequired() 33 | .ValueGeneratedOnAdd() 34 | .HasColumnType("nvarchar(100)") 35 | .HasDefaultValueSql("(CURRENT_USER)") 36 | .HasAnnotation("ColumnOrder", 102); 37 | 38 | b.Property("CreatedDateUTC") 39 | .ValueGeneratedOnAdd() 40 | .HasColumnType("datetime") 41 | .HasDefaultValueSql("(getUTCDate())") 42 | .HasAnnotation("ColumnOrder", 101); 43 | 44 | b.Property("ModifiedBy") 45 | .IsRequired() 46 | .ValueGeneratedOnAdd() 47 | .HasColumnType("nvarchar(100)") 48 | .HasDefaultValueSql("(CURRENT_USER)") 49 | .HasAnnotation("ColumnOrder", 104); 50 | 51 | b.Property("ModifiedDateUTC") 52 | .ValueGeneratedOnAdd() 53 | .HasColumnType("datetime") 54 | .HasDefaultValueSql("(getUTCDate())") 55 | .HasAnnotation("ColumnOrder", 103); 56 | 57 | b.Property("ProductId") 58 | .HasColumnType("bigint"); 59 | 60 | b.Property("UnitPrice") 61 | .HasColumnType("decimal(9,2)"); 62 | 63 | b.Property("Units") 64 | .HasColumnType("int"); 65 | 66 | b.HasKey("OrderId"); 67 | 68 | b.ToTable("Order","dbo"); 69 | }); 70 | 71 | modelBuilder.Entity("RepoSample.Entities.Product", b => 72 | { 73 | b.Property("ProductId") 74 | .ValueGeneratedOnAdd() 75 | .HasColumnType("bigint") 76 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 77 | 78 | b.Property("CreatedBy") 79 | .IsRequired() 80 | .ValueGeneratedOnAdd() 81 | .HasColumnType("nvarchar(100)") 82 | .HasDefaultValueSql("(CURRENT_USER)") 83 | .HasAnnotation("ColumnOrder", 102); 84 | 85 | b.Property("CreatedDateUTC") 86 | .ValueGeneratedOnAdd() 87 | .HasColumnType("datetime") 88 | .HasDefaultValueSql("(getUTCDate())") 89 | .HasAnnotation("ColumnOrder", 101); 90 | 91 | b.Property("ModifiedBy") 92 | .IsRequired() 93 | .ValueGeneratedOnAdd() 94 | .HasColumnType("nvarchar(100)") 95 | .HasDefaultValueSql("(CURRENT_USER)") 96 | .HasAnnotation("ColumnOrder", 104); 97 | 98 | b.Property("ModifiedDateUTC") 99 | .ValueGeneratedOnAdd() 100 | .HasColumnType("datetime") 101 | .HasDefaultValueSql("(getUTCDate())") 102 | .HasAnnotation("ColumnOrder", 103); 103 | 104 | b.Property("Name") 105 | .IsRequired() 106 | .HasColumnType("nvarchar(256)") 107 | .HasMaxLength(256); 108 | 109 | b.Property("ProductCode") 110 | .IsRequired() 111 | .HasColumnType("nvarchar(10)") 112 | .HasMaxLength(10); 113 | 114 | b.Property("UnitPrice") 115 | .HasColumnType("decimal(9,2)"); 116 | 117 | b.HasKey("ProductId"); 118 | 119 | b.ToTable("Product","dbo"); 120 | }); 121 | #pragma warning restore 612, 618 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Sample/RepoSample/Migrations/20200214114303_First-Migration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | namespace RepoSample.Migrations 5 | { 6 | public partial class FirstMigration : Migration 7 | { 8 | protected override void Up(MigrationBuilder migrationBuilder) 9 | { 10 | migrationBuilder.EnsureSchema( 11 | name: "dbo"); 12 | 13 | migrationBuilder.CreateTable( 14 | name: "Order", 15 | schema: "dbo", 16 | columns: table => new 17 | { 18 | OrderId = table.Column(nullable: false) 19 | .Annotation("SqlServer:Identity", "1, 1"), 20 | CreatedDateUTC = table.Column(type: "datetime", nullable: false, defaultValueSql: "(getUTCDate())") 21 | .Annotation("ColumnOrder", 101), 22 | CreatedBy = table.Column(type: "nvarchar(100)", nullable: false, defaultValueSql: "(CURRENT_USER)") 23 | .Annotation("ColumnOrder", 102), 24 | ModifiedDateUTC = table.Column(type: "datetime", nullable: false, defaultValueSql: "(getUTCDate())") 25 | .Annotation("ColumnOrder", 103), 26 | ModifiedBy = table.Column(type: "nvarchar(100)", nullable: false, defaultValueSql: "(CURRENT_USER)") 27 | .Annotation("ColumnOrder", 104), 28 | ProductId = table.Column(type: "bigint", nullable: false), 29 | Units = table.Column(type: "int", nullable: false), 30 | UnitPrice = table.Column(type: "decimal(9,2)", nullable: false) 31 | }, 32 | constraints: table => 33 | { 34 | table.PrimaryKey("PK_Order", x => x.OrderId); 35 | }); 36 | 37 | migrationBuilder.CreateTable( 38 | name: "Product", 39 | schema: "dbo", 40 | columns: table => new 41 | { 42 | ProductId = table.Column(nullable: false) 43 | .Annotation("SqlServer:Identity", "1, 1"), 44 | CreatedDateUTC = table.Column(type: "datetime", nullable: false, defaultValueSql: "(getUTCDate())") 45 | .Annotation("ColumnOrder", 101), 46 | CreatedBy = table.Column(type: "nvarchar(100)", nullable: false, defaultValueSql: "(CURRENT_USER)") 47 | .Annotation("ColumnOrder", 102), 48 | ModifiedDateUTC = table.Column(type: "datetime", nullable: false, defaultValueSql: "(getUTCDate())") 49 | .Annotation("ColumnOrder", 103), 50 | ModifiedBy = table.Column(type: "nvarchar(100)", nullable: false, defaultValueSql: "(CURRENT_USER)") 51 | .Annotation("ColumnOrder", 104), 52 | ProductCode = table.Column(type: "nvarchar(10)", maxLength: 10, nullable: false), 53 | Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), 54 | UnitPrice = table.Column(type: "decimal(9,2)", nullable: false) 55 | }, 56 | constraints: table => 57 | { 58 | table.PrimaryKey("PK_Product", x => x.ProductId); 59 | }); 60 | } 61 | 62 | protected override void Down(MigrationBuilder migrationBuilder) 63 | { 64 | migrationBuilder.DropTable( 65 | name: "Order", 66 | schema: "dbo"); 67 | 68 | migrationBuilder.DropTable( 69 | name: "Product", 70 | schema: "dbo"); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Sample/RepoSample/Migrations/AppDbContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 7 | using RepoSample; 8 | 9 | namespace RepoSample.Migrations 10 | { 11 | [DbContext(typeof(AppDbContext))] 12 | partial class AppDbContextModelSnapshot : ModelSnapshot 13 | { 14 | protected override void BuildModel(ModelBuilder modelBuilder) 15 | { 16 | #pragma warning disable 612, 618 17 | modelBuilder 18 | .HasAnnotation("ProductVersion", "3.1.0") 19 | .HasAnnotation("Relational:MaxIdentifierLength", 128) 20 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 21 | 22 | modelBuilder.Entity("RepoSample.Entities.Order", b => 23 | { 24 | b.Property("OrderId") 25 | .ValueGeneratedOnAdd() 26 | .HasColumnType("bigint") 27 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 28 | 29 | b.Property("CreatedBy") 30 | .IsRequired() 31 | .ValueGeneratedOnAdd() 32 | .HasColumnType("nvarchar(100)") 33 | .HasDefaultValueSql("(CURRENT_USER)") 34 | .HasAnnotation("ColumnOrder", 102); 35 | 36 | b.Property("CreatedDateUTC") 37 | .ValueGeneratedOnAdd() 38 | .HasColumnType("datetime") 39 | .HasDefaultValueSql("(getUTCDate())") 40 | .HasAnnotation("ColumnOrder", 101); 41 | 42 | b.Property("ModifiedBy") 43 | .IsRequired() 44 | .ValueGeneratedOnAdd() 45 | .HasColumnType("nvarchar(100)") 46 | .HasDefaultValueSql("(CURRENT_USER)") 47 | .HasAnnotation("ColumnOrder", 104); 48 | 49 | b.Property("ModifiedDateUTC") 50 | .ValueGeneratedOnAdd() 51 | .HasColumnType("datetime") 52 | .HasDefaultValueSql("(getUTCDate())") 53 | .HasAnnotation("ColumnOrder", 103); 54 | 55 | b.Property("ProductId") 56 | .HasColumnType("bigint"); 57 | 58 | b.Property("UnitPrice") 59 | .HasColumnType("decimal(9,2)"); 60 | 61 | b.Property("Units") 62 | .HasColumnType("int"); 63 | 64 | b.HasKey("OrderId"); 65 | 66 | b.ToTable("Order","dbo"); 67 | }); 68 | 69 | modelBuilder.Entity("RepoSample.Entities.Product", b => 70 | { 71 | b.Property("ProductId") 72 | .ValueGeneratedOnAdd() 73 | .HasColumnType("bigint") 74 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 75 | 76 | b.Property("CreatedBy") 77 | .IsRequired() 78 | .ValueGeneratedOnAdd() 79 | .HasColumnType("nvarchar(100)") 80 | .HasDefaultValueSql("(CURRENT_USER)") 81 | .HasAnnotation("ColumnOrder", 102); 82 | 83 | b.Property("CreatedDateUTC") 84 | .ValueGeneratedOnAdd() 85 | .HasColumnType("datetime") 86 | .HasDefaultValueSql("(getUTCDate())") 87 | .HasAnnotation("ColumnOrder", 101); 88 | 89 | b.Property("ModifiedBy") 90 | .IsRequired() 91 | .ValueGeneratedOnAdd() 92 | .HasColumnType("nvarchar(100)") 93 | .HasDefaultValueSql("(CURRENT_USER)") 94 | .HasAnnotation("ColumnOrder", 104); 95 | 96 | b.Property("ModifiedDateUTC") 97 | .ValueGeneratedOnAdd() 98 | .HasColumnType("datetime") 99 | .HasDefaultValueSql("(getUTCDate())") 100 | .HasAnnotation("ColumnOrder", 103); 101 | 102 | b.Property("Name") 103 | .IsRequired() 104 | .HasColumnType("nvarchar(256)") 105 | .HasMaxLength(256); 106 | 107 | b.Property("ProductCode") 108 | .IsRequired() 109 | .HasColumnType("nvarchar(10)") 110 | .HasMaxLength(10); 111 | 112 | b.Property("UnitPrice") 113 | .HasColumnType("decimal(9,2)"); 114 | 115 | b.HasKey("ProductId"); 116 | 117 | b.ToTable("Product","dbo"); 118 | }); 119 | #pragma warning restore 612, 618 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Sample/RepoSample/Migrations/Infrastructure/AppDbContextFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace RepoSample.Migrations.Infrastructure 4 | { 5 | /// 6 | /// WARNING(PREM): 7 | /// Even though this factory class is not reference from anywhere or by any runtime part of the application. 8 | /// It is BEING USED by MIGRATION(ONLY) when firing nuget command Add-Migration . Removing this class will result in failure to migration. 9 | /// For more detail go to parent class and read description of 10 | /// 11 | public class AppDbContextFactory : DesignTimeDbContextFactoryBase 12 | { 13 | 14 | protected override AppDbContext CreateNewInstance(DbContextOptions options) 15 | { 16 | //// https://www.koskila.net/visual-studio-debugger-instance-from-code/ 17 | //// Launching a new debugger instance from code in Visual Studio 18 | //if (System.Diagnostics.Debugger.IsAttached == false) 19 | //{ 20 | // System.Diagnostics.Debugger.Launch(); 21 | //} 22 | 23 | return new AppDbContext(options); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sample/RepoSample/Migrations/Infrastructure/DesignTimeConfigurationProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using System; 3 | using System.IO; 4 | 5 | namespace RepoSample.Migrations.Infrastructure 6 | { 7 | internal class DesignTimeConfigurationProvider 8 | { 9 | private IConfiguration _configuration = null; 10 | 11 | public DesignTimeConfigurationProvider() 12 | { 13 | EnvironmentName = Environment.GetEnvironmentVariable(Constants.ASPNETCORE_ENVIRONMENT); 14 | } 15 | 16 | public string EnvironmentName { get; private set; } 17 | 18 | public IConfiguration Configuration 19 | { 20 | get 21 | { 22 | if (_configuration != null) 23 | return _configuration; 24 | 25 | _configuration = new ConfigurationBuilder() 26 | .SetBasePath(GetCorrectBasePath()) 27 | .AddJsonFile("appsettings.json") 28 | .AddJsonFile($"appsettings.Development.json", optional: true) 29 | .AddJsonFile($"appsettings.{EnvironmentName}.json", optional: true) 30 | .AddEnvironmentVariables() 31 | .Build(); 32 | 33 | return _configuration; 34 | } 35 | } 36 | private static string GetCorrectBasePath() 37 | { 38 | string basePath = Directory.GetCurrentDirectory(); 39 | Console.WriteLine($"Start looking for 'application.json' @ {basePath}"); 40 | 41 | // Basic logic:- check "appsettings.json" exist at the path 42 | Func isAppsettingJsonExist = (_path) => 43 | { 44 | return File.Exists(Path.Combine(_path, "appsettings.json")); 45 | }; 46 | 47 | // Case 1) When Api or Web is set as "Start up project" and try migration with nuget commad "Add-Migration Migration_Initial" 48 | // illustration basePath:- Cision.Web.Api 49 | string path = basePath; 50 | if (isAppsettingJsonExist(path)) 51 | { 52 | // Case 1.a) check if it is in development or debug mode, if yes move up to project root 53 | // illustration basePath:- Cision.Web.Api\bin\debug\dotnetcore2.2 54 | if (path.IndexOf("bin\\debug", StringComparison.InvariantCultureIgnoreCase) > 0) 55 | { 56 | path = Path.GetFullPath(Path.Combine(basePath, "..\\..\\..")); 57 | } 58 | return path; 59 | } 60 | 61 | throw new Exception("Path not found where application.json supposed to be. Try by setting the web or api project as 'start up'"); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sample/RepoSample/Migrations/Infrastructure/DesignTimeDbContextFactoryBase.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Design; 3 | using Microsoft.Extensions.Configuration; 4 | using System; 5 | 6 | namespace RepoSample.Migrations.Infrastructure 7 | { 8 | public abstract class DesignTimeDbContextFactoryBase : 9 | IDesignTimeDbContextFactory where TContext : DbContext 10 | { 11 | 12 | public TContext CreateDbContext(string[] args) 13 | { 14 | //if (System.Diagnostics.Debugger.IsAttached == false) 15 | //{ 16 | // System.Diagnostics.Debugger.Launch(); 17 | //} 18 | 19 | DesignTimeConfigurationProvider configProvider = new DesignTimeConfigurationProvider(); 20 | var connectionString = configProvider.Configuration.GetConnectionString(Constants.CONNECTION_NAME); 21 | 22 | if (string.IsNullOrEmpty(connectionString)) 23 | { 24 | throw new ArgumentException($"Connection string '{Constants.CONNECTION_NAME}' is null or empty.", nameof(connectionString)); 25 | } 26 | 27 | Console.WriteLine($"DesignTimeDbContextFactoryBase.Create(string): Connection string: '{connectionString}'."); 28 | 29 | var optionsBuilder = new DbContextOptionsBuilder(); 30 | 31 | optionsBuilder.UseSqlServer(connectionString); 32 | 33 | return CreateNewInstance(optionsBuilder.Options); 34 | 35 | } 36 | 37 | protected abstract TContext CreateNewInstance(DbContextOptions options); 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sample/RepoSample/ModelBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using RepoSample.Entities; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | 9 | namespace RepoSample 10 | { 11 | public static class ModelBuilderExtensions 12 | { 13 | public static void ApplyAllConfigurations(this ModelBuilder modelBuilder) 14 | { 15 | var applyConfigurationMethodInfo = modelBuilder 16 | .GetType() 17 | .GetMethods(BindingFlags.Instance | BindingFlags.Public) 18 | .First(m => m.Name.Equals("ApplyConfiguration", StringComparison.OrdinalIgnoreCase)); 19 | 20 | // https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7-1#inferred-tuple-element-names 21 | var ret = typeof(AppDbContext).Assembly 22 | .GetTypes() 23 | .Select(t => (t: t, i: t.GetInterfaces().FirstOrDefault(i => i.Name.Equals(typeof(IEntityTypeConfiguration<>).Name, StringComparison.Ordinal)))) 24 | .Where(it => it.i != null && !it.t.IsAbstract) 25 | .Select(it => (et: it.i.GetGenericArguments()[0], cfgObj: Activator.CreateInstance(it.t))) 26 | .Select(it => applyConfigurationMethodInfo.MakeGenericMethod(it.et).Invoke(modelBuilder, new[] { it.cfgObj })) 27 | .ToList(); 28 | } 29 | 30 | public static void CascadeAllRelationsOnDelete(this ModelBuilder modelBuilder, DeleteBehavior behavior = DeleteBehavior.Restrict) 31 | { 32 | // This is the only way to do it right now is to iterate through all relationships 33 | foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys())) 34 | { 35 | relationship.DeleteBehavior = behavior; 36 | } 37 | } 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sample/RepoSample/RepoSample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | all 11 | runtime; build; native; contentfiles; analyzers; buildtransitive 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | --------------------------------------------------------------------------------