|
|
|
@ -17,36 +17,70 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi |
|
|
|
{ |
|
|
|
public const string Name = "CSHARP"; |
|
|
|
|
|
|
|
private const string UsingPlaceholder = "<using placeholder>"; |
|
|
|
private const string MethodPlaceholder = "<method placeholder>"; |
|
|
|
private const string ClassName = "<className>"; |
|
|
|
private const string ServiceInterface = "<serviceInterface>"; |
|
|
|
private readonly static string[] ServicePostfixes = { "AppService", "ApplicationService", "IntService", "IntegrationService" , "Service"}; |
|
|
|
private const string DefaultNamespace = "ClientProxies"; |
|
|
|
private const string Namespace = "<namespace>"; |
|
|
|
private const string ProxyDirectory = "ClientProxies"; |
|
|
|
|
|
|
|
private static readonly string[] ServicePostfixes = { "AppService", "ApplicationService", "IntService", "IntegrationService" , "Service"}; |
|
|
|
private const string AppServicePrefix = "Volo.Abp.Application.Services"; |
|
|
|
private readonly string _clientProxyGeneratedTemplate = "// This file is automatically generated by ABP framework to use MVC Controllers from CSharp" + |
|
|
|
$"{Environment.NewLine}<using placeholder>" + |
|
|
|
|
|
|
|
private const string NamespacePlaceholder = "<namespace>"; |
|
|
|
private const string UsingPlaceholder = "<using>"; |
|
|
|
private const string MethodPlaceholder = "<method>"; |
|
|
|
private const string PropertyPlaceholder = "<property>"; |
|
|
|
private const string ClassNamePlaceholder = "<className>"; |
|
|
|
private const string ServiceInterfacePlaceholder = "<serviceInterface>"; |
|
|
|
private const string DtoClassNamePlaceholder = "<dtoName>"; |
|
|
|
private static readonly string ClassTemplate = "// This file is automatically generated by ABP framework to use MVC Controllers from CSharp" + |
|
|
|
$"{Environment.NewLine}<using>" + |
|
|
|
$"{Environment.NewLine}" + |
|
|
|
$"{Environment.NewLine}// ReSharper disable once CheckNamespace" + |
|
|
|
$"{Environment.NewLine}namespace <namespace>;" + |
|
|
|
$"{Environment.NewLine}" + |
|
|
|
$"{Environment.NewLine}[Dependency(ReplaceServices = true)]" + |
|
|
|
$"{Environment.NewLine}[ExposeServices(typeof(<serviceInterface>), typeof(<className>))]" + |
|
|
|
$"{Environment.NewLine}[IntegrationService]" + |
|
|
|
$"{Environment.NewLine}public partial class <className> : ClientProxyBase<<serviceInterface>>, <serviceInterface>" + |
|
|
|
$"{Environment.NewLine}{{" + |
|
|
|
$"{Environment.NewLine} <method placeholder>" + |
|
|
|
$"{Environment.NewLine} <method>" + |
|
|
|
$"{Environment.NewLine}}}" + |
|
|
|
$"{Environment.NewLine}"; |
|
|
|
|
|
|
|
private static readonly string ClassTemplateEmptyPart = "// This file is part of <className>, you can customize it here" + |
|
|
|
$"{Environment.NewLine}// ReSharper disable once CheckNamespace" + |
|
|
|
$"{Environment.NewLine}namespace <namespace>;" + |
|
|
|
$"{Environment.NewLine}" + |
|
|
|
$"{Environment.NewLine}public partial class <className>" + |
|
|
|
$"{Environment.NewLine}{{" + |
|
|
|
$"{Environment.NewLine}}}" + |
|
|
|
$"{Environment.NewLine}"; |
|
|
|
private readonly string _clientProxyTemplate = "// This file is part of <className>, you can customize it here" + |
|
|
|
$"{Environment.NewLine}// ReSharper disable once CheckNamespace" + |
|
|
|
$"{Environment.NewLine}namespace <namespace>;" + |
|
|
|
$"{Environment.NewLine}" + |
|
|
|
$"{Environment.NewLine}public partial class <className>" + |
|
|
|
$"{Environment.NewLine}{{" + |
|
|
|
$"{Environment.NewLine}}}" + |
|
|
|
$"{Environment.NewLine}"; |
|
|
|
private readonly List<string> _usingNamespaceList = new() |
|
|
|
|
|
|
|
private static readonly string InterfaceTemplate = "// This file is automatically generated by ABP framework to use MVC Controllers from CSharp" + |
|
|
|
$"{Environment.NewLine}<using>" + |
|
|
|
$"{Environment.NewLine}" + |
|
|
|
$"{Environment.NewLine}// ReSharper disable once CheckNamespace" + |
|
|
|
$"{Environment.NewLine}namespace <namespace>;" + |
|
|
|
$"{Environment.NewLine}" + |
|
|
|
$"{Environment.NewLine}public interface <serviceInterface> : IApplicationService" + |
|
|
|
$"{Environment.NewLine}{{" + |
|
|
|
$"{Environment.NewLine} <method>" + |
|
|
|
$"{Environment.NewLine}}}" + |
|
|
|
$"{Environment.NewLine}"; |
|
|
|
|
|
|
|
private static readonly string DtoTemplate = "// This file is automatically generated by ABP framework to use MVC Controllers from CSharp" + |
|
|
|
$"{Environment.NewLine}using System;" + |
|
|
|
$"{Environment.NewLine}using System.Collections.Generic;" + |
|
|
|
$"{Environment.NewLine}using Volo.Abp.Application.Dtos;" + |
|
|
|
$"{Environment.NewLine}using Volo.Abp.ObjectExtending;" + |
|
|
|
$"{Environment.NewLine}<using>" + |
|
|
|
$"{Environment.NewLine}" + |
|
|
|
$"{Environment.NewLine}// ReSharper disable once CheckNamespace" + |
|
|
|
$"{Environment.NewLine}namespace <namespace>;" + |
|
|
|
$"{Environment.NewLine}" + |
|
|
|
$"{Environment.NewLine}public class <dtoName>" + |
|
|
|
$"{Environment.NewLine}{{" + |
|
|
|
$"{Environment.NewLine} <property>" + |
|
|
|
$"{Environment.NewLine}}}" + |
|
|
|
$"{Environment.NewLine}"; |
|
|
|
private static readonly List<string> ClassUsingNamespaceList = new() |
|
|
|
{ |
|
|
|
"using System;", |
|
|
|
"using System.Threading.Tasks;", |
|
|
|
@ -57,6 +91,14 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi |
|
|
|
"using Volo.Abp.Http.Client.ClientProxying;" |
|
|
|
}; |
|
|
|
|
|
|
|
private static readonly List<string> InterfaceUsingNamespaceList = new() |
|
|
|
{ |
|
|
|
"using System;", |
|
|
|
"using System.Threading.Tasks;", |
|
|
|
"using Volo.Abp.Application.Dtos;", |
|
|
|
"using Volo.Abp.Application.Services;" |
|
|
|
}; |
|
|
|
|
|
|
|
public CSharpServiceProxyGenerator( |
|
|
|
CliHttpClientFactory cliHttpClientFactory, |
|
|
|
IJsonSerializer jsonSerializer) : |
|
|
|
@ -64,160 +106,187 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi |
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
public async override Task GenerateProxyAsync(GenerateProxyArgs args) |
|
|
|
public override async Task GenerateProxyAsync(GenerateProxyArgs args) |
|
|
|
{ |
|
|
|
CheckWorkDirectory(args.WorkDirectory); |
|
|
|
CheckFolder(args.Folder); |
|
|
|
|
|
|
|
if (args.CommandName == RemoveProxyCommand.Name) |
|
|
|
{ |
|
|
|
RemoveClientProxyFile(args); |
|
|
|
var folder = args.Folder.IsNullOrWhiteSpace() ? ProxyDirectory : args.Folder; |
|
|
|
var folderPath = Path.Combine(args.WorkDirectory, folder); |
|
|
|
|
|
|
|
if (Directory.Exists(folderPath)) |
|
|
|
{ |
|
|
|
Directory.Delete(folderPath, true); |
|
|
|
} |
|
|
|
|
|
|
|
Logger.LogInformation($"Delete {GetLoggerOutputPath(folderPath, args.WorkDirectory)}"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
var applicationApiDescriptionModel = await GetApplicationApiDescriptionModelAsync(args); |
|
|
|
var applicationApiDescriptionModel = await GetApplicationApiDescriptionModelAsync(args, new ApplicationApiDescriptionModelRequestDto |
|
|
|
{ |
|
|
|
IncludeTypes = !args.WithoutContracts |
|
|
|
}); |
|
|
|
|
|
|
|
foreach (var controller in applicationApiDescriptionModel.Modules.Values.SelectMany(x => x.Controllers)) |
|
|
|
foreach (var controller in applicationApiDescriptionModel.Modules.Values.SelectMany(x => x.Controllers) |
|
|
|
.Where(x => x.Value.Interfaces.Any() && ServicePostfixes.Any(s => x.Value.Interfaces.Last().Type.EndsWith(s)))) |
|
|
|
{ |
|
|
|
if (ShouldGenerateProxy(controller.Value)) |
|
|
|
{ |
|
|
|
await GenerateClientProxyFileAsync(args, controller.Value); |
|
|
|
} |
|
|
|
await GenerateClassFileAsync(args, controller.Value); |
|
|
|
} |
|
|
|
foreach (var controller in applicationApiDescriptionModel.Modules.Values.SelectMany(x => x.Controllers).Where(x => ShouldGenerateProxy(x.Value))) |
|
|
|
{ |
|
|
|
await GenerateClassFileAsync(args, controller.Value); |
|
|
|
} |
|
|
|
|
|
|
|
if (!args.WithoutContracts) |
|
|
|
{ |
|
|
|
await GenerateDtoFileAsync(args, applicationApiDescriptionModel); |
|
|
|
} |
|
|
|
|
|
|
|
await CreateGenerateProxyJsonFile(args, applicationApiDescriptionModel); |
|
|
|
await CreateJsonFile(args, applicationApiDescriptionModel); |
|
|
|
} |
|
|
|
|
|
|
|
private async Task CreateGenerateProxyJsonFile(GenerateProxyArgs args, ApplicationApiDescriptionModel applicationApiDescriptionModel) |
|
|
|
private void DeleteClientProxyDirectory(GenerateProxyArgs args) |
|
|
|
{ |
|
|
|
var folder = args.Folder.IsNullOrWhiteSpace() ? DefaultNamespace : args.Folder; |
|
|
|
var filePath = Path.Combine(args.WorkDirectory, folder, $"{args.Module}-generate-proxy.json"); |
|
|
|
|
|
|
|
using (var writer = new StreamWriter(filePath)) |
|
|
|
} |
|
|
|
|
|
|
|
private bool ShouldGenerateProxy(ControllerApiDescriptionModel controllerApiDescription) |
|
|
|
{ |
|
|
|
if (!controllerApiDescription.Interfaces.Any()) |
|
|
|
{ |
|
|
|
await writer.WriteAsync(JsonSerializer.Serialize(applicationApiDescriptionModel, indented: true)); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
var serviceInterface = controllerApiDescription.Interfaces.Last(); |
|
|
|
return ServicePostfixes.Any(x => serviceInterface.Type.EndsWith(x)); |
|
|
|
} |
|
|
|
|
|
|
|
private void RemoveClientProxyFile(GenerateProxyArgs args) |
|
|
|
private async Task CreateJsonFile(GenerateProxyArgs args, ApplicationApiDescriptionModel applicationApiDescriptionModel) |
|
|
|
{ |
|
|
|
var folder = args.Folder.IsNullOrWhiteSpace() ? DefaultNamespace : args.Folder; |
|
|
|
var folderPath = Path.Combine(args.WorkDirectory, folder); |
|
|
|
|
|
|
|
if (Directory.Exists(folderPath)) |
|
|
|
var folder = args.Folder.IsNullOrWhiteSpace() ? ProxyDirectory : args.Folder; |
|
|
|
var filePath = Path.Combine(args.WorkDirectory, folder, $"{args.Module}-generate-proxy.json"); |
|
|
|
using (var writer = new StreamWriter(filePath)) |
|
|
|
{ |
|
|
|
Directory.Delete(folderPath, true); |
|
|
|
await writer.WriteAsync(JsonSerializer.Serialize(applicationApiDescriptionModel, indented: true)); |
|
|
|
} |
|
|
|
|
|
|
|
Logger.LogInformation($"Delete {GetLoggerOutputPath(folderPath, args.WorkDirectory)}"); |
|
|
|
} |
|
|
|
|
|
|
|
private async Task GenerateClientProxyFileAsync( |
|
|
|
private async Task GenerateClassFileAsync( |
|
|
|
GenerateProxyArgs args, |
|
|
|
ControllerApiDescriptionModel controllerApiDescription) |
|
|
|
{ |
|
|
|
var folder = args.Folder.IsNullOrWhiteSpace() ? DefaultNamespace : args.Folder; |
|
|
|
var folder = args.Folder.IsNullOrWhiteSpace() |
|
|
|
? ProxyDirectory + Path.DirectorySeparatorChar + GetTypeNamespace(controllerApiDescription.Type).Replace(".", Path.DirectorySeparatorChar.ToString()) |
|
|
|
: args.Folder; |
|
|
|
|
|
|
|
var appServiceTypeFullName = controllerApiDescription.Interfaces.Last().Type; |
|
|
|
var appServiceTypeName = appServiceTypeFullName.Split('.').Last(); |
|
|
|
var clientProxyName = $"{controllerApiDescription.ControllerName}ClientProxy"; |
|
|
|
var rootNamespace = $"{GetTypeNamespace(controllerApiDescription.Type)}.{folder.Replace('/', '.')}"; |
|
|
|
var rootNamespace = $"{GetTypeNamespace(controllerApiDescription.Type)}"; |
|
|
|
|
|
|
|
var clientProxyBuilder = new StringBuilder(_clientProxyTemplate); |
|
|
|
clientProxyBuilder.Replace(ClassName, clientProxyName); |
|
|
|
clientProxyBuilder.Replace(Namespace, rootNamespace); |
|
|
|
var classTemplateEmptyPart = new StringBuilder(ClassTemplateEmptyPart); |
|
|
|
classTemplateEmptyPart.Replace(ClassNamePlaceholder, clientProxyName); |
|
|
|
classTemplateEmptyPart.Replace(NamespacePlaceholder, rootNamespace); |
|
|
|
|
|
|
|
var filePath = Path.Combine(args.WorkDirectory, folder, $"{clientProxyName}.cs"); |
|
|
|
Directory.CreateDirectory(Path.GetDirectoryName(filePath)); |
|
|
|
|
|
|
|
if (!File.Exists(filePath)) |
|
|
|
{ |
|
|
|
using (var writer = new StreamWriter(filePath)) |
|
|
|
{ |
|
|
|
await writer.WriteAsync(clientProxyBuilder.ToString()); |
|
|
|
await writer.WriteAsync(classTemplateEmptyPart.ToString()); |
|
|
|
} |
|
|
|
|
|
|
|
Logger.LogInformation($"Create {GetLoggerOutputPath(filePath, args.WorkDirectory)}"); |
|
|
|
} |
|
|
|
|
|
|
|
await GenerateClientProxyGeneratedFileAsync( |
|
|
|
args, |
|
|
|
controllerApiDescription, |
|
|
|
clientProxyName, |
|
|
|
appServiceTypeName, |
|
|
|
appServiceTypeFullName, |
|
|
|
rootNamespace, |
|
|
|
folder); |
|
|
|
} |
|
|
|
|
|
|
|
private async Task GenerateClientProxyGeneratedFileAsync( |
|
|
|
GenerateProxyArgs args, |
|
|
|
ControllerApiDescriptionModel controllerApiDescription, |
|
|
|
string clientProxyName, |
|
|
|
string appServiceTypeName, |
|
|
|
string appServiceTypeFullName, |
|
|
|
string rootNamespace, |
|
|
|
string folder) |
|
|
|
{ |
|
|
|
var clientProxyBuilder = new StringBuilder(_clientProxyGeneratedTemplate); |
|
|
|
|
|
|
|
var usingNamespaceList = new List<string>(_usingNamespaceList) |
|
|
|
{ |
|
|
|
$"using {GetTypeNamespace(appServiceTypeFullName)};" |
|
|
|
}; |
|
|
|
var classTemplate = new StringBuilder(ClassTemplate); |
|
|
|
|
|
|
|
clientProxyBuilder.Replace(ClassName, clientProxyName); |
|
|
|
clientProxyBuilder.Replace(Namespace, rootNamespace); |
|
|
|
clientProxyBuilder.Replace(ServiceInterface, appServiceTypeName); |
|
|
|
|
|
|
|
foreach (var action in controllerApiDescription.Actions.Values) |
|
|
|
var classUsingNamespaceList = new List<string>(ClassUsingNamespaceList) |
|
|
|
{ |
|
|
|
if (!ShouldGenerateMethod(appServiceTypeFullName, action)) |
|
|
|
{ |
|
|
|
continue; |
|
|
|
} |
|
|
|
$"using {GetTypeNamespace(appServiceTypeFullName)};" |
|
|
|
}; |
|
|
|
|
|
|
|
GenerateMethod(action, clientProxyBuilder, usingNamespaceList); |
|
|
|
if (!controllerApiDescription.IntegrationService) |
|
|
|
{ |
|
|
|
classTemplate.Replace($"{Environment.NewLine}[IntegrationService]", string.Empty); |
|
|
|
} |
|
|
|
|
|
|
|
foreach (var usingNamespace in usingNamespaceList) |
|
|
|
classTemplate.Replace(ClassNamePlaceholder, clientProxyName); |
|
|
|
classTemplate.Replace(NamespacePlaceholder, rootNamespace); |
|
|
|
classTemplate.Replace(ServiceInterfacePlaceholder, appServiceTypeName); |
|
|
|
|
|
|
|
foreach (var action in controllerApiDescription.Actions.Values.Where(x => ShouldGenerateMethod(appServiceTypeFullName, x))) |
|
|
|
{ |
|
|
|
clientProxyBuilder.Replace($"{UsingPlaceholder}", $"{usingNamespace}{Environment.NewLine}{UsingPlaceholder}"); |
|
|
|
GenerateClassMethod(action, classTemplate, classUsingNamespaceList); |
|
|
|
} |
|
|
|
|
|
|
|
clientProxyBuilder.Replace($"{Environment.NewLine}{UsingPlaceholder}", string.Empty); |
|
|
|
clientProxyBuilder.Replace($"{Environment.NewLine}{Environment.NewLine} {MethodPlaceholder}", string.Empty); |
|
|
|
|
|
|
|
var filePath = Path.Combine(args.WorkDirectory, folder, $"{clientProxyName}.Generated.cs"); |
|
|
|
classTemplate.Replace($"{UsingPlaceholder}", string.Join(Environment.NewLine, classUsingNamespaceList.Distinct().OrderBy(x => x).Select(x => x))); |
|
|
|
classTemplate.Replace($"{Environment.NewLine}{Environment.NewLine} {MethodPlaceholder}", string.Empty); |
|
|
|
|
|
|
|
filePath = Path.Combine(args.WorkDirectory, folder, $"{clientProxyName}.Generated.cs"); |
|
|
|
Directory.CreateDirectory(Path.GetDirectoryName(filePath)); |
|
|
|
using (var writer = new StreamWriter(filePath)) |
|
|
|
{ |
|
|
|
await writer.WriteAsync(clientProxyBuilder.ToString()); |
|
|
|
await writer.WriteAsync(classTemplate.ToString()); |
|
|
|
Logger.LogInformation($"Create {GetLoggerOutputPath(filePath, args.WorkDirectory)}"); |
|
|
|
} |
|
|
|
|
|
|
|
if (!args.WithoutContracts) |
|
|
|
{ |
|
|
|
var interfaceTemplate = new StringBuilder(InterfaceTemplate); |
|
|
|
|
|
|
|
interfaceTemplate.Replace(ServiceInterfacePlaceholder, appServiceTypeName); |
|
|
|
|
|
|
|
var @interface = controllerApiDescription.Interfaces.Last(); |
|
|
|
|
|
|
|
var interfaceUsingNamespaceList = new List<string>(InterfaceUsingNamespaceList) |
|
|
|
{ |
|
|
|
$"using {GetTypeNamespace(appServiceTypeFullName)};" |
|
|
|
}; |
|
|
|
|
|
|
|
foreach (var method in @interface.Methods) |
|
|
|
{ |
|
|
|
GenerateInterfaceMethod(method, interfaceTemplate, interfaceUsingNamespaceList); |
|
|
|
} |
|
|
|
|
|
|
|
interfaceTemplate.Replace($"{UsingPlaceholder}", string.Join(Environment.NewLine, interfaceUsingNamespaceList.Distinct().OrderBy(x => x).Select(x => x))); |
|
|
|
interfaceTemplate.Replace($"{Environment.NewLine}{Environment.NewLine} {MethodPlaceholder}", string.Empty); |
|
|
|
interfaceTemplate.Replace(NamespacePlaceholder, rootNamespace); |
|
|
|
|
|
|
|
filePath = Path.Combine(args.WorkDirectory, folder, $"{appServiceTypeName}.cs"); |
|
|
|
Directory.CreateDirectory(Path.GetDirectoryName(filePath)); |
|
|
|
using (var writer = new StreamWriter(filePath)) |
|
|
|
{ |
|
|
|
await writer.WriteAsync(interfaceTemplate.ToString()); |
|
|
|
Logger.LogInformation($"Create {GetLoggerOutputPath(filePath, args.WorkDirectory)}"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void GenerateMethod( |
|
|
|
private void GenerateClassMethod( |
|
|
|
ActionApiDescriptionModel action, |
|
|
|
StringBuilder clientProxyBuilder, |
|
|
|
List<string> usingNamespaceList) |
|
|
|
{ |
|
|
|
var methodBuilder = new StringBuilder(); |
|
|
|
|
|
|
|
var returnTypeName = GetRealTypeName(action.ReturnValue.Type, usingNamespaceList); |
|
|
|
|
|
|
|
if (!action.Name.EndsWith("Async")) |
|
|
|
if (action.Name.EndsWith("Async")) |
|
|
|
{ |
|
|
|
GenerateSynchronizationMethod(action, returnTypeName, methodBuilder, usingNamespaceList); |
|
|
|
clientProxyBuilder.Replace(MethodPlaceholder, $"{methodBuilder}{Environment.NewLine} {MethodPlaceholder}"); |
|
|
|
return; |
|
|
|
GenerateAsyncClassMethod(action, returnTypeName, methodBuilder, usingNamespaceList); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
GenerateSyncClassMethod(action, returnTypeName, methodBuilder, usingNamespaceList); |
|
|
|
} |
|
|
|
|
|
|
|
GenerateAsynchronousMethod(action, returnTypeName, methodBuilder, usingNamespaceList); |
|
|
|
clientProxyBuilder.Replace(MethodPlaceholder, $"{methodBuilder}{Environment.NewLine} {MethodPlaceholder}"); |
|
|
|
} |
|
|
|
|
|
|
|
private void GenerateSynchronizationMethod(ActionApiDescriptionModel action, string returnTypeName, StringBuilder methodBuilder, List<string> usingNamespaceList) |
|
|
|
private void GenerateSyncClassMethod(ActionApiDescriptionModel action, string returnTypeName, StringBuilder methodBuilder, List<string> usingNamespaceList) |
|
|
|
{ |
|
|
|
methodBuilder.AppendLine($"public virtual {returnTypeName} {action.Name}(<args>)"); |
|
|
|
|
|
|
|
@ -235,7 +304,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi |
|
|
|
methodBuilder.AppendLine(" }"); |
|
|
|
} |
|
|
|
|
|
|
|
private void GenerateAsynchronousMethod( |
|
|
|
private void GenerateAsyncClassMethod( |
|
|
|
ActionApiDescriptionModel action, |
|
|
|
string returnTypeName, |
|
|
|
StringBuilder methodBuilder, |
|
|
|
@ -261,14 +330,9 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi |
|
|
|
|
|
|
|
var args = action.ParametersOnMethod.Any() ? argsTemplate : string.Empty; |
|
|
|
|
|
|
|
if (returnTypeName == "void") |
|
|
|
{ |
|
|
|
methodBuilder.AppendLine($" await RequestAsync(nameof({action.Name}), {args});"); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
methodBuilder.AppendLine($" return await RequestAsync<{returnTypeName}>(nameof({action.Name}), {args});"); |
|
|
|
} |
|
|
|
methodBuilder.AppendLine(returnTypeName == "void" |
|
|
|
? $" await RequestAsync(nameof({action.Name}), {args});" |
|
|
|
: $" return await RequestAsync<{returnTypeName}>(nameof({action.Name}), {args});"); |
|
|
|
|
|
|
|
foreach (var parameter in action.ParametersOnMethod) |
|
|
|
{ |
|
|
|
@ -280,41 +344,192 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi |
|
|
|
methodBuilder.AppendLine(" }"); |
|
|
|
} |
|
|
|
|
|
|
|
private bool ShouldGenerateProxy(ControllerApiDescriptionModel controllerApiDescription) |
|
|
|
private void GenerateInterfaceMethod(InterfaceMethodApiDescriptionModel action, StringBuilder clientProxyBuilder, List<string> usingNamespaceList) |
|
|
|
{ |
|
|
|
if (!controllerApiDescription.Interfaces.Any()) |
|
|
|
var methodBuilder = new StringBuilder(); |
|
|
|
|
|
|
|
var returnTypeName = GetRealTypeName(action.ReturnValue.Type, usingNamespaceList); |
|
|
|
|
|
|
|
if (action.Name.EndsWith("Async")) |
|
|
|
{ |
|
|
|
return false; |
|
|
|
GenerateAsyncInterfaceMethod(action, returnTypeName, methodBuilder, usingNamespaceList); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
GenerateSyncInterfaceMethod(action, returnTypeName, methodBuilder, usingNamespaceList); |
|
|
|
} |
|
|
|
|
|
|
|
var serviceInterface = controllerApiDescription.Interfaces.Last(); |
|
|
|
return ServicePostfixes.Any(x => serviceInterface.Type.EndsWith(x)); |
|
|
|
clientProxyBuilder.Replace(MethodPlaceholder, $"{methodBuilder}{Environment.NewLine} {MethodPlaceholder}"); |
|
|
|
} |
|
|
|
|
|
|
|
private void GenerateSyncInterfaceMethod(InterfaceMethodApiDescriptionModel action, string returnTypeName, StringBuilder methodBuilder, List<string> usingNamespaceList) |
|
|
|
{ |
|
|
|
methodBuilder.AppendLine($"public {returnTypeName} {action.Name}(<args>)"); |
|
|
|
|
|
|
|
foreach (var parameter in action.ParametersOnMethod.GroupBy(x => x.Name).Select(x => x.First())) |
|
|
|
{ |
|
|
|
methodBuilder.Replace("<args>", $"{GetRealTypeName(parameter.Type, usingNamespaceList)} {parameter.Name}, <args>"); |
|
|
|
} |
|
|
|
|
|
|
|
if (action.ParametersOnMethod.Count == 0) |
|
|
|
{ |
|
|
|
methodBuilder.Replace("(<args>)", "(<args>);"); |
|
|
|
} |
|
|
|
|
|
|
|
methodBuilder.Replace("<args>", string.Empty); |
|
|
|
methodBuilder.Replace(", )", ");"); |
|
|
|
} |
|
|
|
|
|
|
|
private void GenerateAsyncInterfaceMethod(InterfaceMethodApiDescriptionModel action, string returnTypeName, StringBuilder methodBuilder, List<string> usingNamespaceList) |
|
|
|
{ |
|
|
|
var returnSign = returnTypeName == "void" ? "Task" : $"Task<{returnTypeName}>"; |
|
|
|
|
|
|
|
methodBuilder.AppendLine($"{returnSign} {action.Name}(<args>)"); |
|
|
|
|
|
|
|
foreach (var parameter in action.ParametersOnMethod) |
|
|
|
{ |
|
|
|
methodBuilder.Replace("<args>", $"{GetRealTypeName(parameter.Type, usingNamespaceList)} {parameter.Name}, <args>"); |
|
|
|
} |
|
|
|
|
|
|
|
if (action.ParametersOnMethod.Count == 0) |
|
|
|
{ |
|
|
|
methodBuilder.Replace("(<args>)", "(<args>);"); |
|
|
|
} |
|
|
|
|
|
|
|
methodBuilder.Replace("<args>", string.Empty); |
|
|
|
methodBuilder.Replace(", )", ");"); |
|
|
|
} |
|
|
|
|
|
|
|
private bool ShouldGenerateMethod(string appServiceTypeName, ActionApiDescriptionModel action) |
|
|
|
{ |
|
|
|
var shouldGenerateMethod = action.ImplementFrom.StartsWith(AppServicePrefix) || action.ImplementFrom.StartsWith(appServiceTypeName); |
|
|
|
return action.ImplementFrom.StartsWith(AppServicePrefix) || |
|
|
|
action.ImplementFrom.StartsWith(appServiceTypeName) || |
|
|
|
IsAppServiceInterface(GetRealTypeName(action.ImplementFrom)); |
|
|
|
} |
|
|
|
|
|
|
|
private async Task GenerateDtoFileAsync(GenerateProxyArgs args, ApplicationApiDescriptionModel applicationApiDescriptionModel) |
|
|
|
{ |
|
|
|
var types = new List<string>(); |
|
|
|
|
|
|
|
if (!shouldGenerateMethod) |
|
|
|
foreach (var controller in applicationApiDescriptionModel.Modules.Values.First().Controllers) |
|
|
|
{ |
|
|
|
shouldGenerateMethod = IsAppServiceInterface(GetRealTypeName(action.ImplementFrom)); |
|
|
|
foreach (var action in controller.Value.Actions) |
|
|
|
{ |
|
|
|
foreach (var parameter in action.Value.ParametersOnMethod) |
|
|
|
{ |
|
|
|
types.AddIfNotContains(parameter.Type); |
|
|
|
} |
|
|
|
types.AddIfNotContains(action.Value.ReturnValue.Type); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
types = RemoveSystemAndDtoType(types); |
|
|
|
|
|
|
|
foreach (var type in applicationApiDescriptionModel.Types.Where(x => types.Contains(x.Key))) |
|
|
|
{ |
|
|
|
GetRelatedTypes(types, applicationApiDescriptionModel, type.Value); |
|
|
|
} |
|
|
|
|
|
|
|
types = RemoveSystemAndDtoType(types); |
|
|
|
|
|
|
|
foreach (var type in applicationApiDescriptionModel.Types.Where(x => types.Contains(x.Key))) |
|
|
|
{ |
|
|
|
var dto = new StringBuilder(DtoTemplate); |
|
|
|
var dtoUsingNamespaceList = new List<string>() |
|
|
|
{ |
|
|
|
$"using {GetTypeNamespace(type.Key)};" |
|
|
|
}; |
|
|
|
|
|
|
|
dto.Replace(NamespacePlaceholder, GetTypeNamespace(type.Key)); |
|
|
|
dto.Replace(DtoClassNamePlaceholder,GetRealTypeName(type.Key, dtoUsingNamespaceList) + (type.Value.BaseType.IsNullOrEmpty() ? "" : $" : {GetRealTypeName(type.Value.BaseType, dtoUsingNamespaceList)}")); |
|
|
|
var properties = new StringBuilder(); |
|
|
|
for (var i = 0; i < type.Value.Properties.Length; i++) |
|
|
|
{ |
|
|
|
var property = type.Value.Properties[i]; |
|
|
|
properties.Append("public "); |
|
|
|
properties.Append(GetRealTypeName(property.Type, dtoUsingNamespaceList)); |
|
|
|
properties.Append($" {property.Name}"); |
|
|
|
properties.Append(" { get; set; }"); |
|
|
|
if (i < type.Value.Properties.Length - 1) |
|
|
|
{ |
|
|
|
properties.AppendLine(); |
|
|
|
properties.AppendLine(); |
|
|
|
properties.Append(" "); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
dto.Replace($"{UsingPlaceholder}", string.Join(Environment.NewLine, dtoUsingNamespaceList.OrderBy(x => x).Select(x => x))); |
|
|
|
dto.Replace(PropertyPlaceholder, properties.ToString()); |
|
|
|
|
|
|
|
var folder = args.Folder.IsNullOrWhiteSpace() |
|
|
|
? ProxyDirectory + Path.DirectorySeparatorChar + GetTypeNamespace(type.Key) |
|
|
|
.Replace(".", Path.DirectorySeparatorChar.ToString()) |
|
|
|
: args.Folder; |
|
|
|
|
|
|
|
var filePath = Path.Combine(args.WorkDirectory, folder, $"{GetRealTypeName(type.Key)}.cs"); |
|
|
|
Directory.CreateDirectory(Path.GetDirectoryName(filePath)); |
|
|
|
using (var writer = new StreamWriter(filePath)) |
|
|
|
{ |
|
|
|
await writer.WriteAsync(dto.ToString()); |
|
|
|
Logger.LogInformation($"Create {GetLoggerOutputPath(filePath, args.WorkDirectory)}"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private static void GetRelatedTypes(List<string> types, ApplicationApiDescriptionModel applicationApiDescriptionModel, TypeApiDescriptionModel typeApiDescriptionModel) |
|
|
|
{ |
|
|
|
if (!typeApiDescriptionModel.BaseType.IsNullOrEmpty() && |
|
|
|
!typeApiDescriptionModel.BaseType.StartsWith("Volo.Abp.Application.Dtos") && |
|
|
|
!typeApiDescriptionModel.BaseType.StartsWith("Volo.Abp.ObjectExtending")) |
|
|
|
{ |
|
|
|
types.AddIfNotContains(typeApiDescriptionModel.BaseType); |
|
|
|
} |
|
|
|
|
|
|
|
foreach (var property in typeApiDescriptionModel.Properties.Where(x => !types.Contains(x.Type))) |
|
|
|
{ |
|
|
|
types.AddIfNotContains(property.Type); |
|
|
|
|
|
|
|
if (applicationApiDescriptionModel.Types.ContainsKey(property.Type)) |
|
|
|
{ |
|
|
|
GetRelatedTypes(types, applicationApiDescriptionModel, applicationApiDescriptionModel.Types[property.Type]); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return shouldGenerateMethod; |
|
|
|
private static List<string> RemoveSystemAndDtoType(List<string> types) |
|
|
|
{ |
|
|
|
for (var i = 0; i < types.Count; i++) |
|
|
|
{ |
|
|
|
var className = types[i]; |
|
|
|
if (className.StartsWith("Volo.Abp.Application.Dtos.") && className.Contains("<") && className.Contains(">")) |
|
|
|
{ |
|
|
|
types[i] = className.Substring( |
|
|
|
className.IndexOf("<", StringComparison.Ordinal) + 1, |
|
|
|
className.IndexOf(">", StringComparison.Ordinal) - |
|
|
|
className.IndexOf("<", StringComparison.Ordinal) -1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return types.Where(x => !x.StartsWith("System.") && !x.StartsWith("[System.")).Distinct().OrderBy(x => x).ToList(); |
|
|
|
} |
|
|
|
|
|
|
|
private bool IsAppServiceInterface(string typeName) |
|
|
|
private static bool IsAppServiceInterface(string typeName) |
|
|
|
{ |
|
|
|
return typeName.StartsWith("I") && ServicePostfixes.Any(typeName.EndsWith); |
|
|
|
} |
|
|
|
|
|
|
|
private string GetTypeNamespace(string typeFullName) |
|
|
|
private static string GetTypeNamespace(string typeFullName) |
|
|
|
{ |
|
|
|
return typeFullName.Substring(0, typeFullName.LastIndexOf('.')); |
|
|
|
} |
|
|
|
|
|
|
|
private string GetRealTypeName(string typeName, List<string> usingNamespaceList = null) |
|
|
|
{ |
|
|
|
if (typeName.StartsWith("[") && typeName.EndsWith("]")) |
|
|
|
{ |
|
|
|
return GetRealTypeName(typeName.Substring(1, typeName.Length - 2), usingNamespaceList) + "[]"; |
|
|
|
} |
|
|
|
|
|
|
|
var filter = new[] { "<", ",", ">" }; |
|
|
|
var stringBuilder = new StringBuilder(); |
|
|
|
var typeNames = typeName.Split('.'); |
|
|
|
@ -361,7 +576,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi |
|
|
|
return stringBuilder.ToString(); |
|
|
|
} |
|
|
|
|
|
|
|
private void AddUsingNamespace(List<string> usingNamespaceList, string typeName) |
|
|
|
private static void AddUsingNamespace(List<string> usingNamespaceList, string typeName) |
|
|
|
{ |
|
|
|
var rootNamespace = $"using {GetTypeNamespace(typeName)};"; |
|
|
|
if (usingNamespaceList.Contains(rootNamespace)) |
|
|
|
@ -372,7 +587,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi |
|
|
|
usingNamespaceList.Add(rootNamespace); |
|
|
|
} |
|
|
|
|
|
|
|
private string NormalizeTypeName(string typeName) |
|
|
|
private static string NormalizeTypeName(string typeName) |
|
|
|
{ |
|
|
|
var nullable = string.Empty; |
|
|
|
if (typeName.EndsWith("?")) |
|
|
|
@ -398,7 +613,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi |
|
|
|
return $"{typeName}{nullable}"; |
|
|
|
} |
|
|
|
|
|
|
|
private void CheckWorkDirectory(string directory) |
|
|
|
private static void CheckWorkDirectory(string directory) |
|
|
|
{ |
|
|
|
if (!Directory.Exists(directory)) |
|
|
|
{ |
|
|
|
@ -412,7 +627,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void CheckFolder(string folder) |
|
|
|
private static void CheckFolder(string folder) |
|
|
|
{ |
|
|
|
if (!folder.IsNullOrWhiteSpace() && Path.HasExtension(folder)) |
|
|
|
{ |
|
|
|
|