// // ServiceContractGenerator.cs // // Author: // Atsushi Enomoto // // Copyright (C) 2005 Novell, Inc. http://www.novell.com // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.CodeDom; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Configuration; using System.Linq; using System.Reflection; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Configuration; using System.Threading; using System.Xml.Schema; using System.Xml.Serialization; using ConfigurationType = System.Configuration.Configuration; using QName = System.Xml.XmlQualifiedName; using OPair = System.Collections.Generic.KeyValuePair; namespace System.ServiceModel.Description { public class ServiceContractGenerator { CodeCompileUnit ccu; ConfigurationType config; CodeIdentifiers identifiers = new CodeIdentifiers (); Collection errors = new Collection (); Dictionary nsmappings = new Dictionary (); Dictionary referenced_types = new Dictionary (); Dictionary generated_contracts = new Dictionary (); ServiceContractGenerationOptions options; Dictionary imported_names = new Dictionary (); ServiceContractGenerationContext contract_context; List operation_contexts = new List (); XsdDataContractImporter data_contract_importer; XmlSerializerMessageContractImporterInternal xml_serialization_importer; class ContractCacheEntry { public ContractDescription Contract { get; private set; } public string ConfigurationName { get; private set; } public CodeTypeDeclaration TypeDeclaration { get; private set; } public bool GeneratedContractType { get; set; } public CodeTypeReference GetReference () { return reference; } public ContractCacheEntry (ContractDescription cd, string config, CodeTypeDeclaration tdecl) { Contract = cd; ConfigurationName = config; TypeDeclaration = tdecl; reference = new CodeTypeReference (tdecl.Name); } readonly CodeTypeReference reference; } public ServiceContractGenerator () : this (null, null) { } public ServiceContractGenerator (CodeCompileUnit targetCompileUnit) : this (targetCompileUnit, null) { } public ServiceContractGenerator (ConfigurationType targetConfig) : this (null, targetConfig) { } public ServiceContractGenerator (CodeCompileUnit targetCompileUnit, ConfigurationType targetConfig) { if (targetCompileUnit == null) this.ccu = new CodeCompileUnit (); else this.ccu = targetCompileUnit; this.config = targetConfig; Options |= ServiceContractGenerationOptions.ChannelInterface | ServiceContractGenerationOptions.ClientClass; } public ConfigurationType Configuration { get { return config; } } public Collection Errors { get { return errors; } } public Dictionary NamespaceMappings { get { return nsmappings; } } public ServiceContractGenerationOptions Options { get { return options; } set { options = value; } } bool GenerateAsync { get { return GenerateEventBasedAsync || (options & ServiceContractGenerationOptions.AsynchronousMethods) != 0; } } bool GenerateEventBasedAsync { get { return (options & ServiceContractGenerationOptions.EventBasedAsynchronousMethods) != 0; } } public Dictionary ReferencedTypes { get { return referenced_types; } } public CodeCompileUnit TargetCompileUnit { get { return ccu; } } public void GenerateBinding (Binding binding, out string bindingSectionName, out string configurationName) { if (config == null) throw new InvalidOperationException (); var element = ConfigUtil.FindCollectionElement (binding, config); if (element == null) throw new InvalidOperationException (); bindingSectionName = element.BindingName; int idx = 0; configurationName = binding.Name; while (element.ContainsKey (configurationName)) configurationName = binding.Name + (++idx); if (!element.TryAdd (configurationName, binding, config)) throw new InvalidOperationException (); } #region Service Contract Type // Those implementation classes are very likely to be split // into different classes. [MonoTODO] public CodeTypeReference GenerateServiceContractType ( ContractDescription contractDescription) { CodeNamespace cns = GetNamespace (contractDescription.Namespace); var cache = ExportInterface_internal (contractDescription, cns); if (cache.GeneratedContractType) return cache.GetReference (); // FIXME: handle duplex callback if ((Options & ServiceContractGenerationOptions.ChannelInterface) != 0) GenerateChannelInterface (contractDescription, cns); if ((Options & ServiceContractGenerationOptions.ClientClass) != 0) GenerateProxyClass (contractDescription, cns); if (data_contract_importer != null) MergeCompileUnit (data_contract_importer.CodeCompileUnit, ccu); if (xml_serialization_importer != null) MergeCompileUnit (xml_serialization_importer.CodeCompileUnit, ccu); // Process extensions. Class first, then methods. // (built-in ones must present before processing class extensions). foreach (var cb in contractDescription.Behaviors) { var gex = cb as IServiceContractGenerationExtension; if (gex != null) gex.GenerateContract (contract_context); } foreach (var opair in operation_contexts) opair.Key.GenerateOperation (opair.Value); cache.GeneratedContractType = true; return cache.GetReference (); } CodeNamespace GetNamespace (string contractNs) { if (contractNs == null) contractNs = String.Empty; string csharpNs; if (nsmappings.ContainsKey (contractNs)) csharpNs = nsmappings [contractNs]; else if (nsmappings.ContainsKey ("*")) csharpNs = nsmappings ["*"]; else csharpNs = string.Empty; foreach (CodeNamespace cns in ccu.Namespaces) if (cns.Name == csharpNs) return cns; CodeNamespace ncns = new CodeNamespace (); ncns.Name = csharpNs; ccu.Namespaces.Add (ncns); return ncns; } CodeTypeDeclaration GetTypeDeclaration (CodeNamespace cns, string name) { foreach (CodeTypeDeclaration type in cns.Types) if (type.Name == name) return type; return null; } void GenerateProxyClass (ContractDescription cd, CodeNamespace cns) { string name = cd.Name + "Client"; if (name [0] == 'I') name = name.Substring (1); name = identifiers.AddUnique (name, null); CodeTypeDeclaration type = GetTypeDeclaration (cns, name); if (type != null) return; // already imported CodeTypeReference clientBase = new CodeTypeReference (typeof (ClientBase<>)); clientBase.TypeArguments.Add (new CodeTypeReference (cd.Name)); type = new CodeTypeDeclaration (name); cns.Types.Add (type); type.TypeAttributes = TypeAttributes.Public; type.BaseTypes.Add (clientBase); type.BaseTypes.Add (new CodeTypeReference (cd.Name)); // .ctor() CodeConstructor ctor = new CodeConstructor (); ctor.Attributes = MemberAttributes.Public; type.Members.Add (ctor); // .ctor(string endpointConfigurationName) ctor = new CodeConstructor (); ctor.Attributes = MemberAttributes.Public; ctor.Parameters.Add ( new CodeParameterDeclarationExpression ( new CodeTypeReference (typeof (string)), "endpointConfigurationName")); ctor.BaseConstructorArgs.Add ( new CodeArgumentReferenceExpression ("endpointConfigurationName")); type.Members.Add (ctor); // .ctor(string endpointConfigurationName, string remoteAddress) ctor = new CodeConstructor (); ctor.Attributes = MemberAttributes.Public; ctor.Parameters.Add ( new CodeParameterDeclarationExpression ( new CodeTypeReference (typeof (string)), "endpointConfigurationName")); ctor.Parameters.Add ( new CodeParameterDeclarationExpression ( new CodeTypeReference (typeof (string)), "remoteAddress")); ctor.BaseConstructorArgs.Add ( new CodeArgumentReferenceExpression ("endpointConfigurationName")); ctor.BaseConstructorArgs.Add ( new CodeArgumentReferenceExpression ("remoteAddress")); type.Members.Add (ctor); // .ctor(string endpointConfigurationName, EndpointAddress remoteAddress) ctor = new CodeConstructor (); ctor.Attributes = MemberAttributes.Public; ctor.Parameters.Add ( new CodeParameterDeclarationExpression ( new CodeTypeReference (typeof (string)), "endpointConfigurationName")); ctor.Parameters.Add ( new CodeParameterDeclarationExpression ( new CodeTypeReference (typeof (EndpointAddress)), "remoteAddress")); ctor.BaseConstructorArgs.Add ( new CodeArgumentReferenceExpression ("endpointConfigurationName")); ctor.BaseConstructorArgs.Add ( new CodeArgumentReferenceExpression ("remoteAddress")); type.Members.Add (ctor); // .ctor(Binding,EndpointAddress) ctor = new CodeConstructor (); ctor.Attributes = MemberAttributes.Public; ctor.Parameters.Add ( new CodeParameterDeclarationExpression ( new CodeTypeReference (typeof (Binding)), "binding")); ctor.Parameters.Add ( new CodeParameterDeclarationExpression ( new CodeTypeReference (typeof (EndpointAddress)), "endpoint")); ctor.BaseConstructorArgs.Add ( new CodeArgumentReferenceExpression ("binding")); ctor.BaseConstructorArgs.Add ( new CodeArgumentReferenceExpression ("endpoint")); type.Members.Add (ctor); // service contract methods AddImplementationClientMethods (type, cd); if (GenerateEventBasedAsync) foreach (var od in cd.Operations) GenerateEventBasedAsyncSupport (type, od, cns); } void GenerateChannelInterface (ContractDescription cd, CodeNamespace cns) { string name = cd.Name + "Channel"; name = identifiers.AddUnique (name, null); CodeTypeDeclaration type = GetTypeDeclaration (cns, name); if (type != null) return; type = new CodeTypeDeclaration (); type.Name = name; type.TypeAttributes = TypeAttributes.Interface | TypeAttributes.Public; cns.Types.Add (type); type.BaseTypes.Add (ExportInterface (cd, cns)); type.BaseTypes.Add (new CodeTypeReference (typeof (System.ServiceModel.IClientChannel))); } CodeTypeReference ExportInterface (ContractDescription cd, CodeNamespace cns) { var cache = ExportInterface_internal (cd, cns); return cache.GetReference (); } ContractCacheEntry ExportInterface_internal (ContractDescription cd, CodeNamespace cns) { if (generated_contracts.ContainsKey (cd)) return generated_contracts [cd]; var type = new CodeTypeDeclaration (); type.TypeAttributes = TypeAttributes.Interface; type.TypeAttributes |= TypeAttributes.Public; cns.Types.Add (type); type.Name = identifiers.AddUnique (cd.Name, null); var configName = type.Name; CodeAttributeDeclaration ad = new CodeAttributeDeclaration ( new CodeTypeReference ( typeof (ServiceContractAttribute))); ad.Arguments.Add (new CodeAttributeArgument ("Namespace", new CodePrimitiveExpression (cd.Namespace))); ad.Arguments.Add (new CodeAttributeArgument ("ConfigurationName", new CodePrimitiveExpression (configName))); type.CustomAttributes.Add (ad); contract_context = new ServiceContractGenerationContext (this, cd, type); AddOperationMethods (type, cd); var cache = new ContractCacheEntry (cd, configName, type); generated_contracts.Add (cd, cache); return cache; } void AddBeginAsyncArgs (CodeMemberMethod cm) { var acb = new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (AsyncCallback)), "asyncCallback"); cm.Parameters.Add (acb); var us = new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (object)), "userState"); cm.Parameters.Add (us); } void AddOperationMethods (CodeTypeDeclaration type, ContractDescription cd) { foreach (OperationDescription od in cd.Operations) { CodeMemberMethod syncMethod = null, beginMethod = null, endMethod = null; CodeTypeReference returnTypeFromMessageContract = null; syncMethod = GenerateOperationMethod (type, cd, od, false, out returnTypeFromMessageContract); type.Members.Add (syncMethod); if (GenerateAsync) { beginMethod = GenerateOperationMethod (type, cd, od, true, out returnTypeFromMessageContract); type.Members.Add (beginMethod); var cm = new CodeMemberMethod (); type.Members.Add (cm); cm.Name = "End" + od.Name; endMethod = cm; var res = new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (IAsyncResult)), "result"); cm.Parameters.Add (res); if (od.SyncMethod != null) // FIXME: it depends on sync method! cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType); else cm.ReturnType = returnTypeFromMessageContract; } foreach (var ob in od.Behaviors) { var gex = ob as IOperationContractGenerationExtension; if (gex != null) operation_contexts.Add (new OPair (gex, new OperationContractGenerationContext (this, contract_context, od, type, syncMethod, beginMethod, endMethod))); } } } CodeMemberMethod GenerateOperationMethod (CodeTypeDeclaration type, ContractDescription cd, OperationDescription od, bool async, out CodeTypeReference returnType) { CodeMemberMethod cm = new CodeMemberMethod (); if (od.Behaviors.Find () != null) cm.CustomAttributes.Add (new CodeAttributeDeclaration (new CodeTypeReference (typeof (XmlSerializerFormatAttribute)))); if (async) cm.Name = "Begin" + od.Name; else cm.Name = od.Name; if (od.SyncMethod != null) { ExportParameters (cm, od.SyncMethod.GetParameters ()); if (async) { AddBeginAsyncArgs (cm); cm.ReturnType = new CodeTypeReference (typeof (IAsyncResult)); } else cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType); returnType = new CodeTypeReference (od.SyncMethod.ReturnType); } else { ExportMessages (od.Messages, cm, false); returnType = cm.ReturnType; if (async) { AddBeginAsyncArgs (cm); cm.ReturnType = new CodeTypeReference (typeof (IAsyncResult)); } } // [OperationContract (Action = "...", ReplyAction = "..")] var ad = new CodeAttributeDeclaration (new CodeTypeReference (typeof (OperationContractAttribute))); foreach (MessageDescription md in od.Messages) { if (md.Direction == MessageDirection.Input) ad.Arguments.Add (new CodeAttributeArgument ("Action", new CodePrimitiveExpression (md.Action))); else ad.Arguments.Add (new CodeAttributeArgument ("ReplyAction", new CodePrimitiveExpression (md.Action))); } if (async) ad.Arguments.Add (new CodeAttributeArgument ("AsyncPattern", new CodePrimitiveExpression (true))); cm.CustomAttributes.Add (ad); return cm; } void ExportParameters (CodeMemberMethod method, ParameterInfo [] parameters) { foreach (ParameterInfo pi in parameters) method.Parameters.Add ( new CodeParameterDeclarationExpression ( new CodeTypeReference (pi.ParameterType), pi.Name)); } void AddImplementationClientMethods (CodeTypeDeclaration type, ContractDescription cd) { foreach (OperationDescription od in cd.Operations) { CodeMemberMethod cm; CodeTypeReference returnTypeFromMessageContract = null; cm = GenerateImplementationClientMethod (type, cd, od, false, out returnTypeFromMessageContract); type.Members.Add (cm); if (!GenerateAsync) continue; cm = GenerateImplementationClientMethod (type, cd, od, true, out returnTypeFromMessageContract); type.Members.Add (cm); // EndXxx() implementation cm = new CodeMemberMethod (); cm.Attributes = MemberAttributes.Public | MemberAttributes.Final; type.Members.Add (cm); cm.Name = "End" + od.Name; var res = new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (IAsyncResult)), "result"); cm.Parameters.Add (res); if (od.SyncMethod != null) // FIXME: it depends on sync method! cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType); else cm.ReturnType = returnTypeFromMessageContract; string resultArgName = "result"; if (od.EndMethod != null) resultArgName = od.EndMethod.GetParameters () [0].Name; var call = new CodeMethodInvokeExpression ( new CodePropertyReferenceExpression ( new CodeBaseReferenceExpression (), "Channel"), cm.Name, new CodeArgumentReferenceExpression (resultArgName)); if (cm.ReturnType.BaseType == "System.Void") cm.Statements.Add (new CodeExpressionStatement (call)); else cm.Statements.Add (new CodeMethodReturnStatement (call)); } } CodeMemberMethod GenerateImplementationClientMethod (CodeTypeDeclaration type, ContractDescription cd, OperationDescription od, bool async, out CodeTypeReference returnTypeFromMessageContract) { CodeMemberMethod cm = new CodeMemberMethod (); if (async) cm.Name = "Begin" + od.Name; else cm.Name = od.Name; cm.Attributes = MemberAttributes.Public | MemberAttributes.Final; returnTypeFromMessageContract = null; List args = new List (); if (od.SyncMethod != null) { ParameterInfo [] pars = od.SyncMethod.GetParameters (); ExportParameters (cm, pars); cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType); int i = 0; foreach (ParameterInfo pi in pars) args.Add (new CodeArgumentReferenceExpression (pi.Name)); } else { args.AddRange (ExportMessages (od.Messages, cm, true)); returnTypeFromMessageContract = cm.ReturnType; if (async) { AddBeginAsyncArgs (cm); cm.ReturnType = new CodeTypeReference (typeof (IAsyncResult)); } } if (async) { args.Add (new CodeArgumentReferenceExpression ("asyncCallback")); args.Add (new CodeArgumentReferenceExpression ("userState")); } CodeExpression call = new CodeMethodInvokeExpression ( new CodePropertyReferenceExpression ( new CodeBaseReferenceExpression (), "Channel"), cm.Name, args.ToArray ()); if (cm.ReturnType.BaseType == "System.Void") cm.Statements.Add (new CodeExpressionStatement (call)); else cm.Statements.Add (new CodeMethodReturnStatement (call)); return cm; } CodeMemberMethod FindByName (CodeTypeDeclaration type, string name) { foreach (var m in type.Members) { var method = m as CodeMemberMethod; if (method != null && method.Name == name) return method; } return null; } void GenerateEventBasedAsyncSupport (CodeTypeDeclaration type, OperationDescription od, CodeNamespace cns) { var method = FindByName (type, od.Name) ?? FindByName (type, "Begin" + od.Name); var endMethod = method.Name == od.Name ? null : FindByName (type, "End" + od.Name); bool methodAsync = method.Name.StartsWith ("Begin", StringComparison.Ordinal); var resultType = endMethod != null ? endMethod.ReturnType : method.ReturnType; var thisExpr = new CodeThisReferenceExpression (); var baseExpr = new CodeBaseReferenceExpression (); var nullExpr = new CodePrimitiveExpression (null); var asyncResultType = new CodeTypeReference (typeof (IAsyncResult)); // OnBeginXxx() implementation var cm = new CodeMemberMethod () { Name = "OnBegin" + od.Name, Attributes = MemberAttributes.Private | MemberAttributes.Final, ReturnType = asyncResultType }; type.Members.Add (cm); AddMethodParam (cm, typeof (object []), "args"); AddMethodParam (cm, typeof (AsyncCallback), "asyncCallback"); AddMethodParam (cm, typeof (object), "userState"); var call = new CodeMethodInvokeExpression ( thisExpr, "Begin" + od.Name); for (int idx = 0; idx < method.Parameters.Count - (methodAsync ? 2 : 0); idx++) { var p = method.Parameters [idx]; cm.Statements.Add (new CodeVariableDeclarationStatement (p.Type, p.Name, new CodeCastExpression (p.Type, new CodeArrayIndexerExpression (new CodeArgumentReferenceExpression ("args"), new CodePrimitiveExpression (idx))))); call.Parameters.Add (new CodeVariableReferenceExpression (p.Name)); } call.Parameters.Add (new CodeArgumentReferenceExpression ("asyncCallback")); call.Parameters.Add (new CodeArgumentReferenceExpression ("userState")); cm.Statements.Add (new CodeMethodReturnStatement (call)); // OnEndXxx() implementation cm = new CodeMemberMethod () { Name = "OnEnd" + od.Name, Attributes = MemberAttributes.Private | MemberAttributes.Final, ReturnType = new CodeTypeReference (typeof (object [])) }; type.Members.Add (cm); AddMethodParam (cm, typeof (IAsyncResult), "result"); var outArgRefs = new List (); for (int idx = 0; idx < method.Parameters.Count; idx++) { var p = method.Parameters [idx]; if (p.Direction != FieldDirection.In) { cm.Statements.Add (new CodeVariableDeclarationStatement (p.Type, p.Name)); outArgRefs.Add (new CodeVariableReferenceExpression (p.Name)); // FIXME: should this work? They need "out" or "ref" modifiers. } } call = new CodeMethodInvokeExpression ( thisExpr, "End" + od.Name, new CodeArgumentReferenceExpression ("result")); call.Parameters.AddRange (outArgRefs.Cast ().ToArray ()); // questionable var retCreate = new CodeArrayCreateExpression (typeof (object)); if (resultType.BaseType == "System.Void") cm.Statements.Add (call); else { cm.Statements.Add (new CodeVariableDeclarationStatement (typeof (object), "__ret", call)); retCreate.Initializers.Add (new CodeVariableReferenceExpression ("__ret")); } foreach (var outArgRef in outArgRefs) retCreate.Initializers.Add (new CodeVariableReferenceExpression (outArgRef.VariableName)); cm.Statements.Add (new CodeMethodReturnStatement (retCreate)); // OnXxxCompleted() implementation cm = new CodeMemberMethod () { Name = "On" + od.Name + "Completed", Attributes = MemberAttributes.Private | MemberAttributes.Final }; type.Members.Add (cm); AddMethodParam (cm, typeof (object), "state"); string argsname = identifiers.AddUnique (od.Name + "CompletedEventArgs", null); var iaargs = new CodeTypeReference ("InvokeAsyncCompletedEventArgs"); // avoid messy System.Type instance for generic nested type :| var iaref = new CodeVariableReferenceExpression ("args"); var methodEventArgs = new CodeObjectCreateExpression (new CodeTypeReference (argsname), new CodePropertyReferenceExpression (iaref, "Results"), new CodePropertyReferenceExpression (iaref, "Error"), new CodePropertyReferenceExpression (iaref, "Cancelled"), new CodePropertyReferenceExpression (iaref, "UserState")); cm.Statements.Add (new CodeConditionStatement ( new CodeBinaryOperatorExpression ( new CodeEventReferenceExpression (thisExpr, od.Name + "Completed"), CodeBinaryOperatorType.IdentityInequality, nullExpr), new CodeVariableDeclarationStatement (iaargs, "args", new CodeCastExpression (iaargs, new CodeArgumentReferenceExpression ("state"))), new CodeExpressionStatement (new CodeMethodInvokeExpression (thisExpr, od.Name + "Completed", thisExpr, methodEventArgs)))); // delegate fields type.Members.Add (new CodeMemberField (new CodeTypeReference ("BeginOperationDelegate"), "onBegin" + od.Name + "Delegate")); type.Members.Add (new CodeMemberField (new CodeTypeReference ("EndOperationDelegate"), "onEnd" + od.Name + "Delegate")); type.Members.Add (new CodeMemberField (new CodeTypeReference (typeof (SendOrPostCallback)), "on" + od.Name + "CompletedDelegate")); // XxxCompletedEventArgs class var argsType = new CodeTypeDeclaration (argsname); argsType.BaseTypes.Add (new CodeTypeReference (typeof (AsyncCompletedEventArgs))); cns.Types.Add (argsType); var argsCtor = new CodeConstructor () { Attributes = MemberAttributes.Public | MemberAttributes.Final }; argsCtor.Parameters.Add (new CodeParameterDeclarationExpression (typeof (object []), "results")); argsCtor.Parameters.Add (new CodeParameterDeclarationExpression (typeof (Exception), "error")); argsCtor.Parameters.Add (new CodeParameterDeclarationExpression (typeof (bool), "cancelled")); argsCtor.Parameters.Add (new CodeParameterDeclarationExpression (typeof (object), "userState")); argsCtor.BaseConstructorArgs.Add (new CodeArgumentReferenceExpression ("error")); argsCtor.BaseConstructorArgs.Add (new CodeArgumentReferenceExpression ("cancelled")); argsCtor.BaseConstructorArgs.Add (new CodeArgumentReferenceExpression ("userState")); var resultsField = new CodeFieldReferenceExpression (thisExpr, "results"); argsCtor.Statements.Add (new CodeAssignStatement (resultsField, new CodeArgumentReferenceExpression ("results"))); argsType.Members.Add (argsCtor); argsType.Members.Add (new CodeMemberField (typeof (object []), "results")); if (resultType.BaseType != "System.Void") { var resultProp = new CodeMemberProperty { Name = "Result", Type = resultType, Attributes = MemberAttributes.Public | MemberAttributes.Final }; resultProp.GetStatements.Add (new CodeMethodReturnStatement (new CodeCastExpression (resultProp.Type, new CodeArrayIndexerExpression (resultsField, new CodePrimitiveExpression (0))))); argsType.Members.Add (resultProp); } // event field var handlerType = new CodeTypeReference (typeof (EventHandler<>)); handlerType.TypeArguments.Add (new CodeTypeReference (argsType.Name)); type.Members.Add (new CodeMemberEvent () { Name = od.Name + "Completed", Type = handlerType, Attributes = MemberAttributes.Public | MemberAttributes.Final }); // XxxAsync() implementations bool hasAsync = false; foreach (int __x in Enumerable.Range (0, 2)) { cm = new CodeMemberMethod (); type.Members.Add (cm); cm.Name = od.Name + "Async"; cm.Attributes = MemberAttributes.Public | MemberAttributes.Final; var inArgs = new List (); for (int idx = 0; idx < method.Parameters.Count - (methodAsync ? 2 : 0); idx++) { var pd = method.Parameters [idx]; inArgs.Add (pd); cm.Parameters.Add (pd); } // First one is overload without asyncState arg. if (!hasAsync) { call = new CodeMethodInvokeExpression (thisExpr, cm.Name, inArgs.ConvertAll (decl => new CodeArgumentReferenceExpression (decl.Name)).ToArray ()); call.Parameters.Add (nullExpr); cm.Statements.Add (new CodeExpressionStatement (call)); hasAsync = true; continue; } // Second one is the primary one. cm.Parameters.Add (new CodeParameterDeclarationExpression (typeof (object), "userState")); // if (onBeginBarOperDelegate == null) onBeginBarOperDelegate = new BeginOperationDelegate (OnBeginBarOper); // if (onEndBarOperDelegate == null) onEndBarOperDelegate = new EndOperationDelegate (OnEndBarOper); // if (onBarOperCompletedDelegate == null) onBarOperCompletedDelegate = new BeginOperationDelegate (OnBarOperCompleted); var beginOperDelegateRef = new CodeFieldReferenceExpression (thisExpr, "onBegin" + od.Name + "Delegate"); var endOperDelegateRef = new CodeFieldReferenceExpression (thisExpr, "onEnd" + od.Name + "Delegate"); var operCompletedDelegateRef = new CodeFieldReferenceExpression (thisExpr, "on" + od.Name + "CompletedDelegate"); var ifstmt = new CodeConditionStatement ( new CodeBinaryOperatorExpression (beginOperDelegateRef, CodeBinaryOperatorType.IdentityEquality, nullExpr), new CodeAssignStatement (beginOperDelegateRef, new CodeDelegateCreateExpression (new CodeTypeReference ("BeginOperationDelegate"), thisExpr, "OnBegin" + od.Name))); cm.Statements.Add (ifstmt); ifstmt = new CodeConditionStatement ( new CodeBinaryOperatorExpression (endOperDelegateRef, CodeBinaryOperatorType.IdentityEquality, nullExpr), new CodeAssignStatement (endOperDelegateRef, new CodeDelegateCreateExpression (new CodeTypeReference ("EndOperationDelegate"), thisExpr, "OnEnd" + od.Name))); cm.Statements.Add (ifstmt); ifstmt = new CodeConditionStatement ( new CodeBinaryOperatorExpression (operCompletedDelegateRef, CodeBinaryOperatorType.IdentityEquality, nullExpr), new CodeAssignStatement (operCompletedDelegateRef, new CodeDelegateCreateExpression (new CodeTypeReference (typeof (SendOrPostCallback)), thisExpr, "On" + od.Name + "Completed"))); cm.Statements.Add (ifstmt); // InvokeAsync (onBeginBarOperDelegate, inValues, onEndBarOperDelegate, onBarOperCompletedDelegate, userState); inArgs.Add (new CodeParameterDeclarationExpression (typeof (object), "userState")); var args = new List (); args.Add (beginOperDelegateRef); args.Add (new CodeArrayCreateExpression (typeof (object), inArgs.ConvertAll (decl => new CodeArgumentReferenceExpression (decl.Name)).ToArray ())); args.Add (endOperDelegateRef); args.Add (new CodeFieldReferenceExpression (thisExpr, "on" + od.Name + "CompletedDelegate")); args.Add (new CodeArgumentReferenceExpression ("userState")); call = new CodeMethodInvokeExpression (baseExpr, "InvokeAsync", args.ToArray ()); cm.Statements.Add (new CodeExpressionStatement (call)); } } void AddMethodParam (CodeMemberMethod cm, Type type, string name) { cm.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference (type), name)); } const string ms_arrays_ns = "http://schemas.microsoft.com/2003/10/Serialization/Arrays"; private CodeExpression[] ExportMessages (MessageDescriptionCollection messages, CodeMemberMethod method, bool return_args) { CodeExpression [] args = null; foreach (MessageDescription md in messages) { if (md.Direction == MessageDirection.Output) { if (md.Body.ReturnValue != null) { ExportDataContract (md.Body.ReturnValue); method.ReturnType = md.Body.ReturnValue.CodeTypeReference; } continue; } if (return_args) args = new CodeExpression [md.Body.Parts.Count]; MessagePartDescriptionCollection parts = md.Body.Parts; for (int i = 0; i < parts.Count; i++) { ExportDataContract (parts [i]); method.Parameters.Add ( new CodeParameterDeclarationExpression ( parts [i].CodeTypeReference, parts [i].Name)); if (return_args) args [i] = new CodeArgumentReferenceExpression (parts [i].Name); } } return args; } #endregion public CodeTypeReference GenerateServiceEndpoint ( ServiceEndpoint endpoint, out ChannelEndpointElement channelElement) { if (config == null) throw new InvalidOperationException (); var cd = endpoint.Contract; var cns = GetNamespace (cd.Namespace); var cache = ExportInterface_internal (cd, cns); string bindingSectionName, configurationName; GenerateBinding (endpoint.Binding, out bindingSectionName, out configurationName); channelElement = new ChannelEndpointElement (); channelElement.Binding = bindingSectionName; channelElement.BindingConfiguration = configurationName; channelElement.Name = configurationName; channelElement.Contract = cache.ConfigurationName; channelElement.Address = endpoint.Address.Uri; var section = (ClientSection)config.GetSection ("system.serviceModel/client"); section.Endpoints.Add (channelElement); return cache.GetReference (); } void MergeCompileUnit (CodeCompileUnit from, CodeCompileUnit to) { if (from == to) return; foreach (CodeNamespace fns in from.Namespaces) { bool merged = false; foreach (CodeNamespace tns in to.Namespaces) if (fns.Name == tns.Name) { // namespaces are merged. MergeNamespace (fns, tns); merged = true; break; } if (!merged) to.Namespaces.Add (fns); } } // existing type is skipped. void MergeNamespace (CodeNamespace from, CodeNamespace to) { foreach (CodeTypeDeclaration ftd in from.Types) { bool skip = false; foreach (CodeTypeDeclaration ttd in to.Types) if (ftd.Name == ttd.Name) { skip = true; break; } if (!skip) to.Types.Add (ftd); } } private void ExportDataContract (MessagePartDescription md) { if (data_contract_importer == null) data_contract_importer = md.DataContractImporter; else if (md.DataContractImporter != null && data_contract_importer != md.DataContractImporter) throw new Exception ("INTERNAL ERROR: should not happen"); if (xml_serialization_importer == null) xml_serialization_importer = md.XmlSerializationImporter; else if (md.XmlSerializationImporter != null && xml_serialization_importer != md.XmlSerializationImporter) throw new Exception ("INTERNAL ERROR: should not happen"); } private string GetXmlNamespace (CodeTypeDeclaration type) { foreach (CodeAttributeDeclaration attr in type.CustomAttributes) { if (attr.Name == "System.Xml.Serialization.XmlTypeAttribute" || attr.Name == "System.Xml.Serialization.XmlRootAttribute") { foreach (CodeAttributeArgument arg in attr.Arguments) if (arg.Name == "Namespace") return ((CodePrimitiveExpression)arg.Value).Value as string; //Could not find Namespace arg! return null; } } return null; } } }