├── .gitignore ├── CatFactory.EntityFrameworkCore.Tests ├── CatFactory.EntityFrameworkCore.Tests.csproj ├── MockTests.cs ├── Models │ └── CollegeDatabase.cs ├── NamingConventionTests.cs ├── ProjectCreationTests.cs ├── ProjectSelectionTests.cs ├── ScaffoldingEventHandlersTests.cs ├── ScaffoldingTests.cs └── ValueConversion.cs ├── CatFactory.EntityFrameworkCore.sln ├── CatFactory.EntityFrameworkCore ├── AuditEntity.cs ├── AuthorInfo.cs ├── CatFactory.EntityFrameworkCore.csproj ├── ConstraintExtensions.cs ├── DataLayerExtensions.cs ├── DatabaseExtensions.cs ├── DbObjectExtensions.cs ├── Definitions │ ├── AuditEntityInterfaceDefinition.cs │ ├── DataContractClassDefinition.cs │ ├── DbContextClassDefinition.cs │ ├── DbContextQueryExtensionsClassDefinition.cs │ ├── EntityClassDefinition.cs │ ├── EntityConfigurationClassDefinition.cs │ ├── EntityInterfaceDefinition.cs │ ├── Extensions │ │ ├── AuditEntityInterfaceBuilder.cs │ │ ├── DataContractClassBuilder.cs │ │ ├── DbContextClassBuilder.cs │ │ ├── DbContextQueryExtensionsClassBuilder.cs │ │ ├── EntityClassBuilder.cs │ │ ├── EntityConfigurationClassBuilder.cs │ │ ├── EntityInterfaceBuilder.cs │ │ ├── PagingExtensionsClassBuilder.cs │ │ ├── QueryModelClassBuilder.cs │ │ ├── RepositoryBaseClassBuilder.cs │ │ ├── RepositoryClassBuilder.cs │ │ └── RepositoryInterfaceBuilder.cs │ ├── PagingExtensionsClassDefinition.cs │ ├── QueryModelClassDefinition.cs │ ├── RepositoryClassDefinition.cs │ └── RepositoryInterfaceDefinition.cs ├── DomainExtensions.cs ├── EntityFrameworkCoreProject.cs ├── EntityFrameworkCoreProjectDomainExtensions.cs ├── EntityFrameworkCoreProjectLayersExtensions.cs ├── EntityFrameworkCoreProjectNamespaces.cs ├── EntityFrameworkCoreProjectRepositoriesExtensions.cs ├── EntityFrameworkCoreProjectSelectionExtensions.cs ├── EntityFrameworkCoreProjectSettings.cs ├── EntityFrameworkCoreVersion.cs ├── EntityLayerExtensions.cs ├── IDotNetClassDefinitionExtensions.cs └── ProjectFeatureExtensions.cs ├── LICENSE ├── README.md └── build.bat /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore.Tests/CatFactory.EntityFrameworkCore.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | latest 6 | 1701;1702;NU1701 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore.Tests/MockTests.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.EntityFrameworkCore.Tests.Models; 2 | using Xunit; 3 | 4 | namespace CatFactory.EntityFrameworkCore.Tests 5 | { 6 | public class MockTests 7 | { 8 | [Fact] 9 | public void ProjectScaffoldingForMockDb() 10 | { 11 | // Create instance of EF Core Project 12 | var project = EntityFrameworkCoreProject 13 | .CreateForV2x("College.Mock", Databases.College, @"C:\Temp\CatFactory.EntityFrameworkCore\College.Mock"); 14 | 15 | // Apply settings for EF Core project 16 | project.GlobalSelection(settings => settings.ForceOverwrite = true); 17 | 18 | // Build features for project, group all entities by schema into a feature 19 | project.BuildFeatures(); 20 | 21 | // Scaffolding =^^= 22 | project 23 | .ScaffoldEntityLayer() 24 | .ScaffoldDataLayer() 25 | ; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore.Tests/Models/CollegeDatabase.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using CatFactory.ObjectRelationalMapping; 3 | using CatFactory.SqlServer; 4 | 5 | namespace CatFactory.EntityFrameworkCore.Tests.Models 6 | { 7 | public static partial class Databases 8 | { 9 | public static Database College 10 | => new Database 11 | { 12 | Name = "college", 13 | DefaultSchema = "dbo", 14 | DatabaseTypeMaps = new SqlServerDatabaseFactory().DatabaseTypeMaps.ToList(), 15 | Tables = 16 | { 17 | new Table 18 | { 19 | Schema = "dbo", 20 | Name = "student", 21 | Columns = 22 | { 23 | new Column { Name = "student_id", Type = "int" }, 24 | new Column { Name = "first_name", Type = "varchar", Length = 25 }, 25 | new Column { Name = "middle_name", Type = "varchar", Length = 25, Nullable = true }, 26 | new Column { Name = "last_name", Type = "varchar", Length = 25 }, 27 | new Column { Name = "birth_date", Type = "datetime" }, 28 | new Column { Name = "gender", Type = "varchar", Length = 1 } 29 | }, 30 | Identity = new Identity("student_id", 1, 1) 31 | }, 32 | new Table 33 | { 34 | Schema = "dbo", 35 | Name = "teacher", 36 | Columns = 37 | { 38 | new Column { Name = "teacher_id", Type = "int" }, 39 | new Column { Name = "first_name", Type = "varchar", Length = 25 }, 40 | new Column { Name = "middle_name", Type = "varchar", Length = 25, Nullable = true }, 41 | new Column { Name = "last_name", Type = "varchar", Length = 25 }, 42 | new Column { Name = "birth_date", Type = "datetime" }, 43 | new Column { Name = "gender", Type = "varchar", Length = 1 } 44 | }, 45 | Identity = new Identity("teacher_id", 1, 1) 46 | }, 47 | new Table 48 | { 49 | Schema = "dbo", 50 | Name = "course", 51 | Columns = 52 | { 53 | new Column { Name = "course_id", Type = "int" }, 54 | new Column { Name = "course_name", Type = "varchar", Length = 25 }, 55 | new Column { Name = "course_description", Type = "varchar", Nullable = true } 56 | }, 57 | Identity = new Identity("course_id", 1, 1) 58 | }, 59 | new Table 60 | { 61 | Schema = "dbo", 62 | Name = "course_student", 63 | Columns = 64 | { 65 | new Column { Name = "course_student_id", Type = "int" }, 66 | new Column { Name = "course_id", Type = "int" }, 67 | new Column { Name = "student_id", Type = "int" }, 68 | }, 69 | Identity = new Identity("course_student_id", 1, 1) 70 | } 71 | } 72 | } 73 | .AddDbObjectsFromTables() 74 | .AddDbObjectsFromViews() 75 | .SetPrimaryKeyForTables() 76 | .LinkTables() 77 | ; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore.Tests/NamingConventionTests.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.ObjectRelationalMapping; 2 | using Xunit; 3 | 4 | namespace CatFactory.EntityFrameworkCore.Tests 5 | { 6 | public class NamingConventionTests 7 | { 8 | [Fact] 9 | public void CheckEntityConfigurationName() 10 | { 11 | // Arrange 12 | var efCoreProject = new EntityFrameworkCoreProject(); 13 | 14 | // Act and Assert 15 | Assert.True("OrderConfiguration" == efCoreProject.GetEntityConfigurationName(new Table { Name = "Order" })); 16 | Assert.True("OrdersQryConfiguration" == efCoreProject.GetEntityConfigurationName(new View { Name = "Orders Qry" })); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore.Tests/ProjectCreationTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace CatFactory.EntityFrameworkCore.Tests 4 | { 5 | public class ProjectCreationTests 6 | { 7 | [Fact] 8 | public void DefineProjectForV2x() 9 | { 10 | var project = EntityFrameworkCoreProject.CreateForV2x("OnlineStore", new ObjectRelationalMapping.Database(), ""); 11 | 12 | Assert.True(project.Version <= EntityFrameworkCoreVersion.Version_2_2); 13 | } 14 | 15 | [Fact] 16 | public void DefineProjectForV3x() 17 | { 18 | var project = EntityFrameworkCoreProject.CreateForV3x("OnlineStore", new ObjectRelationalMapping.Database(), ""); 19 | 20 | Assert.True(project.Version >= EntityFrameworkCoreVersion.Version_3_0); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore.Tests/ProjectSelectionTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using CatFactory.SqlServer; 3 | using Xunit; 4 | 5 | namespace CatFactory.EntityFrameworkCore.Tests 6 | { 7 | public class ProjectSelectionTests 8 | { 9 | [Fact] 10 | public async Task ProjectSelectionScopeAsync() 11 | { 12 | // Arrange 13 | 14 | // Import database 15 | var database = await SqlServerDatabaseFactory 16 | .ImportAsync("server=(local); database=OnlineStore; integrated security=yes; TrustServerCertificate=True;", "dbo.sysdiagrams"); 17 | 18 | // Create instance of Entity Framework Core project 19 | var project = EntityFrameworkCoreProject 20 | .CreateForV2x("OnlineStore", database, @"C:\Temp\CatFactory.EntityFrameworkCore\OnlineStore"); 21 | 22 | // Act 23 | 24 | // Apply settings for Entity Framework Core project 25 | project.GlobalSelection(settings => 26 | { 27 | settings.ForceOverwrite = true; 28 | settings.ConcurrencyToken = "Timestamp"; 29 | settings.AuditEntity = new AuditEntity 30 | { 31 | CreationUserColumnName = "CreationUser", 32 | CreationDateTimeColumnName = "CreationDateTime", 33 | LastUpdateUserColumnName = "LastUpdateUser", 34 | LastUpdateDateTimeColumnName = "LastUpdateDateTime" 35 | }; 36 | }); 37 | 38 | project.Selection("Sales.OrderHeader", settings => settings.EntitiesWithDataContracts = true); 39 | 40 | var orderHeader = database.FindTable("Sales.OrderHeader"); 41 | 42 | var selectionForOrder = project.GetSelection(orderHeader); 43 | 44 | // Assert 45 | 46 | Assert.True(project.Selections.Count == 2); 47 | 48 | Assert.True(project.GlobalSelection().Settings.EntitiesWithDataContracts == false); 49 | Assert.True(selectionForOrder.Settings.EntitiesWithDataContracts == true); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore.Tests/ScaffoldingEventHandlersTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using CatFactory.EntityFrameworkCore.Definitions; 3 | using CatFactory.SqlServer; 4 | using Xunit; 5 | 6 | namespace CatFactory.EntityFrameworkCore.Tests 7 | { 8 | public class ScaffoldingEventHandlersTests 9 | { 10 | [Fact] 11 | public async Task ScaffoldingWithEventHandlersForOnlineStoreDbAsync() 12 | { 13 | // Create database factory 14 | var databaseFactory = new SqlServerDatabaseFactory 15 | { 16 | DatabaseImportSettings = new DatabaseImportSettings 17 | { 18 | ConnectionString = "server=(local); database=OnlineStore; integrated security=yes; TrustServerCertificate=True;" 19 | } 20 | }; 21 | 22 | // Import database 23 | var database = await databaseFactory.ImportAsync(); 24 | 25 | // Create instance of Entity Framework Core project 26 | var project = EntityFrameworkCoreProject 27 | .CreateForV2x("ScaffoldingEventHandlers.OnlineStore.Core", database, @"C:\Temp\CatFactory.EntityFrameworkCore\ScaffoldingEventHandlers.OnlineStore.Core"); 28 | 29 | // Apply settings for Entity Framework Core project 30 | project.GlobalSelection(settings => settings.ForceOverwrite = true); 31 | 32 | project.ScaffoldingDefinition += (sender, args) => 33 | { 34 | if (args.CodeBuilder.ObjectDefinition is EntityClassDefinition entityDef) 35 | { 36 | foreach (var prop in entityDef.Properties) 37 | { 38 | if (prop.Type == "string") 39 | prop.Type = "string?"; 40 | } 41 | } 42 | else if (args.CodeBuilder.ObjectDefinition is DbContextClassDefinition dbContextDef) 43 | { 44 | dbContextDef.Documentation.Summary = "foo bar zaz"; 45 | 46 | foreach (var prop in dbContextDef.Properties) 47 | { 48 | // todo: Add 49 | prop.InitializationValue = "null"; 50 | } 51 | } 52 | }; 53 | 54 | project.ScaffoldedDefinition += (sender, args) => 55 | { 56 | // Check if file exists 57 | }; 58 | 59 | // Build features for project, group all entities by schema into a feature 60 | project.BuildFeatures(); 61 | 62 | // Scaffolding =^^= 63 | project 64 | .ScaffoldDomain() 65 | ; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore.Tests/ScaffoldingTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using CatFactory.SqlServer; 3 | using Xunit; 4 | 5 | namespace CatFactory.EntityFrameworkCore.Tests 6 | { 7 | public class ScaffoldingTests 8 | { 9 | [Fact] 10 | public async Task ScaffoldingDomainProjectForOnlineStoreDbAsync() 11 | { 12 | // Create database factory 13 | var databaseFactory = new SqlServerDatabaseFactory 14 | { 15 | DatabaseImportSettings = new DatabaseImportSettings 16 | { 17 | ConnectionString = "server=(local); database=OnlineStore; integrated security=yes; TrustServerCertificate=True;", 18 | Exclusions = 19 | { 20 | "dbo.sysdiagrams" 21 | } 22 | } 23 | }; 24 | 25 | // Import database 26 | var database = await databaseFactory.ImportAsync(); 27 | 28 | // Create instance of Entity Framework Core project 29 | var project = EntityFrameworkCoreProject 30 | .CreateForV2x("OnlineStore.Domain", database, @"C:\Temp\CatFactory.EntityFrameworkCore\OnlineStore.Domain"); 31 | 32 | // Apply settings for Entity Framework Core project 33 | project.GlobalSelection(settings => 34 | { 35 | settings.ForceOverwrite = true; 36 | settings.ConcurrencyToken = "Timestamp"; 37 | settings.AuditEntity = new AuditEntity 38 | { 39 | CreationUserColumnName = "CreationUser", 40 | CreationDateTimeColumnName = "CreationDateTime", 41 | LastUpdateUserColumnName = "LastUpdateUser", 42 | LastUpdateDateTimeColumnName = "LastUpdateDateTime" 43 | }; 44 | 45 | settings.AddConfigurationForUniquesInFluentAPI = true; 46 | settings.AddConfigurationForForeignKeysInFluentAPI = true; 47 | settings.DeclareNavigationProperties = true; 48 | }); 49 | 50 | project.Selection("Sales.OrderHeader", settings => 51 | { 52 | settings.EntitiesWithDataContracts = true; 53 | settings.AddConfigurationForForeignKeysInFluentAPI = true; 54 | settings.DeclareNavigationProperties = true; 55 | }); 56 | 57 | // Build features for project, group all entities by schema into a feature 58 | project.BuildFeatures(); 59 | 60 | // Scaffolding =^^= 61 | project 62 | .ScaffoldDomain() 63 | ; 64 | } 65 | 66 | [Fact] 67 | public async Task ScaffoldingProjectWithRepositoriesForOnlineStoreDbAsync() 68 | { 69 | // Create database factory 70 | var databaseFactory = new SqlServerDatabaseFactory 71 | { 72 | DatabaseImportSettings = new DatabaseImportSettings 73 | { 74 | ConnectionString = "server=(local); database=OnlineStore; integrated security=yes; TrustServerCertificate=True;", 75 | ImportTableFunctions = true, 76 | Exclusions = 77 | { 78 | "dbo.sysdiagrams", 79 | "dbo.fn_diagramobjects" 80 | } 81 | } 82 | }; 83 | 84 | // Import database 85 | var database = await databaseFactory.ImportAsync(); 86 | 87 | // Create instance of Entity Framework Core project 88 | var project = EntityFrameworkCoreProject 89 | .CreateForV2x("OnlineStore.Core", database, @"C:\Temp\CatFactory.EntityFrameworkCore\OnlineStore.Core"); 90 | 91 | // Apply settings for Entity Framework Core project 92 | project.GlobalSelection(settings => 93 | { 94 | settings.ForceOverwrite = true; 95 | settings.ConcurrencyToken = "Timestamp"; 96 | settings.AuditEntity = new AuditEntity 97 | { 98 | CreationUserColumnName = "CreationUser", 99 | CreationDateTimeColumnName = "CreationDateTime", 100 | LastUpdateUserColumnName = "LastUpdateUser", 101 | LastUpdateDateTimeColumnName = "LastUpdateDateTime" 102 | }; 103 | 104 | settings.AddConfigurationForUniquesInFluentAPI = true; 105 | settings.AddConfigurationForForeignKeysInFluentAPI = true; 106 | settings.DeclareNavigationProperties = true; 107 | }); 108 | 109 | project.Selection("Sales.OrderHeader", settings => 110 | { 111 | settings.EntitiesWithDataContracts = true; 112 | settings.AddConfigurationForForeignKeysInFluentAPI = true; 113 | settings.DeclareNavigationProperties = true; 114 | }); 115 | 116 | // Build features for project, group all entities by schema into a feature 117 | project.BuildFeatures(); 118 | 119 | // Scaffolding =^^= 120 | project 121 | .ScaffoldEntityLayer() 122 | .ScaffoldDataLayer() 123 | ; 124 | } 125 | 126 | [Fact] 127 | public async Task ScaffoldingProjectWithDataAnnotationsForOnlineStoreDbAsync() 128 | { 129 | // Import database 130 | var database = await SqlServerDatabaseFactory 131 | .ImportAsync("server=(local); database=OnlineStore; integrated security=yes; TrustServerCertificate=True;", "dbo.sysdiagrams"); 132 | 133 | // Create instance of Entity Framework Core Project 134 | var project = EntityFrameworkCoreProject 135 | .CreateForV2x("OnlineStoreWithDataAnnotations.Core", database, @"C:\Temp\CatFactory.EntityFrameworkCore\OnlineStoreWithDataAnnotations.Core"); 136 | 137 | // Apply settings for Entity Framework Core project 138 | project.GlobalSelection(settings => 139 | { 140 | settings.ForceOverwrite = true; 141 | settings.UseDataAnnotations = true; 142 | settings.ConcurrencyToken = "Timestamp"; 143 | settings.AuditEntity = new AuditEntity 144 | { 145 | CreationUserColumnName = "CreationUser", 146 | CreationDateTimeColumnName = "CreationDateTime", 147 | LastUpdateUserColumnName = "LastUpdateUser", 148 | LastUpdateDateTimeColumnName = "LastUpdateDateTime" 149 | }; 150 | settings.DeclareNavigationProperties = true; 151 | }); 152 | 153 | project.Selection("Sales.OrderHeader", settings => settings.EntitiesWithDataContracts = true); 154 | 155 | // Build features for project, group all entities by schema into a feature 156 | project.BuildFeatures(); 157 | 158 | // Scaffolding =^^= 159 | project 160 | .ScaffoldEntityLayer() 161 | .ScaffoldDataLayer() 162 | ; 163 | } 164 | 165 | [Fact] 166 | public async Task ScaffoldingProjectForNorthwindDbAsync() 167 | { 168 | // Import database 169 | var factory = new SqlServerDatabaseFactory 170 | { 171 | DatabaseImportSettings = new DatabaseImportSettings 172 | { 173 | ConnectionString = "server=(local); database=Northwind; integrated security=yes; TrustServerCertificate=True;", 174 | ImportScalarFunctions = true, 175 | ImportTableFunctions = true, 176 | ImportStoredProcedures = true, 177 | Exclusions = 178 | { 179 | "dbo.sysdiagrams", 180 | "dbo.sp_alterdiagram", 181 | "dbo.sp_creatediagram", 182 | "dbo.sp_dropdiagram", 183 | "dbo.sp_helpdiagramdefinition", 184 | "dbo.sp_helpdiagrams", 185 | "dbo.sp_renamediagram", 186 | "dbo.sp_upgraddiagrams", 187 | "dbo.fn_diagramobjects" 188 | } 189 | } 190 | }; 191 | 192 | var database = await factory.ImportAsync(); 193 | 194 | // Create instance of Entity Framework Core Project 195 | var project = EntityFrameworkCoreProject 196 | .CreateForV2x("Northwind.Core", database, @"C:\Temp\CatFactory.EntityFrameworkCore\Northwind.Core"); 197 | 198 | // Apply settings for Entity Framework Core project 199 | project.GlobalSelection(settings => 200 | { 201 | settings.ForceOverwrite = true; 202 | settings.DeclareNavigationProperties = true; 203 | }); 204 | 205 | project.Selection("dbo.Orders", settings => settings.EntitiesWithDataContracts = true); 206 | 207 | // Build features for project, group all entities by schema into a feature 208 | project.BuildFeatures(); 209 | 210 | // Scaffolding =^^= 211 | project 212 | .ScaffoldEntityLayer() 213 | .ScaffoldDataLayer() 214 | ; 215 | } 216 | 217 | [Fact] 218 | public async Task ScaffoldingProjectForAdventureWorksDbAsync() 219 | { 220 | // Create instance of factory for SQL Server 221 | var databaseFactory = new SqlServerDatabaseFactory 222 | { 223 | DatabaseImportSettings = new DatabaseImportSettings 224 | { 225 | ConnectionString = "server=(local); database=AdventureWorks2017; integrated security=yes; TrustServerCertificate=True;", 226 | ImportScalarFunctions = true, 227 | ImportTableFunctions = true, 228 | Exclusions = 229 | { 230 | "dbo.sysdiagrams", 231 | "Production.Document", 232 | "Production.ProductDocument" 233 | }, 234 | ExclusionTypes = 235 | { 236 | "hierarchyid", 237 | "geography" 238 | } 239 | } 240 | }; 241 | 242 | // Import database 243 | var database = await databaseFactory.ImportAsync(); 244 | 245 | // Create instance of Entity Framework Core Project 246 | var project = EntityFrameworkCoreProject 247 | .CreateForV2x("AdventureWorks", database, @"C:\Temp\CatFactory.EntityFrameworkCore\AdventureWorks.Core"); 248 | 249 | // Apply settings for Entity Framework Core project 250 | project.GlobalSelection(settings => 251 | { 252 | settings.ForceOverwrite = true; 253 | settings.DeclareNavigationProperties = true; 254 | 255 | settings.AddConfigurationForForeignKeysInFluentAPI = true; 256 | }); 257 | 258 | // Build features for project, group all entities by schema into a feature 259 | project.BuildFeatures(); 260 | 261 | // Scaffolding =^^= 262 | project 263 | .ScaffoldEntityLayer() 264 | .ScaffoldDataLayer() 265 | ; 266 | } 267 | 268 | [Fact] 269 | public async Task ScaffoldingProjectForWideWorldImportersDbAsync() 270 | { 271 | // Create database factory 272 | var databaseFactory = new SqlServerDatabaseFactory 273 | { 274 | DatabaseImportSettings = new DatabaseImportSettings 275 | { 276 | ConnectionString = "server=(local); database=WideWorldImporters; integrated security=yes; TrustServerCertificate=True;", 277 | ImportTableFunctions = true, 278 | Exclusions = 279 | { 280 | "dbo.sysdiagrams", 281 | "dbo.fn_diagramobjects" 282 | } 283 | } 284 | }; 285 | 286 | // Import database 287 | var database = await databaseFactory.ImportAsync(); 288 | 289 | // Create instance of Entity Framework Core project 290 | var project = EntityFrameworkCoreProject 291 | .CreateForV2x("WideWorldImporters.Core", database, @"C:\Temp\CatFactory.EntityFrameworkCore\WideWorldImporters.Core"); 292 | 293 | // Apply settings for Entity Framework Core project 294 | project.GlobalSelection(settings => settings.ForceOverwrite = true); 295 | 296 | // Build features for project, group all entities by schema into a feature 297 | project.BuildFeatures(); 298 | 299 | // Scaffolding =^^= 300 | project 301 | .ScaffoldEntityLayer() 302 | .ScaffoldDataLayer() 303 | ; 304 | } 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore.Tests/ValueConversion.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.NetCore.CodeFactory; 2 | using CatFactory.NetCore.ObjectOrientedProgramming; 3 | using CatFactory.ObjectOrientedProgramming; 4 | 5 | namespace CatFactory.EntityFrameworkCore 6 | { 7 | public static class ValueConversion 8 | { 9 | public static EntityFrameworkCoreProject ScaffoldValueConversion(this EntityFrameworkCoreProject project) 10 | { 11 | var boolToStringConverters = new CSharpClassDefinition 12 | { 13 | Namespaces = 14 | { 15 | "Microsoft.EntityFrameworkCore.Storage.ValueConversion" 16 | }, 17 | Namespace = "ValueConversion", 18 | AccessModifier = AccessModifier.Public, 19 | IsStatic = true, 20 | Name = "BoolToStringConverters", 21 | Fields = 22 | { 23 | new FieldDefinition 24 | { 25 | AccessModifier = AccessModifier.Private, 26 | IsStatic = true, 27 | IsReadOnly = true, 28 | Type = "BoolToStringConverter", 29 | Name = "bYN", 30 | Value = "new BoolToStringConverter(\"N\", \"Y\")" 31 | } 32 | } 33 | }; 34 | 35 | CSharpCodeBuilder.CreateFiles(project.OutputDirectory, project.GetDataLayerDirectory("ValueConversion"), true, boolToStringConverters); 36 | 37 | return project; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.2037 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CatFactory.EntityFrameworkCore", "CatFactory.EntityFrameworkCore\CatFactory.EntityFrameworkCore.csproj", "{5D163B1F-7324-4270-B0B4-348AEEB9FED2}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatFactory.EntityFrameworkCore.Tests", "CatFactory.EntityFrameworkCore.Tests\CatFactory.EntityFrameworkCore.Tests.csproj", "{3ABABDE9-C9E6-494F-85BA-92557A284CEC}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {5D163B1F-7324-4270-B0B4-348AEEB9FED2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {5D163B1F-7324-4270-B0B4-348AEEB9FED2}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {5D163B1F-7324-4270-B0B4-348AEEB9FED2}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {5D163B1F-7324-4270-B0B4-348AEEB9FED2}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {3ABABDE9-C9E6-494F-85BA-92557A284CEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {3ABABDE9-C9E6-494F-85BA-92557A284CEC}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {3ABABDE9-C9E6-494F-85BA-92557A284CEC}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {3ABABDE9-C9E6-494F-85BA-92557A284CEC}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {FD0FA40A-6265-442C-9583-981D6A274503} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/AuditEntity.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CatFactory.EntityFrameworkCore 4 | { 5 | public class AuditEntity 6 | { 7 | public AuditEntity() 8 | { 9 | } 10 | 11 | public string CreationUserColumnName { get; set; } 12 | 13 | public string CreationDateTimeColumnName { get; set; } 14 | 15 | public string LastUpdateUserColumnName { get; set; } 16 | 17 | public string LastUpdateDateTimeColumnName { get; set; } 18 | 19 | public IEnumerable Names 20 | => new string[] 21 | { 22 | CreationUserColumnName, 23 | CreationDateTimeColumnName, 24 | LastUpdateUserColumnName, 25 | LastUpdateDateTimeColumnName 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/AuthorInfo.cs: -------------------------------------------------------------------------------- 1 | namespace CatFactory.EntityFrameworkCore 2 | { 3 | public class AuthorInfo 4 | { 5 | public string CompanyName { get; set; } 6 | 7 | public string Name { get; set; } 8 | 9 | public string Email { get; set; } 10 | 11 | public string WebSite { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/CatFactory.EntityFrameworkCore.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | latest 6 | 1.0.0-beta-aurora-build00 7 | H. Herzl 8 | Herzl Corp. 9 | CatFactory Package for Entity Framework Core 10 | git 11 | https://github.com/hherzl/CatFactory.EntityFrameworkCore 12 | Entity Framework Core; ORM; Scaffolding 13 | 14 | 1.0.0-beta-aurora-build00 Version: 15 | 16 | Fix issue 27 https://github.com/hherzl/CatFactory.EntityFrameworkCore/issues/27 17 | 18 | Quick start: 19 | 20 | https://www.codeproject.com/Articles/1160615/Scaffolding-Entity-Framework-Core-with-CatFactory 21 | 22 | https://github.com/hherzl/CatFactory.EntityFrameworkCore 23 | Apache-2.0 24 | true 25 | true 26 | snupkg 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/ConstraintExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using CatFactory.ObjectOrientedProgramming; 4 | using CatFactory.ObjectRelationalMapping; 5 | 6 | namespace CatFactory.EntityFrameworkCore 7 | { 8 | public static class ConstraintExtensions 9 | { 10 | public static PropertyDefinition GetParentNavigationProperty(this ForeignKey foreignKey, ITable table, EntityFrameworkCoreProject project) 11 | { 12 | var propertyType = ""; 13 | 14 | if (table.HasSameEnclosingName()) 15 | { 16 | propertyType = string.Join(".", (new string[] 17 | { 18 | project.ProjectNamespaces.EntityLayer, 19 | project.Database.HasDefaultSchema(table) ? string.Empty : table.Schema, project.GetEntityName(table) 20 | }).Where(item => !string.IsNullOrEmpty(item))); 21 | } 22 | else 23 | { 24 | propertyType = project.GetEntityName(table); 25 | } 26 | 27 | var selection = project.GetSelection(table); 28 | 29 | return new PropertyDefinition(propertyType, $"{project.GetEntityName(table)}Fk") 30 | { 31 | AccessModifier = AccessModifier.Public, 32 | IsVirtual = selection.Settings.DeclareNavigationPropertiesAsVirtual, 33 | Attributes = selection.Settings.UseDataAnnotations ? new List 34 | { 35 | new MetadataAttribute("ForeignKey", $"\"{string.Join(",", foreignKey.Key)}\"") 36 | } : null 37 | }; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/DataLayerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using CatFactory.EntityFrameworkCore.Definitions.Extensions; 3 | using CatFactory.Markdown; 4 | using CatFactory.NetCore.ObjectOrientedProgramming; 5 | using CatFactory.ObjectRelationalMapping; 6 | 7 | namespace CatFactory.EntityFrameworkCore 8 | { 9 | public static class DataLayerExtensions 10 | { 11 | public static EntityFrameworkCoreProject ScaffoldDataLayer(this EntityFrameworkCoreProject project) 12 | { 13 | ScaffoldConfigurations(project); 14 | ScaffoldDbContext(project); 15 | ScaffoldDataContracts(project); 16 | ScaffoldDataRepositories(project); 17 | ScaffoldMdReadMe(project); 18 | 19 | return project; 20 | } 21 | 22 | internal static void ScaffoldConfigurations(EntityFrameworkCoreProject project) 23 | { 24 | var projectSelection = project.GlobalSelection(); 25 | 26 | if (!projectSelection.Settings.UseDataAnnotations) 27 | { 28 | foreach (var table in project.Database.Tables) 29 | { 30 | var definition = project.GetEntityConfigurationClassDefinition(table, false); 31 | 32 | project.Scaffold(definition, project.GetDataLayerConfigurationsDirectory(), project.Database.HasDefaultSchema(table) ? "" : table.Schema); 33 | } 34 | 35 | foreach (var view in project.Database.Views) 36 | { 37 | var definition = project.GetEntityConfigurationClassDefinition(view, false); 38 | 39 | project.Scaffold(definition, project.GetDataLayerConfigurationsDirectory(), project.Database.HasDefaultSchema(view) ? "" : view.Schema); 40 | } 41 | } 42 | } 43 | 44 | internal static void ScaffoldDbContext(EntityFrameworkCoreProject project) 45 | { 46 | var projectSelection = project.GlobalSelection(); 47 | 48 | project.Scaffold(project.GetDbContextClassDefinition(projectSelection, false), project.GetDataLayerDirectory()); 49 | } 50 | 51 | internal static void ScaffoldRepositoryInterface(EntityFrameworkCoreProject project) 52 | { 53 | project.Scaffold(project.GetRepositoryInterfaceDefinition(), project.GetDataLayerContractsDirectory()); 54 | } 55 | 56 | internal static void ScaffoldBaseRepositoryClassDefinition(EntityFrameworkCoreProject project) 57 | { 58 | project.Scaffold(project.GetRepositoryBaseClassDefinition(), project.GetDataLayerRepositoriesDirectory()); 59 | } 60 | 61 | internal static void ScaffoldRepositoryExtensionsClassDefinition(EntityFrameworkCoreProject project) 62 | { 63 | project.Scaffold(project.GetPagingExtensionsClassDefinition(false), project.GetDataLayerRepositoriesDirectory()); 64 | } 65 | 66 | internal static void ScaffoldDataContracts(EntityFrameworkCoreProject project) 67 | { 68 | foreach (var table in project.Database.Tables) 69 | { 70 | var selection = project.GetSelection(table); 71 | 72 | if (!selection.Settings.EntitiesWithDataContracts) 73 | continue; 74 | 75 | project.Scaffold(project.GetDataContractClassDefinition(table, false), project.GetDataLayerDataContractsDirectory()); 76 | } 77 | } 78 | 79 | internal static void ScaffoldDataRepositories(EntityFrameworkCoreProject project) 80 | { 81 | var projectSelection = project.GlobalSelection(); 82 | 83 | if (!string.IsNullOrEmpty(projectSelection.Settings.ConcurrencyToken)) 84 | { 85 | projectSelection.Settings.InsertExclusions.Add(projectSelection.Settings.ConcurrencyToken); 86 | projectSelection.Settings.UpdateExclusions.Add(projectSelection.Settings.ConcurrencyToken); 87 | } 88 | 89 | ScaffoldRepositoryInterface(project); 90 | ScaffoldBaseRepositoryClassDefinition(project); 91 | ScaffoldRepositoryExtensionsClassDefinition(project); 92 | 93 | foreach (var projectFeature in project.Features) 94 | { 95 | var repositoryClassDefinition = projectFeature.GetRepositoryClassDefinition(); 96 | 97 | project.Scaffold(repositoryClassDefinition, project.GetDataLayerRepositoriesDirectory()); 98 | 99 | var repositoryInterfaceDefinition = repositoryClassDefinition.RefactInterface(); 100 | 101 | repositoryInterfaceDefinition.Namespace = project.GetDataLayerContractsNamespace(); 102 | repositoryInterfaceDefinition.Implements.Add("IRepository"); 103 | 104 | project.Scaffold(repositoryInterfaceDefinition, project.GetDataLayerContractsDirectory()); 105 | } 106 | } 107 | 108 | internal static void ScaffoldMdReadMe(this EntityFrameworkCoreProject project) 109 | { 110 | var readMe = new MdDocument(); 111 | 112 | readMe.H1("CatFactory ==^^==: Scaffolding Made Easy"); 113 | 114 | readMe.WriteLine("How to use this code on your ASP.NET Core Application:"); 115 | 116 | readMe.OrderedList( 117 | "Install EntityFrameworkCore.SqlServer package", 118 | "Register the DbContext and Repositories in ConfigureServices method (Startup class)" 119 | ); 120 | 121 | readMe.H2("Install package"); 122 | 123 | readMe.WriteLine("You can install the NuGet packages in Visual Studio or Windows Command Line, for more info:"); 124 | 125 | readMe.WriteLine( 126 | Md.Link("Install and manage packages with the Package Manager Console in Visual Studio (PowerShell)", "https://docs.microsoft.com/en-us/nuget/consume-packages/install-use-packages-powershell") 127 | ); 128 | 129 | readMe.WriteLine( 130 | Md.Link(".NET Core CLI", "https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-add-package") 131 | ); 132 | 133 | readMe.H2("Register DbContext and Repositories"); 134 | 135 | readMe.WriteLine("Add the following code lines in {0} method (Startup class):", Md.Bold("ConfigureServices")); 136 | readMe.WriteLine(" services.AddDbContext<{0}>(options => options.UseSqlServer(\"ConnectionString\"));", project.GetDbContextName(project.Database)); 137 | readMe.WriteLine(" services.AddScope<{0}, {1}>()", "IDboRepository", "DboRepository"); 138 | 139 | readMe.WriteLine("Happy scaffolding!"); 140 | 141 | var codeProjectLink = Md.Link("Scaffolding Entity Framework Core with CatFactory", "https://www.codeproject.com/Articles/1160615/Scaffolding-Entity-Framework-Core-with-CatFactory"); 142 | 143 | readMe.WriteLine("You can check the guide for this package in: {0}", codeProjectLink); 144 | 145 | var gitHubRepositoryLink = Md.Link("GitHub repository", "https://github.com/hherzl/CatFactory.EntityFrameworkCore"); 146 | 147 | readMe.WriteLine("Also you can check the source code on {0}", gitHubRepositoryLink); 148 | 149 | readMe.WriteLine("CatFactory Development Team ==^^=="); 150 | 151 | File.WriteAllText(Path.Combine(project.OutputDirectory, "CatFactory.EntityFrameworkCore.ReadMe.MD"), readMe.ToString()); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/DatabaseExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using CatFactory.ObjectRelationalMapping; 3 | using CatFactory.ObjectRelationalMapping.Programmability; 4 | 5 | namespace CatFactory.EntityFrameworkCore 6 | { 7 | public static class DatabaseExtensions 8 | { 9 | public static List GetScalarFunctions(this Database database) 10 | { 11 | var importBag = database.ImportBag as IDictionary; 12 | 13 | if (importBag.ContainsKey("ScalarFunctions")) 14 | { 15 | if (importBag["ScalarFunctions"] is List scalarFunctions) 16 | return scalarFunctions; 17 | } 18 | 19 | return new List(); 20 | } 21 | 22 | public static List GetTableFunctions(this Database database) 23 | { 24 | var importBag = database.ImportBag as IDictionary; 25 | 26 | if (importBag.ContainsKey("TableFunctions")) 27 | { 28 | if (importBag["TableFunctions"] is List tableFunctions) 29 | return tableFunctions; 30 | } 31 | 32 | return new List(); 33 | } 34 | 35 | public static List GetStoredProcedures(this Database database) 36 | { 37 | var importBag = database.ImportBag as IDictionary; 38 | 39 | if (importBag.ContainsKey("StoredProcedures")) 40 | { 41 | if (importBag["StoredProcedures"] is List storedProcedures) 42 | return storedProcedures; 43 | } 44 | 45 | return new List(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/DbObjectExtensions.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.ObjectRelationalMapping; 2 | 3 | namespace CatFactory.EntityFrameworkCore 4 | { 5 | public static class DbObjectExtensions 6 | { 7 | public static string GetFullName(this Database db, IDbObject dbObject) 8 | => db.NamingConvention.GetObjectName(dbObject.Schema, dbObject.Name); 9 | 10 | public static string GetFullColumnName(this ITable table, Column column) 11 | => string.Join(".", new string[] { table.Schema, table.Name, column.Name }); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/AuditEntityInterfaceDefinition.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.NetCore.ObjectOrientedProgramming; 2 | 3 | namespace CatFactory.EntityFrameworkCore.Definitions 4 | { 5 | public class AuditEntityInterfaceDefinition : CSharpInterfaceDefinition 6 | { 7 | public AuditEntityInterfaceDefinition() 8 | : base() 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/DataContractClassDefinition.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.NetCore.ObjectOrientedProgramming; 2 | 3 | namespace CatFactory.EntityFrameworkCore.Definitions 4 | { 5 | public class DataContractClassDefinition : CSharpClassDefinition 6 | { 7 | public DataContractClassDefinition() 8 | : base() 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/DbContextClassDefinition.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.NetCore.ObjectOrientedProgramming; 2 | 3 | namespace CatFactory.EntityFrameworkCore.Definitions 4 | { 5 | public class DbContextClassDefinition : CSharpClassDefinition 6 | { 7 | public DbContextClassDefinition() 8 | : base() 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/DbContextQueryExtensionsClassDefinition.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.NetCore.ObjectOrientedProgramming; 2 | 3 | namespace CatFactory.EntityFrameworkCore.Definitions 4 | { 5 | public class DbContextQueryExtensionsClassDefinition : CSharpClassDefinition 6 | { 7 | public DbContextQueryExtensionsClassDefinition() 8 | : base() 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/EntityClassDefinition.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.NetCore.ObjectOrientedProgramming; 2 | 3 | namespace CatFactory.EntityFrameworkCore.Definitions 4 | { 5 | public class EntityClassDefinition : CSharpClassDefinition 6 | { 7 | public EntityClassDefinition() 8 | : base() 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/EntityConfigurationClassDefinition.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.NetCore.ObjectOrientedProgramming; 2 | 3 | namespace CatFactory.EntityFrameworkCore.Definitions 4 | { 5 | public class EntityConfigurationClassDefinition : CSharpClassDefinition 6 | { 7 | public EntityConfigurationClassDefinition() 8 | : base() 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/EntityInterfaceDefinition.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.NetCore.ObjectOrientedProgramming; 2 | 3 | namespace CatFactory.EntityFrameworkCore.Definitions 4 | { 5 | public class EntityInterfaceDefinition : CSharpInterfaceDefinition 6 | { 7 | public EntityInterfaceDefinition() 8 | : base() 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/Extensions/AuditEntityInterfaceBuilder.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.ObjectOrientedProgramming; 2 | 3 | namespace CatFactory.EntityFrameworkCore.Definitions.Extensions 4 | { 5 | public static class AuditEntityInterfaceBuilder 6 | { 7 | public static AuditEntityInterfaceDefinition GetAuditEntityInterfaceDefinition(this EntityFrameworkCoreProject project, bool isDomainDrivenDesign) 8 | => new AuditEntityInterfaceDefinition 9 | { 10 | Namespaces = 11 | { 12 | "System" 13 | }, 14 | Namespace = isDomainDrivenDesign ? project.GetDomainModelsNamespace() : project.GetEntityLayerNamespace(), 15 | AccessModifier = AccessModifier.Public, 16 | Name = "IAuditEntity", 17 | Implements = 18 | { 19 | "IEntity" 20 | }, 21 | Properties = 22 | { 23 | new PropertyDefinition("string", "CreationUser"), 24 | new PropertyDefinition("DateTime?", "CreationDateTime"), 25 | new PropertyDefinition("string", "LastUpdateUser"), 26 | new PropertyDefinition("DateTime?", "LastUpdateDateTime") 27 | } 28 | }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/Extensions/DataContractClassBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using CatFactory.NetCore; 3 | using CatFactory.NetCore.ObjectOrientedProgramming; 4 | using CatFactory.ObjectOrientedProgramming; 5 | using CatFactory.ObjectRelationalMapping; 6 | 7 | namespace CatFactory.EntityFrameworkCore.Definitions.Extensions 8 | { 9 | public static class DataContractClassBuilder 10 | { 11 | public static DataContractClassDefinition GetDataContractClassDefinition(this EntityFrameworkCoreProject project, ITable table, bool isDomainDrivenDesign) 12 | { 13 | var definition = new DataContractClassDefinition 14 | { 15 | Namespaces = 16 | { 17 | "System" 18 | }, 19 | Namespace = isDomainDrivenDesign ? project.GetDomainQueryModelsNamespace() : project.GetDataLayerDataContractsNamespace(), 20 | AccessModifier = AccessModifier.Public, 21 | Name = project.GetDataContractName(table), 22 | DbObject = table 23 | }; 24 | 25 | foreach (var column in table.Columns) 26 | { 27 | definition.Properties.Add(new PropertyDefinition(AccessModifier.Public, project.Database.ResolveDatabaseType(column), project.GetPropertyName(table, column)) 28 | { 29 | IsAutomatic = true 30 | }); 31 | } 32 | 33 | foreach (var foreignKey in table.ForeignKeys) 34 | { 35 | var foreignTable = project.Database.FindTable(foreignKey.References); 36 | 37 | if (foreignTable == null) 38 | continue; 39 | 40 | var foreignKeyAlias = NamingConvention.GetCamelCase(project.GetEntityName(foreignTable)); 41 | 42 | foreach (var column in foreignTable?.GetColumnsWithNoPrimaryKey()) 43 | { 44 | var col = (Column)column; 45 | 46 | var propertyName = project.GetPropertyName(foreignTable, col); 47 | 48 | var target = string.Format("{0}{1}", project.GetEntityName(foreignTable), propertyName); 49 | 50 | if (definition.Properties.Count(item => item.Name == propertyName) == 0) 51 | definition.Properties.Add(new PropertyDefinition(AccessModifier.Public, project.Database.ResolveDatabaseType(col), target) 52 | { 53 | IsAutomatic = true 54 | }); 55 | } 56 | } 57 | 58 | definition.SimplifyDataTypes(); 59 | 60 | return definition; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/Extensions/DbContextClassBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using CatFactory.CodeFactory; 4 | using CatFactory.CodeFactory.Scaffolding; 5 | using CatFactory.Collections; 6 | using CatFactory.NetCore; 7 | using CatFactory.NetCore.ObjectOrientedProgramming; 8 | using CatFactory.ObjectOrientedProgramming; 9 | using CatFactory.ObjectRelationalMapping; 10 | 11 | namespace CatFactory.EntityFrameworkCore.Definitions.Extensions 12 | { 13 | public static class DbContextClassBuilder 14 | { 15 | public static DbContextClassDefinition GetDbContextClassDefinition(this EntityFrameworkCoreProject project, ProjectSelection projectSelection, bool isDomainDrivenDesign) 16 | { 17 | var definition = new DbContextClassDefinition 18 | { 19 | Namespaces = 20 | { 21 | "System", 22 | "Microsoft.EntityFrameworkCore", 23 | isDomainDrivenDesign ? project.GetDomainModelsNamespace() : project.GetEntityLayerNamespace() 24 | }, 25 | Namespace = isDomainDrivenDesign ? project.Name : project.GetDataLayerNamespace(), 26 | AccessModifier = AccessModifier.Public, 27 | Name = project.GetDbContextName(project.Database), 28 | BaseClass = "DbContext" 29 | }; 30 | 31 | if (projectSelection.Settings.UseApplyConfigurationsFromAssemblyMethod) 32 | definition.Namespaces.Add("System.Reflection"); 33 | 34 | if (isDomainDrivenDesign) 35 | { 36 | if (!projectSelection.Settings.UseDataAnnotations) 37 | definition.Namespaces.Add(project.GetDomainConfigurationsNamespace()); 38 | } 39 | else 40 | { 41 | if (!projectSelection.Settings.UseDataAnnotations) 42 | definition.Namespaces.Add(project.GetDataLayerConfigurationsNamespace()); 43 | } 44 | 45 | definition.Constructors.Add(new ClassConstructorDefinition 46 | { 47 | AccessModifier = AccessModifier.Public, 48 | Parameters = 49 | { 50 | new ParameterDefinition(string.Format("DbContextOptions<{0}>", definition.Name), "options") 51 | }, 52 | Invocation = "base(options)" 53 | }); 54 | 55 | definition.Methods.Add(GetOnModelCreatingMethod(project)); 56 | 57 | foreach (var table in project.Database.Tables) 58 | { 59 | if (isDomainDrivenDesign) 60 | { 61 | if (!project.Database.HasDefaultSchema(table)) 62 | definition.Namespaces.AddUnique(project.GetDomainModelsNamespace(table.Schema)); 63 | } 64 | else 65 | { 66 | if (!project.Database.HasDefaultSchema(table)) 67 | definition.Namespaces.AddUnique(project.GetEntityLayerNamespace(table.Schema)); 68 | } 69 | 70 | var existingViews = project.Database.Views.Count(item => item.Name == table.Name); 71 | 72 | var genericTypeName = existingViews == 0 ? project.GetEntityName(table) : project.GetFullEntityName(table); 73 | var name = existingViews == 0 ? project.GetDbSetPropertyName(table, projectSelection.Settings.PluralizeDbSetPropertyNames) : project.GetFullDbSetPropertyName(table); 74 | 75 | definition.Properties.Add(new PropertyDefinition(AccessModifier.Public, string.Format("DbSet<{0}>", genericTypeName), name) { IsAutomatic = true }); 76 | } 77 | 78 | foreach (var view in project.Database.Views) 79 | { 80 | if (isDomainDrivenDesign) 81 | { 82 | if (!project.Database.HasDefaultSchema(view)) 83 | definition.Namespaces.AddUnique(project.GetDomainModelsNamespace(view.Schema)); 84 | } 85 | else 86 | { 87 | if (!project.Database.HasDefaultSchema(view)) 88 | definition.Namespaces.AddUnique(project.GetEntityLayerNamespace(view.Schema)); 89 | } 90 | 91 | var existingTables = project.Database.Tables.Count(item => item.Name == view.Name); 92 | 93 | var genericTypeName = existingTables == 0 ? project.GetEntityName(view) : project.GetFullEntityName(view); 94 | var name = existingTables == 0 ? project.GetDbSetPropertyName(view, projectSelection.Settings.PluralizeDbSetPropertyNames) : project.GetFullDbSetPropertyName(view); 95 | 96 | definition.Properties.Add(new PropertyDefinition(AccessModifier.Public, string.Format("DbSet<{0}>", genericTypeName), name) { IsAutomatic = true }); 97 | } 98 | 99 | foreach (var table in project.Database.Tables) 100 | { 101 | if (isDomainDrivenDesign) 102 | { 103 | if (!projectSelection.Settings.UseDataAnnotations && !project.Database.HasDefaultSchema(table)) 104 | definition.Namespaces.AddUnique(project.GetDomainConfigurationsNamespace(table.Schema)); 105 | } 106 | else 107 | { 108 | if (!projectSelection.Settings.UseDataAnnotations && !project.Database.HasDefaultSchema(table)) 109 | definition.Namespaces.AddUnique(project.GetDataLayerConfigurationsNamespace(table.Schema)); 110 | } 111 | } 112 | 113 | foreach (var view in project.Database.Views) 114 | { 115 | if (isDomainDrivenDesign) 116 | { 117 | if (!projectSelection.Settings.UseDataAnnotations && !project.Database.HasDefaultSchema(view)) 118 | definition.Namespaces.AddUnique(project.GetDomainConfigurationsNamespace(view.Schema)); 119 | } 120 | else 121 | { 122 | if (!projectSelection.Settings.UseDataAnnotations && !project.Database.HasDefaultSchema(view)) 123 | definition.Namespaces.AddUnique(project.GetDataLayerConfigurationsNamespace(view.Schema)); 124 | } 125 | } 126 | 127 | var scalarFunctions = project.Database.GetScalarFunctions(); 128 | 129 | foreach (var scalarFunction in scalarFunctions) 130 | { 131 | var parameterType = string.Empty; 132 | 133 | if (project.Database.HasTypeMappedToClr(scalarFunction.Parameters[0])) 134 | { 135 | var clrType = project.Database.GetClrMapForType(scalarFunction.Parameters[0]); 136 | 137 | parameterType = clrType.AllowClrNullable ? string.Format("{0}?", clrType.GetClrType().Name) : clrType.GetClrType().Name; 138 | } 139 | else 140 | { 141 | parameterType = "object"; 142 | } 143 | 144 | var method = new MethodDefinition 145 | { 146 | Attributes = 147 | { 148 | new MetadataAttribute("DbFunction") 149 | { 150 | Sets = 151 | { 152 | new MetadataAttributeSet(project.Version >= EntityFrameworkCoreVersion.Version_3_0 ? "Name" : "FunctionName", string.Format("\"{0}\"", scalarFunction.Name)), 153 | new MetadataAttributeSet("Schema", string.Format("\"{0}\"", scalarFunction.Schema)) 154 | } 155 | } 156 | }, 157 | AccessModifier = AccessModifier.Public, 158 | IsStatic = true, 159 | Type = parameterType, 160 | Name = project.GetScalarFunctionMethodName(scalarFunction), 161 | Lines = 162 | { 163 | new CodeLine("throw new Exception();") 164 | } 165 | }; 166 | 167 | var parameters = scalarFunction.Parameters.Where(item => !string.IsNullOrEmpty(item.Name)).ToList(); 168 | 169 | foreach (var parameter in parameters) 170 | { 171 | var propertyType = project.Database.ResolveDatabaseType(parameter); 172 | 173 | method.Parameters.Add(new ParameterDefinition(parameterType, project.CodeNamingConvention.GetParameterName(parameter.Name))); 174 | } 175 | 176 | definition.Methods.Add(method); 177 | } 178 | 179 | if (projectSelection.Settings.SimplifyDataTypes) 180 | definition.SimplifyDataTypes(); 181 | 182 | return definition; 183 | } 184 | 185 | private static MethodDefinition GetOnModelCreatingMethod(EntityFrameworkCoreProject project) 186 | { 187 | var lines = new List(); 188 | 189 | var selection = project.GlobalSelection(); 190 | 191 | if (selection.Settings.UseDataAnnotations) 192 | { 193 | var primaryKeys = project 194 | .Database 195 | .Tables 196 | .Where(item => item.PrimaryKey != null) 197 | .Select(item => item.GetColumnsFromConstraint(item.PrimaryKey).Select(key => key.Name).First()) 198 | .ToList(); 199 | 200 | foreach (var view in project.Database.Views) 201 | { 202 | var result = view.Columns.Where(item => primaryKeys.Contains(item.Name)).ToList(); 203 | 204 | var exp = "modelBuilder.Entity<{0}>().HasKey(e => new {{ {1} }});"; 205 | var properties = string.Join(", ", view.Columns.Select(item => string.Format("e.{0}", project.GetPropertyName(view, item)))); 206 | 207 | if (result.Count == 0) 208 | { 209 | lines.Add(new CodeLine(exp, project.GetEntityName(view), properties)); 210 | lines.Add(new EmptyLine()); 211 | } 212 | else 213 | { 214 | properties = string.Join(", ", result.Select(item => string.Format("e.{0}", project.GetPropertyName(view, item)))); 215 | 216 | lines.Add(new CodeLine(exp, project.GetEntityName(view), properties)); 217 | lines.Add(new EmptyLine()); 218 | } 219 | } 220 | } 221 | else 222 | { 223 | if (selection.Settings.UseApplyConfigurationsFromAssemblyMethod) 224 | { 225 | lines.Add(new CommentLine(" Apply all configurations from assembly")); 226 | lines.Add(new EmptyLine()); 227 | 228 | lines.Add(new CodeLine("modelBuilder")); 229 | lines.Add(new CodeLine(1, ".ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly())")); 230 | lines.Add(new CodeLine(1, ";")); 231 | lines.Add(new EmptyLine()); 232 | } 233 | else 234 | { 235 | var schemas = project.Database.DbObjects.Select(item => item.Schema).Distinct().OrderBy(item => item); 236 | 237 | if (project.Database.Tables.Count > 0) 238 | { 239 | lines.Add(new CommentLine(" Apply all configurations for tables")); 240 | lines.Add(new EmptyLine()); 241 | 242 | foreach (var schema in schemas) 243 | { 244 | var tables = project.Database.FindTablesBySchema(schema).ToList(); 245 | 246 | if (tables.Count == 0) 247 | continue; 248 | 249 | lines.Add(new CommentLine(" Schema '{0}'", schema)); 250 | 251 | lines.Add(new CodeLine("modelBuilder")); 252 | 253 | foreach (var table in tables) 254 | { 255 | var existingViews = project.Database.Views.Count(item => item.Name == table.Name); 256 | 257 | var genericTypeName = existingViews == 0 ? project.GetEntityName(table) : project.GetFullEntityName(table); 258 | var name = existingViews == 0 ? project.GetEntityConfigurationName(table) : project.GetFullEntityConfigurationName(table); 259 | 260 | lines.Add(new CodeLine(1, ".ApplyConfiguration(new {0}())", name)); 261 | } 262 | 263 | lines.Add(new CodeLine(";")); 264 | lines.Add(new EmptyLine()); 265 | } 266 | } 267 | 268 | if (project.Database.Views.Count > 0) 269 | { 270 | lines.Add(new CommentLine(" Apply all configurations for views")); 271 | lines.Add(new EmptyLine()); 272 | 273 | foreach (var schema in schemas) 274 | { 275 | var views = project.Database.FindViewsBySchema(schema).ToList(); 276 | 277 | if (views.Count == 0) 278 | continue; 279 | 280 | lines.Add(new CommentLine(" Schema '{0}'", schema)); 281 | 282 | lines.Add(new CodeLine("modelBuilder")); 283 | 284 | foreach (var view in views) 285 | { 286 | var existingTables = project.Database.Tables.Count(item => item.Name == view.Name); 287 | 288 | var genericTypeName = existingTables == 0 ? project.GetEntityName(view) : project.GetFullEntityName(view); 289 | var name = existingTables == 0 ? project.GetEntityConfigurationName(view) : project.GetFullEntityConfigurationName(view); 290 | 291 | lines.Add(new CodeLine(1, ".ApplyConfiguration(new {0}())", name)); 292 | } 293 | 294 | lines.Add(new CodeLine(";")); 295 | lines.Add(new EmptyLine()); 296 | } 297 | } 298 | } 299 | 300 | var tableFunctions = project.Database.GetTableFunctions(); 301 | 302 | if (tableFunctions.Count > 0) 303 | { 304 | lines.Add(new CommentLine(" Register query types for table functions")); 305 | lines.Add(new EmptyLine()); 306 | 307 | foreach (var view in tableFunctions) 308 | { 309 | lines.Add(new CodeLine("modelBuilder.Query<{0}>();", project.GetEntityResultName(view))); 310 | } 311 | 312 | lines.Add(new EmptyLine()); 313 | } 314 | 315 | var storedProcedures = project.Database.GetStoredProcedures(); 316 | 317 | if (storedProcedures.Count > 0) 318 | { 319 | lines.Add(new CommentLine(" Register query types for stored procedures")); 320 | lines.Add(new EmptyLine()); 321 | 322 | foreach (var view in storedProcedures) 323 | { 324 | lines.Add(new CodeLine("modelBuilder.Query<{0}>();", project.GetEntityResultName(view))); 325 | } 326 | 327 | lines.Add(new EmptyLine()); 328 | } 329 | } 330 | 331 | lines.Add(new CodeLine("base.OnModelCreating(modelBuilder);")); 332 | 333 | return new MethodDefinition 334 | { 335 | AccessModifier = AccessModifier.Protected, 336 | IsOverride = true, 337 | Type = "void", 338 | Name = "OnModelCreating", 339 | Parameters = 340 | { 341 | new ParameterDefinition("ModelBuilder", "modelBuilder") 342 | }, 343 | Lines = lines 344 | }; 345 | } 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/Extensions/DbContextQueryExtensionsClassBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using CatFactory.CodeFactory; 5 | using CatFactory.CodeFactory.Scaffolding; 6 | using CatFactory.Collections; 7 | using CatFactory.NetCore; 8 | using CatFactory.NetCore.CodeFactory; 9 | using CatFactory.NetCore.ObjectOrientedProgramming; 10 | using CatFactory.ObjectOrientedProgramming; 11 | using CatFactory.ObjectRelationalMapping; 12 | 13 | namespace CatFactory.EntityFrameworkCore.Definitions.Extensions 14 | { 15 | public static class DbContextQueryExtensionsClassBuilder 16 | { 17 | public static DbContextQueryExtensionsClassDefinition GetDbContextQueryExtensionsClassDefinition(this ProjectFeature projectFeature) 18 | { 19 | var project = projectFeature.GetEntityFrameworkCoreProject(); 20 | 21 | var definition = new DbContextQueryExtensionsClassDefinition 22 | { 23 | Namespaces = 24 | { 25 | "System", 26 | "System.Collections.Generic", 27 | "System.Linq", 28 | "System.Threading.Tasks", 29 | "Microsoft.EntityFrameworkCore", 30 | project.GetDomainModelsNamespace() 31 | }, 32 | Namespace = project.Name, 33 | AccessModifier = AccessModifier.Public, 34 | IsStatic = true, 35 | Name = projectFeature.GetQueryExtensionsClassName() 36 | }; 37 | 38 | foreach (var table in projectFeature.GetTables()) 39 | { 40 | var selection = projectFeature.GetEntityFrameworkCoreProject().GetSelection(table); 41 | 42 | if (!project.Database.HasDefaultSchema(table)) 43 | definition.Namespaces.AddUnique(project.GetDomainModelsNamespace(table.Schema)); 44 | 45 | if (selection.Settings.EntitiesWithDataContracts) 46 | { 47 | definition.Namespaces.AddUnique(project.GetDomainQueryModelsNamespace()); 48 | } 49 | 50 | definition.AddGetAllMethod(projectFeature, selection, table); 51 | 52 | if (table.PrimaryKey != null) 53 | definition.Methods.Add(GetGetMethod(projectFeature, selection, table)); 54 | 55 | foreach (var unique in table.Uniques) 56 | { 57 | definition.Methods.Add(GetGetByUniqueMethods(projectFeature, table, unique)); 58 | } 59 | 60 | if (selection.Settings.SimplifyDataTypes) 61 | definition.SimplifyDataTypes(); 62 | } 63 | 64 | return definition; 65 | } 66 | 67 | private static void AddGetAllMethod(this CSharpClassDefinition definition, ProjectFeature projectFeature, ProjectSelection projectSelection, ITable table) 68 | { 69 | var project = projectFeature.GetEntityFrameworkCoreProject(); 70 | 71 | var returnType = string.Empty; 72 | 73 | var lines = new List(); 74 | 75 | if (projectSelection.Settings.EntitiesWithDataContracts) 76 | { 77 | returnType = project.GetQueryModelName(table); 78 | 79 | var dataContractPropertiesSets = new[] 80 | { 81 | new 82 | { 83 | IsForeign = false, 84 | Type = string.Empty, 85 | Nullable = false, 86 | ObjectSource = string.Empty, 87 | PropertySource = string.Empty, 88 | Target = string.Empty 89 | } 90 | }.ToList(); 91 | 92 | var entityAlias = NamingConvention.GetCamelCase(project.GetEntityName(table)); 93 | 94 | foreach (var column in table.Columns) 95 | { 96 | var propertyName = project.GetPropertyName(table, column); 97 | 98 | dataContractPropertiesSets.Add(new 99 | { 100 | IsForeign = false, 101 | column.Type, 102 | column.Nullable, 103 | ObjectSource = entityAlias, 104 | PropertySource = propertyName, 105 | Target = propertyName 106 | }); 107 | } 108 | 109 | foreach (var foreignKey in table.ForeignKeys) 110 | { 111 | var foreignTable = projectFeature.Project.Database.FindTable(foreignKey.References); 112 | 113 | if (foreignTable == null) 114 | continue; 115 | 116 | var foreignKeyAlias = NamingConvention.GetCamelCase(project.GetEntityName(foreignTable)); 117 | 118 | foreach (var column in foreignTable?.GetColumnsWithNoPrimaryKey()) 119 | { 120 | var col = (Column)column; 121 | 122 | var propertyName = project.GetPropertyName(foreignTable, col); 123 | 124 | if (dataContractPropertiesSets.Where(item => string.Format("{0}.{1}", item.ObjectSource, item.PropertySource) == string.Format("{0}.{1}", entityAlias, propertyName)).Count() == 0) 125 | { 126 | var target = string.Format("{0}{1}", project.GetEntityName(foreignTable), propertyName); 127 | 128 | dataContractPropertiesSets.Add(new 129 | { 130 | IsForeign = true, 131 | column.Type, 132 | column.Nullable, 133 | ObjectSource = foreignKeyAlias, 134 | PropertySource = propertyName, 135 | Target = target 136 | }); 137 | } 138 | } 139 | } 140 | 141 | lines.Add(new CommentLine(" Get query from DbSet")); 142 | lines.Add(new CodeLine("var query = from {0} in dbContext.{1}", entityAlias, project.GetDbSetPropertyName(table, projectSelection.Settings.PluralizeDbSetPropertyNames))); 143 | 144 | foreach (var foreignKey in table.ForeignKeys) 145 | { 146 | var foreignTable = projectFeature.Project.Database.FindTable(foreignKey.References); 147 | 148 | if (foreignTable == null) 149 | continue; 150 | 151 | var foreignKeyEntityName = project.GetDbSetPropertyName(foreignTable, projectSelection.Settings.PluralizeDbSetPropertyNames); 152 | 153 | var foreignKeyAlias = NamingConvention.GetCamelCase(project.GetEntityName(foreignTable)); 154 | 155 | if (projectFeature.Project.Database.HasDefaultSchema(foreignTable)) 156 | definition.Namespaces.AddUnique(project.GetDomainModelsNamespace()); 157 | else 158 | definition.Namespaces.AddUnique(project.GetDomainModelsNamespace(foreignTable.Schema)); 159 | 160 | if (foreignKey.Key.Count == 0) 161 | { 162 | lines.Add(new PreprocessorDirectiveLine(1, " There isn't definition for key in foreign key '{0}' in your current database", foreignKey.References)); 163 | } 164 | else if (foreignKey.Key.Count == 1) 165 | { 166 | if (foreignTable == null) 167 | { 168 | lines.Add(LineHelper.Warning(" There isn't definition for '{0}' in your current database", foreignKey.References)); 169 | } 170 | else 171 | { 172 | var column = table.Columns.FirstOrDefault(item => item.Name == foreignKey.Key.First()); 173 | 174 | var x = project.CodeNamingConvention.GetPropertyName(foreignKey.Key.First()); 175 | var y = project.CodeNamingConvention.GetPropertyName(foreignTable.PrimaryKey.Key.First()); 176 | 177 | if (column.Nullable) 178 | { 179 | lines.Add(new CodeLine(1, "join {0}Join in dbContext.{1} on {2}.{3} equals {0}Join.{4} into {0}Temp", foreignKeyAlias, foreignKeyEntityName, entityAlias, x, y)); 180 | lines.Add(new CodeLine(2, "from {0} in {0}Temp.DefaultIfEmpty()", foreignKeyAlias, entityAlias, x, y)); 181 | } 182 | else 183 | { 184 | lines.Add(new CodeLine(1, "join {0} in dbContext.{1} on {2}.{3} equals {0}.{4}", foreignKeyAlias, foreignKeyEntityName, entityAlias, x, y)); 185 | } 186 | } 187 | } 188 | else 189 | { 190 | lines.Add(LineHelper.Warning(" Add logic for foreign key with multiple key")); 191 | } 192 | } 193 | 194 | lines.Add(new CodeLine(1, "select new {0}", returnType)); 195 | lines.Add(new CodeLine(1, "{")); 196 | 197 | for (var i = 0; i < dataContractPropertiesSets.Count; i++) 198 | { 199 | var property = dataContractPropertiesSets[i]; 200 | 201 | if (string.IsNullOrEmpty(property.ObjectSource) && string.IsNullOrEmpty(property.Target)) 202 | continue; 203 | 204 | if (property.IsForeign) 205 | { 206 | // todo: Add extension method to retrieve database type map by name 207 | 208 | var dbTypeMap = projectFeature.Project.Database.DatabaseTypeMaps.FirstOrDefault(item => item.DatabaseType == property.Type); 209 | 210 | if (dbTypeMap == null) 211 | throw new ObjectRelationMappingException(string.Format("There isn't mapping for '{0}' type", property.Type)); 212 | 213 | var clrType = dbTypeMap.GetClrType(); 214 | 215 | if (clrType.FullName == typeof(byte[]).FullName) 216 | lines.Add(new CodeLine(2, "{0} = {1} == null ? default(byte[]) : {1}.{2},", property.Target, property.ObjectSource, property.PropertySource)); 217 | else if (clrType.FullName == typeof(bool).FullName) 218 | lines.Add(new CodeLine(2, "{0} = {1} == null ? default(bool?) : {1}.{2},", property.Target, property.ObjectSource, property.PropertySource)); 219 | else if (clrType.FullName == typeof(string).FullName) 220 | lines.Add(new CodeLine(2, "{0} = {1} == null ? string.Empty : {1}.{2},", property.Target, property.ObjectSource, property.PropertySource)); 221 | else if (clrType.FullName == typeof(DateTime).FullName) 222 | lines.Add(new CodeLine(2, "{0} = {1} == null ? default(DateTime?) : {1}.{2},", property.Target, property.ObjectSource, property.PropertySource)); 223 | else if (clrType.FullName == typeof(TimeSpan).FullName) 224 | lines.Add(new CodeLine(2, "{0} = {1} == null ? default(TimeSpan?) : {1}.{2},", property.Target, property.ObjectSource, property.PropertySource)); 225 | else if (clrType.FullName == typeof(byte).FullName) 226 | lines.Add(new CodeLine(2, "{0} = {1} == null ? default(byte?) : {1}.{2},", property.Target, property.ObjectSource, property.PropertySource)); 227 | else if (clrType.FullName == typeof(short).FullName) 228 | lines.Add(new CodeLine(2, "{0} = {1} == null ? default(short?) : {1}.{2},", property.Target, property.ObjectSource, property.PropertySource)); 229 | else if (clrType.FullName == typeof(int).FullName) 230 | lines.Add(new CodeLine(2, "{0} = {1} == null ? default(int?) : {1}.{2},", property.Target, property.ObjectSource, property.PropertySource)); 231 | else if (clrType.FullName == typeof(long).FullName) 232 | lines.Add(new CodeLine(2, "{0} = {1} == null ? default(long?) : {1}.{2},", property.Target, property.ObjectSource, property.PropertySource)); 233 | else if (clrType.FullName == typeof(decimal).FullName) 234 | lines.Add(new CodeLine(2, "{0} = {1} == null ? default(decimal?) : {1}.{2},", property.Target, property.ObjectSource, property.PropertySource)); 235 | else if (clrType.FullName == typeof(double).FullName) 236 | lines.Add(new CodeLine(2, "{0} = {1} == null ? default(double?) : {1}.{2},", property.Target, property.ObjectSource, property.PropertySource)); 237 | else if (clrType.FullName == typeof(float).FullName) 238 | lines.Add(new CodeLine(2, "{0} = {1} == null ? default(float?) : {1}.{2},", property.Target, property.ObjectSource, property.PropertySource)); 239 | else if (clrType.FullName == typeof(Guid).FullName) 240 | lines.Add(new CodeLine(2, "{0} = {1} == null ? default(Guid?) : {1}.{2},", property.Target, property.ObjectSource, property.PropertySource)); 241 | else 242 | lines.Add(new CodeLine(2, "{0} = {1} == null ? default(object) : {1}.{2},", property.Target, property.ObjectSource, property.PropertySource)); 243 | } 244 | else 245 | { 246 | lines.Add(new CodeLine(2, "{0} = {1}.{2},", property.Target, property.ObjectSource, property.PropertySource)); 247 | } 248 | } 249 | 250 | lines.Add(new CodeLine(1, "};")); 251 | lines.Add(new EmptyLine()); 252 | } 253 | else 254 | { 255 | returnType = project.GetEntityName(table); 256 | 257 | var existingViews = project.Database.Views.Count(item => item.Name == table.Name); 258 | 259 | returnType = existingViews == 0 ? project.GetEntityName(table) : project.GetFullEntityName(table); 260 | 261 | var dbSetName = existingViews == 0 ? project.GetDbSetPropertyName(table, projectSelection.Settings.PluralizeDbSetPropertyNames) : project.GetFullDbSetPropertyName(table); 262 | 263 | lines.Add(new CommentLine(" Get query from DbSet")); 264 | lines.Add(new CodeLine("var query = dbContext.{0}.AsQueryable();", dbSetName)); 265 | 266 | lines.Add(new EmptyLine()); 267 | } 268 | 269 | var parameters = new List 270 | { 271 | new ParameterDefinition(project.GetDbContextName(project.Database), "dbContext") 272 | }; 273 | 274 | if (table.ForeignKeys.Count == 0) 275 | { 276 | lines.Add(new CodeLine("return query;")); 277 | } 278 | else 279 | { 280 | for (var i = 0; i < table.ForeignKeys.Count; i++) 281 | { 282 | var foreignKey = table.ForeignKeys[i]; 283 | 284 | if (foreignKey.Key.Count == 1) 285 | { 286 | var column = table.Columns.First(item => item.Name == foreignKey.Key.First()); 287 | 288 | var parameterName = project.GetParameterName(column); 289 | 290 | parameters.Add(new ParameterDefinition(projectFeature.Project.Database.ResolveDatabaseType(column), parameterName, "null")); 291 | 292 | if (projectFeature.Project.Database.ColumnIsDateTime(column)) 293 | { 294 | lines.Add(new CommentLine(" Filter by: '{0}'", column.Name)); 295 | lines.Add(new CodeLine("if ({0}.HasValue)", parameterName)); 296 | lines.Add(new CodeLine(1, "query = query.Where(item => item.{0} == {1});", project.GetPropertyName(table, column), parameterName)); 297 | lines.Add(new EmptyLine()); 298 | } 299 | else if (projectFeature.Project.Database.ColumnIsNumber(column)) 300 | { 301 | lines.Add(new CommentLine(" Filter by: '{0}'", column.Name)); 302 | lines.Add(new CodeLine("if ({0}.HasValue)", parameterName)); 303 | lines.Add(new CodeLine(1, "query = query.Where(item => item.{0} == {1});", project.GetPropertyName(table, column), parameterName)); 304 | lines.Add(new EmptyLine()); 305 | } 306 | else if (projectFeature.Project.Database.ColumnIsString(column)) 307 | { 308 | lines.Add(new CommentLine(" Filter by: '{0}'", column.Name)); 309 | lines.Add(new CodeLine("if (!string.IsNullOrEmpty({0}))", parameterName)); 310 | lines.Add(new CodeLine(1, "query = query.Where(item => item.{0} == {1});", project.GetPropertyName(table, column), parameterName)); 311 | lines.Add(new EmptyLine()); 312 | } 313 | else 314 | { 315 | lines.Add(new CommentLine(" Filter by: '{0}'", column.Name)); 316 | lines.Add(new CodeLine("if ({0} != null)", parameterName)); 317 | lines.Add(new CodeLine(1, "query = query.Where(item => item.{0} == {1});", project.GetPropertyName(table, column), parameterName)); 318 | lines.Add(new EmptyLine()); 319 | } 320 | } 321 | else 322 | { 323 | lines.Add(LineHelper.Warning("Add logic for foreign key with multiple key")); 324 | } 325 | } 326 | 327 | lines.Add(new CodeLine("return query;")); 328 | } 329 | 330 | definition.Methods.Add(new MethodDefinition 331 | { 332 | AccessModifier = AccessModifier.Public, 333 | IsStatic = true, 334 | Type = string.Format("IQueryable<{0}>", returnType), 335 | Name = project.GetGetAllExtensionMethodName(table), 336 | IsExtension = true, 337 | Parameters = parameters, 338 | Lines = lines 339 | }); 340 | } 341 | 342 | private static MethodDefinition GetGetMethod(ProjectFeature projectFeature, ProjectSelection projectSelection, ITable table) 343 | { 344 | var project = projectFeature.GetEntityFrameworkCoreProject(); 345 | 346 | var expression = string.Empty; 347 | 348 | if (table.Identity == null) 349 | expression = string.Format("item => {0}", string.Join(" && ", table.PrimaryKey.Key.Select(item => string.Format("item.{0} == entity.{0}", project.CodeNamingConvention.GetPropertyName(item))))); 350 | else 351 | expression = string.Format("item => item.{0} == entity.{0}", project.CodeNamingConvention.GetPropertyName(table.Identity.Name)); 352 | 353 | var existingViews = project.Database.Views.Count(item => item.Name == table.Name); 354 | 355 | var genericTypeName = existingViews == 0 ? project.GetEntityName(table) : project.GetFullEntityName(table); 356 | var dbSetName = existingViews == 0 ? project.GetDbSetPropertyName(table, projectSelection.Settings.PluralizeDbSetPropertyNames) : project.GetFullDbSetPropertyName(table); 357 | 358 | var includeExpression = new List(); 359 | 360 | if (projectSelection.Settings.DeclareNavigationProperties) 361 | { 362 | foreach (var foreignKey in table.ForeignKeys) 363 | { 364 | var foreignTable = project.Database.FindTable(foreignKey.References); 365 | 366 | if (foreignKey == null) 367 | continue; 368 | 369 | includeExpression.Add(string.Format("Include(e => e.{0})", foreignKey.GetParentNavigationProperty(foreignTable, project).Name)); 370 | } 371 | } 372 | 373 | var method = new MethodDefinition 374 | { 375 | AccessModifier = AccessModifier.Public, 376 | IsStatic = true, 377 | IsAsync = true, 378 | Type = string.Format("Task<{0}>", project.GetEntityName(table)), 379 | Name = project.GetGetRepositoryMethodName(table), 380 | IsExtension = true, 381 | Parameters = 382 | { 383 | new ParameterDefinition(project.GetDbContextName(project.Database), "dbContext"), 384 | new ParameterDefinition(project.GetEntityName(table), "entity"), 385 | new ParameterDefinition("bool", "tracking", "true"), 386 | new ParameterDefinition("bool", "include", "true") 387 | } 388 | }; 389 | 390 | if (includeExpression.Count == 0) 391 | { 392 | method.Lines = new() 393 | { 394 | new CodeLine("var query = dbContext.{0}.AsQueryable();", dbSetName), 395 | new EmptyLine(), 396 | new CodeLine("if (!tracking)"), 397 | new CodeLine(1, "query = query.AsNoTracking();"), 398 | new EmptyLine(), 399 | new CodeLine("return await query.FirstOrDefaultAsync({1});", dbSetName, expression) 400 | }; 401 | } 402 | else 403 | { 404 | method.Lines = new() 405 | { 406 | new CodeLine("var query = dbContext.{0}.AsQueryable();", dbSetName), 407 | new EmptyLine(), 408 | new CodeLine("if (!tracking)"), 409 | new CodeLine(1, "query = query.AsNoTracking();"), 410 | new EmptyLine(), 411 | new CodeLine("if (include)"), 412 | new CodeLine(1, "query = query.{0};", string.Join(".", includeExpression)), 413 | new EmptyLine(), 414 | new CodeLine("return await query.FirstOrDefaultAsync({1});", dbSetName, expression) 415 | }; 416 | } 417 | 418 | return method; 419 | } 420 | 421 | private static MethodDefinition GetGetByUniqueMethods(ProjectFeature projectFeature, ITable table, Unique unique) 422 | { 423 | var project = projectFeature.GetEntityFrameworkCoreProject(); 424 | 425 | var selection = project.GetSelection(table); 426 | 427 | var expression = string.Format("item => {0}", string.Join(" && ", unique.Key.Select(item => string.Format("item.{0} == entity.{0}", project.CodeNamingConvention.GetPropertyName(item))))); 428 | 429 | var existingViews = project.Database.Views.Count(item => item.Name == table.Name); 430 | 431 | var genericTypeName = existingViews == 0 ? project.GetEntityName(table) : project.GetFullEntityName(table); 432 | var dbSetName = existingViews == 0 ? project.GetDbSetPropertyName(table, selection.Settings.PluralizeDbSetPropertyNames) : project.GetFullDbSetPropertyName(table); 433 | 434 | var includeExpression = new List(); 435 | 436 | if (selection.Settings.DeclareNavigationProperties) 437 | { 438 | foreach (var foreignKey in table.ForeignKeys) 439 | { 440 | var foreignTable = project.Database.FindTable(foreignKey.References); 441 | 442 | if (foreignKey == null) 443 | continue; 444 | 445 | includeExpression.Add(string.Format("Include(e => e.{0})", foreignKey.GetParentNavigationProperty(foreignTable, project).Name)); 446 | } 447 | } 448 | 449 | return new MethodDefinition 450 | { 451 | AccessModifier = AccessModifier.Public, 452 | IsStatic = true, 453 | IsAsync = true, 454 | Type = string.Format("Task<{0}>", project.GetEntityName(table)), 455 | Name = project.GetGetByUniqueRepositoryMethodName(table, unique), 456 | IsExtension = true, 457 | Parameters = 458 | { 459 | new ParameterDefinition(project.GetDbContextName(project.Database), "dbContext"), 460 | new ParameterDefinition(project.GetEntityName(table), "entity"), 461 | new ParameterDefinition("bool", "tracking", "true"), 462 | new ParameterDefinition("bool", "include", "true") 463 | }, 464 | Lines = 465 | { 466 | new CodeLine("var query = dbContext.{0}.AsQueryable();", dbSetName), 467 | new EmptyLine(), 468 | new CodeLine("if (!tracking)"), 469 | new CodeLine(1, "query = query.AsNoTracking();"), 470 | new EmptyLine(), 471 | new CodeLine("if (include)"), 472 | new CodeLine(1, "query = query.{0};", string.Join(".", includeExpression)), 473 | new EmptyLine(), 474 | new CodeLine("return await query.FirstOrDefaultAsync({1});", dbSetName, expression) 475 | } 476 | }; 477 | } 478 | } 479 | } 480 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/Extensions/EntityClassBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using CatFactory.CodeFactory; 3 | using CatFactory.Collections; 4 | using CatFactory.NetCore; 5 | using CatFactory.NetCore.ObjectOrientedProgramming; 6 | using CatFactory.ObjectOrientedProgramming; 7 | using CatFactory.ObjectRelationalMapping; 8 | using CatFactory.ObjectRelationalMapping.Programmability; 9 | 10 | namespace CatFactory.EntityFrameworkCore.Definitions.Extensions 11 | { 12 | public static class EntityClassBuilder 13 | { 14 | public static EntityClassDefinition GetEntityClassDefinition(this EntityFrameworkCoreProject project, ITable table, bool isDomainDrivenDesign) 15 | { 16 | var definition = new EntityClassDefinition 17 | { 18 | Namespaces = 19 | { 20 | "System" 21 | }, 22 | AccessModifier = AccessModifier.Public, 23 | Name = project.GetEntityName(table), 24 | IsPartial = true, 25 | Constructors = 26 | { 27 | new ClassConstructorDefinition(AccessModifier.Public) 28 | }, 29 | DbObject = table 30 | }; 31 | 32 | if (isDomainDrivenDesign) 33 | definition.Namespace = project.Database.HasDefaultSchema(table) ? project.GetDomainModelsNamespace() : project.GetDomainModelsNamespace(table.Schema); 34 | else 35 | definition.Namespace = project.Database.HasDefaultSchema(table) ? project.GetEntityLayerNamespace() : project.GetEntityLayerNamespace(table.Schema); 36 | 37 | if (!string.IsNullOrEmpty(table.Description)) 38 | definition.Documentation.Summary = table.Description; 39 | 40 | var projectSelection = project.GetSelection(table); 41 | 42 | if (projectSelection.Settings.UseDataAnnotations) 43 | { 44 | definition.Namespaces.Add("System.ComponentModel.DataAnnotations"); 45 | definition.Namespaces.Add("System.ComponentModel.DataAnnotations.Schema"); 46 | } 47 | 48 | if (projectSelection.Settings.EnableDataBindings) 49 | { 50 | definition.Namespaces.Add("System.ComponentModel"); 51 | 52 | definition.Implements.Add("INotifyPropertyChanged"); 53 | 54 | definition.Events.Add(new EventDefinition("PropertyChangedEventHandler", "PropertyChanged")); 55 | } 56 | 57 | if (table.PrimaryKey != null) 58 | { 59 | var constructor = new ClassConstructorDefinition 60 | { 61 | AccessModifier = AccessModifier.Public 62 | }; 63 | 64 | foreach (var key in table.GetColumnsFromConstraint(table.PrimaryKey)) 65 | { 66 | var col = (Column)key; 67 | 68 | var propertyType = project.Database.ResolveDatabaseType(col); 69 | 70 | constructor.Parameters.Add(new ParameterDefinition(propertyType, project.GetParameterName(col))); 71 | 72 | constructor.Lines.Add(new CodeLine("{0} = {1};", project.GetPropertyName(key.Name), project.GetParameterName(col))); 73 | } 74 | 75 | definition.Constructors.Add(constructor); 76 | } 77 | 78 | var columns = table.Columns; 79 | 80 | foreach (var column in columns) 81 | { 82 | var propertyType = project.Database.ResolveDatabaseType(column); 83 | 84 | if (projectSelection.Settings.EnableDataBindings) 85 | { 86 | definition.AddPropWithField(propertyType, project.GetPropertyName(table, column)); 87 | } 88 | else 89 | { 90 | if (projectSelection.Settings.BackingFields.Contains(table.GetFullColumnName(column))) 91 | { 92 | definition.AddPropWithField(propertyType, project.GetPropertyName(table, column)); 93 | } 94 | else if (projectSelection.Settings.UseAutomaticPropertiesForEntities) 95 | { 96 | definition.Properties.Add(new PropertyDefinition(AccessModifier.Public, propertyType, project.GetPropertyName(table, column)) 97 | { 98 | IsAutomatic = true 99 | }); 100 | } 101 | else 102 | { 103 | definition.AddPropWithField(propertyType, project.GetPropertyName(table, column)); 104 | } 105 | } 106 | } 107 | 108 | if (projectSelection.Settings.AuditEntity == null) 109 | { 110 | definition.Implements.Add(projectSelection.Settings.EntityInterfaceName); 111 | } 112 | else 113 | { 114 | var count = 0; 115 | 116 | foreach (var column in columns) 117 | { 118 | if (projectSelection.Settings.AuditEntity.Names.Contains(column.Name)) 119 | count += 1; 120 | } 121 | 122 | if (count == projectSelection.Settings.AuditEntity.Names.Count()) 123 | definition.Implements.Add("IAuditEntity"); 124 | else 125 | definition.Implements.Add("IEntity"); 126 | } 127 | 128 | if (projectSelection.Settings.SimplifyDataTypes) 129 | definition.SimplifyDataTypes(); 130 | 131 | if (projectSelection.Settings.DeclareNavigationProperties) 132 | { 133 | foreach (var foreignKey in table.ForeignKeys) 134 | { 135 | var foreignTable = project.Database.FindTable(foreignKey.References); 136 | 137 | if (foreignTable == null) 138 | continue; 139 | 140 | if (isDomainDrivenDesign) 141 | { 142 | if (definition.Namespace != project.GetDomainModelsNamespace(foreignTable.Schema)) 143 | { 144 | definition 145 | .Namespaces 146 | .AddUnique(project.Database.HasDefaultSchema(foreignTable) ? project.GetDomainModelsNamespace() : project.GetDomainModelsNamespace(foreignTable.Schema)) 147 | ; 148 | } 149 | } 150 | else 151 | { 152 | if (definition.Namespace != project.GetEntityLayerNamespace(foreignTable.Schema)) 153 | { 154 | definition 155 | .Namespaces 156 | .AddUnique(project.Database.HasDefaultSchema(foreignTable) ? project.GetEntityLayerNamespace() : project.GetEntityLayerNamespace(foreignTable.Schema)) 157 | ; 158 | } 159 | } 160 | 161 | var fkProperty = foreignKey.GetParentNavigationProperty(foreignTable, project); 162 | 163 | if (!definition.Properties.Any(item => item.Name == fkProperty.Name)) 164 | definition.Properties.Add(fkProperty); 165 | } 166 | 167 | foreach (var child in project.Database.Tables) 168 | { 169 | foreach (var foreignKey in child.ForeignKeys) 170 | { 171 | if (foreignKey.References.EndsWith(table.FullName)) 172 | { 173 | definition.Namespaces 174 | .AddUnique(projectSelection.Settings.NavigationPropertyEnumerableNamespace); 175 | 176 | if (isDomainDrivenDesign) 177 | { 178 | if (definition.Namespace != project.GetDomainModelsNamespace(child.Schema)) 179 | { 180 | definition 181 | .Namespaces 182 | .AddUnique(project.Database.HasDefaultSchema(child) ? project.GetDomainModelsNamespace() : project.GetDomainModelsNamespace(child.Schema)) 183 | ; 184 | } 185 | } 186 | else 187 | { 188 | if (definition.Namespace != project.GetEntityLayerNamespace(child.Schema)) 189 | { 190 | definition 191 | .Namespaces 192 | .AddUnique(project.Database.HasDefaultSchema(child) ? project.GetEntityLayerNamespace() : project.GetEntityLayerNamespace(child.Schema)) 193 | ; 194 | } 195 | } 196 | 197 | var navigationProperty = project.GetChildNavigationProperty(projectSelection, child, foreignKey); 198 | 199 | if (!definition.Properties.Any(item => item.Name == navigationProperty.Name)) 200 | definition.Properties.Add(navigationProperty); 201 | } 202 | } 203 | } 204 | } 205 | 206 | return definition; 207 | } 208 | 209 | public static EntityClassDefinition GetEntityClassDefinition(this EntityFrameworkCoreProject project, IView view, string ns) 210 | { 211 | var definition = new EntityClassDefinition 212 | { 213 | Namespaces = 214 | { 215 | "System" 216 | }, 217 | Namespace = ns, 218 | AccessModifier = AccessModifier.Public, 219 | Name = project.GetEntityName(view), 220 | IsPartial = true, 221 | Constructors = 222 | { 223 | new ClassConstructorDefinition 224 | { 225 | AccessModifier = AccessModifier.Public 226 | } 227 | }, 228 | DbObject = view 229 | }; 230 | 231 | if (!string.IsNullOrEmpty(view.Description)) 232 | definition.Documentation.Summary = view.Description; 233 | 234 | var projectSelection = project.GetSelection(view); 235 | 236 | if (projectSelection.Settings.UseDataAnnotations) 237 | { 238 | definition.Namespaces.Add("System.ComponentModel.DataAnnotations"); 239 | definition.Namespaces.Add("System.ComponentModel.DataAnnotations.Schema"); 240 | } 241 | 242 | foreach (var column in view.Columns) 243 | { 244 | var propertyType = project.Database.ResolveDatabaseType(column); 245 | 246 | definition.Properties.Add(new PropertyDefinition 247 | { 248 | AccessModifier = AccessModifier.Public, 249 | Type = propertyType, 250 | Name = project.GetPropertyName(view, column), 251 | IsAutomatic = true 252 | }); 253 | } 254 | 255 | if (projectSelection.Settings.SimplifyDataTypes) 256 | definition.SimplifyDataTypes(); 257 | 258 | return definition; 259 | } 260 | 261 | public static EntityClassDefinition GetEntityClassDefinition(this EntityFrameworkCoreProject project, TableFunction tableFunction) 262 | { 263 | var definition = new EntityClassDefinition 264 | { 265 | Namespaces = 266 | { 267 | "System" 268 | }, 269 | Namespace = project.Database.HasDefaultSchema(tableFunction) ? project.GetEntityLayerNamespace() : project.GetEntityLayerNamespace(tableFunction.Schema), 270 | AccessModifier = AccessModifier.Public, 271 | Name = project.GetEntityResultName(tableFunction), 272 | IsPartial = true, 273 | Constructors = 274 | { 275 | new ClassConstructorDefinition 276 | { 277 | AccessModifier = AccessModifier.Public 278 | } 279 | }, 280 | DbObject = tableFunction 281 | }; 282 | 283 | if (!string.IsNullOrEmpty(tableFunction.Description)) 284 | definition.Documentation.Summary = tableFunction.Description; 285 | 286 | var projectSelection = project.GetSelection(tableFunction); 287 | 288 | foreach (var column in tableFunction.Columns) 289 | { 290 | var type = project.Database.ResolveDatabaseType(column); 291 | 292 | definition.Properties.Add(new PropertyDefinition(AccessModifier.Public, type, project.GetPropertyName(column.Name)) { IsAutomatic = true }); 293 | } 294 | 295 | if (projectSelection.Settings.SimplifyDataTypes) 296 | definition.SimplifyDataTypes(); 297 | 298 | return definition; 299 | } 300 | 301 | public static EntityClassDefinition GetEntityClassDefinition(this EntityFrameworkCoreProject project, StoredProcedure storedProcedure) 302 | { 303 | var definition = new EntityClassDefinition 304 | { 305 | Namespaces = 306 | { 307 | "System" 308 | }, 309 | Namespace = project.Database.HasDefaultSchema(storedProcedure) ? project.GetEntityLayerNamespace() : project.GetEntityLayerNamespace(storedProcedure.Schema), 310 | AccessModifier = AccessModifier.Public, 311 | Name = project.GetEntityResultName(storedProcedure), 312 | IsPartial = true, 313 | Constructors = 314 | { 315 | new ClassConstructorDefinition 316 | { 317 | AccessModifier = AccessModifier.Public 318 | } 319 | }, 320 | DbObject = storedProcedure 321 | }; 322 | 323 | if (!string.IsNullOrEmpty(storedProcedure.Description)) 324 | definition.Documentation.Summary = storedProcedure.Description; 325 | 326 | var projectSelection = project.GetSelection(storedProcedure); 327 | 328 | if (storedProcedure.ResultSets.Count == 0) 329 | { 330 | // todo: Add logic to stored procedures with no result set 331 | } 332 | else 333 | { 334 | foreach (var property in storedProcedure.ResultSets) 335 | { 336 | var propertyType = project.Database.ResolveDatabaseType(property.Type); 337 | 338 | definition.Properties.Add(new PropertyDefinition(AccessModifier.Public, propertyType, project.GetPropertyName(property.Name)) 339 | { 340 | IsAutomatic = true 341 | }); 342 | } 343 | } 344 | 345 | if (projectSelection.Settings.SimplifyDataTypes) 346 | definition.SimplifyDataTypes(); 347 | 348 | return definition; 349 | } 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/Extensions/EntityConfigurationClassBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using CatFactory.CodeFactory; 5 | using CatFactory.Collections; 6 | using CatFactory.NetCore; 7 | using CatFactory.NetCore.CodeFactory; 8 | using CatFactory.ObjectOrientedProgramming; 9 | using CatFactory.ObjectRelationalMapping; 10 | 11 | namespace CatFactory.EntityFrameworkCore.Definitions.Extensions 12 | { 13 | public static class EntityConfigurationClassBuilder 14 | { 15 | public static EntityConfigurationClassDefinition GetEntityConfigurationClassDefinition(this EntityFrameworkCoreProject project, ITable table, bool isDomainDrivenDesign) 16 | { 17 | var definition = new EntityConfigurationClassDefinition 18 | { 19 | Namespaces = 20 | { 21 | "Microsoft.EntityFrameworkCore", 22 | "Microsoft.EntityFrameworkCore.Metadata.Builders" 23 | }, 24 | AccessModifier = AccessModifier.Internal, 25 | Name = project.GetEntityConfigurationName(table), 26 | DbObject = table 27 | }; 28 | 29 | if (isDomainDrivenDesign) 30 | { 31 | definition.Namespace = project.GetDomainConfigurationsNamespace(project.Database.HasDefaultSchema(table) ? string.Empty : table.Schema); 32 | definition.Namespaces.AddUnique(project.GetDomainModelsNamespace(project.Database.HasDefaultSchema(table) ? string.Empty : table.Schema)); 33 | } 34 | else 35 | { 36 | definition.Namespace = project.Database.HasDefaultSchema(table) ? project.GetDataLayerConfigurationsNamespace() : project.GetDataLayerConfigurationsNamespace(table.Schema); 37 | definition.Namespaces.AddUnique(project.GetEntityLayerNamespace(project.Database.HasDefaultSchema(table) ? string.Empty : table.Schema)); 38 | } 39 | 40 | // todo: Check logic to build property's name 41 | 42 | var propertyType = ""; 43 | 44 | if (table.HasSameEnclosingName()) 45 | propertyType = string.Join(".", (new string[] { project.ProjectNamespaces.EntityLayer, project.Database.HasDefaultSchema(table) ? string.Empty : table.Schema, project.GetEntityName(table) }).Where(item => !string.IsNullOrEmpty(item))); 46 | else 47 | propertyType = project.GetEntityName(table); 48 | 49 | definition.Implements.Add(string.Format("IEntityTypeConfiguration<{0}>", propertyType)); 50 | 51 | var configLines = new List 52 | { 53 | new CommentLine(" Set configuration for entity") 54 | }; 55 | 56 | if (string.IsNullOrEmpty(table.Schema)) 57 | configLines.Add(new CodeLine("builder.ToTable(\"{0}\");", table.Name)); 58 | else 59 | configLines.Add(new CodeLine("builder.ToTable(\"{0}\", \"{1}\");", table.Name, table.Schema)); 60 | 61 | configLines.Add(new EmptyLine()); 62 | 63 | var columns = default(List); 64 | 65 | if (table.PrimaryKey == null || table.PrimaryKey.Key.Count == 0) 66 | { 67 | configLines.Add(LineHelper.Warning("Add configuration for entity's key")); 68 | configLines.Add(new EmptyLine()); 69 | } 70 | else 71 | { 72 | configLines.Add(new CommentLine(" Set key for entity")); 73 | 74 | if (table.PrimaryKey.Key.Count == 1) 75 | { 76 | configLines.Add(new CodeLine("builder.HasKey(p => p.{0});", project.CodeNamingConvention.GetPropertyName(table.PrimaryKey.Key[0]))); 77 | configLines.Add(new EmptyLine()); 78 | } 79 | else if (table.PrimaryKey.Key.Count > 1) 80 | { 81 | configLines.Add(new CodeLine("builder.HasKey(p => new {{ {0} }});", string.Join(", ", table.PrimaryKey.Key.Select(item => string.Format("p.{0}", project.CodeNamingConvention.GetPropertyName(item)))))); 82 | configLines.Add(new EmptyLine()); 83 | } 84 | } 85 | 86 | if (table.Identity != null) 87 | { 88 | configLines.Add(new CommentLine(" Set identity for entity (auto increment)")); 89 | 90 | if (project.Version >= EntityFrameworkCoreVersion.Version_3_0) 91 | { 92 | var exp = new List(); 93 | 94 | if (table.Identity.Seed != 1) 95 | exp.Add(string.Format("seed: {0}", table.Identity.Seed)); 96 | 97 | if (table.Identity.Increment != 1) 98 | exp.Add(string.Format("increment: {0}", table.Identity.Increment)); 99 | 100 | configLines.Add(new CodeLine("builder.Property(p => p.{0}).UseIdentityColumn({1});", project.CodeNamingConvention.GetPropertyName(table.Identity.Name), string.Join(",", exp))); 101 | } 102 | else 103 | configLines.Add(new CodeLine("builder.Property(p => p.{0}).UseSqlServerIdentityColumn();", project.CodeNamingConvention.GetPropertyName(table.Identity.Name))); 104 | 105 | configLines.Add(new EmptyLine()); 106 | } 107 | 108 | columns = table.Columns; 109 | 110 | configLines.Add(new CommentLine(" Set configuration for columns")); 111 | 112 | for (var i = 0; i < columns.Count; i++) 113 | { 114 | var column = columns[i]; 115 | 116 | var valueConversion = default(Type); 117 | 118 | if (project.Database.HasTypeMappedToClr(column)) 119 | { 120 | var lines = new List 121 | { 122 | string.Format("Property(p => p.{0})", project.GetPropertyName(table, column)) 123 | }; 124 | 125 | if (string.Compare(column.Name, project.GetPropertyName(table, column)) != 0) 126 | lines.Add(string.Format("HasColumnName(\"{0}\")", column.Name)); 127 | 128 | if (project.Database.ColumnIsByteArray(column)) 129 | lines.Add(string.Format("HasColumnType(\"{0}({1})\")", column.Type, column.Length)); 130 | else if (project.Database.ColumnIsDecimal(column)) 131 | lines.Add(string.Format("HasColumnType(\"{0}({1}, {2})\")", column.Type, column.Prec, column.Scale)); 132 | else if (project.Database.ColumnIsDouble(column) || project.Database.ColumnIsSingle(column)) 133 | lines.Add(string.Format("HasColumnType(\"{0}({1})\")", column.Type, column.Prec)); 134 | else if (project.Database.ColumnIsString(column)) 135 | { 136 | if (column.Length <= 0) 137 | { 138 | lines.Add(string.Format("HasColumnType(\"{0}(max)\")", column.Type)); 139 | } 140 | else 141 | { 142 | lines.Add(string.Format("HasColumnType(\"{0}\")", column.Type)); 143 | lines.Add(string.Format("HasMaxLength({0})", column.Length)); 144 | } 145 | } 146 | else 147 | lines.Add(string.Format("HasColumnType(\"{0}\")", column.Type)); 148 | 149 | // todo: Use ValueConversionMaps to detect and apply ValueConversion Type based on Type 150 | 151 | if (project.ValueConversionMaps.TryGetValue(column.Type, out valueConversion) == true) 152 | lines.Add($".HasConversion(typeof({valueConversion?.FullName}))"); 153 | 154 | if (!column.Nullable) 155 | lines.Add("IsRequired()"); 156 | 157 | configLines.Add(new CodeLine("builder")); 158 | 159 | foreach (var line in lines) 160 | { 161 | configLines.Add(new CodeLine(1, ".{0}", line)); 162 | } 163 | 164 | configLines.Add(new CodeLine(1, ";")); 165 | configLines.Add(new EmptyLine()); 166 | } 167 | else 168 | { 169 | configLines.Add(new CodeLine("builder.Ignore(p => p.{0});", project.GetPropertyName(table, column))); 170 | configLines.Add(new EmptyLine()); 171 | } 172 | } 173 | 174 | var projectSelection = project.GetSelection(table); 175 | 176 | for (var i = 0; i < columns.Count; i++) 177 | { 178 | var column = columns[i]; 179 | 180 | if (projectSelection.Settings.HasConcurrencyToken && string.Compare(column.Name, projectSelection.Settings.ConcurrencyToken) == 0) 181 | { 182 | configLines.Add(new CommentLine(" Set concurrency token for entity")); 183 | configLines.Add(new CodeLine("builder")); 184 | configLines.Add(new CodeLine(1, ".Property(p => p.{0})", project.GetPropertyName(table, column))); 185 | configLines.Add(new CodeLine(1, ".ValueGeneratedOnAddOrUpdate()")); 186 | configLines.Add(new CodeLine(1, ".IsConcurrencyToken()")); 187 | configLines.Add(new CodeLine(1, ";")); 188 | configLines.Add(new EmptyLine()); 189 | } 190 | 191 | if (projectSelection.Settings.HasRowVersion && string.Compare(column.Name, projectSelection.Settings.RowVersion) == 0) 192 | { 193 | configLines.Add(new CommentLine(" Set row version for entity")); 194 | configLines.Add(new CodeLine("builder")); 195 | configLines.Add(new CodeLine(1, ".Property(p => p.{0})", project.GetPropertyName(table, column))); 196 | configLines.Add(new CodeLine(1, ".ValueGeneratedOnAddOrUpdate()")); 197 | configLines.Add(new CodeLine(1, ".IsRowVersion()")); 198 | configLines.Add(new CodeLine(1, ";")); 199 | configLines.Add(new EmptyLine()); 200 | } 201 | } 202 | 203 | if (projectSelection.Settings.AddConfigurationForUniquesInFluentAPI && table.Uniques.Count > 0) 204 | { 205 | configLines.Add(new CommentLine(" Add configuration for uniques")); 206 | configLines.Add(new EmptyLine()); 207 | 208 | foreach (var unique in table.Uniques) 209 | { 210 | configLines.Add(new CodeLine("builder")); 211 | 212 | if (unique.Key.Count == 1) 213 | { 214 | configLines.Add(new CodeLine(1, ".HasIndex(p => p.{0})", project.CodeNamingConvention.GetPropertyName(unique.Key.First()))); 215 | configLines.Add(new CodeLine(1, ".IsUnique()")); 216 | } 217 | else 218 | { 219 | configLines.Add(new CodeLine(1, ".HasIndex(p => new {{ {0} }})", string.Join(", ", unique.Key.Select(item => string.Format("p.{0}", project.CodeNamingConvention.GetPropertyName(item)))))); 220 | configLines.Add(new CodeLine(1, ".IsUnique()")); 221 | } 222 | 223 | if (project.Version >= EntityFrameworkCoreVersion.Version_5_0) 224 | configLines.Add(new CodeLine(1, ".HasDatabaseName(\"{0}\")", unique.ConstraintName)); 225 | else 226 | configLines.Add(new CodeLine(1, ".HasName(\"{0}\")", unique.ConstraintName)); 227 | 228 | configLines.Add(new CodeLine(1, ";")); 229 | configLines.Add(new EmptyLine()); 230 | } 231 | } 232 | 233 | if (projectSelection.Settings.AddConfigurationForForeignKeysInFluentAPI && projectSelection.Settings.DeclareNavigationProperties && table.ForeignKeys.Count > 0) 234 | { 235 | configLines.Add(new CommentLine(" Add configuration for foreign keys")); 236 | configLines.Add(new EmptyLine()); 237 | 238 | foreach (var foreignKey in table.ForeignKeys) 239 | { 240 | var foreignTable = project.Database.FindTable(foreignKey.References); 241 | 242 | if (foreignTable == null || foreignKey.Key.Count == 0) 243 | continue; 244 | 245 | if (foreignKey.Key.Count == 1) 246 | { 247 | var foreignProperty = foreignKey.GetParentNavigationProperty(foreignTable, project); 248 | 249 | configLines.Add(new CodeLine("builder")); 250 | configLines.Add(new CodeLine(1, ".HasOne(p => p.{0})", foreignProperty.Name)); 251 | configLines.Add(new CodeLine(1, ".WithMany(b => b.{0})", project.GetNavigationPropertyName(table))); 252 | configLines.Add(new CodeLine(1, ".HasForeignKey(p => {0})", string.Format("p.{0}", project.CodeNamingConvention.GetPropertyName(foreignKey.Key.First())))); 253 | configLines.Add(new CodeLine(1, ".HasConstraintName(\"{0}\")", foreignKey.ConstraintName)); 254 | configLines.Add(new CodeLine(1, ";")); 255 | configLines.Add(new EmptyLine()); 256 | } 257 | else 258 | { 259 | configLines.Add(LineHelper.Warning(" Add logic for foreign key with multiple key")); 260 | } 261 | } 262 | } 263 | 264 | if (projectSelection.Settings.AddConfigurationForDefaultsInFluentAPI && table.Defaults.Count > 0) 265 | { 266 | configLines.Add(new CommentLine(" Add configuration for defaults")); 267 | configLines.Add(new EmptyLine()); 268 | 269 | foreach (var def in table.Defaults) 270 | { 271 | var propertyName = def.Key.First(); 272 | 273 | configLines.Add(new CodeLine("builder")); 274 | configLines.Add(new CodeLine(1, ".Property(p => p.{0})", project.GetPropertyName(propertyName))); 275 | configLines.Add(new CodeLine(1, ".HasDefaultValueSql(\"{0}\");", def.Value)); 276 | configLines.Add(new EmptyLine()); 277 | } 278 | } 279 | 280 | definition.Methods.Add(new MethodDefinition 281 | { 282 | AccessModifier = AccessModifier.Public, 283 | Type = "void", 284 | Name = "Configure", 285 | Parameters = 286 | { 287 | new ParameterDefinition(string.Format("EntityTypeBuilder<{0}>", propertyType), "builder") 288 | }, 289 | Lines = configLines 290 | }); 291 | 292 | return definition; 293 | } 294 | 295 | public static EntityConfigurationClassDefinition GetEntityConfigurationClassDefinition(this EntityFrameworkCoreProject project, IView view, bool isDomainDrivenDesign) 296 | { 297 | var definition = new EntityConfigurationClassDefinition 298 | { 299 | Namespaces = 300 | { 301 | "Microsoft.EntityFrameworkCore", 302 | "Microsoft.EntityFrameworkCore.Metadata.Builders" 303 | }, 304 | AccessModifier = AccessModifier.Internal, 305 | Name = project.GetEntityConfigurationName(view), 306 | Implements = 307 | { 308 | string.Format("IEntityTypeConfiguration<{0}>", project.GetEntityName(view)) 309 | }, 310 | DbObject = view 311 | }; 312 | 313 | if (isDomainDrivenDesign) 314 | { 315 | definition.Namespace = project.GetDomainConfigurationsNamespace(project.Database.HasDefaultSchema(view) ? string.Empty : view.Schema); 316 | 317 | definition.Namespaces.AddUnique(project.GetDomainModelsNamespace(project.Database.HasDefaultSchema(view) ? string.Empty : view.Schema)); 318 | definition.Namespaces.AddUnique(project.GetDomainModelsNamespace(project.Database.HasDefaultSchema(view) ? string.Empty : view.Schema)); 319 | } 320 | else 321 | { 322 | definition.Namespace = project.Database.HasDefaultSchema(view) ? project.GetDataLayerConfigurationsNamespace() : project.GetDataLayerConfigurationsNamespace(view.Schema); 323 | 324 | definition.Namespaces.AddUnique(project.GetEntityLayerNamespace(project.Database.HasDefaultSchema(view) ? string.Empty : view.Schema)); 325 | definition.Namespaces.AddUnique(project.GetEntityLayerNamespace(project.Database.HasDefaultSchema(view) ? string.Empty : view.Schema)); 326 | } 327 | 328 | var configLines = new List 329 | { 330 | new CommentLine(" Set configuration for entity") 331 | }; 332 | 333 | if (string.IsNullOrEmpty(view.Schema)) 334 | configLines.Add(new CodeLine("builder.ToTable(\"{0}\");", view.Name)); 335 | else 336 | configLines.Add(new CodeLine("builder.ToTable(\"{0}\", \"{1}\");", view.Name, view.Schema)); 337 | 338 | configLines.Add(new EmptyLine()); 339 | 340 | var primaryKeys = project 341 | .Database 342 | .Tables 343 | .Where(item => item.PrimaryKey != null) 344 | .Select(item => item.GetColumnsFromConstraint(item.PrimaryKey).Select(c => c.Name).First()) 345 | .ToList(); 346 | 347 | var result = view.Columns.Where(item => primaryKeys.Contains(item.Name)).ToList(); 348 | 349 | if (result.Count == 0) 350 | result = view.Columns.Where(item => !item.Nullable).ToList(); 351 | 352 | configLines.Add(new CommentLine(" Add configuration for entity's key")); 353 | 354 | if (project.Version >= EntityFrameworkCoreVersion.Version_3_0) 355 | { 356 | configLines.Add(new CodeLine("builder.HasNoKey();")); 357 | } 358 | else 359 | { 360 | if (result.Count == 1) 361 | configLines.Add(new CodeLine("builder.HasKey(p => {0});", string.Format("p.{0}", project.CodeNamingConvention.GetPropertyName(result.First().Name)))); 362 | else 363 | configLines.Add(new CodeLine("builder.HasKey(p => new {{ {0} }});", string.Join(", ", result.Select(item => string.Format("p.{0}", project.CodeNamingConvention.GetPropertyName(item.Name)))))); 364 | } 365 | 366 | configLines.Add(new EmptyLine()); 367 | 368 | configLines.Add(new CommentLine(" Set configuration for columns")); 369 | 370 | for (var i = 0; i < view.Columns.Count; i++) 371 | { 372 | var column = view.Columns[i]; 373 | 374 | var valueConversion = default(Type); 375 | 376 | if (project.Database.HasTypeMappedToClr(column)) 377 | { 378 | var lines = new List 379 | { 380 | string.Format("Property(p => p.{0})" , project.GetPropertyName( view, column)) 381 | }; 382 | 383 | if (string.Compare(column.Name, project.GetPropertyName(view, column)) != 0) 384 | lines.Add(string.Format("HasColumnName(\"{0}\")", column.Name)); 385 | 386 | else if (project.Database.ColumnIsDecimal(column)) 387 | lines.Add(string.Format("HasColumnType(\"{0}({1}, {2})\")", column.Type, column.Prec, column.Scale)); 388 | else if (project.Database.ColumnIsDouble(column) || project.Database.ColumnIsSingle(column)) 389 | lines.Add(string.Format("HasColumnType(\"{0}({1})\")", column.Type, column.Prec)); 390 | if (project.Database.ColumnIsString(column)) 391 | { 392 | if (column.Length <= 0) 393 | { 394 | lines.Add(string.Format("HasColumnType(\"{0}(max)\")", column.Type)); 395 | } 396 | else 397 | { 398 | lines.Add(string.Format("HasColumnType(\"{0}\")", column.Type)); 399 | lines.Add(string.Format("HasMaxLength({0})", column.Length)); 400 | } 401 | } 402 | else 403 | lines.Add(string.Format("HasColumnType(\"{0}\")", column.Type)); 404 | 405 | // todo: Use ValueConversionMaps to detect and apply ValueConversion Type based on Type 406 | 407 | if (project.ValueConversionMaps?.TryGetValue(column.Type, out valueConversion) == true) 408 | lines.Add($"HasConversion(typeof({valueConversion?.FullName}))"); 409 | 410 | configLines.Add(new CodeLine("builder")); 411 | 412 | foreach (var line in lines) 413 | { 414 | configLines.Add(new CodeLine(1, ".{0}", line)); 415 | } 416 | 417 | configLines.Add(new CodeLine(1, ";")); 418 | configLines.Add(new EmptyLine()); 419 | } 420 | else 421 | { 422 | var lines = new List 423 | { 424 | string.Format("builder.Ignore(p => p.{0})", project.GetPropertyName( view, column)) 425 | }; 426 | 427 | configLines.Add(new CodeLine("{0}", string.Join(".", lines))); 428 | configLines.Add(new CodeLine(";")); 429 | } 430 | } 431 | 432 | definition.Methods.Add(new MethodDefinition 433 | { 434 | AccessModifier = AccessModifier.Public, 435 | Type = "void", 436 | Name = "Configure", 437 | Parameters = 438 | { 439 | new ParameterDefinition(string.Format("EntityTypeBuilder<{0}>", project.GetEntityName(view)), "builder") 440 | }, 441 | Lines = configLines 442 | }); 443 | 444 | return definition; 445 | } 446 | } 447 | } 448 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/Extensions/EntityInterfaceBuilder.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.ObjectOrientedProgramming; 2 | 3 | namespace CatFactory.EntityFrameworkCore.Definitions.Extensions 4 | { 5 | public static class EntityInterfaceBuilder 6 | { 7 | public static EntityInterfaceDefinition GetEntityInterfaceDefinition(this EntityFrameworkCoreProject project, bool isDomainDrivenDesign) 8 | => new EntityInterfaceDefinition 9 | { 10 | Namespace = isDomainDrivenDesign ? project.GetDomainModelsNamespace() : project.GetEntityLayerNamespace(), 11 | Namespaces = 12 | { 13 | "System" 14 | }, 15 | AccessModifier = AccessModifier.Public, 16 | Name = "IEntity" 17 | }; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/Extensions/PagingExtensionsClassBuilder.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.CodeFactory; 2 | using CatFactory.ObjectOrientedProgramming; 3 | 4 | namespace CatFactory.EntityFrameworkCore.Definitions.Extensions 5 | { 6 | public static class PagingExtensionsClassBuilder 7 | { 8 | public static PagingExtensionsClassDefinition GetPagingExtensionsClassDefinition(this EntityFrameworkCoreProject project, bool isDomainDrivenDesign) 9 | { 10 | var definition = new PagingExtensionsClassDefinition 11 | { 12 | Namespaces = 13 | { 14 | "System", 15 | "System.Linq", 16 | }, 17 | Namespace = isDomainDrivenDesign ? project.Name : project.GetDataLayerRepositoriesNamespace(), 18 | AccessModifier = AccessModifier.Public, 19 | IsStatic = true, 20 | Name = "PagingExtensions" 21 | }; 22 | 23 | if (!isDomainDrivenDesign) 24 | { 25 | definition.Namespaces.Add(project.GetDataLayerNamespace()); 26 | definition.Namespaces.Add(project.GetEntityLayerNamespace()); 27 | } 28 | 29 | definition.Methods.Add(new MethodDefinition("IQueryable", "Paging", new ParameterDefinition(project.GetDbContextName(project.Database), "dbContext"), new ParameterDefinition("int", "pageSize", "0"), new ParameterDefinition("int", "pageNumber", "0")) 30 | { 31 | AccessModifier = AccessModifier.Public, 32 | IsExtension = true, 33 | IsStatic = true, 34 | GenericTypes = 35 | { 36 | new GenericTypeDefinition 37 | { 38 | Name = "TEntity", 39 | Constraint = "TEntity : class" 40 | } 41 | }, 42 | Lines = 43 | { 44 | new CodeLine("var query = dbContext.Set().AsQueryable();"), 45 | new CodeLine(), 46 | new CodeLine("return pageSize > 0 && pageNumber > 0 ? query.Skip((pageNumber - 1) * pageSize).Take(pageSize) : query;") 47 | } 48 | }); 49 | 50 | definition.Methods.Add(new MethodDefinition("IQueryable", "Paging", new ParameterDefinition("IQueryable", "query"), new ParameterDefinition("int", "pageSize", "0"), new ParameterDefinition("int", "pageNumber", "0")) 51 | { 52 | AccessModifier = AccessModifier.Public, 53 | IsExtension = true, 54 | IsStatic = true, 55 | GenericTypes = 56 | { 57 | new GenericTypeDefinition 58 | { 59 | Name = "TModel", 60 | Constraint = "TModel : class" 61 | } 62 | }, 63 | Lines = 64 | { 65 | new CodeLine("return pageSize > 0 && pageNumber > 0 ? query.Skip((pageNumber - 1) * pageSize).Take(pageSize) : query;") 66 | } 67 | }); 68 | 69 | return definition; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/Extensions/QueryModelClassBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using CatFactory.NetCore; 3 | using CatFactory.NetCore.ObjectOrientedProgramming; 4 | using CatFactory.ObjectOrientedProgramming; 5 | using CatFactory.ObjectRelationalMapping; 6 | 7 | namespace CatFactory.EntityFrameworkCore.Definitions.Extensions 8 | { 9 | public static class QueryModelClassBuilder 10 | { 11 | public static QueryModelClassDefinition GetQueryModelClassDefinition(this EntityFrameworkCoreProject project, ITable table) 12 | { 13 | var definition = new QueryModelClassDefinition 14 | { 15 | Namespaces = 16 | { 17 | "System" 18 | }, 19 | Namespace = project.GetDomainQueryModelsNamespace(), 20 | AccessModifier = AccessModifier.Public, 21 | Name = project.GetQueryModelName(table), 22 | DbObject = table 23 | }; 24 | 25 | foreach (var column in table.Columns) 26 | { 27 | definition.Properties.Add(new PropertyDefinition(AccessModifier.Public, project.Database.ResolveDatabaseType(column), project.GetPropertyName(table, column)) 28 | { 29 | IsAutomatic = true 30 | }); 31 | } 32 | 33 | foreach (var foreignKey in table.ForeignKeys) 34 | { 35 | var foreignTable = project.Database.FindTable(foreignKey.References); 36 | 37 | if (foreignTable == null) 38 | continue; 39 | 40 | var foreignKeyAlias = NamingConvention.GetCamelCase(project.GetEntityName(foreignTable)); 41 | 42 | foreach (var column in foreignTable?.GetColumnsWithNoPrimaryKey()) 43 | { 44 | var col = (Column)column; 45 | 46 | var propertyName = project.GetPropertyName(foreignTable, col); 47 | 48 | var target = string.Format("{0}{1}", project.GetEntityName(foreignTable), propertyName); 49 | 50 | if (definition.Properties.Count(item => item.Name == propertyName) == 0) 51 | definition.Properties.Add(new PropertyDefinition(AccessModifier.Public, project.Database.ResolveDatabaseType(col), target) 52 | { 53 | IsAutomatic = true 54 | }); 55 | } 56 | } 57 | 58 | definition.SimplifyDataTypes(); 59 | 60 | return definition; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/Extensions/RepositoryBaseClassBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using CatFactory.CodeFactory; 3 | using CatFactory.ObjectOrientedProgramming; 4 | 5 | namespace CatFactory.EntityFrameworkCore.Definitions.Extensions 6 | { 7 | public static class RepositoryBaseClassBuilder 8 | { 9 | public static PagingExtensionsClassDefinition GetRepositoryBaseClassDefinition(this EntityFrameworkCoreProject project) 10 | { 11 | var definition = new PagingExtensionsClassDefinition 12 | { 13 | Namespaces = 14 | { 15 | "System", 16 | "System.Threading.Tasks", 17 | }, 18 | Namespace = project.GetDataLayerContractsNamespace(), 19 | AccessModifier = AccessModifier.Public, 20 | Name = "Repository", 21 | Fields = 22 | { 23 | new FieldDefinition(AccessModifier.Protected, "bool", "Disposed"), 24 | new FieldDefinition(AccessModifier.Protected, project.GetDbContextName(project.Database), "DbContext") 25 | { 26 | IsReadOnly = true 27 | } 28 | }, 29 | Constructors = 30 | { 31 | new ClassConstructorDefinition(AccessModifier.Public, new ParameterDefinition(project.GetDbContextName(project.Database), "dbContext")) 32 | { 33 | Lines = 34 | { 35 | new CodeLine("DbContext = dbContext;") 36 | } 37 | } 38 | }, 39 | Methods = 40 | { 41 | new MethodDefinition(AccessModifier.Public, "void", "Dispose") 42 | { 43 | IsVirtual = true, 44 | Lines = 45 | { 46 | new CodeLine("if (Disposed)"), 47 | new CodeLine(1, "return;"), 48 | new EmptyLine(), 49 | new CodeLine("DbContext?.Dispose();"), 50 | new EmptyLine(), 51 | new CodeLine("Disposed = true;") 52 | } 53 | }, 54 | GetAddMethod(project), 55 | GetUpdateMethod(project), 56 | GetRemoveMethod(project), 57 | new MethodDefinition(AccessModifier.Public, "int", "CommitChanges") 58 | { 59 | Lines = 60 | { 61 | new CodeLine("return DbContext.SaveChanges();") 62 | } 63 | }, 64 | new MethodDefinition(AccessModifier.Public, "Task", "CommitChangesAsync") 65 | { 66 | IsAsync = true, 67 | Lines = 68 | { 69 | new CodeLine("return await DbContext.SaveChangesAsync();") 70 | } 71 | } 72 | } 73 | }; 74 | 75 | var selection = project.GlobalSelection(); 76 | 77 | if (selection.Settings.AuditEntity != null) 78 | definition.Namespaces.Add(project.GetEntityLayerNamespace()); 79 | 80 | return definition; 81 | } 82 | 83 | private static MethodDefinition GetAddMethod(EntityFrameworkCoreProject project) 84 | { 85 | var lines = new List(); 86 | 87 | var selection = project.GlobalSelection(); 88 | 89 | if (selection.Settings.AuditEntity == null) 90 | { 91 | lines.AddRange(new List 92 | { 93 | new CodeLine("DbContext.Add(entity);") 94 | }); 95 | } 96 | else 97 | { 98 | lines.AddRange(new List 99 | { 100 | new CommentLine(" Cast entity to IAuditEntity"), 101 | new CodeLine("if(entity is IAuditEntity cast)"), 102 | new CodeLine("{"), 103 | new CommentLine(1, " Set creation datetime"), 104 | new CodeLine(1, "cast.CreationDateTime = DateTime.Now;"), 105 | new CodeLine("}"), 106 | new CodeLine(), 107 | new CodeLine("DbContext.Add(entity);"), 108 | }); 109 | } 110 | 111 | return new MethodDefinition("void", "Add", new ParameterDefinition("TEntity", "entity")) 112 | { 113 | AccessModifier = AccessModifier.Public, 114 | IsVirtual = true, 115 | GenericTypes = 116 | { 117 | new GenericTypeDefinition 118 | { 119 | Name = "TEntity", 120 | Constraint = "TEntity : class" 121 | } 122 | }, 123 | Lines = lines 124 | }; 125 | } 126 | 127 | private static MethodDefinition GetUpdateMethod(EntityFrameworkCoreProject project) 128 | { 129 | var lines = new List(); 130 | 131 | var selection = project.GlobalSelection(); 132 | 133 | if (selection.Settings.AuditEntity == null) 134 | { 135 | lines.AddRange(new List 136 | { 137 | new CodeLine("DbContext.Update(entity);") 138 | }); 139 | } 140 | else 141 | { 142 | lines.AddRange(new List 143 | { 144 | new CommentLine(" Cast entity to IAuditEntity"), 145 | new CodeLine("if (entity is IAuditEntity cast)"), 146 | new CodeLine("{"), 147 | new CommentLine(1, " Set update datetime"), 148 | new CodeLine(1, "cast.LastUpdateDateTime = DateTime.Now;"), 149 | new CodeLine("}"), 150 | new CodeLine(), 151 | new CodeLine("DbContext.Update(entity);") 152 | }); 153 | } 154 | 155 | return new MethodDefinition("void", "Update", new ParameterDefinition("TEntity", "entity")) 156 | { 157 | AccessModifier = AccessModifier.Public, 158 | IsVirtual = true, 159 | GenericTypes = 160 | { 161 | new GenericTypeDefinition 162 | { 163 | Name = "TEntity", 164 | Constraint = "TEntity : class" 165 | } 166 | }, 167 | Lines = lines 168 | }; 169 | } 170 | 171 | private static MethodDefinition GetRemoveMethod(EntityFrameworkCoreProject project) 172 | => new MethodDefinition("void", "Remove", new ParameterDefinition("TEntity", "entity")) 173 | { 174 | AccessModifier = AccessModifier.Public, 175 | IsVirtual = true, 176 | GenericTypes = 177 | { 178 | new GenericTypeDefinition 179 | { 180 | Name = "TEntity", 181 | Constraint = "TEntity : class" 182 | } 183 | }, 184 | Lines = 185 | { 186 | new CodeLine("DbContext.Remove(entity);") 187 | } 188 | }; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/Extensions/RepositoryInterfaceBuilder.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.ObjectOrientedProgramming; 2 | 3 | namespace CatFactory.EntityFrameworkCore.Definitions.Extensions 4 | { 5 | public static class RepositoryInterfaceBuilder 6 | { 7 | public static RepositoryInterfaceDefinition GetRepositoryInterfaceDefinition(this EntityFrameworkCoreProject project) 8 | => new RepositoryInterfaceDefinition 9 | { 10 | Namespaces = 11 | { 12 | "System", 13 | "System.Threading.Tasks" 14 | }, 15 | Namespace = project.GetDataLayerContractsNamespace(), 16 | AccessModifier = AccessModifier.Public, 17 | Name = "IRepository", 18 | Implements = 19 | { 20 | "IDisposable" 21 | }, 22 | Methods = 23 | { 24 | new MethodDefinition("void", "Add", new ParameterDefinition("TEntity", "entity")) 25 | { 26 | GenericTypes = 27 | { 28 | new GenericTypeDefinition 29 | { 30 | Name = "TEntity", 31 | Constraint = "TEntity : class" 32 | } 33 | } 34 | }, 35 | new MethodDefinition("void", "Update", new ParameterDefinition("TEntity", "entity")) 36 | { 37 | GenericTypes = 38 | { 39 | new GenericTypeDefinition 40 | { 41 | Name = "TEntity", 42 | Constraint = "TEntity : class" 43 | } 44 | } 45 | }, 46 | new MethodDefinition("void", "Remove", new ParameterDefinition("TEntity", "entity")) 47 | { 48 | GenericTypes = 49 | { 50 | new GenericTypeDefinition 51 | { 52 | Name = "TEntity", 53 | Constraint = "TEntity : class" 54 | } 55 | } 56 | }, 57 | new MethodDefinition("int", "CommitChanges"), 58 | new MethodDefinition("Task", "CommitChangesAsync") 59 | } 60 | }; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/PagingExtensionsClassDefinition.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.NetCore.ObjectOrientedProgramming; 2 | 3 | namespace CatFactory.EntityFrameworkCore.Definitions 4 | { 5 | public class PagingExtensionsClassDefinition : CSharpClassDefinition 6 | { 7 | public PagingExtensionsClassDefinition() 8 | : base() 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/QueryModelClassDefinition.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.NetCore.ObjectOrientedProgramming; 2 | 3 | namespace CatFactory.EntityFrameworkCore.Definitions 4 | { 5 | public class QueryModelClassDefinition : CSharpClassDefinition 6 | { 7 | public QueryModelClassDefinition() 8 | : base() 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/RepositoryClassDefinition.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.NetCore.ObjectOrientedProgramming; 2 | 3 | namespace CatFactory.EntityFrameworkCore.Definitions 4 | { 5 | public class RepositoryClassDefinition : CSharpClassDefinition 6 | { 7 | public RepositoryClassDefinition() 8 | : base() 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/Definitions/RepositoryInterfaceDefinition.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.NetCore.ObjectOrientedProgramming; 2 | 3 | namespace CatFactory.EntityFrameworkCore.Definitions 4 | { 5 | public class RepositoryInterfaceDefinition : CSharpInterfaceDefinition 6 | { 7 | public RepositoryInterfaceDefinition() 8 | : base() 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/DomainExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using CatFactory.EntityFrameworkCore.Definitions.Extensions; 3 | using CatFactory.Markdown; 4 | using CatFactory.ObjectRelationalMapping; 5 | 6 | namespace CatFactory.EntityFrameworkCore 7 | { 8 | public static class DomainExtensions 9 | { 10 | public static EntityFrameworkCoreProject ScaffoldDomain(this EntityFrameworkCoreProject project) 11 | { 12 | ScaffoldEntityInterface(project); 13 | ScaffoldModels(project); 14 | 15 | ScaffoldConfigurations(project); 16 | 17 | ScaffoldDbContext(project); 18 | 19 | ScaffoldQueryModels(project); 20 | 21 | ScaffoldExtensions(project); 22 | 23 | ScaffoldMdReadMe(project); 24 | 25 | return project; 26 | } 27 | 28 | internal static void ScaffoldEntityInterface(EntityFrameworkCoreProject project) 29 | { 30 | project.Scaffold(project.GetEntityInterfaceDefinition(true), project.GetDomainModelsDirectory()); 31 | 32 | if (project.GlobalSelection().Settings.AuditEntity != null) 33 | project.Scaffold(project.GetAuditEntityInterfaceDefinition(true), project.GetDomainModelsDirectory()); 34 | } 35 | 36 | internal static EntityFrameworkCoreProject ScaffoldModels(this EntityFrameworkCoreProject project) 37 | { 38 | foreach (var table in project.Database.Tables) 39 | { 40 | var selection = project.GetSelection(table); 41 | 42 | var definition = project.GetEntityClassDefinition(table, true); 43 | 44 | if (selection.Settings.UseDataAnnotations) 45 | definition.AddDataAnnotations(table, project); 46 | 47 | project.Scaffold(definition, project.GetDomainModelsDirectory(), project.Database.HasDefaultSchema(table) ? "" : table.Schema); 48 | } 49 | 50 | foreach (var view in project.Database.Views) 51 | { 52 | var selection = project.GetSelection(view); 53 | 54 | var definition = project.GetEntityClassDefinition(view, project.Database.HasDefaultSchema(view) ? project.GetDomainModelsNamespace() : project.GetDomainModelsNamespace(view.Schema)); 55 | 56 | if (selection.Settings.UseDataAnnotations) 57 | definition.AddDataAnnotations(view, project); 58 | 59 | project.Scaffold(definition, project.GetDomainModelsDirectory(), project.Database.HasDefaultSchema(view) ? "" : view.Schema); 60 | } 61 | 62 | return project; 63 | } 64 | 65 | internal static void ScaffoldConfigurations(EntityFrameworkCoreProject project) 66 | { 67 | var projectSelection = project.GlobalSelection(); 68 | 69 | if (!projectSelection.Settings.UseDataAnnotations) 70 | { 71 | foreach (var table in project.Database.Tables) 72 | { 73 | var definition = project.GetEntityConfigurationClassDefinition(table, true); 74 | 75 | project.Scaffold(definition, project.GetDomainConfigurationsDirectory(), project.Database.HasDefaultSchema(table) ? "" : table.Schema); 76 | } 77 | 78 | foreach (var view in project.Database.Views) 79 | { 80 | var definition = project.GetEntityConfigurationClassDefinition(view, true); 81 | 82 | project.Scaffold(definition, project.GetDomainConfigurationsDirectory(), project.Database.HasDefaultSchema(view) ? "" : view.Schema); 83 | } 84 | } 85 | } 86 | 87 | internal static void ScaffoldDbContext(EntityFrameworkCoreProject project) 88 | { 89 | var projectSelection = project.GlobalSelection(); 90 | 91 | project.Scaffold(project.GetDbContextClassDefinition(projectSelection, true), project.GetDomainDirectory()); 92 | } 93 | 94 | internal static void ScaffoldQueryModels(EntityFrameworkCoreProject project) 95 | { 96 | foreach (var table in project.Database.Tables) 97 | { 98 | var selection = project.GetSelection(table); 99 | 100 | if (!selection.Settings.EntitiesWithDataContracts) 101 | continue; 102 | 103 | project.Scaffold(project.GetQueryModelClassDefinition(table), project.GetDomainQueryModelsDirectory()); 104 | } 105 | } 106 | 107 | internal static void ScaffoldExtensions(EntityFrameworkCoreProject project) 108 | { 109 | project.Scaffold(project.GetPagingExtensionsClassDefinition(true), project.GetDomainDirectory()); 110 | 111 | foreach (var projectFeature in project.Features) 112 | { 113 | project.Scaffold(projectFeature.GetDbContextQueryExtensionsClassDefinition(), project.GetDomainDirectory()); 114 | } 115 | } 116 | 117 | internal static void ScaffoldMdReadMe(this EntityFrameworkCoreProject project) 118 | { 119 | var readMe = new MdDocument(); 120 | 121 | readMe.H1("CatFactory ==^^==: Scaffolding Made Easy"); 122 | 123 | readMe.WriteLine("How to use this code on your ASP.NET Core Application:"); 124 | 125 | readMe.OrderedList( 126 | "Install EntityFrameworkCore.SqlServer package", 127 | "Register the DbContext and Repositories in ConfigureServices method (Startup class)" 128 | ); 129 | 130 | readMe.H2("Install package"); 131 | 132 | readMe.WriteLine("You can install the NuGet packages in Visual Studio or Windows Command Line, for more info:"); 133 | 134 | readMe.WriteLine( 135 | Md.Link("Install and manage packages with the Package Manager Console in Visual Studio (PowerShell)", "https://docs.microsoft.com/en-us/nuget/consume-packages/install-use-packages-powershell") 136 | ); 137 | 138 | readMe.WriteLine( 139 | Md.Link(".NET Core CLI", "https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-add-package") 140 | ); 141 | 142 | readMe.H2("Register DbContext and Repositories"); 143 | 144 | readMe.WriteLine("Add the following code lines in {0} method (Startup class):", Md.Bold("ConfigureServices")); 145 | readMe.WriteLine(" services.AddDbContext<{0}>(options => options.UseSqlServer(\"ConnectionString\"));", project.GetDbContextName(project.Database)); 146 | readMe.WriteLine(" services.AddScope<{0}, {1}>()", "IDboRepository", "DboRepository"); 147 | 148 | readMe.WriteLine("Happy scaffolding!"); 149 | 150 | var codeProjectLink = Md.Link("Scaffolding Entity Framework Core with CatFactory", "https://www.codeproject.com/Articles/1160615/Scaffolding-Entity-Framework-Core-with-CatFactory"); 151 | 152 | readMe.WriteLine("You can check the guide for this package in: {0}", codeProjectLink); 153 | 154 | var gitHubRepositoryLink = Md.Link("GitHub repository", "https://github.com/hherzl/CatFactory.EntityFrameworkCore"); 155 | 156 | readMe.WriteLine("Also you can check the source code on {0}", gitHubRepositoryLink); 157 | 158 | readMe.WriteLine("CatFactory Development Team ==^^=="); 159 | 160 | File.WriteAllText(Path.Combine(project.OutputDirectory, "README.md"), readMe.ToString()); 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/EntityFrameworkCoreProject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using CatFactory.CodeFactory; 6 | using CatFactory.CodeFactory.Scaffolding; 7 | using CatFactory.NetCore; 8 | using CatFactory.NetCore.CodeFactory; 9 | using CatFactory.NetCore.ObjectOrientedProgramming; 10 | using CatFactory.ObjectOrientedProgramming; 11 | using CatFactory.ObjectRelationalMapping; 12 | using Microsoft.Extensions.Logging; 13 | 14 | namespace CatFactory.EntityFrameworkCore 15 | { 16 | public class EntityFrameworkCoreProject : CSharpProject 17 | { 18 | public static EntityFrameworkCoreProject CreateForV2x(string name, Database database, string outputDirectory) 19 | => new() 20 | { 21 | Name = name, 22 | Database = database, 23 | OutputDirectory = outputDirectory, 24 | Version = EntityFrameworkCoreVersion.Version_2_0 25 | }; 26 | 27 | public static EntityFrameworkCoreProject CreateForV3x(string name, Database database, string outputDirectory) 28 | => new() 29 | { 30 | Name = name, 31 | Database = database, 32 | OutputDirectory = outputDirectory, 33 | Version = EntityFrameworkCoreVersion.Version_3_0 34 | }; 35 | 36 | public static EntityFrameworkCoreProject CreateForV5x(string name, Database database, string outputDirectory) 37 | => new() 38 | { 39 | Name = name, 40 | Database = database, 41 | OutputDirectory = outputDirectory, 42 | Version = EntityFrameworkCoreVersion.Version_5_0 43 | }; 44 | 45 | [Obsolete("Replace using the CreateForV2x method")] 46 | public static EntityFrameworkCoreProject Create(string name, Database database, string outputDirectory) 47 | => new() 48 | { 49 | Name = name, 50 | Database = database, 51 | OutputDirectory = outputDirectory 52 | }; 53 | 54 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 55 | private EntityFrameworkCoreProjectNamespaces m_projectNamespaces; 56 | 57 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 58 | private Dictionary m_valueConversionMaps; 59 | 60 | public EntityFrameworkCoreProject() 61 | : base() 62 | { 63 | } 64 | 65 | public EntityFrameworkCoreProject(ILogger logger) 66 | : base(logger) 67 | { 68 | } 69 | 70 | public EntityFrameworkCoreVersion Version { get; set; } 71 | 72 | public EntityFrameworkCoreProjectNamespaces ProjectNamespaces 73 | { 74 | get => m_projectNamespaces ??= new EntityFrameworkCoreProjectNamespaces(); 75 | set => m_projectNamespaces = value; 76 | } 77 | 78 | /// 79 | /// A dictionary of (string)CatFactory.ObjectRelationalMapping.DatabaseTypeMap.DatabaseType to 80 | /// {OutputDirectory}\{EntityFrameworkCoreProjectNamespaces.ValueConversions}\Type can be 81 | /// submitted to the Entity Framework Core project via ValueConversionMaps for use in {Enity}Configuration.cs 82 | /// files 83 | /// 84 | public Dictionary ValueConversionMaps 85 | { 86 | get => m_valueConversionMaps ??= new Dictionary(); 87 | set => m_valueConversionMaps = value; 88 | } 89 | 90 | // todo: Add logic to show author's info 91 | public AuthorInfo AuthorInfo { get; set; } 92 | 93 | protected override IEnumerable GetDbObjectsBySchema(string schema) 94 | { 95 | foreach (var item in base.GetDbObjectsBySchema(schema)) 96 | { 97 | yield return item; 98 | } 99 | 100 | foreach (var item in Database.GetTableFunctions().Where(tableFunction => tableFunction.Schema == schema)) 101 | { 102 | yield return new DbObject(item.Schema, item.Name) 103 | { 104 | Type = "TableFunction" 105 | }; 106 | } 107 | 108 | foreach (var item in Database.GetStoredProcedures().Where(storedProcedure => storedProcedure.Schema == schema)) 109 | { 110 | yield return new DbObject(item.Schema, item.Name) 111 | { 112 | Type = "StoredProcedure" 113 | }; 114 | } 115 | } 116 | 117 | public override void BuildFeatures() 118 | { 119 | if (Database == null) 120 | return; 121 | 122 | if (this.GlobalSelection().Settings.AuditEntity != null) 123 | this.GlobalSelection().Settings.EntityInterfaceName = "IAuditEntity"; 124 | 125 | Features = Database 126 | .DbObjects 127 | .Select(item => item.Schema) 128 | .Distinct() 129 | .Select(item => new ProjectFeature(item, GetDbObjectsBySchema(item), this)) 130 | .ToList(); 131 | } 132 | 133 | public override void Scaffold(IObjectDefinition objectDefinition, string outputDirectory, string subdirectory = "") 134 | { 135 | var codeBuilder = default(ICodeBuilder); 136 | 137 | var selection = objectDefinition.DbObject == null ? this.GlobalSelection() : this.GetSelection(objectDefinition.DbObject); 138 | 139 | if (objectDefinition is CSharpClassDefinition) 140 | { 141 | codeBuilder = new CSharpClassBuilder 142 | { 143 | OutputDirectory = outputDirectory, 144 | ForceOverwrite = selection.Settings.ForceOverwrite, 145 | ObjectDefinition = objectDefinition 146 | }; 147 | } 148 | else if (objectDefinition is CSharpInterfaceDefinition) 149 | { 150 | codeBuilder = new CSharpInterfaceBuilder 151 | { 152 | OutputDirectory = outputDirectory, 153 | ForceOverwrite = selection.Settings.ForceOverwrite, 154 | ObjectDefinition = objectDefinition 155 | }; 156 | } 157 | 158 | OnScaffoldingDefinition(new ScaffoldingDefinitionEventArgs(Logger, codeBuilder)); 159 | 160 | codeBuilder.CreateFile(subdirectory: subdirectory); 161 | 162 | OnScaffoldedDefinition(new ScaffoldedDefinitionEventArgs(Logger, codeBuilder)); 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/EntityFrameworkCoreProjectDomainExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace CatFactory.EntityFrameworkCore 4 | { 5 | public static partial class EntityFrameworkCoreProjectDomainExtensions 6 | { 7 | public static string GetDomainDirectory(this EntityFrameworkCoreProject project) 8 | => Path.Combine(project.OutputDirectory); 9 | 10 | public static string GetDomainModelsDirectory(this EntityFrameworkCoreProject project) 11 | => Path.Combine(project.OutputDirectory, project.ProjectNamespaces.Models); 12 | 13 | public static string GetDomainQueryModelsDirectory(this EntityFrameworkCoreProject project) 14 | => Path.Combine(project.OutputDirectory, project.ProjectNamespaces.QueryModels); 15 | 16 | public static string GetDomainConfigurationsDirectory(this EntityFrameworkCoreProject project) 17 | => Path.Combine(project.OutputDirectory, project.ProjectNamespaces.Configurations); 18 | 19 | public static string GetDomainConfigurationsDirectory(this EntityFrameworkCoreProject project, string schema) 20 | => Path.Combine(project.OutputDirectory, project.ProjectNamespaces.Configurations, schema); 21 | 22 | public static string GetDomainModelsNamespace(this EntityFrameworkCoreProject project) 23 | => project.CodeNamingConvention.GetNamespace(project.Name, project.ProjectNamespaces.Models); 24 | 25 | public static string GetDomainModelsNamespace(this EntityFrameworkCoreProject project, string ns) 26 | => string.IsNullOrEmpty(ns) ? GetDomainModelsNamespace(project) : project.CodeNamingConvention.GetNamespace(project.Name, project.ProjectNamespaces.Models, ns); 27 | 28 | public static string GetDomainQueryModelsNamespace(this EntityFrameworkCoreProject project) 29 | => project.CodeNamingConvention.GetNamespace(project.Name, project.ProjectNamespaces.QueryModels); 30 | 31 | public static string GetDomainConfigurationsNamespace(this EntityFrameworkCoreProject project) 32 | => project.CodeNamingConvention.GetNamespace(project.Name, project.ProjectNamespaces.Configurations); 33 | 34 | public static string GetDomainConfigurationsNamespace(this EntityFrameworkCoreProject project, string ns) 35 | => string.IsNullOrEmpty(ns) ? GetDomainConfigurationsNamespace(project) : project.CodeNamingConvention.GetNamespace(project.Name, project.ProjectNamespaces.Configurations, ns); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/EntityFrameworkCoreProjectLayersExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace CatFactory.EntityFrameworkCore 4 | { 5 | public static class EntityFrameworkCoreProjectLayersExtensions 6 | { 7 | public static string GetEntityLayerNamespace(this EntityFrameworkCoreProject project) 8 | => string.Join(".", project.CodeNamingConvention.GetNamespace(project.Name), project.CodeNamingConvention.GetNamespace(project.ProjectNamespaces.EntityLayer)); 9 | 10 | public static string GetEntityLayerNamespace(this EntityFrameworkCoreProject project, string ns) 11 | => string.IsNullOrEmpty(ns) ? GetEntityLayerNamespace(project) : string.Join(".", project.Name, project.ProjectNamespaces.EntityLayer, ns); 12 | 13 | public static string GetDataLayerNamespace(this EntityFrameworkCoreProject project) 14 | => string.Join(".", new string[] { project.CodeNamingConvention.GetNamespace(project.Name), project.ProjectNamespaces.DataLayer }); 15 | 16 | public static string GetDataLayerConfigurationsNamespace(this EntityFrameworkCoreProject project) 17 | => string.Join(".", project.CodeNamingConvention.GetNamespace(project.Name), project.ProjectNamespaces.DataLayer, project.ProjectNamespaces.Configurations); 18 | 19 | public static string GetDataLayerConfigurationsNamespace(this EntityFrameworkCoreProject project, string schema) 20 | => project.CodeNamingConvention.GetNamespace(project.Name, project.ProjectNamespaces.DataLayer, project.ProjectNamespaces.Configurations, schema); 21 | 22 | public static string GetDataLayerContractsNamespace(this EntityFrameworkCoreProject project) 23 | => string.Join(".", project.CodeNamingConvention.GetNamespace(project.Name), project.ProjectNamespaces.DataLayer, project.ProjectNamespaces.Contracts); 24 | 25 | public static string GetDataLayerDataContractsNamespace(this EntityFrameworkCoreProject project) 26 | => string.Join(".", project.CodeNamingConvention.GetNamespace(project.Name), project.ProjectNamespaces.DataLayer, project.ProjectNamespaces.DataContracts); 27 | 28 | public static string GetDataLayerRepositoriesNamespace(this EntityFrameworkCoreProject project) 29 | => string.Join(".", project.CodeNamingConvention.GetNamespace(project.Name), project.ProjectNamespaces.DataLayer, project.ProjectNamespaces.Repositories); 30 | 31 | public static string GetEntityLayerDirectory(this EntityFrameworkCoreProject project) 32 | => Path.Combine(project.OutputDirectory, project.ProjectNamespaces.EntityLayer); 33 | 34 | public static string GetEntityLayerDirectory(this EntityFrameworkCoreProject project, string schema) 35 | => Path.Combine(project.OutputDirectory, project.ProjectNamespaces.EntityLayer, schema); 36 | 37 | public static string GetDataLayerDirectory(this EntityFrameworkCoreProject project) 38 | => Path.Combine(project.OutputDirectory, project.ProjectNamespaces.DataLayer); 39 | 40 | public static string GetDataLayerDirectory(this EntityFrameworkCoreProject project, string subdirectory) 41 | => Path.Combine(project.OutputDirectory, project.ProjectNamespaces.DataLayer, subdirectory); 42 | 43 | public static string GetDataLayerConfigurationsDirectory(this EntityFrameworkCoreProject project) 44 | => Path.Combine(project.OutputDirectory, project.ProjectNamespaces.DataLayer, project.ProjectNamespaces.Configurations); 45 | 46 | public static string GetDataLayerConfigurationsDirectory(this EntityFrameworkCoreProject project, string schema) 47 | => Path.Combine(project.OutputDirectory, project.ProjectNamespaces.DataLayer, project.ProjectNamespaces.Configurations, schema); 48 | 49 | public static string GetDataLayerContractsDirectory(this EntityFrameworkCoreProject project) 50 | => Path.Combine(project.OutputDirectory, project.ProjectNamespaces.DataLayer, project.ProjectNamespaces.Contracts); 51 | 52 | public static string GetDataLayerDataContractsDirectory(this EntityFrameworkCoreProject project) 53 | => Path.Combine(project.OutputDirectory, project.ProjectNamespaces.DataLayer, project.ProjectNamespaces.DataContracts); 54 | 55 | public static string GetDataLayerRepositoriesDirectory(this EntityFrameworkCoreProject project) 56 | => Path.Combine(project.OutputDirectory, project.ProjectNamespaces.DataLayer, project.ProjectNamespaces.Repositories); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/EntityFrameworkCoreProjectNamespaces.cs: -------------------------------------------------------------------------------- 1 | namespace CatFactory.EntityFrameworkCore 2 | { 3 | public class EntityFrameworkCoreProjectNamespaces 4 | { 5 | public EntityFrameworkCoreProjectNamespaces() 6 | { 7 | Models = "Models"; 8 | QueryModels = "QueryModels"; 9 | EntityLayer = "EntityLayer"; 10 | DataLayer = "DataLayer"; 11 | Configurations = "Configurations"; 12 | Contracts = "Contracts"; 13 | DataContracts = "DataContracts"; 14 | Repositories = "Repositories"; 15 | ValueConversion = "ValueConversion"; 16 | } 17 | 18 | public string Models { get; set; } 19 | 20 | public string QueryModels { get; set; } 21 | 22 | public string EntityLayer { get; set; } 23 | 24 | public string DataLayer { get; set; } 25 | 26 | public string Configurations { get; set; } 27 | 28 | public string Contracts { get; set; } 29 | 30 | public string DataContracts { get; set; } 31 | 32 | public string Repositories { get; set; } 33 | 34 | /// 35 | /// Project Microsoft.EntityFrameworkCore.Storage.ValueConversion classes and types can be found here; 36 | /// 37 | public string ValueConversion { get; set; } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/EntityFrameworkCoreProjectRepositoriesExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using CatFactory.CodeFactory.Scaffolding; 3 | using CatFactory.ObjectOrientedProgramming; 4 | using CatFactory.ObjectRelationalMapping; 5 | using CatFactory.ObjectRelationalMapping.Programmability; 6 | 7 | namespace CatFactory.EntityFrameworkCore 8 | { 9 | public static partial class EntityFrameworkCoreProjectExtensions 10 | { 11 | public static string GetParameterName(this EntityFrameworkCoreProject project, Column column) 12 | => project.CodeNamingConvention.GetParameterName(column.Name); 13 | 14 | public static string GetNavigationPropertyName(this EntityFrameworkCoreProject project, IDbObject dbObject) 15 | => string.Format("{0}List", project.CodeNamingConvention.GetClassName(dbObject.Name)); 16 | 17 | public static string GetEntityName(this EntityFrameworkCoreProject project, IDbObject dbObject) 18 | => project.CodeNamingConvention.GetClassName(dbObject.Name); 19 | 20 | public static string GetEntityResultName(this EntityFrameworkCoreProject project, IDbObject dbObject) 21 | => string.Format("{0}Result", project.CodeNamingConvention.GetClassName(dbObject.Name)); 22 | 23 | public static string GetPluralName(this EntityFrameworkCoreProject project, IDbObject dbObject) 24 | => project.NamingService.Pluralize(project.GetEntityName(dbObject)); 25 | 26 | public static string GetDbContextName(this EntityFrameworkCoreProject project, Database database) 27 | => project.CodeNamingConvention.GetClassName(string.Format("{0}DbContext", database.Name)); 28 | 29 | public static string GetDbSetPropertyName(this EntityFrameworkCoreProject project, IDbObject dbObject, bool pluralize) 30 | => pluralize ? project.NamingService.Pluralize(project.GetEntityName(dbObject)) : project.GetEntityName(dbObject); 31 | 32 | public static string GetFullDbSetPropertyName(this EntityFrameworkCoreProject project, IDbObject dbObject) 33 | => project.NamingService.Pluralize(string.Concat(project.CodeNamingConvention.GetNamespace(dbObject.Schema), project.GetEntityName(dbObject))); 34 | 35 | public static string GetEntityConfigurationName(this EntityFrameworkCoreProject project, IDbObject dbObject) 36 | => string.Format("{0}Configuration", project.GetEntityName(dbObject)); 37 | 38 | public static string GetFullEntityConfigurationName(this EntityFrameworkCoreProject project, IDbObject dbObject) 39 | => project.CodeNamingConvention.GetNamespace(project.ProjectNamespaces.Configurations, dbObject.Schema, string.Format("{0}Configuration", project.GetEntityName(dbObject))); 40 | 41 | public static string GetFullEntityName(this EntityFrameworkCoreProject project, IDbObject dbObject) 42 | => string.Join(".", project.CodeNamingConvention.GetNamespace(project.ProjectNamespaces.EntityLayer), project.CodeNamingConvention.GetClassName(dbObject.Schema), project.CodeNamingConvention.GetClassName(dbObject.Name)); 43 | 44 | public static string GetQueryModelName(this EntityFrameworkCoreProject project, IDbObject dbObject) 45 | => string.Format("{0}QueryModel", project.CodeNamingConvention.GetClassName(dbObject.Name)); 46 | 47 | public static string GetDataContractName(this EntityFrameworkCoreProject project, IDbObject dbObject) 48 | => string.Format("{0}Dto", project.CodeNamingConvention.GetClassName(dbObject.Name)); 49 | 50 | public static string GetGetAllExtensionMethodName(this EntityFrameworkCoreProject project, IDbObject dbObject) 51 | => string.Format("Get{0}", project.GetPluralName(dbObject)); 52 | 53 | public static string GetGetAllRepositoryMethodName(this EntityFrameworkCoreProject project, IDbObject dbObject) 54 | => string.Format("Get{0}", project.GetPluralName(dbObject)); 55 | 56 | public static string GetGetRepositoryMethodName(this EntityFrameworkCoreProject project, IDbObject dbObject) 57 | => string.Format("Get{0}Async", project.GetEntityName(dbObject)); 58 | 59 | public static string GetGetByUniqueRepositoryMethodName(this EntityFrameworkCoreProject project, ITable table, Unique unique) 60 | => string.Format("Get{0}By{1}Async", project.GetEntityName(table), string.Join("And", unique.Key.Select(item => project.CodeNamingConvention.GetPropertyName(item)))); 61 | 62 | public static string GetAddRepositoryMethodName(this EntityFrameworkCoreProject project, ITable table) 63 | => string.Format("Add{0}Async", project.GetEntityName(table)); 64 | 65 | public static string GetUpdateRepositoryMethodName(this EntityFrameworkCoreProject project, ITable table) 66 | => string.Format("Update{0}Async", project.GetEntityName(table)); 67 | 68 | public static string GetRemoveRepositoryMethodName(this EntityFrameworkCoreProject project, ITable table) 69 | => string.Format("Remove{0}Async", project.GetEntityName(table)); 70 | 71 | public static string GetScalarFunctionMethodName(this EntityFrameworkCoreProject project, ScalarFunction scalarFunction) 72 | => string.Format("{0}{1}", project.CodeNamingConvention.GetClassName(scalarFunction.Schema), project.CodeNamingConvention.GetClassName(scalarFunction.Name)); 73 | 74 | public static bool HasSameEnclosingName(this ITable table) 75 | => table.Schema == table.Name; 76 | 77 | public static PropertyDefinition GetChildNavigationProperty(this EntityFrameworkCoreProject project, ProjectSelection projectSelection, ITable table, ForeignKey foreignKey) 78 | { 79 | var propertyType = string.Format("{0}<{1}>", projectSelection.Settings.NavigationPropertyEnumerableType, project.GetEntityName(table)); 80 | 81 | return new PropertyDefinition(propertyType, project.GetNavigationPropertyName(table)) 82 | { 83 | AccessModifier = AccessModifier.Public, 84 | IsVirtual = projectSelection.Settings.DeclareNavigationPropertiesAsVirtual 85 | }; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/EntityFrameworkCoreProjectSelectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using CatFactory.CodeFactory.Scaffolding; 4 | using CatFactory.ObjectRelationalMapping; 5 | 6 | namespace CatFactory.EntityFrameworkCore 7 | { 8 | public static class EntityFrameworkCoreProjectSelectionExtensions 9 | { 10 | public static ProjectSelection GetSelection(this EntityFrameworkCoreProject project, IDbObject dbObj) 11 | { 12 | // Sales.OrderHeader 13 | var selectionForFullName = project.Selections.FirstOrDefault(item => item.Pattern == dbObj.FullName); 14 | 15 | if (selectionForFullName != null) 16 | return selectionForFullName; 17 | 18 | // Sales.* 19 | var selectionForSchema = project.Selections.FirstOrDefault(item => item.Pattern == string.Format("{0}.*", dbObj.Schema)); 20 | 21 | if (selectionForSchema != null) 22 | return selectionForSchema; 23 | 24 | // *.OrderHeader 25 | var selectionForName = project.Selections.FirstOrDefault(item => item.Pattern == string.Format("*.{0}", dbObj.Name)); 26 | 27 | if (selectionForName != null) 28 | return selectionForName; 29 | 30 | return project.GlobalSelection(); 31 | } 32 | 33 | public static EntityFrameworkCoreProject GlobalSelection(this EntityFrameworkCoreProject project, Action action = null) 34 | { 35 | var settings = new EntityFrameworkCoreProjectSettings(); 36 | 37 | var selection = project.Selections.FirstOrDefault(item => item.IsGlobal); 38 | 39 | if (selection == null) 40 | { 41 | selection = new ProjectSelection 42 | { 43 | Pattern = ProjectSelection.GlobalPattern, 44 | Settings = settings 45 | }; 46 | 47 | project.Selections.Add(selection); 48 | } 49 | else 50 | { 51 | settings = selection.Settings; 52 | } 53 | 54 | action?.Invoke(settings); 55 | 56 | return project; 57 | } 58 | 59 | public static ProjectSelection GlobalSelection(this EntityFrameworkCoreProject project) 60 | => project.Selections.FirstOrDefault(item => item.IsGlobal); 61 | 62 | public static EntityFrameworkCoreProject Selection(this EntityFrameworkCoreProject project, string pattern, Action action = null) 63 | { 64 | var selection = project.Selections.FirstOrDefault(item => item.Pattern == pattern); 65 | 66 | if (selection == null) 67 | { 68 | var globalSettings = project.GlobalSelection().Settings; 69 | 70 | selection = new ProjectSelection 71 | { 72 | Pattern = pattern, 73 | Settings = new EntityFrameworkCoreProjectSettings 74 | { 75 | ForceOverwrite = globalSettings.ForceOverwrite, 76 | SimplifyDataTypes = globalSettings.SimplifyDataTypes, 77 | UseAutomaticPropertiesForEntities = globalSettings.UseAutomaticPropertiesForEntities, 78 | EnableDataBindings = globalSettings.EnableDataBindings, 79 | UseDataAnnotations = globalSettings.UseDataAnnotations, 80 | DeclareNavigationProperties = globalSettings.DeclareNavigationProperties, 81 | DeclareNavigationPropertiesAsVirtual = globalSettings.DeclareNavigationPropertiesAsVirtual, 82 | NavigationPropertyEnumerableNamespace = globalSettings.NavigationPropertyEnumerableNamespace, 83 | NavigationPropertyEnumerableType = globalSettings.NavigationPropertyEnumerableType, 84 | ConcurrencyToken = globalSettings.ConcurrencyToken, 85 | EntityInterfaceName = globalSettings.EntityInterfaceName, 86 | AuditEntity = globalSettings.AuditEntity == null ? null : new AuditEntity 87 | { 88 | CreationUserColumnName = globalSettings.AuditEntity.CreationUserColumnName, 89 | CreationDateTimeColumnName = globalSettings.AuditEntity.CreationDateTimeColumnName, 90 | LastUpdateUserColumnName = globalSettings.AuditEntity.LastUpdateUserColumnName, 91 | LastUpdateDateTimeColumnName = globalSettings.AuditEntity.LastUpdateDateTimeColumnName 92 | }, 93 | EntitiesWithDataContracts = globalSettings.EntitiesWithDataContracts, 94 | BackingFields = globalSettings.BackingFields.Select(item => item).ToList(), 95 | InsertExclusions = globalSettings.InsertExclusions.Select(item => item).ToList(), 96 | UpdateExclusions = globalSettings.UpdateExclusions.Select(item => item).ToList(), 97 | AddConfigurationForForeignKeysInFluentAPI = globalSettings.AddConfigurationForForeignKeysInFluentAPI, 98 | AddConfigurationForUniquesInFluentAPI = globalSettings.AddConfigurationForUniquesInFluentAPI, 99 | AddConfigurationForChecksInFluentAPI = globalSettings.AddConfigurationForChecksInFluentAPI, 100 | AddConfigurationForDefaultsInFluentAPI = globalSettings.AddConfigurationForDefaultsInFluentAPI, 101 | } 102 | }; 103 | 104 | project.Selections.Add(selection); 105 | } 106 | 107 | action?.Invoke(selection.Settings); 108 | 109 | return project; 110 | } 111 | 112 | [Obsolete("Use Selection method.")] 113 | public static EntityFrameworkCoreProject Select(this EntityFrameworkCoreProject project, string pattern, Action action = null) 114 | => project.Selection(pattern, action); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/EntityFrameworkCoreProjectSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using CatFactory.CodeFactory.Scaffolding; 5 | using CatFactory.Diagnostics; 6 | 7 | namespace CatFactory.EntityFrameworkCore 8 | { 9 | public class EntityFrameworkCoreProjectSettings : IProjectSettings 10 | { 11 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 12 | private List m_backingFields; 13 | 14 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 15 | private List m_insertExclusions; 16 | 17 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 18 | private List m_updateExclusions; 19 | 20 | public EntityFrameworkCoreProjectSettings() 21 | { 22 | SimplifyDataTypes = true; 23 | UseAutomaticPropertiesForEntities = true; 24 | UseMefForEntitiesMapping = true; 25 | DeclareDbSetPropertiesInDbContext = true; 26 | NavigationPropertyEnumerableNamespace = "System.Collections.ObjectModel"; 27 | NavigationPropertyEnumerableType = "Collection"; 28 | EntityInterfaceName = "IEntity"; 29 | AddConfigurationForUniquesInFluentAPI = true; 30 | AddConfigurationForDefaultsInFluentAPI = true; 31 | } 32 | 33 | // todo: Add implementation 34 | public ValidationResult Validate() 35 | { 36 | throw new NotImplementedException(); 37 | } 38 | 39 | public bool ForceOverwrite { get; set; } 40 | 41 | public bool SimplifyDataTypes { get; set; } 42 | 43 | public bool UseAutomaticPropertiesForEntities { get; set; } 44 | 45 | public bool EnableDataBindings { get; set; } 46 | 47 | public bool UseDataAnnotations { get; set; } 48 | 49 | [Obsolete("Temporarily disabled")] 50 | public bool UseMefForEntitiesMapping { get; set; } 51 | 52 | public bool DeclareDbSetPropertiesInDbContext { get; } 53 | 54 | public bool PluralizeDbSetPropertyNames { get; set; } 55 | 56 | public bool DeclareNavigationProperties { get; set; } 57 | 58 | public bool DeclareNavigationPropertiesAsVirtual { get; set; } 59 | 60 | public string NavigationPropertyEnumerableNamespace { get; set; } 61 | 62 | public string NavigationPropertyEnumerableType { get; set; } 63 | 64 | public string ConcurrencyToken { get; set; } 65 | 66 | public bool HasConcurrencyToken 67 | => !string.IsNullOrEmpty(ConcurrencyToken); 68 | 69 | public string RowVersion { get; set; } 70 | 71 | public bool HasRowVersion 72 | => !string.IsNullOrEmpty(RowVersion); 73 | 74 | public string EntityInterfaceName { get; set; } 75 | 76 | public AuditEntity AuditEntity { get; set; } 77 | 78 | public bool EntitiesWithDataContracts { get; set; } 79 | 80 | public bool AddConfigurationForUniquesInFluentAPI { get; set; } 81 | 82 | public bool AddConfigurationForChecksInFluentAPI { get; set; } 83 | 84 | public bool AddConfigurationForDefaultsInFluentAPI { get; set; } 85 | 86 | public bool AddConfigurationForForeignKeysInFluentAPI { get; set; } 87 | 88 | public List BackingFields 89 | { 90 | get => m_backingFields ??= new List(); 91 | set => m_backingFields = value; 92 | } 93 | 94 | public List InsertExclusions 95 | { 96 | get => m_insertExclusions ??= new List(); 97 | set => m_insertExclusions = value; 98 | } 99 | 100 | public List UpdateExclusions 101 | { 102 | get => m_updateExclusions ??= new List(); 103 | set => m_updateExclusions = value; 104 | } 105 | 106 | public bool UseApplyConfigurationsFromAssemblyMethod { get; set; } 107 | 108 | /// 109 | /// When true the Database DefaultSchema is also used as a namespace and folder 110 | /// 111 | public bool DefaultSchemaAsSubdirectory { get; set; } 112 | 113 | /// 114 | /// Navigation Property Enumerable Interface Type 115 | /// Typically ICollection 116 | /// 117 | public string NavigationPropertyEnumerableInterfaceType { get; set; } 118 | 119 | /// 120 | /// DefaultSchemaAsNamespace 121 | /// default = false; 122 | /// When true use DefaultSchema (dbo) as namespace and class 123 | /// 124 | public bool DefaultSchemaAsNamespace { get; set; } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/EntityFrameworkCoreVersion.cs: -------------------------------------------------------------------------------- 1 | namespace CatFactory.EntityFrameworkCore 2 | { 3 | public enum EntityFrameworkCoreVersion : short 4 | { 5 | Version_2_0 = 20, 6 | Version_2_1 = 21, 7 | Version_2_2 = 22, 8 | Version_3_0 = 30, 9 | Version_3_1 = 31, 10 | Version_5_0 = 50, 11 | Version_6_0 = 60, 12 | Version_7_0 = 70 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/EntityLayerExtensions.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.EntityFrameworkCore.Definitions.Extensions; 2 | using CatFactory.ObjectRelationalMapping; 3 | 4 | namespace CatFactory.EntityFrameworkCore 5 | { 6 | public static class EntityLayerExtensions 7 | { 8 | public static EntityFrameworkCoreProject ScaffoldEntityLayer(this EntityFrameworkCoreProject project) 9 | { 10 | ScaffoldEntityInterface(project); 11 | ScaffoldEntities(project); 12 | 13 | return project; 14 | } 15 | 16 | private static void ScaffoldEntityInterface(EntityFrameworkCoreProject project) 17 | { 18 | project.Scaffold(project.GetEntityInterfaceDefinition(false), project.GetEntityLayerDirectory()); 19 | 20 | if (project.GlobalSelection().Settings.AuditEntity != null) 21 | project.Scaffold(project.GetAuditEntityInterfaceDefinition(false), project.GetEntityLayerDirectory()); 22 | } 23 | 24 | private static EntityFrameworkCoreProject ScaffoldEntities(this EntityFrameworkCoreProject project) 25 | { 26 | foreach (var table in project.Database.Tables) 27 | { 28 | var selection = project.GetSelection(table); 29 | 30 | var definition = project.GetEntityClassDefinition(table, false); 31 | 32 | if (selection.Settings.UseDataAnnotations) 33 | definition.AddDataAnnotations(table, project); 34 | 35 | project.Scaffold(definition, project.GetEntityLayerDirectory(), project.Database.HasDefaultSchema(table) ? "" : table.Schema); 36 | } 37 | 38 | foreach (var view in project.Database.Views) 39 | { 40 | var selection = project.GetSelection(view); 41 | 42 | var definition = project.GetEntityClassDefinition(view, project.Database.HasDefaultSchema(view) ? project.GetEntityLayerNamespace() : project.GetEntityLayerNamespace(view.Schema)); 43 | 44 | if (selection.Settings.UseDataAnnotations) 45 | definition.AddDataAnnotations(view, project); 46 | 47 | project.Scaffold(definition, project.GetEntityLayerDirectory(), project.Database.HasDefaultSchema(view) ? "" : view.Schema); 48 | } 49 | 50 | // todo: Review this scaffolding 51 | 52 | //foreach (var tableFunction in project.Database.TableFunctions) 53 | //{ 54 | // var selection = project.GetSelection(tableFunction); 55 | 56 | // var definition = project.GetEntityClassDefinition(tableFunction); 57 | 58 | // project.Scaffold(definition, project.GetEntityLayerDirectory(), project.Database.HasDefaultSchema(tableFunction) ? "" : tableFunction.Schema); 59 | //} 60 | 61 | // todo: Review this scaffolding 62 | 63 | //foreach (var storedProcedure in project.Database.StoredProcedures) 64 | //{ 65 | // var selection = project.GetSelection(storedProcedure); 66 | 67 | // var definition = project.GetEntityClassDefinition(storedProcedure); 68 | 69 | // project.Scaffold(definition, project.GetEntityLayerDirectory(), project.Database.HasDefaultSchema(storedProcedure) ? "" : storedProcedure.Schema); 70 | //} 71 | 72 | return project; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/IDotNetClassDefinitionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using CatFactory.NetCore; 3 | using CatFactory.NetCore.ObjectOrientedProgramming; 4 | using CatFactory.ObjectOrientedProgramming; 5 | using CatFactory.ObjectRelationalMapping; 6 | 7 | namespace CatFactory.EntityFrameworkCore 8 | { 9 | public static class IDotNetClassDefinitionExtensions 10 | { 11 | public static void AddDataAnnotations(this IDotNetClassDefinition classDefinition, ITable table, EntityFrameworkCoreProject project) 12 | { 13 | classDefinition.Attributes.Add(new MetadataAttribute("Table", string.Format("\"{0}\"", table.Name)) 14 | { 15 | Sets = 16 | { 17 | new MetadataAttributeSet("Schema", string.Format("\"{0}\"", table.Schema)) 18 | } 19 | }); 20 | 21 | var selection = project.GetSelection(table); 22 | 23 | for (var i = 0; i < table.Columns.Count; i++) 24 | { 25 | var column = table.Columns[i]; 26 | 27 | foreach (var property in classDefinition.Properties) 28 | { 29 | if (project.GetPropertyName(table, column) != property.Name) 30 | continue; 31 | 32 | if (table.Identity?.Name == column.Name) 33 | property.Attributes.Add(new MetadataAttribute("DatabaseGenerated", "DatabaseGeneratedOption.Identity")); 34 | 35 | if (table.PrimaryKey != null && table.PrimaryKey.Key.Contains(column.Name)) 36 | property.Attributes.Add(new MetadataAttribute("Key")); 37 | 38 | if (property.Name == column.Name) 39 | property.Attributes.Add(new MetadataAttribute("Column")); 40 | else 41 | property.Attributes.Add(new MetadataAttribute("Column", string.Format("\"{0}\"", column.Name))); 42 | 43 | if (!column.Nullable && table.Identity != null && table.Identity.Name != column.Name) 44 | property.Attributes.Add(new MetadataAttribute("Required")); 45 | 46 | if (project.Database.ColumnIsString(column) && column.Length > 0) 47 | property.Attributes.Add(new MetadataAttribute("StringLength", column.Length.ToString())); 48 | 49 | if (!string.IsNullOrEmpty(selection.Settings.ConcurrencyToken) && selection.Settings.ConcurrencyToken == column.Name) 50 | property.Attributes.Add(new MetadataAttribute("Timestamp")); 51 | } 52 | } 53 | } 54 | 55 | public static void AddDataAnnotations(this IDotNetClassDefinition classDefinition, IView view, EntityFrameworkCoreProject project) 56 | { 57 | classDefinition.Attributes.Add(new MetadataAttribute("Table", string.Format("\"{0}\"", view.Name)) 58 | { 59 | Sets = 60 | { 61 | new MetadataAttributeSet("Schema", string.Format("\"{0}\"", view.Schema)) 62 | } 63 | }); 64 | 65 | var primaryKeys = project 66 | .Database 67 | .Tables 68 | .Where(item => item.PrimaryKey != null) 69 | .Select(item => item.GetColumnsFromConstraint(item.PrimaryKey).Select(column => column.Name).First()) 70 | .ToList(); 71 | 72 | var result = view.Columns 73 | .Where(item => primaryKeys.Contains(item.Name)) 74 | .ToList(); 75 | 76 | for (var i = 0; i < view.Columns.Count; i++) 77 | { 78 | var column = view.Columns[i]; 79 | 80 | foreach (var property in classDefinition.Properties) 81 | { 82 | if (project.GetPropertyName(view, column) != property.Name) 83 | continue; 84 | 85 | if (property.Name == column.Name) 86 | { 87 | property.Attributes.Add(new MetadataAttribute("Column") 88 | { 89 | Sets = { new MetadataAttributeSet("Order", (i + 1).ToString()) } 90 | }); 91 | } 92 | else 93 | { 94 | property.Attributes.Add(new MetadataAttribute("Column", string.Format("\"{0}\"", column.Name)) 95 | { 96 | Sets = { new MetadataAttributeSet("Order", (i + 1).ToString()) } 97 | }); 98 | } 99 | 100 | if (!column.Nullable && primaryKeys.Contains(column.Name)) 101 | property.Attributes.Add(new MetadataAttribute("Key")); 102 | 103 | if (!column.Nullable) 104 | property.Attributes.Add(new MetadataAttribute("Required")); 105 | } 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /CatFactory.EntityFrameworkCore/ProjectFeatureExtensions.cs: -------------------------------------------------------------------------------- 1 | using CatFactory.CodeFactory.Scaffolding; 2 | 3 | namespace CatFactory.EntityFrameworkCore 4 | { 5 | public static class ProjectFeatureExtensions 6 | { 7 | public static EntityFrameworkCoreProject GetEntityFrameworkCoreProject(this ProjectFeature projectFeature) 8 | => projectFeature.Project as EntityFrameworkCoreProject; 9 | 10 | public static string GetRepositoryInterfaceName(this ProjectFeature projectFeature) 11 | => string.Format("{0}Repository", 12 | projectFeature.GetEntityFrameworkCoreProject().CodeNamingConvention.GetInterfaceName(projectFeature.Name) 13 | ); 14 | 15 | public static string GetRepositoryClassName(this ProjectFeature projectFeature) 16 | => string.Format("{0}Repository", 17 | projectFeature.GetEntityFrameworkCoreProject().CodeNamingConvention.GetClassName(projectFeature.Name) 18 | ); 19 | 20 | public static string GetQueryExtensionsClassName(this ProjectFeature projectFeature) 21 | => string.Format("{0}{1}QueryExtensions", 22 | projectFeature.GetEntityFrameworkCoreProject().GetDbContextName(projectFeature.Project.Database), 23 | projectFeature.Project.CodeNamingConvention.GetClassName(projectFeature.Name) 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 H. Herzl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CatFactory.EntityFrameworkCore ==^^== 2 | 3 | This is the **CatFactory** package for *Entity Framework Core*. 4 | 5 | ## What Is CatFactory? 6 | 7 | **CatFactory** is a scaffolding engine for .NET Core built with C#. 8 | 9 | ## How does it Works? 10 | 11 | The concept behind **CatFactory** is to import an existing database from *SQL Server* instance and then to scaffold a target technology. 12 | 13 | We can also replace the database from *SQL Server* instance with an in-memory database. 14 | 15 | The flow to import an existing database is: 16 | 17 | 1. Create Database Factory 18 | 2. Import Database 19 | 3. Create instance of Project (Entity Framework Core, Dapper, etc) 20 | 4. Build Features (One feature per schema) 21 | 5. Scaffold objects, these methods read all objects from database and create instances for code builders 22 | 23 | ## Donation 24 | 25 | You can make a donation via PayPal using this link: [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=XB49JFNSMGY6U&item_name=CatFactory¤cy_code=USD&source=url) 26 | 27 | Thanks for your help! ==^^== 28 | 29 | ## Code Snippet 30 | 31 | You can check the [`Wiki`](https://github.com/hherzl/CatFactory.EntityFrameworkCore/wiki) to get code snippets for this package. 32 | 33 | Also these technologies are supported: 34 | 35 | + [`ASP.NET Core`](https://github.com/hherzl/CatFactory.AspNetCore) 36 | + [`Dapper`](https://github.com/hherzl/CatFactory.Dapper) 37 | 38 | ## Roadmap 39 | 40 | There will be a lot of improvements for **CatFactory** on road: 41 | 42 | * Scaffolding Services Layer 43 | * Dapper Integration for ASP.NET Core 44 | * MD files 45 | * Scaffolding C# Client for ASP.NET Web API 46 | * Scaffolding Unit Tests for ASP.NET Core 47 | * Scaffolding Integration Tests for ASP.NET Core 48 | * Scaffolding Angular 49 | 50 | ## Concepts behind CatFactory 51 | 52 | Database Type 53 | Project Selection 54 | Event Handlers to Scaffold 55 | Database Object Model 56 | Import Bag 57 | 58 | Read more on: [`Concepts behind CatFactory`](https://github.com/hherzl/CatFactory/wiki/Concepts-behind-CatFactory) 59 | 60 | ## Packages 61 | 62 | CatFactory 63 | CatFactory.SqlServer 64 | CatFactory.PostgreSql 65 | CatFactory.NetCore 66 | CatFactory.EntityFrameworkCore 67 | CatFactory.AspNetCore 68 | CatFactory.Dapper 69 | CatFactory.TypeScript 70 | 71 | Read more on: [`Packages Features Chart`](https://github.com/hherzl/CatFactory/wiki/Packages-Features-Chart) 72 | 73 | ## Quick Starts 74 | 75 | [`Scaffolding Dapper with CatFactory`](https://www.codeproject.com/Articles/1213355/Scaffolding-Dapper-with-CatFactory) 76 | 77 | [`Scaffolding View Models with CatFactory`](https://www.codeproject.com/Tips/1164636/Scaffolding-View-Models-with-CatFactory) 78 | 79 | [`Scaffolding Entity Framework Core 2 with CatFactory`](https://www.codeproject.com/Articles/1160615/Scaffolding-Entity-Framework-Core-with-CatFactory) 80 | 81 | [`Scaffolding ASP.NET Core 2 with CatFactory`](https://www.codeproject.com/Tips/1229909/Scaffolding-ASP-NET-Core-with-CatFactory) 82 | 83 | [`Scaffolding TypeScript with CatFactory`](https://www.codeproject.com/Tips/1166380/Scaffolding-TypeScript-with-CatFactory) 84 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | cls 2 | set initialPath=%cd% 3 | set srcPath=%cd%\CatFactory.EntityFrameworkCore 4 | set testsPath=%cd%\CatFactory.EntityFrameworkCore.Tests 5 | set outputBasePath=C:\Temp\CatFactory.EntityFrameworkCore 6 | cd %srcPath% 7 | dotnet build 8 | cd %testsPath% 9 | dotnet test 10 | cd %outputBasePath%\OnlineStore.Core.Tests 11 | dotnet test 12 | cd %outputBasePath%\OnlineStore.Domain.Tests 13 | dotnet test 14 | cd %outputBasePath%\OnlineStoreWithDataAnnotations.Core.Tests 15 | dotnet test 16 | cd %outputBasePath%\Northwind.Core.Tests 17 | dotnet test 18 | cd %outputBasePath%\AdventureWorks.Core.Tests 19 | dotnet test 20 | cd %srcPath% 21 | dotnet pack 22 | cd %initialPath% 23 | pause 24 | --------------------------------------------------------------------------------