private void GenerateContractMethod(CodeTypeDeclaration contractInterface, DomainOperationEntry operation) { string domainServiceName = this._domainServiceDescription.DomainServiceType.Name; CodeMemberMethod beginMethod = new CodeMemberMethod(); beginMethod.Name = "Begin" + operation.Name; beginMethod.ReturnType = CodeGenUtilities.GetTypeReference(typeof(IAsyncResult), this.ClientProxyGenerator, contractInterface); contractInterface.Members.Add(beginMethod); // Generate <summary> doc comment for the Begin method string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Summary_Comment, operation.Name); beginMethod.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); // Generate <param> doc comment for all the parameters foreach (DomainOperationParameter parameter in operation.Parameters) { comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Parameter_Comment, parameter.Name); beginMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment(parameter.Name, comment, this.ClientProxyGenerator.IsCSharp)); } // <param> for callback and asyncState beginMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("callback", Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Callback_Parameter_Comment, this.ClientProxyGenerator.IsCSharp)); beginMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("asyncState", Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_AsyncState_Parameter_Comment, this.ClientProxyGenerator.IsCSharp)); // Generate <returns> doc comment beginMethod.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Returns_Comment, this.ClientProxyGenerator.IsCSharp)); this.GenerateContractMethodAttributes(contractInterface, beginMethod, domainServiceName, operation.Name); foreach (DomainOperationParameter parameter in operation.Parameters) { beginMethod.Parameters.Add( new CodeParameterDeclarationExpression( CodeGenUtilities.GetTypeReference(CodeGenUtilities.TranslateType(parameter.ParameterType), this.ClientProxyGenerator, contractInterface), parameter.Name)); } beginMethod.Parameters.Add( new CodeParameterDeclarationExpression( CodeGenUtilities.GetTypeReference(typeof(AsyncCallback), this.ClientProxyGenerator, contractInterface), "callback")); beginMethod.Parameters.Add( new CodeParameterDeclarationExpression( CodeGenUtilities.GetTypeReference(typeof(object), this.ClientProxyGenerator, contractInterface), "asyncState")); CodeMemberMethod endMethod = new CodeMemberMethod(); endMethod.Name = "End" + operation.Name; bool hasSideEffects = true; string returnTypeName = null; if (operation.Operation == DomainOperation.Query) { hasSideEffects = ((QueryAttribute)operation.OperationAttribute).HasSideEffects; returnTypeName = "QueryResult"; if (operation.ReturnType == typeof(void)) { endMethod.ReturnType = CodeGenUtilities.GetTypeReference(TypeConstants.QueryResultFullName, contractInterface.UserData["Namespace"] as string, false); } else { endMethod.ReturnType = CodeGenUtilities.GetTypeReference(TypeConstants.QueryResultFullName, contractInterface.UserData["Namespace"] as string, false); endMethod.ReturnType.TypeArguments.Add(CodeGenUtilities.GetTypeReference(CodeGenUtilities.TranslateType(operation.AssociatedType), this.ClientProxyGenerator, contractInterface)); } } else { if (operation.Operation == DomainOperation.Invoke) { hasSideEffects = ((InvokeAttribute)operation.OperationAttribute).HasSideEffects; } returnTypeName = CodeGenUtilities.TranslateType(operation.ReturnType).Name; endMethod.ReturnType = CodeGenUtilities.GetTypeReference(CodeGenUtilities.TranslateType(operation.ReturnType), this.ClientProxyGenerator, contractInterface, false); } // Generate [HasSideEffects(...)]. beginMethod.CustomAttributes.Add(new CodeAttributeDeclaration(CodeGenUtilities.GetTypeReference(TypeConstants.HasSideEffectsFullName, contractInterface.UserData["Namespace"] as string, false), new CodeAttributeArgument(new CodePrimitiveExpression(hasSideEffects)))); // Generate <summary> doc comment for the End method comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_ServiceContract_End_Method_Summary_Comment, beginMethod.Name); endMethod.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); // Generate <param> doc comment for the IAsyncResult parameter comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_ServiceContract_End_Method_Parameter_Comment, beginMethod.Name); endMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("result", comment, this.ClientProxyGenerator.IsCSharp)); // Generate <returns> doc comment if (operation.ReturnType != typeof(void)) { comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_ServiceContract_End_Method_Returns_Comment, returnTypeName, operation.Name); endMethod.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); } contractInterface.Members.Add(endMethod); endMethod.Parameters.Add( new CodeParameterDeclarationExpression( CodeGenUtilities.GetTypeReference(typeof(IAsyncResult), this.ClientProxyGenerator, contractInterface), "result")); }
private void GenerateContractSubmitChangesMethod(CodeTypeDeclaration contractInterface) { string domainServiceName = this._domainServiceDescription.DomainServiceType.Name; // ----------------------------------------------------------------------- // IAsyncResult BeginSubmitChanges(IEnumerable<EntityOperation> changeSet, AsyncCallback callback, object asyncState) // ----------------------------------------------------------------------- CodeMemberMethod beginQueryMethod = new CodeMemberMethod(); beginQueryMethod.Name = "BeginSubmitChanges"; beginQueryMethod.ReturnType = CodeGenUtilities.GetTypeReference(typeof(IAsyncResult), this.ClientProxyGenerator, contractInterface); contractInterface.Members.Add(beginQueryMethod); // Generate <summary> doc comment for the Begin method string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Summary_Comment, "SubmitChanges"); beginQueryMethod.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); // <param> for callback and asyncState beginQueryMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("changeSet", Resource.CodeGen_DomainContext_ServiceContract_Begin_SubmitMethod_Changeset_Parameter_Comment, this.ClientProxyGenerator.IsCSharp)); beginQueryMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("callback", Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Callback_Parameter_Comment, this.ClientProxyGenerator.IsCSharp)); beginQueryMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("asyncState", Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_AsyncState_Parameter_Comment, this.ClientProxyGenerator.IsCSharp)); // Generate <returns> doc comment beginQueryMethod.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Returns_Comment, this.ClientProxyGenerator.IsCSharp)); this.GenerateContractMethodAttributes(contractInterface, beginQueryMethod, domainServiceName, "SubmitChanges"); CodeTypeReference enumTypeRef = CodeGenUtilities.GetTypeReference(TypeConstants.IEnumerableFullName, contractInterface.UserData["Namespace"] as string, false); enumTypeRef.TypeArguments.Add(CodeGenUtilities.GetTypeReference(TypeConstants.ChangeSetEntryTypeFullName, contractInterface.UserData["Namespace"] as string, false)); beginQueryMethod.Parameters.Add( new CodeParameterDeclarationExpression( enumTypeRef, "changeSet")); beginQueryMethod.Parameters.Add( new CodeParameterDeclarationExpression( CodeGenUtilities.GetTypeReference(typeof(AsyncCallback), this.ClientProxyGenerator, contractInterface), "callback")); beginQueryMethod.Parameters.Add( new CodeParameterDeclarationExpression( CodeGenUtilities.GetTypeReference(typeof(object), this.ClientProxyGenerator, contractInterface), "asyncState")); // ----------------------------------------------------------------------- // IEnumerable<EntityOperation> EndSubmitChanges(IAsyncResult result) // ----------------------------------------------------------------------- CodeTypeReference resultTypeRef = CodeGenUtilities.GetTypeReference(TypeConstants.DomainServiceFaultFullName, contractInterface.UserData["Namespace"] as string, false); resultTypeRef.TypeArguments.Add(CodeGenUtilities.GetTypeReference(TypeConstants.ChangeSetEntryTypeFullName, contractInterface.UserData["Namespace"] as string, false)); CodeMemberMethod endQueryMethod = new CodeMemberMethod(); endQueryMethod.Name = "EndSubmitChanges"; endQueryMethod.ReturnType = enumTypeRef; contractInterface.Members.Add(endQueryMethod); // Generate <summary> doc comment for the End method comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_ServiceContract_End_Method_Summary_Comment, "BeginSubmitChanges"); endQueryMethod.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); // Generate <param> doc comment for the IAsyncResult parameter comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_ServiceContract_End_Method_Parameter_Comment, "BeginSubmitChanges"); endQueryMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("result", comment, this.ClientProxyGenerator.IsCSharp)); // Generate <returns> doc comment endQueryMethod.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(Resource.CodeGen_DomainContext_ServiceContract_End_SubmitMethod_Returns_Comment, this.ClientProxyGenerator.IsCSharp)); endQueryMethod.Parameters.Add( new CodeParameterDeclarationExpression( CodeGenUtilities.GetTypeReference(typeof(IAsyncResult), this.ClientProxyGenerator, contractInterface), "result")); }
private void GenerateConstructors(CodeTypeDeclaration proxyClass, CodeTypeDeclaration contractInterface, EnableClientAccessAttribute enableClientAccessAttribute, CodeMethodInvokeExpression onCreatedExpression) { CodeTypeReference uriTypeRef = CodeGenUtilities.GetTypeReference(typeof(Uri), this.ClientProxyGenerator, proxyClass); CodeTypeReference uriKindTypeRef = CodeGenUtilities.GetTypeReference(typeof(UriKind), this.ClientProxyGenerator, proxyClass); string containingNamespace = proxyClass.UserData["Namespace"] as string; CodeTypeReference contractTypeParameter = CodeGenUtilities.GetTypeReference( containingNamespace + "." + proxyClass.Name + "." + contractInterface.Name, containingNamespace, true); // construct relative URI string relativeServiceUri = string.Format(CultureInfo.InvariantCulture, "{0}.svc", this._domainServiceDescription.DomainServiceType.FullName.Replace('.', '-')); CodeExpression relativeUriExpression = new CodeObjectCreateExpression( uriTypeRef, new CodePrimitiveExpression(relativeServiceUri), new CodePropertyReferenceExpression(new CodeTypeReferenceExpression(uriKindTypeRef), "Relative")); // ---------------------------------------------------------------- // Default ctor decl (using relative URI) // ---------------------------------------------------------------- // ctor parameters List <CodeParameterDeclarationExpression> ctorParams = null; // base params List <CodeExpression> baseParams = new List <CodeExpression>(1); baseParams.Add(relativeUriExpression); // add <summary> doc comments string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Default_Constructor_Summary_Comments, proxyClass.Name); CodeCommentStatementCollection comments = CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp); // <comments>...</comments> // public .ctor() : this(new Uri("Foo-Bar.svc", UriKind.Relative)) GenerateConstructor(proxyClass, ctorParams, baseParams, comments, false); // ---------------------------------------------------------------- // DomainContext(System.Uri serviceUri) ctor decl // ---------------------------------------------------------------- // ctor params ctorParams = new List <CodeParameterDeclarationExpression>(1); ctorParams.Add(new CodeParameterDeclarationExpression(uriTypeRef, "serviceUri")); // add <summary> and <param> comments comments = CodeGenUtilities.GenerateSummaryCodeComment(string.Format(CultureInfo.CurrentCulture, Resource.EntityCodeGen_ConstructorComments_Summary_ServiceUri, proxyClass.Name), this.ClientProxyGenerator.IsCSharp); comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("serviceUri", string.Format(CultureInfo.CurrentCulture, Resource.EntityCodeGen_ConstructorComments_Param_ServiceUri, this._domainServiceDescription.DomainServiceType.Name), this.ClientProxyGenerator.IsCSharp)); // <comments>...</comments> // public .ctor(Uri serviceUri) : this(DomainContext.CreateDomainClient(typeof(TContract), serviceUri, true/false)) // ctor base parameters baseParams = new List <CodeExpression>(1); CodeTypeReference domainContextRef = CodeGenUtilities.GetTypeReference(TypeConstants.DomainContextTypeFullName, proxyClass.UserData["Namespace"] as string, false); baseParams.Add(new CodeMethodInvokeExpression( new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(domainContextRef), "CreateDomainClient"), new CodeTypeOfExpression(contractTypeParameter), new CodeArgumentReferenceExpression("serviceUri"), new CodePrimitiveExpression(enableClientAccessAttribute.RequiresSecureEndpoint))); GenerateConstructor(proxyClass, ctorParams, baseParams, comments, false); // ----------------------------------------------------------------------- // DomainContext(DomainClient domainClient) ctor decl // ----------------------------------------------------------------------- // ctor parameters --[(DomainClient domainClient)] ctorParams = new List <CodeParameterDeclarationExpression>(1); ctorParams.Add(new CodeParameterDeclarationExpression(CodeGenUtilities.GetTypeReference(TypeConstants.DomainClientTypeFullName, proxyClass.UserData["Namespace"] as string, false), "domainClient")); // parameters to invoke on base -- [: base(domainClient)] baseParams = new List <CodeExpression>(1); baseParams.Add(new CodeArgumentReferenceExpression("domainClient")); // add <summary> and <param> comments comments = CodeGenUtilities.GenerateSummaryCodeComment(string.Format(CultureInfo.CurrentCulture, Resource.EntityCodeGen_ConstructorComments_Summary_DomainClientAccumulating, proxyClass.Name), this.ClientProxyGenerator.IsCSharp); comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("domainClient", string.Format(CultureInfo.CurrentCulture, Resource.EntityCodeGen_ConstructorComments_Param_DomainClient), this.ClientProxyGenerator.IsCSharp)); // <comments>...</comments> // public .ctor(DomainClient domainClient) : base(domainClient)] CodeConstructor proxyCtor = GenerateConstructor(proxyClass, ctorParams, baseParams, comments, true); proxyCtor.Statements.Add(onCreatedExpression); }
private void GenerateCustomMethod(string customMethodName, DomainOperationEntry customMethod) { this.ClientProxyGenerator.LogMessage(string.Format(CultureInfo.CurrentCulture, Resource.EntityCodeGen_Generating_InvokeMethod, customMethodName)); // ---------------------------------------------------------------- // Method decl // ---------------------------------------------------------------- CodeMemberMethod method = new CodeMemberMethod(); method.Name = customMethodName; method.Attributes = MemberAttributes.Public | MemberAttributes.Final; // The custom method parameter list is the same as the domain operation entries -- except for the first parameter // which is the entity. We omit that first parameter in code gen DomainOperationParameter[] paramInfos = customMethod.Parameters.ToArray(); // Generate <summary> doc comment for the custom method body string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Entity_Custom_Method_Summary_Comment, customMethodName); method.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); // Generate <param> doc comment for all the parameters for (int i = 1; i < paramInfos.Length; ++i) { comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Entity_Custom_Method_Parameter_Comment, paramInfos[i].Name); method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment(paramInfos[i].Name, comment, this.ClientProxyGenerator.IsCSharp)); } // Propagate custom validation attributes from the DomainOperationEntry to this custom method var methodAttributes = customMethod.Attributes.Cast <Attribute>().ToList(); CustomAttributeGenerator.GenerateCustomAttributes( this.ClientProxyGenerator, this._proxyClass, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeMethod, ex.Message, method.Name, this._proxyClass.Name, ex.InnerException.Message), methodAttributes, method.CustomAttributes, method.Comments); // Add [CustomMethod("...")] property var customMethodAttribute = customMethod.OperationAttribute as EntityActionAttribute; bool allowMultipleInvocations = customMethodAttribute != null && customMethodAttribute.AllowMultipleInvocations; method.CustomAttributes.Add( new CodeAttributeDeclaration( CodeGenUtilities.GetTypeReference(TypeConstants.EntityActionAttributeFullName, (string)this._proxyClass.UserData["Namespace"], false), new CodeAttributeArgument(new CodePrimitiveExpression(customMethodName)), new CodeAttributeArgument("AllowMultipleInvocations", new CodePrimitiveExpression(allowMultipleInvocations)) )); // ---------------------------------------------------------------- // generate custom method body: // this.OnMethodNameInvoking(params); // base.Invoke(methodName, params); // this.OnMethodNameInvoked(); // ---------------------------------------------------------------- List <CodeExpression> invokeParams = new List <CodeExpression>(); invokeParams.Add(new CodePrimitiveExpression(customMethodName)); // Create an expression for each parameter, and also use this loop to // propagate the custom attributes for each parameter in the DomainOperationEntry to the custom method. for (int i = 1; i < paramInfos.Length; ++i) { DomainOperationParameter paramInfo = paramInfos[i]; // build up the method parameter signature from the DomainOperationEntry.MethodInfo CodeParameterDeclarationExpression paramDecl = new CodeParameterDeclarationExpression( CodeGenUtilities.GetTypeReference( CodeGenUtilities.TranslateType(paramInfo.ParameterType), this.ClientProxyGenerator, this._proxyClass), paramInfo.Name); // Propagate parameter level validation attributes from custom operation entry. IEnumerable <Attribute> paramAttributes = paramInfo.Attributes.Cast <Attribute>(); string commentHeader = string.Format( CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_Parameter_FailedToGenerate, paramInfo.Name); CustomAttributeGenerator.GenerateCustomAttributes( this.ClientProxyGenerator, this._proxyClass, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeMethodParameter, ex.Message, paramDecl.Name, method.Name, this._proxyClass.Name, ex.InnerException.Message), paramAttributes, paramDecl.CustomAttributes, method.Comments, commentHeader); method.Parameters.Add(paramDecl); // build up the invoke call parameters invokeParams.Add(new CodeVariableReferenceExpression(paramInfo.Name)); } // generate 'OnCustomMethodInvoked/Invoking' string methodInvokingName = customMethodName + "Invoking"; string methodInvokedName = customMethodName + "Invoked"; this._notificationMethodGen.AddMethodFor(methodInvokingName, method.Parameters, null); this._notificationMethodGen.AddMethodFor(methodInvokedName, null); method.Statements.Add(this._notificationMethodGen.GetMethodInvokeExpressionStatementFor(methodInvokingName)); method.Statements.Add( new CodeExpressionStatement( new CodeMethodInvokeExpression( new CodeBaseReferenceExpression(), "InvokeAction", invokeParams.ToArray()))); method.Statements.Add(this._notificationMethodGen.GetMethodInvokeExpressionStatementFor(methodInvokedName)); this._proxyClass.Members.Add(method); // ---------------------------------------------------------------- // generate Is<CustomMethod>Invoked property: // [Display(AutoGenerateField=false)] // public bool IsMyCustomMethodInvoked { get { base.IsActionInvoked(methodName);}} // ---------------------------------------------------------------- CodeMemberProperty invokedProperty = new CodeMemberProperty(); invokedProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final; invokedProperty.HasGet = true; invokedProperty.HasSet = false; invokedProperty.Type = new CodeTypeReference(typeof(bool)); invokedProperty.GetStatements.Add(new CodeMethodReturnStatement( new CodeMethodInvokeExpression( new CodeBaseReferenceExpression(), "IsActionInvoked", new CodeExpression[] { new CodeArgumentReferenceExpression("\"" + customMethodName + "\"") }))); invokedProperty.Name = GetIsInvokedPropertyName(customMethodName); // Generate <summary> doc comment comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Entity_IsInvoked_Property_Summary_Comment, customMethodName); invokedProperty.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); CodeAttributeDeclaration displayAttribute = CodeGenUtilities.CreateDisplayAttributeDeclaration(this.ClientProxyGenerator, this._proxyClass); invokedProperty.CustomAttributes.Add(displayAttribute); this._proxyClass.Members.Add(invokedProperty); }
/// <summary> /// Generates an invoke operation. /// </summary> /// <param name="domainOperationEntry">The invoke operation.</param> /// <param name="invokeKind">the type of invoke method to generate.</param> private void GenerateInvokeOperation(DomainOperationEntry domainOperationEntry, InvokeKind invokeKind) { // Determine the name of the generated invoke function string methodName = domainOperationEntry.Name; // ---------------------------------------------------------------- // Check for name conflicts // ---------------------------------------------------------------- if ((invokeKind == InvokeKind.WithCallback) && this._proxyClass.Members.Cast <CodeTypeMember>().Any(c => c.Name == methodName && c.GetType() != typeof(CodeMemberMethod))) { this.ClientProxyGenerator.LogError( string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_NamingCollision_MemberAlreadyExists, this._proxyClass.Name, methodName)); return; } // ---------------------------------------------------------------- // InvokeResult<T> InvokeOperation(args); // // InvokeResult<T> InvokeOperation(args, callback, userState); // // Task<T> InvokeOperationAsync(args); // ---------------------------------------------------------------- CodeTypeReference operationReturnType = null; Type returnType = CodeGenUtilities.TranslateType(domainOperationEntry.ReturnType); var methodReturnTypeName = (invokeKind == InvokeKind.Async) ? TypeConstants.InvokeResultTypeFullName: TypeConstants.InvokeOperationTypeFullName; CodeTypeReference invokeOperationType = CodeGenUtilities.GetTypeReference(methodReturnTypeName, (string)this._proxyClass.UserData["Namespace"], false); if (returnType != typeof(void)) { // If this is an enum type, we need to ensure it is either shared or // can be generated. Failure to use this enum is only a warning and causes // this invoke operation to be skipped. The test for legality also causes // the enum to be generated if required. Type enumType = TypeUtility.GetNonNullableType(returnType); if (enumType.IsEnum) { string errorMessage = null; if (!this.ClientProxyGenerator.CanExposeEnumType(enumType, out errorMessage)) { this.ClientProxyGenerator.LogError(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Domain_Op_Enum_Error, methodName, this._proxyClass.Name, enumType.FullName, errorMessage)); return; } else { // Register use of this enum type, which could cause deferred generation this.ClientProxyGenerator.RegisterUseOfEnumType(enumType); } } operationReturnType = CodeGenUtilities.GetTypeReference(returnType, this.ClientProxyGenerator, this._proxyClass); operationReturnType.Options |= CodeTypeReferenceOptions.GenericTypeParameter; invokeOperationType.TypeArguments.Add(operationReturnType); } // InvokeResults are wrapped in task (always) if (invokeKind == InvokeKind.Async) { invokeOperationType = new CodeTypeReference(typeof(Task).FullName, invokeOperationType); } CodeMemberMethod method = new CodeMemberMethod() { Attributes = MemberAttributes.Public | MemberAttributes.Final, Name = (invokeKind == InvokeKind.Async) ? (methodName + "Async") : methodName, ReturnType = invokeOperationType, }; this._proxyClass.Members.Add(method); ReadOnlyCollection <DomainOperationParameter> operationParameters = domainOperationEntry.Parameters; // Generate the <summary> doc comments string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_Invoke_Method_Summary_Comment, domainOperationEntry.Name); method.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); // Generate <param> doc comments foreach (DomainOperationParameter parameter in operationParameters) { comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_Invoke_Method_Parameter_Comment, parameter.Name); method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment(parameter.Name, comment, this.ClientProxyGenerator.IsCSharp)); } // Conditionally add the callback and userState <param> doc comments if (invokeKind == InvokeKind.WithCallback) { method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("callback", Resource.CodeGen_DomainContext_Invoke_Method_Callback_Parameter_Comment, this.ClientProxyGenerator.IsCSharp)); method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("userState", Resource.CodeGen_DomainContext_Invoke_Method_UserState_Parameter_Comment, this.ClientProxyGenerator.IsCSharp)); } else if (invokeKind == InvokeKind.Async) { method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("cancellationToken", Resource.CodeGen_DomainContext_Invoke_Method_CancellationToken_Parameter_Comment, this.ClientProxyGenerator.IsCSharp)); } // Generate <returns> doc comments method.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(Resource.CodeGen_DomainContext_Invoke_Returns_Comment, this.ClientProxyGenerator.IsCSharp)); // Propagate custom validation attributes from the DomainOperationEntry to this invoke operation. IEnumerable <Attribute> methodAttributes = domainOperationEntry.Attributes.Cast <Attribute>(); CustomAttributeGenerator.GenerateCustomAttributes( this.ClientProxyGenerator, this._proxyClass, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeMethod, ex.Message, method.Name, this._proxyClass.Name, ex.InnerException.Message), methodAttributes, method.CustomAttributes, method.Comments); // ---------------------------------------------------------------- // generate invoke operation body: // return base.InvokeOperation<T>(methodName, typeof(T), parameters, hasSideEffects, callback, userState); // or return base.InvokeOperationAsync<T>(methodName, parameters, hasSideEffects); // ---------------------------------------------------------------- List <CodeExpression> invokeParams = new List <CodeExpression>(); invokeParams.Add(new CodePrimitiveExpression(methodName)); // add the return Type parameter if (invokeKind != InvokeKind.Async) { invokeParams.Add(new CodeTypeOfExpression(operationReturnType)); } // add any operation parameters CodeVariableReferenceExpression paramsRef = new CodeVariableReferenceExpression("parameters"); if (operationParameters.Count > 0) { // need to generate the user parameters dictionary CodeTypeReference dictionaryTypeReference = CodeGenUtilities.GetTypeReference( typeof(Dictionary <string, object>), this.ClientProxyGenerator, this._proxyClass); CodeVariableDeclarationStatement paramsDef = new CodeVariableDeclarationStatement( dictionaryTypeReference, "parameters", new CodeObjectCreateExpression(dictionaryTypeReference, Array.Empty <CodeExpression>())); method.Statements.Add(paramsDef); } foreach (DomainOperationParameter paramInfo in operationParameters) { // If this is an enum type, we need to ensure it is either shared or // can be generated. Failure to use this enum logs an error and exits. // The test for legality also causes the enum to be generated if required. Type enumType = TypeUtility.GetNonNullableType(paramInfo.ParameterType); if (enumType.IsEnum) { string errorMessage = null; if (!this.ClientProxyGenerator.CanExposeEnumType(enumType, out errorMessage)) { this.ClientProxyGenerator.LogError(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Domain_Op_Enum_Error, method.Name, this._proxyClass.Name, enumType.FullName, errorMessage)); return; } else { // Register use of this enum type, which could cause deferred generation this.ClientProxyGenerator.RegisterUseOfEnumType(enumType); } } // add the parameter to the method CodeParameterDeclarationExpression paramDecl = new CodeParameterDeclarationExpression( CodeGenUtilities.GetTypeReference( CodeGenUtilities.TranslateType(paramInfo.ParameterType), this.ClientProxyGenerator, this._proxyClass), paramInfo.Name); // Propagate parameter level validation attributes from domain operation entry IEnumerable <Attribute> paramAttributes = paramInfo.Attributes.Cast <Attribute>(); string commentHeader = string.Format( CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_Parameter_FailedToGenerate, paramInfo.Name); CustomAttributeGenerator.GenerateCustomAttributes( this.ClientProxyGenerator, this._proxyClass, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeMethodParameter, ex.Message, paramDecl.Name, method.Name, this._proxyClass.Name, ex.InnerException.Message), paramAttributes, paramDecl.CustomAttributes, method.Comments, commentHeader); method.Parameters.Add(paramDecl); // add the parameter and value to the params dictionary method.Statements.Add(new CodeMethodInvokeExpression( new CodeMethodReferenceExpression(paramsRef, "Add"), new CodePrimitiveExpression(paramInfo.Name), new CodeVariableReferenceExpression(paramInfo.Name))); } // add parameters if present if (operationParameters.Count > 0) { invokeParams.Add(paramsRef); } else { invokeParams.Add(new CodePrimitiveExpression(null)); } InvokeAttribute invokeAttribute = (InvokeAttribute)domainOperationEntry.OperationAttribute; invokeParams.Add(new CodePrimitiveExpression(invokeAttribute.HasSideEffects)); switch (invokeKind) { case InvokeKind.WithCallback: { CodeTypeReference callbackType = CodeGenUtilities.GetTypeReference(typeof(Action).FullName, (string)this._proxyClass.UserData["Namespace"], false); callbackType.TypeArguments.Add(invokeOperationType); // add callback method parameter method.Parameters.Add(new CodeParameterDeclarationExpression(callbackType, "callback")); invokeParams.Add(new CodeVariableReferenceExpression("callback")); // add the userState parameter to the end method.Parameters.Add(new CodeParameterDeclarationExpression(CodeGenUtilities.GetTypeReference(typeof(object), this.ClientProxyGenerator, this._proxyClass), "userState")); invokeParams.Add(new CodeVariableReferenceExpression("userState")); } break; case InvokeKind.WithoutCallback: invokeParams.Add(new CodePrimitiveExpression(null)); invokeParams.Add(new CodePrimitiveExpression(null)); break; case InvokeKind.Async: var cancellationTokenType = CodeGenUtilities.GetTypeReference(typeof(CancellationToken), this.ClientProxyGenerator, this._proxyClass); var cancellationTokenParmeter = new CodeParameterDeclarationExpression(cancellationTokenType, "cancellationToken"); // For C# add " = default(CancellationToken)" // For VB add "Optional" ByVal .... = Nothing // otherwise fall back to adding [Optional] attribute, this is the same as adding "= default(CancellationToken)" if (ClientProxyGenerator.IsCSharp) { cancellationTokenParmeter.Name = string.Format("{0} = default({1})", cancellationTokenParmeter.Name, cancellationTokenType.BaseType); } else if (ClientProxyGenerator.IsVB) // VB { // Set an invalid field direction in order to have VB Code gen from generating cancellationTokenParmeter.Direction = (FieldDirection)0xff; cancellationTokenParmeter.Name = "Optional ByVal " + cancellationTokenParmeter.Name; cancellationTokenParmeter.Type = new CodeTypeReference(cancellationTokenType.BaseType + " = Nothing"); } else { // Add [Optional] attribute cancellationTokenParmeter.CustomAttributes.Add(new CodeAttributeDeclaration( CodeGenUtilities.GetTypeReference(typeof(OptionalAttribute), this.ClientProxyGenerator, this._proxyClass))); } method.Parameters.Add(cancellationTokenParmeter); invokeParams.Add(new CodeVariableReferenceExpression("cancellationToken")); break; } // this.ValidateMethod("methodName", parameters); CodeExpression paramsExpr = new CodePrimitiveExpression(null); if (operationParameters.Count > 0) { paramsExpr = paramsRef; } CodeExpressionStatement validateMethodCall = new CodeExpressionStatement( new CodeMethodInvokeExpression( new CodeThisReferenceExpression(), "ValidateMethod", new CodeExpression[] { new CodePrimitiveExpression(methodName), paramsExpr })); method.Statements.Add(validateMethodCall); var invokeMethod = (invokeKind == InvokeKind.Async) ? "InvokeOperationAsync" : "InvokeOperation"; var typeParameters = (domainOperationEntry.ReturnType == typeof(void)) ? Array.Empty <CodeTypeReference>() : new[] { operationReturnType }; var invokeMethodReference = new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), invokeMethod, typeParameters); CodeExpression invokeCall = new CodeMethodInvokeExpression(invokeMethodReference, invokeParams.ToArray()); method.Statements.Add(new CodeMethodReturnStatement(invokeCall)); }
private void GenerateEntityQueryMethod(DomainOperationEntry domainOperationEntry) { string queryMethodName = domainOperationEntry.Name + QuerySuffix; Type entityType = TypeUtility.GetElementType(domainOperationEntry.ReturnType); CodeMemberMethod queryMethod = new CodeMemberMethod(); queryMethod.Name = queryMethodName; queryMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; // Final needed, else becomes virtual queryMethod.ReturnType = CodeGenUtilities.GetTypeReference(TypeConstants.EntityQueryTypeFullName, this._domainServiceDescription.DomainServiceType.Namespace, false); queryMethod.ReturnType.TypeArguments.Add(CodeGenUtilities.GetTypeReference(entityType.FullName, this._domainServiceDescription.DomainServiceType.Namespace, true)); DomainOperationParameter[] domainOperationEntryParameters = domainOperationEntry.Parameters.ToArray(); // Generate <summary> doc comment string comment = string.Format(CultureInfo.CurrentCulture, Resource.EntityCodeGen_ConstructorComments_Summary_DomainContext, entityType.Name, domainOperationEntry.Name); queryMethod.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); // Generate <param> doc comments foreach (DomainOperationParameter paramInfo in domainOperationEntryParameters) { comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Query_Method_Parameter_Comment, paramInfo.Name); queryMethod.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment(paramInfo.Name, comment, this.ClientProxyGenerator.IsCSharp)); } // Generate <returns> doc comments comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Query_Method_Returns_Comment, domainOperationEntry.AssociatedType.Name); queryMethod.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); // Propagate custom validation attributes IEnumerable <Attribute> methodAttributes = domainOperationEntry.Attributes.Cast <Attribute>(); CustomAttributeGenerator.GenerateCustomAttributes( this.ClientProxyGenerator, this._proxyClass, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeMethod, ex.Message, queryMethod.Name, this._proxyClass.Name, ex.InnerException.Message), methodAttributes, queryMethod.CustomAttributes, queryMethod.Comments); // add any domain operation entry parameters first CodeVariableReferenceExpression paramsRef = new CodeVariableReferenceExpression("parameters"); if (domainOperationEntryParameters.Length > 0) { // need to generate the user parameters dictionary CodeTypeReference dictionaryTypeReference = CodeGenUtilities.GetTypeReference( typeof(Dictionary <string, object>), this.ClientProxyGenerator, this._proxyClass); CodeVariableDeclarationStatement paramsDef = new CodeVariableDeclarationStatement( dictionaryTypeReference, "parameters", new CodeObjectCreateExpression(dictionaryTypeReference, new CodeExpression[0])); queryMethod.Statements.Add(paramsDef); } foreach (DomainOperationParameter paramInfo in domainOperationEntryParameters) { CodeParameterDeclarationExpression paramDecl = new CodeParameterDeclarationExpression( CodeGenUtilities.GetTypeReference( CodeGenUtilities.TranslateType(paramInfo.ParameterType), this.ClientProxyGenerator, this._proxyClass), paramInfo.Name); // Propagate parameter level validation attributes IEnumerable <Attribute> paramAttributes = paramInfo.Attributes.Cast <Attribute>(); string commentHeader = string.Format( CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_Parameter_FailedToGenerate, paramInfo.Name); CustomAttributeGenerator.GenerateCustomAttributes( this.ClientProxyGenerator, this._proxyClass, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeMethodParameter, ex.Message, paramDecl.Name, queryMethod.Name, this._proxyClass.Name, ex.InnerException.Message), paramAttributes, paramDecl.CustomAttributes, queryMethod.Comments, commentHeader); // add the parameter to the query method queryMethod.Parameters.Add(paramDecl); // add the parameter and value to the params dictionary queryMethod.Statements.Add(new CodeMethodInvokeExpression( new CodeMethodReferenceExpression(paramsRef, "Add"), new CodePrimitiveExpression(paramInfo.Name), new CodeVariableReferenceExpression(paramInfo.Name))); } // add argument for queryName CodeExpressionCollection arguments = new CodeExpressionCollection(); arguments.Add(new CodePrimitiveExpression(domainOperationEntry.Name)); // add argument for parameters if (domainOperationEntryParameters.Length > 0) { arguments.Add(paramsRef); } else { arguments.Add(new CodePrimitiveExpression(null)); } // add argument for hasSideEffects QueryAttribute queryAttribute = (QueryAttribute)domainOperationEntry.OperationAttribute; arguments.Add(new CodePrimitiveExpression(queryAttribute.HasSideEffects)); // add argument for isComposable arguments.Add(new CodePrimitiveExpression(queryAttribute.IsComposable)); // this.ValidateMethod("methodName", parameters); CodeExpression paramsExpr = new CodePrimitiveExpression(null); if (domainOperationEntryParameters.Length > 0) { paramsExpr = paramsRef; } CodeExpressionStatement validateMethodCall = new CodeExpressionStatement( new CodeMethodInvokeExpression( new CodeThisReferenceExpression(), "ValidateMethod", new CodeExpression[] { new CodePrimitiveExpression(queryMethodName), paramsExpr })); queryMethod.Statements.Add(validateMethodCall); // ---------------------------------------------------------------- // method call: base.CreateQuery(arguments...) // ---------------------------------------------------------------- CodeTypeReference entityTypeRef = CodeGenUtilities.GetTypeReference(entityType.FullName, this._domainServiceDescription.DomainServiceType.Namespace, true); CodeMethodReferenceExpression createQueryMethod = new CodeMethodReferenceExpression(new CodeBaseReferenceExpression(), "CreateQuery", entityTypeRef); CodeMethodReturnStatement createQueryCall = new CodeMethodReturnStatement(new CodeMethodInvokeExpression(createQueryMethod, arguments.Cast <CodeExpression>().ToArray())); queryMethod.Statements.Add(createQueryCall); this._proxyClass.Members.Add(queryMethod); }
/// <summary> /// Generates a domain method on the domain service. /// </summary> /// <param name="domainMethod">The domain method to generate code for.</param> private void GenerateDomainOperationEntry(DomainOperationEntry domainMethod) { // ---------------------------------------------------------------- // Method decl // ---------------------------------------------------------------- CodeMemberMethod method = new CodeMemberMethod(); method.Name = domainMethod.Name; method.Attributes = MemberAttributes.Public | MemberAttributes.Final; // ---------------------------------------------------------------- // generate domain method body: // entity.<methodName>(params); // ---------------------------------------------------------------- List <CodeExpression> invokeParams = new List <CodeExpression>(); // The domain method parameter list is the same as the domain operation entries. DomainOperationParameter[] paramInfos = domainMethod.Parameters.ToArray(); // Generate the <summary> and <param> doc comments string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainClient_Custom_Method_Summary_Comment, domainMethod.Name, domainMethod.AssociatedType.Name); method.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); // Generate <param> doc comment for all the parameters // The first param is the entity comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_Custom_Method_Entity_Parameter_Comment, domainMethod.AssociatedType.Name); method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment(paramInfos[0].Name, comment, this.ClientProxyGenerator.IsCSharp)); // All subsequent params for (int i = 1; i < paramInfos.Length; ++i) { comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_Custom_Method_Parameter_Comment, paramInfos[i].Name); method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment(paramInfos[i].Name, comment, this.ClientProxyGenerator.IsCSharp)); } // Create an expression for each parameter for (int i = 0; i < paramInfos.Length; i++) { DomainOperationParameter paramInfo = paramInfos[i]; // build up the method parameter signature from the DomainOperationEntry.MethodInfo CodeParameterDeclarationExpression paramDecl = new CodeParameterDeclarationExpression( CodeGenUtilities.GetTypeReference( CodeGenUtilities.TranslateType(paramInfo.ParameterType), this.ClientProxyGenerator, this._proxyClass), paramInfo.Name); method.Parameters.Add(paramDecl); // Skip the entity parameter. if (i > 0) { // build up the invoke call parameters invokeParams.Add(new CodeVariableReferenceExpression(paramInfo.Name)); } } method.Statements.Add( new CodeExpressionStatement( new CodeMethodInvokeExpression( new CodeVariableReferenceExpression(paramInfos[0].Name), domainMethod.Name, invokeParams.ToArray()))); this._proxyClass.Members.Add(method); }