Private/Cs/ModelProj/EfModel.ps1
Function New-EfModel([SolnInfo]$solnInfo, [ModelCsprojInfo]$modelCsprojInfo, [DbInfo]$dbInfo, [string[]]$views ) { # Build the scaffolding in project to "Model" folder # It needs to be run with the working directory of the csproj file Write-Host "### Create EntityFramework scaffolding for" $modelCsprojInfo.csprojName Invoke-Command { [string]$csprojDir = $args[0] [string]$contextName = $args[1] [string]$connStr = $args[2] Set-Location $csprojDir &{dotnet.exe ef dbcontext scaffold $connStr Microsoft.EntityFrameworkCore.SqlServer -c $modelCsprojInfo.efContextName -o Model} } -ArgumentList $modelCsprojInfo.csprojDir, $modelCsprojInfo.efContextName, $dbInfo.connStr Confirm-LastExitCode <# # Ok so far, but our context .cs has code like: # # protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) # { # #warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings. # optionsBuilder.UseSqlServer(@"Server=localhost;Database=***;Trusted_Connection=True;"); # } # # We need to replace that with # # public MyProjectContext(DbContextOptions options) :base(options) # { # } # #> [string] $contextCsFile = $modelCsprojInfo.csprojDir + "\Model\" + $modelCsprojInfo.efContextName + ".cs" Write-Host "### Fix EntityFramework" $modelCsprojInfo.efContextName "constructor to remove explicit connection string" $contextCsFile (Get-Content $contextCsFile) ` -replace 'protected override void OnConfiguring\(DbContextOptionsBuilder optionsBuilder\)', "public $($modelCsprojInfo.efContextName)(DbContextOptions options) :base(options)" ` -replace '#warning To protect.*$', '' ` -replace 'if \(!optionsBuilder.IsConfigured\)', '' ` -replace 'optionsBuilder.UseSqlServer.*$', '' | Out-FileUtf8NoBom $contextCsFile # Remove default "Class1.cs" file Write-Host "### Remove " $modelCsprojInfo.csprojName "scaffolding Class1.cs" if(Test-Path "$($modelCsprojInfo.csprojDir)\Class1.cs") { Remove-Item "$($modelCsprojInfo.csprojDir)\Class1.cs" } Write-Host "### Add view(s) $views" $tableInfos = $solnInfo.GetTableInfos() New-EfModelView $solnInfo $modelCsprojInfo $solnInfo.dbInfo $tableInfos $views } Function New-EfModelView([SolnInfo] $solnInfo, [ModelCsprojInfo] $modelCsprojInfo, [DbInfo] $dbInfo, $tableInfos, [string[]]$views) { # Create "context with view" file if missing [string]$modelWithViewDir = "$($modelCsprojInfo.csprojDir)\ModelWithView" if(-not (Test-Path $modelWithViewDir)) { New-Item $modelWithViewDir -itemtype directory } $contextWithViewCsFile = "$($modelWithViewDir)\$($modelCsprojInfo.efWithViewContextName).cs" if(-not (Test-Path $contextWithViewCsFile)) { Write-Host "### Creating new context file $contextWithViewCsFile" New-ContextWithViewCsToString "$($modelCsprojInfo.namespace).Model" "$($modelCsprojInfo.namespace).ModelWithView" $modelCsprojInfo.efContextName $modelCsprojInfo.efWithViewContextName | Out-FileUtf8NoBom $contextWithViewCsFile } [string]$view = "" foreach($view in $views) { $viewName, $viewPrimaryKey = $view.split('.') [string]$contextWithViewCsFileContents = Get-Content -raw $contextWithViewCsFile if(-not ($contextWithViewCsFileContents -match "DbSet<$($viewName)>")) { # Add a pointer to the view in the context file [string]$newText = " public virtual DbSet<$($viewName)> $($viewName) { get; set; }" $contextWithViewCsFileContents = $contextWithViewCsFileContents -replace '([ \t]*// VIRTUAL_DB_SET_ABOVE_HERE)',"$($newText)`r`n`$1" [string]$newText = @" modelBuilder.Entity<$($viewName)>(entity => { entity.HasKey(e => new { e.$($viewPrimaryKey)}) .HasName("PK_FAKEKEY"); }); "@ $contextWithViewCsFileContents = $contextWithViewCsFileContents -replace '([ \t]*// MODEL_BUILD_ENTITY_ABOVE_HERE)',"$($newText)`r`n`$1" $contextWithViewCsFileContents | Out-FileUtf8NoBom $contextWithViewCsFile [TableInfo] $tableInfo = $tableInfos[$viewName] if($tableInfo -eq $null) { throw "Trying build C# code for view $viewName but cannot find table definition in metadata" } # Generate the view $modelCsFileName = "$($modelCsprojInfo.csprojDir)\ModelWithView\$($tableInfo.tableCapitalCamel).cs" New-EfModelViewToString $modelCsprojInfo.efWithViewNamespace $tableInfo.tableCapitalCamel $tableInfo.columnInfos | Out-FileUtf8NoBom $modelCsFileName } } } Function New-EfModelViewToString([string]$namespace, [string]$tableCapitalCamel, [ColumnInfo[]] $columnInfos){ Write-Host "### Building EF model for view $tableCapitalCamel to $modelCsFileName" [string]$columns = "" foreach($columnInfo in $columnInfos) { $tmp = New-EfModelViewColumnToString $columnInfo $columns += "`r`n" + $tmp } <####################################################################> $result = @" using System; using System.Collections.Generic; namespace $namespace { public partial class $($tableCapitalCamel) { public $($tableCapitalCamel)() { } $columns } } "@ <####################################################################> return $result } Function New-EfModelViewColumnToString([ColumnInfo]$columnInfo) { # Get C# column name [string]$colName = $columnInfo.columnCapitalCamel # Get C# data type from SQL data type [string]$csType = "string" if(($columnInfo.dataType -eq "date") -or ($columnInfo.dataType -eq "datetime") -or ($columnInfo.dataType -eq "datetime2")) { $csType = "DateTime" } elseif(($columnInfo.dataType -eq "bigint")) { $csType = "long" } elseif(($columnInfo.dataType -eq "int") -or ($columnInfo.dataType -eq "smallint")) { $csType = "int" } elseif(($columnInfo.dataType -eq "bit")) { $csType = "bool" } elseif(($columnInfo.dataType -eq "numeric")) { $csType = "decimal" } # Add "?" syntax for nullable non-strings [string]$nullableMarker = "" if(($csType -ne "string") -and ($columnInfo.isNullable)) { $nullableMarker = "?" } # Build the C# expression for this column [string]$result = "`t`tpublic $($csType)$($nullableMarker) $colName { get; set; }" return $result } |