/// <summary> /// Generates a custom method guard property /// </summary> /// <param name="customMethodName">name of the custom method to generate guard property for</param> private void GenerateGuardProperty(string customMethodName) { string guardName = GetCanInvokePropertyName(customMethodName); this.ClientProxyGenerator.LogMessage(string.Format(CultureInfo.CurrentCulture, Resource.EntityCodeGen_Generating_GuardProperty, guardName)); // ---------------------------------------------------------------- // Property decl: // [Display(AutoGenerateField=false)] // public bool CanMyCustomMethod // ---------------------------------------------------------------- CodeMemberProperty property = new CodeMemberProperty(); property.Name = guardName; property.Type = CodeGenUtilities.GetTypeReference(typeof(bool), this.ClientProxyGenerator, this._proxyClass); property.Attributes = MemberAttributes.Public | MemberAttributes.Final; // final needed, else becomes virtual // Generate <summary> doc comment string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Entity_CanInvoke_Property_Summary_Comment, customMethodName); property.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); CodeAttributeDeclaration displayAttribute = CodeGenUtilities.CreateDisplayAttributeDeclaration(this.ClientProxyGenerator, this._proxyClass); property.CustomAttributes.Add(displayAttribute); // ---------------------------------------------------------------- // get // { // return base.CanInvoke("XXX"); // } // ---------------------------------------------------------------- property.GetStatements.Add( new CodeMethodReturnStatement( new CodeMethodInvokeExpression( new CodeBaseReferenceExpression(), "CanInvokeAction", new CodePrimitiveExpression(customMethodName)))); this._proxyClass.Members.Add(property); }
/// <summary> /// Generates a property getter/setter pair into the given proxy class to match the given property info. /// </summary> /// <param name="propertyDescriptor">PropertyDescriptor for the property to generate for.</param> protected virtual void GenerateProperty(PropertyDescriptor propertyDescriptor) { string propertyName = propertyDescriptor.Name; Type propertyType = CodeGenUtilities.TranslateType(propertyDescriptor.PropertyType); // ---------------------------------------------------------------- // Property type ref // ---------------------------------------------------------------- var propTypeReference = CodeGenUtilities.GetTypeReference(propertyType, this.ClientProxyGenerator, this.ProxyClass); // ---------------------------------------------------------------- // Property decl // ---------------------------------------------------------------- var property = new CodeMemberProperty(); property.Name = propertyName; property.Type = propTypeReference; property.Attributes = MemberAttributes.Public | MemberAttributes.Final; // final needed, else becomes virtual List <Attribute> propertyAttributes = propertyDescriptor.ExplicitAttributes().Cast <Attribute>().ToList(); // Generate <summary> for property string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Entity_Property_Summary_Comment, propertyName); property.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); // ---------------------------------------------------------------- // [DataMember] -> Add if not already present. // ---------------------------------------------------------------- // Add if not already present. if (!propertyAttributes.OfType <DataMemberAttribute>().Any()) { CodeAttributeDeclaration dataMemberAtt = CodeGenUtilities.CreateAttributeDeclaration(typeof(DataMemberAttribute), this.ClientProxyGenerator, this.ProxyClass); property.CustomAttributes.Add(dataMemberAtt); } // Here, we check for the existence of [ReadOnly(true)] attributes generated when // the property does not not have a setter. We want to inject an [Editable(false)] // attribute into the pipeline. ReadOnlyAttribute readOnlyAttr = propertyAttributes.OfType <ReadOnlyAttribute>().SingleOrDefault(); if (readOnlyAttr != null && !propertyAttributes.OfType <EditableAttribute>().Any()) { propertyAttributes.Add(new EditableAttribute(!readOnlyAttr.IsReadOnly)); // REVIEW: should we strip out [ReadOnly] attributes here? } // Here we check if the type has a RoundtripOriginalAttribute. In that case we strip it out from the property if (this._isRoundtripType) { propertyAttributes.RemoveAll(attr => attr.GetType() == typeof(RoundtripOriginalAttribute)); } // Here we check for database generated fields. In that case we strip any RequiredAttribute from the property. if (propertyAttributes.Any(a => a.GetType().Name == "DatabaseGeneratedAttribute")) { propertyAttributes.RemoveAll(attr => attr.GetType() == typeof(RequiredAttribute)); } // Here, we check for the presence of a complex type. If it exists we need to add a DisplayAttribute // if not already there. DataSources windows do not handle complex types if (TypeUtility.IsSupportedComplexType(propertyType) && !propertyAttributes.OfType <DisplayAttribute>().Any()) { CodeAttributeDeclaration displayAttribute = CodeGenUtilities.CreateDisplayAttributeDeclaration(this.ClientProxyGenerator, this.ProxyClass); property.CustomAttributes.Add(displayAttribute); } // ---------------------------------------------------------------- // Propagate the custom attributes // ---------------------------------------------------------------- CustomAttributeGenerator.GenerateCustomAttributes( this.ClientProxyGenerator, this.ProxyClass, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeTypeMember, ex.Message, property.Name, this.ProxyClass.Name, ex.InnerException.Message), propertyAttributes.Cast <Attribute>(), property.CustomAttributes, property.Comments); // ---------------------------------------------------------------- // backing private field (CodeDom doesn't yet know about auto properties) // ---------------------------------------------------------------- string fieldName = CodeGenUtilities.MakeCompliantFieldName(propertyName); var field = new CodeMemberField(propTypeReference, fieldName); this.ProxyClass.Members.Add(field); var fieldRef = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName); var valueRef = new CodePropertySetValueReferenceExpression(); // ---------------------------------------------------------------- // getter body // ---------------------------------------------------------------- property.GetStatements.Add(new CodeMethodReturnStatement(fieldRef)); // ---------------------------------------------------------------- // setter body // ---------------------------------------------------------------- List <CodeStatement> bodyStatements = new List <CodeStatement>(); // this.OnPropertyXxxChanging(PropType value); bodyStatements.Add(this.NotificationMethodGen.GetMethodInvokeExpressionStatementFor(propertyName + "Changing")); bool propertyIsReadOnly = this.IsPropertyReadOnly(propertyDescriptor); if (!propertyIsReadOnly) { bodyStatements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "RaiseDataMemberChanging", new CodePrimitiveExpression(propertyDescriptor.Name)))); } // Generate the validation tests. CodeStatement validationCode = GeneratePropertySetterValidation(propertyDescriptor.Name); bodyStatements.Add(validationCode); // this._field = value bodyStatements.Add(new CodeAssignStatement(fieldRef, valueRef)); if (!propertyIsReadOnly) { bodyStatements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "RaiseDataMemberChanged", new CodePrimitiveExpression(propertyDescriptor.Name)))); } else { // even read-only members need to raise PropertyChanged bodyStatements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "RaisePropertyChanged", new CodePrimitiveExpression(propertyDescriptor.Name)))); } // this.OnPropertyXxxChanged(); bodyStatements.Add(this.NotificationMethodGen.GetMethodInvokeExpressionStatementFor(propertyName + "Changed")); // if (this._field != value)... CodeExpression valueTest = CodeGenUtilities.MakeNotEqual(propertyType, fieldRef, valueRef, this.ClientProxyGenerator.IsCSharp); CodeConditionStatement body = new CodeConditionStatement(valueTest, bodyStatements.ToArray <CodeStatement>()); property.SetStatements.Add(body); // add property this.ProxyClass.Members.Add(property); }
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); }