/// <summary> /// Generates the "EntityContainer" for all the given entities /// </summary> /// <remarks> /// The EntityContainer is the logical store of the entity instances, and we have to generate /// code to instantiate entries in this store /// </remarks> /// <param name="proxyClass">Code into which to generate code</param> /// <param name="entityTypes">Set of all known entity types for which we need storage</param> /// <param name="domainServiceDescription">The DomainServiceDescription we're code genning for</param> private void GenEntityContainer(CodeTypeDeclaration proxyClass, IEnumerable <Type> entityTypes, DomainServiceDescription domainServiceDescription) { // ---------------------------------------------------------------- // inner class // ---------------------------------------------------------------- var innerClass = this.GenEntityContainerInnerClass(proxyClass, entityTypes, domainServiceDescription); // ---------------------------------------------------------------- // method decl: protected override EntityContainer CreateEntityContainer() // ---------------------------------------------------------------- var method = new CodeMemberMethod(); method.Name = "CreateEntityContainer"; method.Attributes = MemberAttributes.Family | MemberAttributes.Override; method.ReturnType = CodeGenUtilities.GetTypeReference(TypeConstants.EntityContainerTypeFullName, proxyClass.UserData["Namespace"] as string, false); // Add <summary> and <returns> doc comments method.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(Resource.CodeGen_CreateEntityContainer_Method_Summary_Comment, this.ClientProxyGenerator.IsCSharp)); method.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(Resource.CodeGen_CreateEntityContainer_Method_Returns_Comment, this.ClientProxyGenerator.IsCSharp)); // ---------------------------------------------------------------- // method body: return new 'innerClass' // ---------------------------------------------------------------- var innerClassReference = CodeGenUtilities.GetTypeReference(proxyClass.UserData["Namespace"] as string + "." + proxyClass.Name + "." + innerClass.Name, proxyClass.UserData["Namespace"] as string, true); var newInnerClassExpr = new CodeObjectCreateExpression(innerClassReference, new CodeExpression[0]); var returnStatement = new CodeMethodReturnStatement(newInnerClassExpr); method.Statements.Add(returnStatement); proxyClass.Members.Add(method); }
/// <summary> /// Generates the property getter for the given entity type from the given domain operation entry /// </summary> /// <param name="entityType">The type of the entity being exposed</param> private void GenerateEntitySet(Type entityType) { string propertyName = Naming.MakePluralName(entityType.Name); // Check for name conflicts if (this._proxyClass.Members.Cast <CodeTypeMember>().Any(c => c.Name == propertyName)) { this.ClientProxyGenerator.LogError( string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_NamingCollision_MemberAlreadyExists, this._proxyClass.Name, propertyName)); return; } // Generate the generic parameter list for the entity type (e.g. <Customer> var entityTypeRef = CodeGenUtilities.GetTypeReference(entityType, this.ClientProxyGenerator, this._proxyClass); var genericParameters = new CodeTypeReference[] { entityTypeRef }; // ---------------------------------------------------------------- // EntitySet<entityType> generic type reference for return type of property // ---------------------------------------------------------------- var returnType = CodeGenUtilities.GetTypeReference(TypeConstants.EntitySetTypeFullName, entityType.Namespace, false); returnType.TypeArguments.AddRange(genericParameters); // ---------------------------------------------------------------- // this.EntityContainer property reference (Entities will be defined later) // ---------------------------------------------------------------- var entityContainerProperty = new CodePropertyReferenceExpression(new CodeBaseReferenceExpression(), "EntityContainer"); // ---------------------------------------------------------------- // this.EntityContainer.GetEntitySet<entityType>() // ---------------------------------------------------------------- var methodRef = new CodeMethodReferenceExpression(entityContainerProperty, "GetEntitySet", genericParameters); var methodCall = new CodeMethodInvokeExpression(methodRef, new CodeExpression[0]); // ---------------------------------------------------------------- // return this.EntityContainer.GetEntitySet<entityType>() // ---------------------------------------------------------------- var returnStmt = new CodeMethodReturnStatement(methodCall); // ---------------------------------------------------------------- // Property getter // ---------------------------------------------------------------- var property = new CodeMemberProperty(); property.Name = propertyName; property.Type = returnType; property.Attributes = MemberAttributes.Public | MemberAttributes.Final; // Final needed, else becomes virtual property.GetStatements.Add(returnStmt); this._proxyClass.Members.Add(property); // Add the <summary> doc comment property.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_EntitySet_Property_Summary_Comment, entityType.Name, this._proxyClass.Name), this.ClientProxyGenerator.IsCSharp)); }
/// <summary> /// Creates the CodeDom CodeExpression for the given value. Returns null if unable to generate a CodeExpression. /// </summary> /// <remarks>This method exists solely to help generate code for all object types that can appear in an /// attribute declaration, such as typeof()</remarks> /// <param name="proxyGenerator">The context for generating code. It cannot be null.</param> /// <param name="referencingType">The referencing type</param> /// <param name="value">The value. Null is permitted.</param> /// <returns>The code expression</returns> private static CodeExpression CreateCodeExpression(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, object value) { Type typeOfValue = value == null ? null : value.GetType(); if (value == null || typeOfValue.IsPrimitive || value is string) { CodeExpression e = new CodePrimitiveExpression(value); // Workaround CodeDom issue -- it looks like CodePrimitiveExpression is fooled and generates double // literals as integers when there is no fraction. We take a general strategy of forcing an explicit // compile time cast to ensure we recompile the same type. if (value != null && (value is double || value is float)) { e = new CodeCastExpression(value.GetType(), e); } return(e); } // typeof(T) requires special handling Type valueAsType = value as Type; if (valueAsType != null) { // Verify the type is shared // Don't know counts as not shared CodeMemberShareKind shareKind = proxyGenerator.GetTypeShareKind(valueAsType); if ((shareKind & CodeMemberShareKind.Shared) == 0) { // Here we return a fully-qualified type name to ensure we don't cause compilation // errors by adding invalid 'using' statements into our codedom graph. CodeTypeReference valueTypeReference = CodeGenUtilities.GetTypeReference(valueAsType, proxyGenerator, referencingType, false, /*Use fully qualified name*/ true); valueTypeReference.Options = CodeTypeReferenceOptions.GlobalReference; return(new CodeTypeOfExpression(valueTypeReference)); } return(new CodeTypeOfExpression(CodeGenUtilities.GetTypeReference(valueAsType, proxyGenerator, referencingType))); } // Enum values need special handling if (typeOfValue.IsEnum) { string enumValueName = Enum.GetName(typeOfValue, value); string enumTypeName; if (proxyGenerator.ClientProxyCodeGenerationOptions.UseFullTypeNames) { enumTypeName = typeOfValue.FullName; } else { enumTypeName = typeOfValue.Name; } return(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(enumTypeName), enumValueName)); } return(null); }
/// <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); }
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")); }
/// <summary> /// Generates the client proxy code for a domain service. /// </summary> public override void Generate() { // ---------------------------------------------------------------- // Namespace // ---------------------------------------------------------------- Type domainServiceType = this._domainServiceDescription.DomainServiceType; CodeNamespace ns = this.ClientProxyGenerator.GetOrGenNamespace(domainServiceType); AttributeCollection attributes = this._domainServiceDescription.Attributes; // Missing namespace bails out of code-gen -- error has been logged if (ns == null) { return; } // ---------------------------------------------------------------- // public partial sealed class {Name} : DomainContext // ---------------------------------------------------------------- string clientTypeName = DomainContextTypeName(this._domainServiceDescription); CodeTypeDeclaration proxyClass = CodeGenUtilities.CreateTypeDeclaration(clientTypeName, domainServiceType.Namespace); proxyClass.IsPartial = true; proxyClass.TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed; ns.Types.Add(proxyClass); CodeTypeReference domainContextTypeName = CodeGenUtilities.GetTypeReference(TypeConstants.DomainContextTypeFullName, ns.Name, false); proxyClass.BaseTypes.Add(domainContextTypeName); // Add <summary> xml comment to class string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_Class_Summary_Comment, domainServiceType.Name); proxyClass.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); // ---------------------------------------------------------------- // [DomainIdentifier], etc attributes move through metadata pipeline // ---------------------------------------------------------------- CustomAttributeGenerator.GenerateCustomAttributes( this.ClientProxyGenerator, proxyClass, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeType, ex.Message, proxyClass.Name, ex.InnerException.Message), attributes.Cast <Attribute>(), proxyClass.CustomAttributes, proxyClass.Comments); // ---------------------------------------------------------------- // Add default OnCreated partial method // ---------------------------------------------------------------- NotificationMethodGenerator notificationMethodGen = new NotificationMethodGenerator(this.ClientProxyGenerator); proxyClass.Members.AddRange(notificationMethodGen.PartialMethodsSnippetBlock); // ---------------------------------------------------------------- // Generate a contract interface for the service. // ---------------------------------------------------------------- CodeTypeDeclaration contractInterface = this.GenerateContract(proxyClass); // ---------------------------------------------------------------- // Generate constructors // ---------------------------------------------------------------- EnableClientAccessAttribute enableClientAccessAttribute = attributes.OfType <EnableClientAccessAttribute>().Single(); this.GenerateConstructors(proxyClass, contractInterface, enableClientAccessAttribute, notificationMethodGen.OnCreatedMethodInvokeExpression); // ---------------------------------------------------------------- // Separate proxies for each domain operation entry // ---------------------------------------------------------------- DomainOperationEntryProxyGenerator methodProxyGenerator = new DomainOperationEntryProxyGenerator(this.ClientProxyGenerator, proxyClass, this._domainServiceDescription); methodProxyGenerator.Generate(); // ---------------------------------------------------------------- // Invoke operations // ---------------------------------------------------------------- InvokeOperationProxyGenerator invokeOperationProxyGenerator = new InvokeOperationProxyGenerator(this.ClientProxyGenerator, proxyClass, this._domainServiceDescription); invokeOperationProxyGenerator.Generate(); // ---------------------------------------------------------------- // EntityContainer instantiation // ---------------------------------------------------------------- // The entity container holds a collection of EntityLists, one per visible entity root type. // The derived entity types are stored in their respective root's list and do not get their own. this.GenEntityContainer(proxyClass, this._domainServiceDescription.RootEntityTypes, this._domainServiceDescription); // Register created CodeTypeDeclaration with mapping this._typeMapping[domainServiceType] = proxyClass; }
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); }
//// TODO: we need to refactor this to comply with FxCop private CodeTypeDeclaration GenEntityContainerInnerClass(CodeTypeDeclaration proxyClass, IEnumerable <Type> entityTypes, DomainServiceDescription domainServiceDescription) { // ---------------------------------------------------------------- // class xxxEntityContainer : EntityContainer // ---------------------------------------------------------------- string containingNamespace = this.ClientProxyGenerator.GetNamespace(proxyClass).Name; var innerClass = CodeGenUtilities.CreateTypeDeclaration(proxyClass.Name + "EntityContainer", containingNamespace); innerClass.BaseTypes.Add(CodeGenUtilities.GetTypeReference(TypeConstants.EntityContainerTypeFullName, containingNamespace, false)); innerClass.TypeAttributes = TypeAttributes.NotPublic | TypeAttributes.Sealed; proxyClass.Members.Add(innerClass); // ---------------------------------------------------------------- // ctor // ---------------------------------------------------------------- var ctor = new CodeConstructor(); ctor.Attributes = MemberAttributes.Public; innerClass.Members.Add(ctor); // Convert to a set for faster lookups. HashSet <Type> entityTypesToUse = new HashSet <Type>(); foreach (Type entityType in entityTypes) { entityTypesToUse.Add(entityType); } // ---------------------------------------------------------------- // each entity type gets 'CreateEntitySet<entityType>()' statement in ctor // ---------------------------------------------------------------- foreach (Type entityType in entityTypes.OrderBy(t => t.FullName)) { // Skip entity types which have base classes. if (entityTypesToUse.Any(t => t != entityType && t.IsAssignableFrom(entityType))) { continue; } // ---------------------------------------------------------------- // Build EntitySetOperations enum value // ---------------------------------------------------------------- var enumTypeReference = CodeGenUtilities.GetTypeReference(TypeConstants.EntitySetOperationsTypeFullName, containingNamespace, false); CodeExpression entitySetOperations = null; // Check to see what update operations are supported, and build up the EntitySetOperations flags expression bool canInsert = domainServiceDescription.IsOperationSupported(entityType, DomainOperation.Insert); bool canEdit = domainServiceDescription.IsOperationSupported(entityType, DomainOperation.Update); bool canDelete = domainServiceDescription.IsOperationSupported(entityType, DomainOperation.Delete); CodeTypeReferenceExpression enumTypeReferenceExp = new CodeTypeReferenceExpression(enumTypeReference); if (!canInsert && !canEdit && !canDelete) { // if no update operations are supported, set to 'None' entitySetOperations = new CodeFieldReferenceExpression(enumTypeReferenceExp, "None"); } else if (canInsert && canEdit && canDelete) { // if all operations are supported, set to 'All' entitySetOperations = new CodeFieldReferenceExpression(enumTypeReferenceExp, "All"); } else { if (canInsert) { entitySetOperations = new CodeFieldReferenceExpression(enumTypeReferenceExp, "Add"); } if (canEdit) { CodeFieldReferenceExpression setOp = new CodeFieldReferenceExpression(enumTypeReferenceExp, "Edit"); if (entitySetOperations == null) { entitySetOperations = setOp; } else { entitySetOperations = new CodeBinaryOperatorExpression(entitySetOperations, CodeBinaryOperatorType.BitwiseOr, setOp); } } if (canDelete) { CodeFieldReferenceExpression setOp = new CodeFieldReferenceExpression(enumTypeReferenceExp, "Remove"); if (entitySetOperations == null) { entitySetOperations = setOp; } else { entitySetOperations = new CodeBinaryOperatorExpression(entitySetOperations, CodeBinaryOperatorType.BitwiseOr, setOp); } } } // ---------------------------------------------------------------- // method call: this.CreateEntitySet<entityType> // ---------------------------------------------------------------- var entityTypeReference = CodeGenUtilities.GetTypeReference(entityType, this.ClientProxyGenerator, proxyClass); var methodRef = new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), "CreateEntitySet", entityTypeReference); var methodCall = new CodeMethodInvokeExpression(methodRef, entitySetOperations); ctor.Statements.Add(methodCall); } return(innerClass); }
/// <summary> /// Generates all of the properties for the type. /// </summary> private void GenerateProperties() { IEnumerable <PropertyDescriptor> properties = TypeDescriptor.GetProperties(this.Type) .Cast <PropertyDescriptor>() .OrderBy(p => p.Name); foreach (PropertyDescriptor pd in properties) { if (!this.ShouldDeclareProperty(pd)) { continue; } // Generate a property getter/setter pair for every property whose type // we support. Non supported property types will be skipped. if (this.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 (!this.CanGeneratePropertyIfPolymorphic(pd)) { continue; } if (!this.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 this.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 if (!this.ComplexTypes.Contains(type)) { // "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, this.ClientProxyGenerator, this.ProxyClass); this.NotificationMethodGen.AddMethodFor(pd.Name + "Changing", new CodeParameterDeclarationExpression(parameterTypeRef, "value"), null); this.NotificationMethodGen.AddMethodFor(pd.Name + "Changed", null); this.GenerateProperty(pd); } } } else { this.OnPropertySkipped(pd); } } }
protected override void AddBaseTypes(CodeNamespace ns) { this.ProxyClass.BaseTypes.Add(CodeGenUtilities.GetTypeReference(TypeConstants.ComplexObjectTypeFullName, ns.Name, false)); }
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> /// Creates a new <see cref="CodeTypeDeclaration"/> that is the generated form of /// the given <paramref name="enumType"/>. /// </summary> /// <param name="enumType">The enum type to generate.</param> /// <param name="codeGenerator">The current proxy generator context.</param> /// <returns>The newly generated enum type declaration.</returns> internal static CodeTypeDeclaration CreateEnumTypeDeclaration(Type enumType, CodeDomClientCodeGenerator codeGenerator) { System.Diagnostics.Debug.Assert(enumType.IsEnum, "Type must be an enum type"); CodeTypeDeclaration typeDecl = CodeGenUtilities.CreateTypeDeclaration(enumType); typeDecl.IsEnum = true; // Always force generated enums to be public typeDecl.TypeAttributes |= TypeAttributes.Public; // Enums deriving from anything but int get an explicit base type Type underlyingType = enumType.GetEnumUnderlyingType(); if (underlyingType != typeof(int)) { typeDecl.BaseTypes.Add(new CodeTypeReference(underlyingType)); } typeDecl.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment($"Enum {enumType.Name}", codeGenerator.IsCSharp)); // Generate [DataContract] if it appears in the original only. Use Reflection only because that matches // what WCF will do. DataContractAttribute dataContractAttr = (DataContractAttribute)Attribute.GetCustomAttribute(enumType, typeof(DataContractAttribute)); if (dataContractAttr != null) { CodeAttributeDeclaration attrDecl = CodeGenUtilities.CreateDataContractAttributeDeclaration(enumType, codeGenerator, typeDecl); typeDecl.CustomAttributes.Add(attrDecl); } string[] memberNames = Enum.GetNames(enumType); Type enumValueType = Enum.GetUnderlyingType(enumType); for (int i = 0; i < memberNames.Length; ++i) { string memberName = memberNames[i]; CodeTypeReference enumTypeRef = CodeGenUtilities.GetTypeReference(enumValueType, codeGenerator, typeDecl); CodeMemberField enumMember = new CodeMemberField(enumTypeRef, memberName); enumMember.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(memberName, codeGenerator.IsCSharp)); // Generate an initializer for the enum member. // GetRawConstantValue is the safest way to get the raw value of the enum field // and works for both Reflection and ReflectionOnly loaded assemblies. FieldInfo fieldInfo = enumType.GetField(memberName); if (fieldInfo != null) { object memberValue = fieldInfo.GetRawConstantValue(); Debug.Assert(memberValue != null, "Enum type's GetRawConstantValue should never return null"); // We special-case MinValue and MaxValue for the integral types // because VisualBasic will generate overflow compiler error for // Int64.MinValue. If we detect a known MinValue or MaxValue for // this integral type, we generate that reference, otherwise we // just generate a constant integral value of the enum's type object[] minMaxValues = null; CodeGenUtilities.integralMinMaxValues.TryGetValue(underlyingType, out minMaxValues); Debug.Assert(minMaxValues == null || minMaxValues.Length == 3, "integralMinMaxValues elements must always contain 3 values"); // Gen xxx.MinValue if it matches, but give precedence to matching a true zero, // which is the min value for the unsigned integral types // minMaxValues[0]: the MinValue for this type // minMaxValues[1]: the MaxValue for this type // minMaxValues[2]: the zero for this type (memberValue is not boxed and cannot be cast) if (minMaxValues != null && !memberValue.Equals(minMaxValues[2]) && memberValue.Equals(minMaxValues[0])) { enumMember.InitExpression = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(underlyingType), "MinValue"); } // Gen xxx.MaxValue if it matches else if (minMaxValues != null && memberValue.Equals(minMaxValues[1])) { enumMember.InitExpression = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(underlyingType), "MaxValue"); } // All other cases generate an integral constant. // CodeDom knows how to generate the right integral constant based on memberValue's type. else { enumMember.InitExpression = new CodePrimitiveExpression(memberValue); } } typeDecl.Members.Add(enumMember); // Generate an [EnumMember] if appropriate EnumMemberAttribute enumMemberAttr = (EnumMemberAttribute)Attribute.GetCustomAttribute(fieldInfo, typeof(EnumMemberAttribute)); if (enumMemberAttr != null) { CodeAttributeDeclaration enumAttrDecl = CodeGenUtilities.CreateEnumMemberAttributeDeclaration(fieldInfo, codeGenerator, typeDecl); enumMember.CustomAttributes.Add(enumAttrDecl); } // Propagate any other attributes that can be seen by the client CustomAttributeGenerator.GenerateCustomAttributes( codeGenerator, typeDecl, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeTypeMember, ex.Message, fieldInfo.Name, typeDecl.Name, ex.InnerException.Message), fieldInfo.GetCustomAttributes(false).Cast <Attribute>().Where(a => a.GetType() != typeof(EnumMemberAttribute)), enumMember.CustomAttributes, enumMember.Comments); } // Attributes marked with [Flag] propagate it if (enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0) { CodeAttributeDeclaration attrDecl = CodeGenUtilities.CreateAttributeDeclaration(typeof(FlagsAttribute), codeGenerator, typeDecl); typeDecl.CustomAttributes.Add(attrDecl); } return(typeDecl); }
/// <summary> /// Gets a <see cref="CodeTypeReference"/> for a CLR type. /// </summary> /// <param name="type">A CLR type.</param> /// <param name="codeGenerator">A <see cref="CodeDomClientCodeGenerator"/>.</param> /// <param name="referencingType">The referencing type.</param> /// <param name="optimizeAttributeName">Indicates whether or not to optimize <see cref="Attribute"/> names by removing the "Attribute" suffix.</param> /// <param name="forceUseFullyQualifiedName">Indicates whether or not to generate the type using the fully qualified name irrespective the global setting.</param> /// <returns>A <see cref="CodeTypeReference"/> for a CLR type.</returns> internal static CodeTypeReference GetTypeReference(Type type, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType, bool optimizeAttributeName, bool forceUseFullyQualifiedName) { string typeName = type.Name; string typeNamespace = type.Namespace; // Add an import statement to the referencing type if needed CodeNamespace ns = codeGenerator.GetNamespace(referencingType); CodeTypeReference codeTypeReference = null; // Attribute? If so, we special case these and remove the 'Attribute' suffix if present. if (optimizeAttributeName) { typeName = OptimizeAttributeName(type); } // Determine if we should generate this type with a full type name bool useFullyQualifiedName = forceUseFullyQualifiedName || CodeGenUtilities.useFullTypeNames || RegisterTypeName(typeNamespace, typeName, ns.Name); // Make sure we take into account root namespace in VB codegen. typeNamespace = TranslateNamespace(type, codeGenerator); // Conditionally add an import statement. Skip this step if we need to generate a full // type name, if we're already in the target namespace, or if the type is in the global namespace. if (!useFullyQualifiedName && !ns.Name.Equals(type.Namespace) && !string.IsNullOrEmpty(type.Namespace)) { // If the namespace is already imported, the following line will be a no-op. ns.Imports.Add(new CodeNamespaceImport(typeNamespace)); } // If forced using Fully Qualified names, dont look up or store the code reference in the cache. That is because, // we force the use of fully qualified names only in certain cases. Caching at this time will cause the fully qualified name // to be used every time. bool useCache = !forceUseFullyQualifiedName; // See if we already have a reference for this type Tuple <CodeNamespace, Type> tupleKey = new Tuple <CodeNamespace, Type>(ns, type); if (!useCache || !CodeGenUtilities.codeTypeReferences.TryGetValue(tupleKey, out codeTypeReference)) { if (useFullyQualifiedName && !string.IsNullOrEmpty(typeNamespace)) { // While this splicing may seem awkward, we perform this task // rather than rely on 'type.FullName' as we may have performed // a VB root namespace translation task above. typeName = typeNamespace + "." + typeName; } // If not, create a new type reference. Use the constructor for CodeTypeReference // that takes a type's name rather than type to generate short names. if (type.IsArray) { codeTypeReference = new CodeTypeReference( CodeGenUtilities.GetTypeReference(type.GetElementType(), codeGenerator, referencingType, /* optimizeAttributeName */ false, forceUseFullyQualifiedName), type.GetArrayRank()); } else if (type.IsGenericType) { Type[] genericArguments = type.GetGenericArguments(); CodeTypeReference[] typeArguments = new CodeTypeReference[genericArguments.Length]; for (int i = 0; i < genericArguments.Length; i++) { typeArguments[i] = GetTypeReference(genericArguments[i], codeGenerator, referencingType); } codeTypeReference = new CodeTypeReference(typeName, typeArguments); } else { // Generate language-specific shorthands for core types by using CodeTypeReference constructor that takes a Type if (type.IsPrimitive || type == typeof(void) || type == typeof(decimal) || type == typeof(string) || type == typeof(object)) { codeTypeReference = new CodeTypeReference(type); } else { codeTypeReference = new CodeTypeReference(typeName); } } // Keep track of the CLR type for identification purposes. codeTypeReference.UserData["ClrType"] = type; // Cache for later use. if (useCache) { CodeGenUtilities.codeTypeReferences.Add(tupleKey, codeTypeReference); } } return(codeTypeReference); }
/// <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); }
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")); }
/// <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); }
public override void Generate() { // ---------------------------------------------------------------- // namespace // ---------------------------------------------------------------- CodeNamespace ns = this.ClientProxyGenerator.GetOrGenNamespace(this.ClientProxyGenerator.ClientProxyCodeGenerationOptions.ClientRootNamespace); // Missing namespace bails out of code-gen -- error has been logged if (ns == null) { return; } // Log an informational message to help users see progress this.ClientProxyGenerator.LogMessage(Resource.CodeGen_Generating_WebContext); // Find the AuthenticationServices and if there's just one, use it as the default. IEnumerable <DomainServiceDescription> authDescriptions = this.ClientProxyGenerator.DomainServiceDescriptions.Where(d => typeof(IAuthentication <>).DefinitionIsAssignableFrom(d.DomainServiceType)); DomainServiceDescription defaultAuthDescription = null; if (authDescriptions.Count() > 1) { this.ClientProxyGenerator.LogMessage( string.Format( CultureInfo.InvariantCulture, Resource.WebContext_ManyAuthServices, string.Join(",", authDescriptions.Select(d => d.DomainServiceType.Name).ToArray()))); } else { defaultAuthDescription = authDescriptions.FirstOrDefault(); } // ---------------------------------------------------------------- // public partial sealed class WebContext : WebContextBase // ---------------------------------------------------------------- CodeTypeDeclaration proxyClass = CodeGenUtilities.CreateTypeDeclaration("WebContext", ns.Name); proxyClass.IsPartial = true; proxyClass.TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed; proxyClass.BaseTypes.Add(CodeGenUtilities.GetTypeReference(TypeConstants.WebContextBaseName, ns.Name, false)); proxyClass.Comments.AddRange(CodeGenUtilities.GetDocComments(Resource.WebContext_CommentClass, this.ClientProxyGenerator.IsCSharp)); ns.Types.Add(proxyClass); // ---------------------------------------------------------------- // public WebContext() // { // <!-- if there's a default authentication service // this.Authentication = new WebUserService(); // --> // this.OnCreated(); // } // ---------------------------------------------------------------- CodeConstructor constructor = new CodeConstructor(); constructor.Attributes = MemberAttributes.Public; //if (defaultAuthDescription != null) //{ // // TODO: Choose between Forms and Windows when reading from web.config is available // //constructor.Statements.Add( // // new CodeAssignStatement( // // new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "Authentication"), // // new CodeObjectCreateExpression(WebContextGenerator.FormsAuthenticationName))); //} NotificationMethodGenerator onCreatedMethodGenerator = new NotificationMethodGenerator( this.ClientProxyGenerator, this.ClientProxyGenerator.IsCSharp ? IndentationLevel.Namespace : IndentationLevel.GlobalNamespace); constructor.Statements.Add(onCreatedMethodGenerator.OnCreatedMethodInvokeExpression); constructor.Comments.AddRange(CodeGenUtilities.GetDocComments(Resource.WebContext_CommentConstructor, this.ClientProxyGenerator.IsCSharp)); proxyClass.Members.Add(constructor); // ---------------------------------------------------------------- // #region Extensibility Method Definitions // partial void OnCreated(); // #endregion // ---------------------------------------------------------------- proxyClass.Members.AddRange(onCreatedMethodGenerator.PartialMethodsSnippetBlock); // ---------------------------------------------------------------- // public static new WebContext Current // { // get { return (WebContext)WebContextBase.Current; } // } // ---------------------------------------------------------------- CodeMemberProperty currentProperty = new CodeMemberProperty(); string typeFullName = (string.IsNullOrEmpty(ns.Name) ? string.Empty : ns.Name + ".") + "WebContext"; CodeTypeReference targetTypeRef = CodeGenUtilities.GetTypeReference(typeFullName, ns.Name, true); CodeTypeReference baseTypeRef = CodeGenUtilities.GetTypeReference(TypeConstants.WebContextBaseName, ns.Name, false); CodeTypeReferenceExpression baseTypeRefExp = new CodeTypeReferenceExpression(baseTypeRef); currentProperty.Attributes = MemberAttributes.Public | MemberAttributes.Static | MemberAttributes.New; currentProperty.Type = targetTypeRef; currentProperty.Name = "Current"; currentProperty.HasGet = true; currentProperty.GetStatements.Add( new CodeMethodReturnStatement( new CodeCastExpression(currentProperty.Type, new CodePropertyReferenceExpression(baseTypeRefExp, "Current")))); currentProperty.Comments.AddRange(CodeGenUtilities.GetDocComments(Resource.WebContext_CommentCurrent, this.ClientProxyGenerator.IsCSharp)); proxyClass.Members.Add(currentProperty); // ---------------------------------------------------------------- // <!-- if there's a default authentication service // public new MyUser User // { // get { return (MyUser)base.User; } // } // --> // ---------------------------------------------------------------- if (defaultAuthDescription != null) { Type genericType = null; typeof(IAuthentication <>).DefinitionIsAssignableFrom(defaultAuthDescription.DomainServiceType, out genericType); if ((genericType != null) && (genericType.GetGenericArguments().Count() == 1)) { CodeMemberProperty userProperty = new CodeMemberProperty(); userProperty.Attributes = MemberAttributes.Public | MemberAttributes.New | MemberAttributes.Final; userProperty.Type = CodeGenUtilities.GetTypeReference( genericType.GetGenericArguments()[0], this.ClientProxyGenerator, proxyClass); userProperty.Name = "User"; userProperty.HasGet = true; userProperty.GetStatements.Add( new CodeMethodReturnStatement( new CodeCastExpression(userProperty.Type, new CodePropertyReferenceExpression( new CodeBaseReferenceExpression(), "User")))); userProperty.Comments.AddRange(CodeGenUtilities.GetDocComments(Resource.WebContext_CommentUser, this.ClientProxyGenerator.IsCSharp)); proxyClass.Members.Add(userProperty); } } }
/// <summary> /// Generates the client proxy code for the given type. /// </summary> public override void Generate() { // ---------------------------------------------------------------- // namespace // ---------------------------------------------------------------- CodeNamespace ns = this.ClientProxyGenerator.GetOrGenNamespace(this.Type); // Missing namespace bails out of code-gen -- error has been logged if (ns == null) { return; } // ---------------------------------------------------------------- // public partial class {Type} : (Base) // ---------------------------------------------------------------- this.ProxyClass = CodeGenUtilities.CreateTypeDeclaration(this.Type); this.ProxyClass.IsPartial = true; // makes this a partial type this.ProxyClass.TypeAttributes = TypeAttributes.Public; // Abstract classes must be preserved as abstract to avoid explicit instantiation on client bool isAbstract = (this.Type.IsAbstract); if (isAbstract) { this.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 = this.GetDerivedTypes(); // If this type doesn't have any derivatives, seal it. Cannot seal abstracts. if (!isAbstract && !derivedTypes.Any()) { this.ProxyClass.TypeAttributes |= TypeAttributes.Sealed; } // Add all base types including interfaces this.AddBaseTypes(ns); ns.Types.Add(this.ProxyClass); AttributeCollection typeAttributes = this.Type.Attributes(); // Add <summary> xml comment to class string comment = this.GetSummaryComment(); this.ProxyClass.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.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, this.Type.Name); constructor.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); // add call to default OnCreated method constructor.Statements.Add(this.NotificationMethodGen.OnCreatedMethodInvokeExpression); this.ProxyClass.Members.Add(constructor); // ---------------------------------------------------------------- // [KnownType(...), ...] // ---------------------------------------------------------------- // We need to generate a [KnownType] for all derived entities on the visible root. if (!this.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 => this.ClientProxyGenerator.ClientProxyCodeGenerationOptions.UseFullTypeNames ? t.FullName : t.Name)) { CodeAttributeDeclaration knownTypeAttrib = CodeGenUtilities.CreateAttributeDeclaration(typeof(System.Runtime.Serialization.KnownTypeAttribute), this.ClientProxyGenerator, this.ProxyClass); knownTypeAttrib.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(CodeGenUtilities.GetTypeReference(derivedType, this.ClientProxyGenerator, this.ProxyClass)))); this.ProxyClass.CustomAttributes.Add(knownTypeAttrib); } } this.ValidateTypeAttributes(typeAttributes); // ---------------------------------------------------------------- // [DataContract(Namespace=X, Name=Y)] // ---------------------------------------------------------------- CodeAttributeDeclaration dataContractAttrib = CodeGenUtilities.CreateDataContractAttributeDeclaration(this.Type, this.ClientProxyGenerator, this.ProxyClass); this.ProxyClass.CustomAttributes.Add(dataContractAttrib); // ---------------------------------------------------------------- // Propagate all type-level Attributes across (except DataContractAttribute since that is handled above) // ----------------------------------------------------------------- CustomAttributeGenerator.GenerateCustomAttributes( this.ClientProxyGenerator, this.ProxyClass, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeType, ex.Message, this.ProxyClass.Name, ex.InnerException.Message), this.FilterTypeAttributes(typeAttributes), this.ProxyClass.CustomAttributes, this.ProxyClass.Comments); // ---------------------------------------------------------------- // gen proxy getter/setter for each property // ---------------------------------------------------------------- this.GenerateProperties(); // ---------------------------------------------------------------- // gen additional methods/events // ---------------------------------------------------------------- this.GenerateAdditionalMembers(); // Register created CodeTypeDeclaration with mapping this._typeMapping[this.Type] = this.ProxyClass; }
/// <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); }