Files
Furion/tools/cli.ps1

720 lines
23 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#
Param(
#
[string[]] $Tables,
#
[string]$Context,
#
[string]$ConnectionName,
#
[string]$OutputDir,
#
[string]$DbProvider,
#
[string]$EntryProject,
#
[string]$CoreProject,
#
[string] $DbContextLocators,
#
[string]$Product,
#
[string]$Namespace
#
#[string]$UseDatabaseNames
)
#
function ExtractTableHasComment($inputString) {
$pattern = 'HasComment\("([^"]*)"\)'
if ($inputString -match $pattern) {
$matches = $Matches[1]
if ($matches) {
return $matches;
}
} else {
return $null;
}
}
#
function ParseCommentsFromCode($code) {
$commentsDictionary = @{}
$lines = $code.Split([Environment]::NewLine)
$currentPropertyBlock = ''
foreach ($line in $lines) {
if ($line -match '(?s)entityBuilder\.Property\(e => e\.(?<propertyName>\w+)\)') {
$currentPropertyBlock = $line
$currentPropertyName = $Matches['propertyName']
}
elseif ($currentPropertyBlock -ne '' -and $line -match ';') {
$currentPropertyBlock += $line
$propertyBlock = [System.Text.RegularExpressions.Regex]::Escape($currentPropertyBlock)
if ($currentPropertyBlock -match '(?s)\.HasComment\("(?<comment>[^"]*)"\)') {
$commentsDictionary[$currentPropertyName] = $Matches['comment']
} else {
$commentsDictionary[$currentPropertyName] = $null
}
$currentPropertyBlock = ''
}
elseif ($currentPropertyBlock -ne '') {
$currentPropertyBlock += $line
}
}
return $commentsDictionary
}
#
function AddXmlCommentsToProperties($content, $commentsDictionary) {
$lines = $content.Split([Environment]::NewLine)
$modifiedLines = @()
foreach ($line in $lines) {
if ($line.Trim() -match '^\s*public\s+(\S+)\s+(\w+)\s+\{.*') {
$propertyName = $Matches[2]
$propertyType = $Matches[1]
if ($commentsDictionary.ContainsKey($propertyName)) {
$modifiedLines += @"
/// <summary>
/// {$($commentsDictionary[$propertyName])}
/// </summary>
"@
}
}
$modifiedLines += $line
}
$modifiedContent = $modifiedLines -join [Environment]::NewLine
return $modifiedContent
}
$FurTools = "Furion Tools v4.9.7.221";
#
$copyright = @"
// -----------------------------------------------------------------------------
// ______ _ _______ _
// | ____| (_) |__ __| | |
// | |__ _ _ _ __ _ ___ _ __ | | ___ ___ | |___
// | __| | | | '__| |/ _ \| '_ \ | |/ _ \ / _ \| / __|
// | | | |_| | | | | (_) | | | | | | (_) | (_) | \__ \
// |_| \__,_|_| |_|\___/|_| |_| |_|\___/ \___/|_|___/
//
// -----------------------------------------------------------------------------
"@;
#
$pwd = pwd;
$rootPath = $pwd.Path;
#
function GetSystemType {
# PowerShell版本和特性
if ($PSVersionTable.PSEdition -eq "Core") {
# PowerShell CorePowerShell 7+
$runtimeOS = [Runtime.InteropServices.RuntimeInformation]::OSDescription
if ($runtimeOS.Contains("Linux")) {
return "Linux"
} elseif ($runtimeOS.Contains("Microsoft Windows")) {
return "Windows"
} elseif ($runtimeOS.Contains("macOS") -or $runtimeOS.Contains("Darwin")) {
return "macOS"
} else {
return "Unknown OS (PowerShell Core)"
}
} else {
# PowerShell Desktop Windows
return "Windows"
}
}
$runtimeOS = GetSystemType;
#
if ($Product -eq $null -or $Product -eq ""){
$Product = "Furion";
}
if ($EntryProject -eq $null -or $EntryProject -eq ""){
$EntryProject = "$Product.Web.Entry";
}
if ($CoreProject -eq $null -or $CoreProject -eq ""){
$CoreProject = "$Product.Core";
}
if ($DbProvider -eq $null -or $DbProvider -eq ""){
$DbProvider = "Microsoft.EntityFrameworkCore.SqlServer";
}
if ($Context -eq $null -or $Context -eq ""){
$Context = $Product + "DbContext";
}
if ($ConnectionName -eq $null -or $ConnectionName -eq ""){
$ConnectionName = "NonConfigureConnectionString";
}
if ($DbContextLocators -eq $null -or $DbContextLocators -eq ""){
$DbContextLocators = "MasterDbContextLocator";
}
if ($OutputDir -eq $null -or $OutputDir -eq ""){
$OutputDir = "$rootPath/$CoreProject/Entities";
}
if ($Namespace -eq $null -or $Namespace -eq ""){
$Namespace = $CoreProject;
}
# 使
$UseDatabaseNames = $false;
if($args.Contains("-UseDatabaseNames")){
$UseDatabaseNames = $true;
}
#
$copyright;
Write-Output "$FurTools 启动中......";
Write-Output "$FurTools 启动成功!";
#
$TempOutputDir = "$rootPath/$CoreProject/TempEntities";
#
if (-not (Test-Path -Path $TempOutputDir)) {
New-Item -ItemType Directory -Path $TempOutputDir;
}
# dotnet ef dbcontext scaffold
Write-Output "-----------------------------------------------------------------------------";
Write-Warning "请确保 dotnet tool install --global dotnet-ef --version 版本号 已经执行安装操作(没有则执行)";
Write-Output "-----------------------------------------------------------------------------";
Write-Warning "$FurTools 请键入操作类型:[G] 界面操作,[任意字符] 命令行操作";
$options = Read-Host "$FurTools 您的输入是";
# GUI
if($options -eq "G")
{
# -----------------------------------------------------------------------------
# Winform GUI []
# -----------------------------------------------------------------------------
#
function loadDbTable(){
#
$connStr = $comboBox.SelectedItem;
if ($connStr -eq $null -or $connStr -eq ""){
[System.Windows.Forms.MessageBox]::Show("请选择数据库连接字符串后再操作");
return;
}
#
#
$conn = New-Object System.Data.SqlClient.SQLConnection;
#
$conn.ConnectionString = $connStr;
#
$conn.Open();
# CMD执行命令
$cmd = New-Object System.Data.SqlClient.SqlCommand("SELECT i.name+'.'+h.name FROM sys.objects h left join sys.schemas i on h.schema_id=i.SCHEMA_ID WHERE h.type IN('U','V') ORDER BY h.type,i.name,h.name", $conn);
# dataset
$ds = New-Object System.Data.DataSet;
#
$da = New-Object System.Data.SqlClient.SqlDataAdapter($cmd);
#
[void]$da.fill($ds)
#
$conn.Close();
#
$conn.Dispose();
$rowCount = $ds.Tables[0].Rows.Count;
# Listbox
for($i = 0;$i -le $rowCount; $i++)
{
$rows = $ds.Tables[0].Rows[$i];
if($rows -ne $null)
{
[void] $listBox.Items.Add($rows[0]);
}
}
}
#
function loadConnectionSettings($settingsPath){
# Web appsetting.json
# -----------------------------------------------------------------------------
# []
# appsetting.json
$appsetting = Get-Content $settingsPath -raw;
# appsetting.json
$connectionDefine = [regex]::Matches($appsetting, '"ConnectionStrings"\s*.\s+\{(?<define>[\s\S]*?)\}');
if($connectionDefine.Count -eq 0)
{
# Write-Warning "$FurTools 未找到 $settingsPath 中定义的数据库连接字符串!";
# Write-Warning "$FurTools 程序终止!";
return;
}
#
$connectionDefineContent = $connectionDefine[0].Groups.Value[1];
#
$connections = [regex]::Matches($connectionDefineContent, '"(.*?)"\s*.\s*"(?<connectionStr>.*?)"');
#
for ($i = 0; $i -le $connections.Count - 1; $i++){
$key = $connections[$i].Groups.Value[1];
$value = $connections[$i].Groups.Value[2];
if($connDic.ContainsKey($value) -eq $false){
$newValue = $value.Replace("\\","\");
$result = $comboBox.Items.Add($newValue);
$connDic.Add($newValue,$key);
}
}
# []
# -----------------------------------------------------------------------------
}
# Winform
Add-Type -AssemblyName System.Windows.Forms;
Add-Type -AssemblyName System.Drawing;
# Winform
$mainForm = New-Object System.Windows.Forms.Form;
$mainForm.Text = $FurTools;
$mainForm.Size = New-Object System.Drawing.Size(800,600);
$mainForm.StartPosition = "CenterScreen";
#
$baseSetting = New-Object System.Windows.Forms.GroupBox;
$baseSetting.SuspendLayout();
$baseSetting.Location = New-Object System.Drawing.Point(15, 15);
$baseSetting.Size = New-Object System.Drawing.Size(760, 120);
$baseSetting.Text = "基础设置";
$baseSetting.TabIndex = 10;
$baseSetting.TabStop = $false;
$baseSetting.ResumeLayout($false);
$baseSetting.PerformLayout();
$mainForm.Controls.Add($baseSetting);
#
$label = New-Object System.Windows.Forms.Label;
$label.Location = New-Object System.Drawing.Point(15,35);
$label.AutoSize = $true;
$label.Size = New-Object System.Drawing.Size(280,20);
$label.Text = '';
$label.TabIndex = 9;
$baseSetting.Controls.Add($label);
#
$locatorLabel = New-Object System.Windows.Forms.Label;
$locatorLabel.Location = New-Object System.Drawing.Point(15,80);
$locatorLabel.AutoSize = $true;
$locatorLabel.Size = New-Object System.Drawing.Size(280,20);
$locatorLabel.Text = '';
$locatorLabel.TabIndex = 9;
$baseSetting.Controls.Add($locatorLabel);
#
$locatorTextBox = New-Object System.Windows.Forms.TextBox;
$locatorTextBox.Location = New-Object System.Drawing.Point(200,75);
$locatorTextBox.Size = New-Object System.Drawing.Size(370,20);
$locatorTextBox.TabIndex = 9;
$locatorTextBox.Text = $DbContextLocators;
$baseSetting.Controls.Add($locatorTextBox);
#
$connDic = New-Object -TypeName 'System.Collections.Generic.Dictionary[System.String, System.String]';
#
$comboBox = New-Object System.Windows.Forms.ComboBox;
$comboBox.Location = New-Object System.Drawing.Point(200,30);
$comboBox.Size = New-Object System.Drawing.Size(370,20);
$comboBox.TabIndex = 9;
$comboBox.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList;
#
$comboBoxClickEventHandler = [System.EventHandler] {
$connStr = $comboBox.SelectedItem;
if ($connStr -eq $null -or $connStr -eq ""){
$btnGenerate.Enabled =$false;
}
else{
$btnGenerate.Enabled =$true;
$ConnectionName = $connDic[$connStr];
}
}
$comboBox.Add_SelectedIndexChanged($comboBoxClickEventHandler);
$baseSetting.Controls.Add($comboBox);
#
# -----------------------------------------------------------------------------
# []
$jsons = Get-ChildItem $rootPath -Include "*.json" -Recurse;
for ($i = 0; $i -le $jsons.Count - 1; $i++){
$json = $jsons[$i];
if(!($json.DirectoryName.Contains("bin") -or $json.DirectoryName.Contains("obj") -or $json.DirectoryName.Contains(".vscode") -or $json.FullName.Contains(".deps.json"))){
loadConnectionSettings($json.FullName);
}
}
# []
# -----------------------------------------------------------------------------
#
$btnLoad = New-Object System.Windows.Forms.Button;
$btnLoad.Location = New-Object System.Drawing.Point(595,30);
$btnLoad.Size = New-Object System.Drawing.Size(150, 25);
$btnLoad.TabIndex = 9;
$btnLoad.Text = "加载数据库表和视图";
#
$btnLoadClickEventHandler = [System.EventHandler] {
#
$DbContextLocators = $locatorTextBox.Text;
try{
Write-Warning "$FurTools 正在加载数据库表和视图......"
loadDbTable;
Write-Warning "$FurTools 加载成功!"
}
catch{
Write-Warning "$FurTools 加载数据库表和视图出错,请重试!";
}
}
$btnLoad.Add_Click($btnLoadClickEventHandler);
$baseSetting.Controls.Add($btnLoad);
#
$tableSetting = New-Object System.Windows.Forms.GroupBox;
$tableSetting.SuspendLayout();
$tableSetting.Location = New-Object System.Drawing.Point(15, 155);
$tableSetting.Size = New-Object System.Drawing.Size(760, 345);
$tableSetting.Text = "数据库表和视图";
$tableSetting.TabIndex = 10;
$tableSetting.TabStop = $false;
$tableSetting.ResumeLayout($false);
$tableSetting.PerformLayout();
$mainForm.Controls.Add($tableSetting);
#
$listBox = New-Object System.Windows.Forms.Listbox;
$listBox.BackColor = [System.Drawing.SystemColors]::Window;
$listBox.FormattingEnabled = $true;
$listBox.ItemHeight = 20;
$listBox.TabIndex = 9;
$listBox.Location = New-Object System.Drawing.Point(15,35);
$listBox.Size = New-Object System.Drawing.Size(730,295);
$listBox.SelectionMode = "MultiExtended";
$tableSetting.Controls.Add($listBox);
#
$btnGenerate = New-Object System.Windows.Forms.Button;
$btnGenerate.Location = New-Object System.Drawing.Point(530,520);
$btnGenerate.Size = New-Object System.Drawing.Size(100, 25);
$btnGenerate.TabIndex = 8;
$btnGenerate.Text = "立即生成";
$btnGenerate.Enabled =$false;
$btnGenerate.BackColor = [System.Drawing.SystemColors]::ControlLight
$btnGenerate.DialogResult = [System.Windows.Forms.DialogResult]::OK;
$mainForm.AcceptButton = $btnGenerate;
$mainForm.Controls.Add($btnGenerate);
$btnCancel = New-Object System.Windows.Forms.Button;
$btnCancel.Location = New-Object System.Drawing.Point(650,520);
$btnCancel.Size = New-Object System.Drawing.Size(100, 25);
$btnCancel.TabIndex = 8;
$btnCancel.Text = "取消生成";
$btnCancel.DialogResult = [System.Windows.Forms.DialogResult]::Cancel;
$mainForm.CancelButton = $btnCancel;
$mainForm.Controls.Add($btnCancel);
#
$mainForm.Topmost = $true;
$dialogResult = $mainForm.ShowDialog();
#
if ($dialogResult -eq [System.Windows.Forms.DialogResult]::OK){
#
$Tables = $listBox.SelectedItems;
$connKey = $comboBox.SelectedItem;
#
$app = New-Object -com Shell.Application;
$selectFolder = $app.BrowseForFolder(0, "选择 $CoreProject 项目层目录", 0, "$rootPath/$CoreProject");
#
$OutputDir = $selectFolder.Self.Path;
$ConnectionName = $connDic[$connKey];
if($OutputDir -eq $null -and $OutputDir -eq "")
{
Write-Warning "$FurTools 用户取消操作,程序终止!";
return;
}
}
else{
Write-Warning "$FurTools 用户取消操作,程序终止!";
return;
}
# -----------------------------------------------------------------------------
# Winform GUI []
# -----------------------------------------------------------------------------
}
else{
#
$selectFolder = "";
if($runtimeOS -eq "Windows")
{
$app = New-Object -com Shell.Application;
$selectFolder = $app.BrowseForFolder(0, "选择 $CoreProject 项目层目录", 0, "$rootPath\$CoreProject");
}
elseif($runtimeOS -eq "macOS")
{
$script = @'
tell application "Finder"
activate
try
set selectedFolder to choose folder with prompt "Please select a folder:"
set folderPath to POSIX path of selectedFolder
on error
set folderPath to ""
end try
return folderPath
end tell
'@
$selectFolder = osascript -e $script;
}
elseif($runtimeOS -eq "Linux")
{
$selectFolder = & /usr/bin/zenity --file-selection --directory;
}
else
{
Write-Warning "$FurTools 未知操作系统类型!";
return;
}
if ([string]::IsNullOrEmpty($selectFolder))
{
Write-Warning "$FurTools 用户取消操作,程序终止!";
return;
}
#
if($runtimeOS -eq "macOS")
{
$OutputDir = $selectFolder;
}
else
{
$OutputDir = $selectFolder.Self.Path;
}
if($OutputDir -eq $null -and $OutputDir -eq "")
{
Write-Warning "$FurTools 用户取消操作,程序终止!";
return;
}
}
if($ConnectionName -eq "NonConfigureConnectionString")
{
Write-Warning "$FurTools 未找到连接字符串,程序终止!";
return;
}
# dotnet ef dbcontext scaffold
Write-Output "$FurTools 正在编译解决方案代码......";
#
$CommandString = "";
try
{
#
if ($Tables.Count -eq 0)
{
$CommandString = "dotnet ef dbcontext scaffold Name=ConnectionStrings:$ConnectionName $DbProvider --project $EntryProject --output-dir $TempOutputDir --context $Context --namespace $Namespace --no-onconfiguring --no-pluralize";
if($UseDatabaseNames)
{
$CommandString += " --use-database-names";
}
$CommandString += " --force";
}
#
else
{
#
$TableArray = $Tables.Split(',') | Where-Object { $_ -ne '' };
# --table
$TableParams = $TableArray | ForEach-Object { "--table $_" };
$CommandString = "dotnet ef dbcontext scaffold Name=ConnectionStrings:$ConnectionName $DbProvider --project $EntryProject --output-dir $TempOutputDir --context $Context --namespace $Namespace $($TableParams -join ' ') --no-onconfiguring --no-pluralize";
if($UseDatabaseNames)
{
$CommandString += " --use-database-names";
}
$CommandString += " --force";
}
#
Invoke-Expression $CommandString;
Write-Output "$FurTools 编译成功!";
Write-Output "$FurTools 开始生成实体文件......";
# DbContext
$dbContextContent = Get-Content "$TempOutputDir\$Context.cs" -raw;
}
catch
{
Write-Warning "$FurTools 生成失败:$_.Message";
return;
}
$entityConfigures = [regex]::Matches($dbContextContent, "modelBuilder.Entity\<(?<table>\w+)\>\(entity\s=\>\n*[\s\S]*?\{(?<content>(?:[^{}]|(?<open>{)|(?<-open>}))+(?(open)(?!)))\}\);");
#
$dic = New-Object -TypeName 'System.Collections.Generic.Dictionary[System.String, System.String]';
#
for ($i = 0; $i -le $entityConfigures.Count - 1; $i++){
$groups = $entityConfigures[$i].Groups;
$tableName = $groups.Value[1];
$configure = $groups.Value[2] -replace '(?ms)(entity\s*\.\s*)', 'entityBuilder.'
$dic.Add($tableName, $configure);
}
#
$fileHeader = @"
// -----------------------------------------------------------------------------
// Generate By $FurTools
// -----------------------------------------------------------------------------
using Furion.DatabaseAccessor;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Collections.Generic;
using System.Linq;
using $CoreProject;
"@;
#
$entityConfigure = @"
public void Configure(EntityTypeBuilder<#Table#> entityBuilder, DbContext dbContext, Type dbContextLocator)
{#Code#
}
"@
#
$classRegex = "public\s+partial\s+class\s+(?<table>\w+)";
#
$propRegex = "(namespace\s+.+\n*[\s\S]*?\{\n*[\s\S]*?public\s+partial\s+class\s+(?<table>\w+)\n*[\s\S]*?\{(?<content>[\s\S]*)\}\n*[\s\S]*\})|(namespace\s+.+;\n*[\s\S]*?public\s+partial\s+class\s+(?<table>\w+)\n*[\s\S]*?\{(?<content>[\s\S]*)\}?\n*[\s\S]*\})";
#
$files = Get-ChildItem $TempOutputDir -Include *.cs -recurse
for ($i = 0; $i -le $files.Count - 1; $i++){
#
$fileName = $files[$i].BaseName;
#
$filePath = $files[$i].FullName;
if ($fileName -eq $Context){
continue;
}
#
Write-Output "$FurTools 正在生成 $fileName.cs 实体代码......";
#
$entityContent = Get-Content $filePath -raw;
#
$propsContent = [regex]::Match($entityContent, $propRegex).Groups["content"].value;
#
$commentsDictionary = ParseCommentsFromCode -code $propsContent;
$modifiedContent = AddXmlCommentsToProperties -content $propsContent -commentsDictionary $commentsDictionary;
$extents = " : IEntity<$DbContextLocators>";
$newPropsContent = $propsContent;
#
if ($dic.ContainsKey($fileName)){
$extents += ", IEntityTypeBuilder<$fileName, $DbContextLocators>";
#
$newPropsContent = $propsContent + ($entityConfigure.Replace("#Table#",$fileName).Replace("#Code#",$dic[$fileName]));
}
#
$tableDescription = ExtractTableHasComment -inputString $newPropsContent;
if($tableDescription -ne $null -and $tableDescription -ne ''){
$tableDescription = @"
/// <summary>
/// $tableDescription
/// </summary>
"@;
}
#
$finalClass = $fileHeader + @"
namespace $Namespace;
$tableDescription
public partial class $fileName$extents
{$newPropsContent}
"@;
#
Set-Content -Path $filePath -Value $finalClass -Encoding utf8;
#
Write-Output "$FurTools 成功生成 $fileName.cs 实体代码";
$finalClass;
#
Move-Item $filePath "$OutputDir/$fileName.cs" -force
}
#
Remove-Item "$TempOutputDir/$Context.cs";
#
Remove-Item $TempOutputDir -force;
Write-Warning "$FurTools 全部实体生成成功!";