private void buildInterfaceCode(ICodeGeneratorContext codeGeneratorContext, InterfaceContract interfaceContract, out CodeTypeDeclaration codeType, out CodeNamespaceImportCollection imports) { IDictionary <XmlQualifiedName, string> ElementName2TypeNameMapping = codeGeneratorContext.ElementName2TypeNameMapping; IDictionary <string, CodeTypeDeclaration> CodeTypeMap = codeGeneratorContext.CodeTypeMap; imports = new CodeNamespaceImportCollection(); // generate service or client CodeGeneratorMode generatorMode = codeGeneratorContext.CodeGenOptions.CodeGeneratorMode; string interfaceName = "I" + interfaceContract.ServiceName.Replace("Interface", string.Empty); CodeTypeDeclaration interfaceType = new CodeTypeDeclaration(interfaceName); interfaceType.UserData.Add(Constants.GENERATED_TYPE, interfaceName); interfaceType.IsClass = false; interfaceType.TypeAttributes = TypeAttributes.Public; interfaceType.IsInterface = true; interfaceType.Comments.Clear(); // Generate service documentation string serviceDoc = "Service interface auto-generated by SOA tool, DO NOT CHANGE!\n\n"; serviceDoc += "注意,实现该接口的服务在AntServiceStack服务容器中是以new instance per request的形式被初始化的,\n"; serviceDoc += "也就是说,容器会为每个请求创建一个新的服务实例,并在请求结束时释放(release),而不是单个\n"; serviceDoc += "服务实例(singleton)服务所有的请求, 所以请务必不要在服务初始化(例如构造函数中)时做较重的初始化\n"; serviceDoc += "(例如初始化数据库等)动作,否则对性能有很大影响,如果有较重的初始化动作,\n"; serviceDoc += "请在服务实现中以静态方式(例如静态构造函数中)一次性完成,或者以IoC注入方式初始化,在服务容器\n"; serviceDoc += "启动时事先将依赖初始化并注册在容器中,让容器在构造服务实例时自动解析和注入依赖(也可在服务实现中手动解析依赖),\n"; serviceDoc += "关于静态和依赖注入初始化的样例,请参考AntServiceStack提供的样例程序.\n\n"; if (!string.IsNullOrEmpty(interfaceContract.ServiceDocumentation)) { serviceDoc += interfaceContract.ServiceDocumentation; } CodeDomHelper.CreateSummaryComment(interfaceType.Comments, serviceDoc); // Import AntServiceStack.ServiceHost namespace, requried by both client and service sides imports.Add(new CodeNamespaceImport(Constants.C_SERVICE_STACK_SERVICE_HOST_NAMESPACE)); if (generatorMode == CodeGeneratorMode.Service) { // Mark as AntServiceStack supported service CodeAttributeDeclaration cServiceAttribute = new CodeAttributeDeclaration("AntServiceInterface"); string serviceName = interfaceContract.ServiceName; CodeAttributeArgument serviceNameArgument = new CodeAttributeArgument(new CodePrimitiveExpression(serviceName)); cServiceAttribute.Arguments.Add(serviceNameArgument); string serviceNamespace = interfaceContract.ServiceNamespace; CodeAttributeArgument serviceNamespaceArgument = new CodeAttributeArgument(new CodePrimitiveExpression(serviceNamespace)); cServiceAttribute.Arguments.Add(serviceNamespaceArgument); Version ver = Assembly.GetExecutingAssembly().GetName().Version; string version = ver.Major.ToString() + "." + ver.Minor.ToString() + "." + ver.Build.ToString() + "." + ver.Revision.ToString(); CodeAttributeArgument codeGeneratorVersionArgument = new CodeAttributeArgument(new CodePrimitiveExpression(version)); cServiceAttribute.Arguments.Add(codeGeneratorVersionArgument); interfaceType.CustomAttributes.Add(cServiceAttribute); } //CodeTypeDeclaration healthCheckRequestType = null; //CodeTypeDeclaration healthCheckResponseType = null; bool hasAsync = false; foreach (Operation operation in interfaceContract.OperationsCollection) { var isHealthCheckOperation = false; CodeMemberMethod method = new CodeMemberMethod(); method.Name = operation.Name; if (operation.Name.ToLower() == HEALTH_CHECK_OPERATION_NAME.ToLower()) { isHealthCheckOperation = true; } Message inMessage = operation.Input; XmlQualifiedName inMessageElementQName = new XmlQualifiedName(inMessage.Element.ElementName, inMessage.Element.ElementNamespace); string requestTypeName = null; ElementName2TypeNameMapping.TryGetValue(inMessageElementQName, out requestTypeName); Enforce.IsNotNull <string>(requestTypeName, "Fail to retrieve request type from wsdl using innput message element QName : " + inMessageElementQName); CodeTypeReference requestTypeReference = new CodeTypeReference(requestTypeName); CodeParameterDeclarationExpression methodParam = new CodeParameterDeclarationExpression(requestTypeReference, "request"); methodParam.Type = requestTypeReference; method.Parameters.Add(methodParam); Message outMessage = operation.Output; Enforce.IsNotNull <Message>(outMessage, "Fail to get out message in operation : " + operation.Name + ", only requst/response style operation is supported"); XmlQualifiedName outMessageElementQName = new XmlQualifiedName(outMessage.Element.ElementName, outMessage.Element.ElementNamespace); string responseTypeName = null; ElementName2TypeNameMapping.TryGetValue(outMessageElementQName, out responseTypeName); Enforce.IsNotNull <string>(responseTypeName, "Fail to retrieve response type from wsdl using output message element QName : " + outMessageElementQName); if (codeGeneratorContext.CodeGenOptions.GenerateAsyncOperations && outMessageElementQName.Name.EndsWith("AsyncResponse")) { method.ReturnType = new CodeTypeReference("Task<" + responseTypeName + ">"); hasAsync = true; } else { method.ReturnType = new CodeTypeReference(responseTypeName); } // SOA Policy enforcement : response type must extend SOA common AbstractResponseType CodeTypeDeclaration responseType = null; CodeTypeMap.TryGetValue(responseTypeName, out responseType); Enforce.IsNotNull <CodeTypeDeclaration>(responseType, "Weird code generator internal error, please ask soa framework team for help."); if (isHealthCheckOperation) { //healthCheckResponseType = responseType; } if (!CodeExtension.HasProperty(responseType, Constants.RESPONSE_STATUS_PROPERTY_NAME, Constants.RESPONSE_STATUS_TYPE_NAME)) { throw new SOAPolicyViolationException(string.Format(" SOA Policy Violation, response type '{0}' does not include requried {1} property of type {2}", responseTypeName, Constants.RESPONSE_STATUS_PROPERTY_NAME, Constants.RESPONSE_STATUS_TYPE_NAME)); } CodeTypeDeclaration responseStatusType = null; CodeTypeMap.TryGetValue(Constants.RESPONSE_STATUS_TYPE_NAME, out responseStatusType); Enforce.IsNotNull <CodeTypeDeclaration>(responseStatusType, string.Format("Weird code generator internal error, missing requried {0}, please ask soa framework team for help.", Constants.RESPONSE_STATUS_TYPE_NAME)); if (!CodeExtension.IsSOACommonType(responseStatusType)) { throw new SOAPolicyViolationException(string.Format(" SOA Policy Violation, {0} reference is not SOA Common {1}.", Constants.RESPONSE_STATUS_TYPE_NAME, Constants.RESPONSE_STATUS_TYPE_NAME)); } if (!CodeExtension.HasInterface(responseType, HAS_RESPONSE_STATUS_INTERFACE_NAME)) { // make response type implement IHasResponseStatus interface responseType.BaseTypes.Add(HAS_RESPONSE_STATUS_INTERFACE_NAME); } // optional common request handling CodeTypeDeclaration requestType = null; CodeTypeMap.TryGetValue(requestTypeName, out requestType); Enforce.IsNotNull <CodeTypeDeclaration>(requestType, "Weird code generator internal error, please ask soa framework team for help."); if (isHealthCheckOperation) { //healthCheckRequestType = requestType; } if (CodeExtension.HasProperty(requestType, MOBILE_REQUEST_HEAD_PROPERTY_NAME, MOBILE_REQUEST_HEAD_TYPE_NAME) && !CodeExtension.HasInterface(requestType, HAS_MOBILE_REQUEST_HEAD_INTERFACE_NAME)) { requestType.BaseTypes.Add(HAS_MOBILE_REQUEST_HEAD_INTERFACE_NAME); } if (CodeExtension.HasProperty(responseType, Constants.COMMON_REQUEST_PROPERTY_NAME, Constants.COMMON_REQUEST_TYPE_NAME)) { CodeTypeDeclaration commonRequestType = null; CodeTypeMap.TryGetValue(Constants.COMMON_REQUEST_TYPE_NAME, out commonRequestType); Enforce.IsNotNull <CodeTypeDeclaration>(commonRequestType, string.Format("Weird code generator internal error, missing requried {0}, please ask soa framework team for help.", Constants.COMMON_REQUEST_TYPE_NAME)); if (!CodeExtension.IsSOACommonType(commonRequestType)) { throw new SOAPolicyViolationException(string.Format(" SOA Policy Violation, {0} reference is not Ant SOA Common {1}.", Constants.COMMON_REQUEST_TYPE_NAME, Constants.COMMON_REQUEST_TYPE_NAME)); } if (!CodeExtension.HasInterface(responseType, HAS_COMMON_REQUEST_INTERFACE_NAME)) { // make request type implement IHasCommonRequest interface responseType.BaseTypes.Add(HAS_COMMON_REQUEST_INTERFACE_NAME); } } // Generate operation documentation if (!string.IsNullOrEmpty(operation.Documentation)) { CodeDomHelper.CreateSummaryComment(method.Comments, operation.Documentation); } interfaceType.Members.Add(method); } if (hasAsync) { imports.Add(new CodeNamespaceImport(Constants.SYSTEM_THREADING_TASKS_NAMESPACE)); } // SOA Policy enforcement : healtch check operation is mandatory //if (healthCheckRequestType == null || healthCheckResponseType == null) //{ // throw new SOAPolicyViolationException(string.Format("SOA Policy Violation, missing mandatory check health operation.")); //} //if (!CodeExtension.IsSOACommonType(healthCheckRequestType) || !CodeExtension.IsSOACommonType(healthCheckResponseType)) //{ // throw new SOAPolicyViolationException(string.Format("SOA Policy Violation, wrong SOA common healthcheck types.")); //} if (generatorMode == CodeGeneratorMode.Service) { codeType = interfaceType; return; } imports.Add(new CodeNamespaceImport(SYSTEM_NAMESPACE)); imports.Add(new CodeNamespaceImport(SYSTEM_THREADING_NAMESPACE)); imports.Add(new CodeNamespaceImport(SYSTEM_THREADING_TASKS_NAMESPACE)); imports.Add(new CodeNamespaceImport( generatorMode == CodeGeneratorMode.Client ? C_SERVICE_STACK_SERVICE_CLIENT_NAMESPACE : C_SERVICE_STACK_AUTOMATION_TEST_CLIENT_NAMESPACE)); string clientName = interfaceContract.ServiceName.Replace("Interface", string.Empty) + "Client"; CodeTypeDeclaration clientType = new CodeTypeDeclaration(clientName); clientType.UserData.Add(Constants.GENERATED_TYPE, clientName); var baseType = new CodeTypeReference( generatorMode == CodeGeneratorMode.Client ? SERVICE_CLIENT_BASE_NAME : SERVICE_CLIENT_FOR_AUTOMATION_BASE_NAME, new CodeTypeReference[] { new CodeTypeReference(clientName) }); clientType.BaseTypes.Add(baseType); codeType = clientType; // Generate client documentation string clientDoc = "Service client auto-generated by SOA tool, DO NOT CHANGE!\n\n"; if (!string.IsNullOrEmpty(interfaceContract.ServiceDocumentation)) { clientDoc += interfaceContract.ServiceDocumentation; } CodeDomHelper.CreateSummaryComment(clientType.Comments, clientDoc); // base constructor //CodeConstructor baseConstructor = new CodeConstructor(); //baseConstructor.Attributes = MemberAttributes.Public; //baseConstructor.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("")); //clientType.Members.Add(baseConstructor); // public constant string service name and namespace CodeMemberField codeMemberField = new CodeMemberField(typeof(string), SERVICE_CLIENT_CODE_GENERATOR_VERSION_FIELD_NAME); codeMemberField.Attributes = (codeMemberField.Attributes & ~MemberAttributes.AccessMask & ~MemberAttributes.ScopeMask) | MemberAttributes.Public | MemberAttributes.Const; codeMemberField.InitExpression = new CodePrimitiveExpression(typeof(InterfaceContractGenerator).Assembly.GetName().Version.ToString()); clientType.Members.Add(codeMemberField); codeMemberField = new CodeMemberField(typeof(string), SERVICE_CLIENT_ORIGINAL_SERVICE_NAME_FIELD_NAME); codeMemberField.Attributes = (codeMemberField.Attributes & ~MemberAttributes.AccessMask & ~MemberAttributes.ScopeMask) | MemberAttributes.Public | MemberAttributes.Const; codeMemberField.InitExpression = new CodePrimitiveExpression(interfaceContract.ServiceName); clientType.Members.Add(codeMemberField); codeMemberField = new CodeMemberField(typeof(string), SERVICE_CLIENT_ORIGINAL_SERVICE_NAMESPACE_FIELD_NAME); codeMemberField.Attributes = (codeMemberField.Attributes & ~MemberAttributes.AccessMask & ~MemberAttributes.ScopeMask) | MemberAttributes.Public | MemberAttributes.Const; codeMemberField.InitExpression = new CodePrimitiveExpression(interfaceContract.ServiceNamespace); clientType.Members.Add(codeMemberField); codeMemberField = new CodeMemberField(typeof(string), SERVICE_CLIENT_ORIGINAL_SERVICE_TYPE_FIELD_NAME); codeMemberField.Attributes = (codeMemberField.Attributes & ~MemberAttributes.AccessMask & ~MemberAttributes.ScopeMask) | MemberAttributes.Public | MemberAttributes.Const; codeMemberField.InitExpression = new CodePrimitiveExpression(SERVICE_CLIENT_NON_SLB_SERVICE_TYPE_FIELD_NAME); clientType.Members.Add(codeMemberField); // private constructor with baseUri parameter CodeConstructor baseConstructorWithParameter = new CodeConstructor(); baseConstructorWithParameter.Attributes = MemberAttributes.Private; baseConstructorWithParameter.Parameters.Add(new CodeParameterDeclarationExpression("System.String", "baseUri")); baseConstructorWithParameter.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("baseUri")); clientType.Members.Add(baseConstructorWithParameter); // private constructor with serviceName & serviceNamespace parameter baseConstructorWithParameter = new CodeConstructor(); baseConstructorWithParameter.Attributes = MemberAttributes.Private; baseConstructorWithParameter.Parameters.Add(new CodeParameterDeclarationExpression("System.String", "serviceName")); baseConstructorWithParameter.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("serviceName")); baseConstructorWithParameter.Parameters.Add(new CodeParameterDeclarationExpression("System.String", "serviceNamespace")); baseConstructorWithParameter.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("serviceNamespace")); baseConstructorWithParameter.Parameters.Add(new CodeParameterDeclarationExpression("System.String", "subEnv")); baseConstructorWithParameter.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("subEnv")); clientType.Members.Add(baseConstructorWithParameter); // build methods foreach (Operation operation in interfaceContract.OperationsCollection) { string operationName = operation.Name; Message inMessage = operation.Input; XmlQualifiedName inMessageElementQName = new XmlQualifiedName(inMessage.Element.ElementName, inMessage.Element.ElementNamespace); string requestTypeName = null; ElementName2TypeNameMapping.TryGetValue(inMessageElementQName, out requestTypeName); Message outMessage = operation.Output; XmlQualifiedName outMessageElementQName = new XmlQualifiedName(outMessage.Element.ElementName, outMessage.Element.ElementNamespace); string responseTypeName = null; ElementName2TypeNameMapping.TryGetValue(outMessageElementQName, out responseTypeName); CodeTypeReference responseType = new CodeTypeReference(responseTypeName); BuildSyncMethod(clientType, operation, requestTypeName, responseTypeName); BuildSyncWithCallbackMethod(clientType, operation, requestTypeName, responseTypeName); BuildCreateRequestTaskMethod(clientType, operation, requestTypeName, responseTypeName); BuildStartIOCPTaskMethod(clientType, operation, requestTypeName, responseTypeName); } }