/// <summary> /// Generates a custom method. /// </summary> /// <param name="domainMethod">The custom method to be generated.</param> protected virtual void GenerateCustomMethod(DomainOperationEntry domainMethod) { this.Write("public void "); this.Write(this.ToStringHelper.ToStringWithCulture(domainMethod.Name)); this.Write("("); List <string> invokeParams = new List <string>(); DomainOperationParameter[] paramInfos = domainMethod.Parameters.ToArray(); for (int i = 0; i < paramInfos.Length; i++) { DomainOperationParameter paramInfo = paramInfos[i]; string paramTypeName = CodeGenUtilities.GetTypeName(CodeGenUtilities.TranslateType(paramInfo.ParameterType)); this.Write(this.ToStringHelper.ToStringWithCulture(paramTypeName)); this.Write(" "); this.Write(this.ToStringHelper.ToStringWithCulture(CodeGenUtilities.GetSafeName(paramInfo.Name))); if (i + 1 < paramInfos.Length) { this.Write(", "); } if (i > 0) { invokeParams.Add(paramInfo.Name); } } this.Write(")\r\n"); this.GenerateOpeningBrace(); this.Write(this.ToStringHelper.ToStringWithCulture(paramInfos[0].Name)); this.Write("."); this.Write(this.ToStringHelper.ToStringWithCulture(domainMethod.Name)); this.Write("("); for (int i = 0; i < invokeParams.Count; i++) { this.Write(this.ToStringHelper.ToStringWithCulture(invokeParams[i])); if (i + 1 < invokeParams.Count) { this.Write(", "); } } this.Write(");\r\n"); this.GenerateClosingBrace(); }
private string GetEntityQueryMethodElementReturnTypeName(DomainOperationEntry domainOperationEntry) { Type entityType = TypeUtility.GetElementType(domainOperationEntry.ReturnType); return(CodeGenUtilities.GetTypeName(entityType)); }
/// <summary> /// Generates attribute declarations in C#. /// </summary> /// <param name="attributes">The attributes to be generated.</param> /// <param name="forcePropagation">Causes the attributes to be generated even if the attribute verification fails.</param> protected virtual void GenerateAttributes(IEnumerable <Attribute> attributes, bool forcePropagation) { foreach (Attribute attribute in attributes.OrderBy(a => a.GetType().Name)) { AttributeDeclaration attributeDeclaration = AttributeGeneratorHelper.GetAttributeDeclaration(attribute, this.ClientCodeGenerator, forcePropagation); if (attributeDeclaration == null || attributeDeclaration.HasErrors) { continue; } string attributeTypeName = CodeGenUtilities.GetTypeName(attributeDeclaration.AttributeType); this.Write("["); this.Write(this.ToStringHelper.ToStringWithCulture(attributeTypeName)); this.Write("("); if (attributeDeclaration.ConstructorArguments.Count > 0) { for (int i = 0; i < attributeDeclaration.ConstructorArguments.Count; i++) { object value = attributeDeclaration.ConstructorArguments[i]; string stringValue = AttributeGeneratorHelper.ConvertValueToCode(value, true); this.Write(this.ToStringHelper.ToStringWithCulture(stringValue)); if (i + 1 < attributeDeclaration.ConstructorArguments.Count) { this.Write(", "); } } } if (attributeDeclaration.NamedParameters.Count > 0) { if (attributeDeclaration.ConstructorArguments.Count > 0) { this.Write(", "); } for (int i = 0; i < attributeDeclaration.NamedParameters.Count; i++) { KeyValuePair <string, object> pair = attributeDeclaration.NamedParameters.ElementAt(i); string stringValue = AttributeGeneratorHelper.ConvertValueToCode(pair.Value, true); this.Write(this.ToStringHelper.ToStringWithCulture(pair.Key)); this.Write("="); this.Write(this.ToStringHelper.ToStringWithCulture(stringValue)); if (i + 1 < attributeDeclaration.NamedParameters.Count) { this.Write(","); } } } this.Write(")]\r\n"); } }
private void GenerateEnumMembers(Type enumType) { Type underlyingType = enumType.GetEnumUnderlyingType(); string[] memberNames = Enum.GetNames(enumType); Type enumValueType = Enum.GetUnderlyingType(enumType); for (int i = 0; i < memberNames.Length; ++i) { string memberName = memberNames[i]; FieldInfo fieldInfo = enumType.GetField(memberName); this.GenerateEnumMemberAttributes(fieldInfo); if (fieldInfo != null) { object memberValue = fieldInfo.GetRawConstantValue(); object[] minMaxValues = null; CodeGenUtilities.IntegralMinMaxValues.TryGetValue(underlyingType, out minMaxValues); if (minMaxValues != null && !memberValue.Equals(minMaxValues[2]) && memberValue.Equals(minMaxValues[0])) { this.Write(this.ToStringHelper.ToStringWithCulture(memberName)); this.Write(" = "); this.Write(this.ToStringHelper.ToStringWithCulture(CodeGenUtilities.GetTypeName(underlyingType))); this.Write(".MinValue "); } else if (minMaxValues != null && memberValue.Equals(minMaxValues[1])) { this.Write(" \r\n"); this.Write(this.ToStringHelper.ToStringWithCulture(memberName)); this.Write(" = "); this.Write(this.ToStringHelper.ToStringWithCulture(CodeGenUtilities.GetTypeName(underlyingType))); this.Write(".MaxValue "); } else { this.Write(" \r\n"); this.Write(this.ToStringHelper.ToStringWithCulture(memberName)); this.Write(" = "); this.Write(this.ToStringHelper.ToStringWithCulture(memberValue.ToString())); this.Write(" "); } if (i + 1 < memberNames.Length) { this.Write(","); } } } }
/// <summary> /// Generates the client proxy code for the given type. /// </summary> public override void Generate() { // ---------------------------------------------------------------- // namespace // ---------------------------------------------------------------- var ns = ClientProxyGenerator.GetOrGenNamespace(Type); // Missing namespace bails out of code-gen -- error has been logged if (ns == null) { return; } // ---------------------------------------------------------------- // public partial class {Type} : (Base) // ---------------------------------------------------------------- ProxyClass = CodeGenUtilities.CreateTypeDeclaration(Type); ProxyClass.IsPartial = true; // makes this a partial type ProxyClass.TypeAttributes = TypeAttributes.Public; // Abstract classes must be preserved as abstract to avoid explicit instantiation on client bool isAbstract = (Type.IsAbstract); if (isAbstract) { ProxyClass.TypeAttributes |= TypeAttributes.Abstract; } // Determine all types derived from this one. // Note this list does not assume the current type is the visible root. That is a separate test. IEnumerable <Type> derivedTypes = GetDerivedTypes(); // If this type doesn't have any derivatives, seal it. Cannot seal abstracts. if (!isAbstract && !derivedTypes.Any()) { ProxyClass.TypeAttributes |= TypeAttributes.Sealed; } // Add all base types including interfaces AddBaseTypes(ns); ns.Types.Add(ProxyClass); AttributeCollection typeAttributes = Type.Attributes(); // Add <summary> xml comment to class string comment = GetSummaryComment(); ProxyClass.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, ClientProxyGenerator.IsCSharp)); // ---------------------------------------------------------------- // Add default ctr // ---------------------------------------------------------------- CodeConstructor constructor = new CodeConstructor(); // Default ctor is public for concrete types but protected for abstracts. // This prevents direct instantiation on client constructor.Attributes = isAbstract ? MemberAttributes.Family : MemberAttributes.Public; // add default ctor doc comments comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Default_Constructor_Summary_Comments, Type.Name); constructor.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, ClientProxyGenerator.IsCSharp)); // add call to default OnCreated method constructor.Statements.Add(NotificationMethodGen.OnCreatedMethodInvokeExpression); ProxyClass.Members.Add(constructor); // ---------------------------------------------------------------- // [KnownType(...), ...] // ---------------------------------------------------------------- // We need to generate a [KnownType] for all derived entities on the visible root. if (!IsDerivedType) { // Generate a [KnownType] for every derived type. // We specifically exclude [KnownTypes] from the set of attributes we ask // the metadata pipeline to generate below, meaning we take total control // here for which [KnownType] attributes get through the metadata pipeline. // // Note, we sort in alphabetic order to give predictability in baselines and // client readability. For cosmetic reasons, we sort by short or long name // depending on what our utility helpers will actually generated foreach (Type derivedType in derivedTypes.OrderBy(t => ClientProxyGenerator.ClientProxyCodeGenerationOptions.UseFullTypeNames ? t.FullName : t.Name)) { CodeAttributeDeclaration knownTypeAttrib = CodeGenUtilities.CreateAttributeDeclaration(typeof(System.Runtime.Serialization.KnownTypeAttribute), ClientProxyGenerator, ProxyClass); knownTypeAttrib.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(CodeGenUtilities.GetTypeReference(derivedType, ClientProxyGenerator, ProxyClass)))); ProxyClass.CustomAttributes.Add(knownTypeAttrib); } } ValidateTypeAttributes(typeAttributes); // ---------------------------------------------------------------- // [DataContract(Namespace=X, Name=Y)] // ---------------------------------------------------------------- CodeAttributeDeclaration dataContractAttrib = CodeGenUtilities.CreateDataContractAttributeDeclaration(Type, ClientProxyGenerator, ProxyClass); ProxyClass.CustomAttributes.Add(dataContractAttrib); // ---------------------------------------------------------------- // Propagate all type-level Attributes across (except DataContractAttribute since that is handled above) // ----------------------------------------------------------------- CustomAttributeGenerator.GenerateCustomAttributes( ClientProxyGenerator, ProxyClass, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeType, ex.Message, ProxyClass.Name, ex.InnerException.Message), FilterTypeAttributes(typeAttributes), ProxyClass.CustomAttributes, ProxyClass.Comments); // ---------------------------------------------------------------- // gen proxy getter/setter for each property // ---------------------------------------------------------------- GenerateProperties(); // ---------------------------------------------------------------- // gen additional methods/events // ---------------------------------------------------------------- GenerateAdditionalMembers(); // Register created CodeTypeDeclaration with mapping _typeMapping[Type] = ProxyClass; }
/// <summary> /// Generates all of the properties for the type. /// </summary> private void GenerateProperties() { IEnumerable <PropertyDescriptor> properties = TypeDescriptor.GetProperties(Type) .Cast <PropertyDescriptor>() .OrderBy(p => p.Name); foreach (PropertyDescriptor pd in properties) { if (!ShouldDeclareProperty(pd)) { continue; } // Generate a property getter/setter pair for every property whose type // we support. Non supported property types will be skipped. if (CanGenerateProperty(pd)) { // Ensure the property is not virtual, abstract or new // If there is a violation, we log the error and keep // running to accumulate all such errors. This function // may return an "okay" for non-error case polymorphics. if (!CanGeneratePropertyIfPolymorphic(pd)) { continue; } if (!GenerateNonSerializableProperty(pd)) { Type propType = CodeGenUtilities.TranslateType(pd.PropertyType); List <Type> typesToCodeGen = new List <Type>(); bool isTypeSafeToGenerate = true; // Create a list containing the types we will require on the client if (TypeUtility.IsPredefinedDictionaryType(propType)) { typesToCodeGen.AddRange(CodeGenUtilities.GetDictionaryGenericArgumentTypes(propType)); } else { typesToCodeGen.Add(TypeUtility.GetElementType(propType)); } // We consider all predefined types as legal to code-gen *except* those // that would generate a compile error on the client due to missing reference. // We treat "don't know" and "false" as grounds for a warning. // Note that we do this *after* TranslateType so that types like System.Data.Linq.Binary // which cannot exist on the client anyway has been translated foreach (Type type in typesToCodeGen) { // Enum (and nullable<enum>) types may require generation on client Type nonNullableType = TypeUtility.GetNonNullableType(type); if (nonNullableType.IsEnum) { // Register use of this enum type, which could cause deferred generation ClientProxyGenerator.RegisterUseOfEnumType(nonNullableType); } // If this is not an enum or nullable<enum> and we're not generating the complex type, determine whether this // property type is visible to the client. If it is not, log a warning. else { // "Don't know" counts as "no" CodeMemberShareKind enumShareKind = this.ClientProxyGenerator.GetTypeShareKind(nonNullableType); if ((enumShareKind & CodeMemberShareKind.Shared) == 0) { this.ClientProxyGenerator.LogWarning(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_PropertyType_Not_Shared, pd.Name, this.Type.FullName, type.FullName, this.ClientProxyGenerator.ClientProjectName)); isTypeSafeToGenerate = false; // Flag error but continue to allow accumulation of additional errors. } } } if (isTypeSafeToGenerate) { // Generate OnMethodXxxChanging/Changed partial methods. // Note: the parameter type reference needs to handle the possibility the // property type is defined in the project's root namespace and that VB prepends // that namespace. The utility helper gives us the right type reference. CodeTypeReference parameterTypeRef = CodeGenUtilities.GetTypeReference(propType, ClientProxyGenerator, ProxyClass); NotificationMethodGen.AddMethodFor(pd.Name + "Changing", new CodeParameterDeclarationExpression(parameterTypeRef, "value"), null); NotificationMethodGen.AddMethodFor(pd.Name + "Changed", null); GenerateProperty(pd); } } } else { OnPropertySkipped(pd); } } }
/// <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, ClientProxyGenerator, 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, ClientProxyGenerator.IsCSharp)); // ---------------------------------------------------------------- // [DataMember] -> Add if not already present. // ---------------------------------------------------------------- // Add if not already present. if (!propertyAttributes.OfType <DataMemberAttribute>().Any()) { CodeAttributeDeclaration dataMemberAtt = CodeGenUtilities.CreateAttributeDeclaration(typeof(DataMemberAttribute), ClientProxyGenerator, 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 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(ClientProxyGenerator, ProxyClass); property.CustomAttributes.Add(displayAttribute); } // ---------------------------------------------------------------- // Propagate the custom attributes // ---------------------------------------------------------------- CustomAttributeGenerator.GenerateCustomAttributes( ClientProxyGenerator, ProxyClass, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeTypeMember, ex.Message, property.Name, 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); 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(NotificationMethodGen.GetMethodInvokeExpressionStatementFor(propertyName + "Changing")); bool propertyIsReadOnly = 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(NotificationMethodGen.GetMethodInvokeExpressionStatementFor(propertyName + "Changed")); // if (this._field != value)... CodeExpression valueTest = CodeGenUtilities.MakeNotEqual(propertyType, fieldRef, valueRef, ClientProxyGenerator.IsCSharp); CodeConditionStatement body = new CodeConditionStatement(valueTest, bodyStatements.ToArray <CodeStatement>()); property.SetStatements.Add(body); // add property ProxyClass.Members.Add(property); }