ServiceContractGenerator.cs 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. namespace System.ServiceModel.Description
  5. {
  6. using System;
  7. using System.CodeDom;
  8. using System.CodeDom.Compiler;
  9. using System.Collections.Generic;
  10. using System.Collections.ObjectModel;
  11. using System.Configuration;
  12. using System.Diagnostics;
  13. using System.Net.Security;
  14. using System.Reflection;
  15. using System.Runtime;
  16. using System.ServiceModel;
  17. using System.ServiceModel.Channels;
  18. using System.ServiceModel.Configuration;
  19. public class ServiceContractGenerator
  20. {
  21. CodeCompileUnit compileUnit;
  22. ConfigWriter configWriter;
  23. Configuration configuration;
  24. NamespaceHelper namespaceManager;
  25. // options
  26. OptionsHelper options = new OptionsHelper(ServiceContractGenerationOptions.ChannelInterface |
  27. ServiceContractGenerationOptions.ClientClass);
  28. Dictionary<ContractDescription, Type> referencedTypes;
  29. Dictionary<ContractDescription, ServiceContractGenerationContext> generatedTypes;
  30. Dictionary<OperationDescription, OperationContractGenerationContext> generatedOperations;
  31. Dictionary<MessageDescription, CodeTypeReference> generatedTypedMessages;
  32. Collection<MetadataConversionError> errors = new Collection<MetadataConversionError>();
  33. public ServiceContractGenerator()
  34. : this(null, null)
  35. {
  36. }
  37. public ServiceContractGenerator(Configuration targetConfig)
  38. : this(null, targetConfig)
  39. {
  40. }
  41. public ServiceContractGenerator(CodeCompileUnit targetCompileUnit)
  42. : this(targetCompileUnit, null)
  43. {
  44. }
  45. public ServiceContractGenerator(CodeCompileUnit targetCompileUnit, Configuration targetConfig)
  46. {
  47. this.compileUnit = targetCompileUnit ?? new CodeCompileUnit();
  48. this.namespaceManager = new NamespaceHelper(this.compileUnit.Namespaces);
  49. AddReferencedAssembly(typeof(ServiceContractGenerator).Assembly);
  50. this.configuration = targetConfig;
  51. if (targetConfig != null)
  52. this.configWriter = new ConfigWriter(targetConfig);
  53. this.generatedTypes = new Dictionary<ContractDescription, ServiceContractGenerationContext>();
  54. this.generatedOperations = new Dictionary<OperationDescription, OperationContractGenerationContext>();
  55. this.referencedTypes = new Dictionary<ContractDescription, Type>();
  56. }
  57. internal CodeTypeReference GetCodeTypeReference(Type type)
  58. {
  59. AddReferencedAssembly(type.Assembly);
  60. return new CodeTypeReference(type);
  61. }
  62. internal void AddReferencedAssembly(Assembly assembly)
  63. {
  64. string assemblyName = System.IO.Path.GetFileName(assembly.Location);
  65. bool alreadyExisting = false;
  66. foreach (string existingName in this.compileUnit.ReferencedAssemblies)
  67. {
  68. if (String.Compare(existingName, assemblyName, StringComparison.OrdinalIgnoreCase) == 0)
  69. {
  70. alreadyExisting = true;
  71. break;
  72. }
  73. }
  74. if (!alreadyExisting)
  75. this.compileUnit.ReferencedAssemblies.Add(assemblyName);
  76. }
  77. // options
  78. public ServiceContractGenerationOptions Options
  79. {
  80. get { return this.options.Options; }
  81. set { this.options = new OptionsHelper(value); }
  82. }
  83. internal OptionsHelper OptionsInternal
  84. {
  85. get { return this.options; }
  86. }
  87. public Dictionary<ContractDescription, Type> ReferencedTypes
  88. {
  89. get { return this.referencedTypes; }
  90. }
  91. public CodeCompileUnit TargetCompileUnit
  92. {
  93. get { return this.compileUnit; }
  94. }
  95. public Configuration Configuration
  96. {
  97. get { return this.configuration; }
  98. }
  99. public Dictionary<string, string> NamespaceMappings
  100. {
  101. get { return this.NamespaceManager.NamespaceMappings; }
  102. }
  103. public Collection<MetadataConversionError> Errors
  104. {
  105. get { return errors; }
  106. }
  107. internal NamespaceHelper NamespaceManager
  108. {
  109. get { return this.namespaceManager; }
  110. }
  111. public void GenerateBinding(Binding binding, out string bindingSectionName, out string configurationName)
  112. {
  113. configWriter.WriteBinding(binding, out bindingSectionName, out configurationName);
  114. }
  115. public CodeTypeReference GenerateServiceEndpoint(ServiceEndpoint endpoint, out ChannelEndpointElement channelElement)
  116. {
  117. if (endpoint == null)
  118. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoint");
  119. if (configuration == null)
  120. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceContractGeneratorConfigRequired)));
  121. CodeTypeReference retVal;
  122. string typeName;
  123. Type existingType;
  124. if (referencedTypes.TryGetValue(endpoint.Contract, out existingType))
  125. {
  126. retVal = GetCodeTypeReference(existingType);
  127. typeName = existingType.FullName;
  128. }
  129. else
  130. {
  131. retVal = GenerateServiceContractType(endpoint.Contract);
  132. typeName = retVal.BaseType;
  133. }
  134. channelElement = configWriter.WriteChannelDescription(endpoint, typeName);
  135. return retVal;
  136. }
  137. public CodeTypeReference GenerateServiceContractType(ContractDescription contractDescription)
  138. {
  139. CodeTypeReference retVal = GenerateServiceContractTypeInternal(contractDescription);
  140. System.CodeDom.Compiler.CodeGenerator.ValidateIdentifiers(TargetCompileUnit);
  141. return retVal;
  142. }
  143. CodeTypeReference GenerateServiceContractTypeInternal(ContractDescription contractDescription)
  144. {
  145. if (contractDescription == null)
  146. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contractDescription");
  147. Type existingType;
  148. if (referencedTypes.TryGetValue(contractDescription, out existingType))
  149. {
  150. return GetCodeTypeReference(existingType);
  151. }
  152. ServiceContractGenerationContext context;
  153. CodeNamespace ns = this.NamespaceManager.EnsureNamespace(contractDescription.Namespace);
  154. if (!generatedTypes.TryGetValue(contractDescription, out context))
  155. {
  156. context = new ContextInitializer(this, new CodeTypeFactory(this, options.IsSet(ServiceContractGenerationOptions.InternalTypes))).CreateContext(contractDescription);
  157. ExtensionsHelper.CallContractExtensions(GetBeforeExtensionsBuiltInContractGenerators(), context);
  158. ExtensionsHelper.CallOperationExtensions(GetBeforeExtensionsBuiltInOperationGenerators(), context);
  159. ExtensionsHelper.CallBehaviorExtensions(context);
  160. ExtensionsHelper.CallContractExtensions(GetAfterExtensionsBuiltInContractGenerators(), context);
  161. ExtensionsHelper.CallOperationExtensions(GetAfterExtensionsBuiltInOperationGenerators(), context);
  162. generatedTypes.Add(contractDescription, context);
  163. }
  164. return context.ContractTypeReference;
  165. }
  166. IEnumerable<IServiceContractGenerationExtension> GetBeforeExtensionsBuiltInContractGenerators()
  167. {
  168. return EmptyArray<IServiceContractGenerationExtension>.Instance;
  169. }
  170. IEnumerable<IOperationContractGenerationExtension> GetBeforeExtensionsBuiltInOperationGenerators()
  171. {
  172. yield return new FaultContractAttributeGenerator();
  173. yield return new TransactionFlowAttributeGenerator();
  174. }
  175. IEnumerable<IServiceContractGenerationExtension> GetAfterExtensionsBuiltInContractGenerators()
  176. {
  177. if (this.options.IsSet(ServiceContractGenerationOptions.ChannelInterface))
  178. {
  179. yield return new ChannelInterfaceGenerator();
  180. }
  181. if (this.options.IsSet(ServiceContractGenerationOptions.ClientClass))
  182. {
  183. // unless the caller explicitly asks for TM we try to generate a helpful overload if we end up with TM
  184. bool tryAddHelperMethod = !this.options.IsSet(ServiceContractGenerationOptions.TypedMessages);
  185. bool generateEventAsyncMethods = this.options.IsSet(ServiceContractGenerationOptions.EventBasedAsynchronousMethods);
  186. yield return new ClientClassGenerator(tryAddHelperMethod, generateEventAsyncMethods);
  187. }
  188. }
  189. IEnumerable<IOperationContractGenerationExtension> GetAfterExtensionsBuiltInOperationGenerators()
  190. {
  191. return EmptyArray<IOperationContractGenerationExtension>.Instance;
  192. }
  193. internal static CodeExpression GetEnumReference<EnumType>(EnumType value)
  194. {
  195. return new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EnumType)), Enum.Format(typeof(EnumType), value, "G"));
  196. }
  197. internal Dictionary<MessageDescription, CodeTypeReference> GeneratedTypedMessages
  198. {
  199. get
  200. {
  201. if (generatedTypedMessages == null)
  202. generatedTypedMessages = new Dictionary<MessageDescription, CodeTypeReference>(MessageDescriptionComparer.Singleton);
  203. return generatedTypedMessages;
  204. }
  205. }
  206. internal class ContextInitializer
  207. {
  208. readonly ServiceContractGenerator parent;
  209. readonly CodeTypeFactory typeFactory;
  210. readonly bool asyncMethods;
  211. readonly bool taskMethod;
  212. ServiceContractGenerationContext context;
  213. UniqueCodeIdentifierScope contractMemberScope;
  214. UniqueCodeIdentifierScope callbackMemberScope;
  215. internal ContextInitializer(ServiceContractGenerator parent, CodeTypeFactory typeFactory)
  216. {
  217. this.parent = parent;
  218. this.typeFactory = typeFactory;
  219. this.asyncMethods = parent.OptionsInternal.IsSet(ServiceContractGenerationOptions.AsynchronousMethods);
  220. this.taskMethod = parent.OptionsInternal.IsSet(ServiceContractGenerationOptions.TaskBasedAsynchronousMethod);
  221. }
  222. public ServiceContractGenerationContext CreateContext(ContractDescription contractDescription)
  223. {
  224. VisitContract(contractDescription);
  225. Fx.Assert(this.context != null, "context was not initialized");
  226. return this.context;
  227. }
  228. // this could usefully be factored into a base class for use by WSDL export and others
  229. void VisitContract(ContractDescription contract)
  230. {
  231. this.Visit(contract);
  232. foreach (OperationDescription operation in contract.Operations)
  233. {
  234. this.Visit(operation);
  235. // not used in this case
  236. //foreach (MessageDescription message in operation.Messages)
  237. //{
  238. // this.Visit(message);
  239. //}
  240. }
  241. }
  242. void Visit(ContractDescription contractDescription)
  243. {
  244. bool isDuplex = IsDuplex(contractDescription);
  245. this.contractMemberScope = new UniqueCodeIdentifierScope();
  246. this.callbackMemberScope = isDuplex ? new UniqueCodeIdentifierScope() : null;
  247. UniqueCodeNamespaceScope codeNamespaceScope = new UniqueCodeNamespaceScope(parent.NamespaceManager.EnsureNamespace(contractDescription.Namespace));
  248. CodeTypeDeclaration contract = typeFactory.CreateInterfaceType();
  249. CodeTypeReference contractReference = codeNamespaceScope.AddUnique(contract, contractDescription.CodeName, Strings.DefaultContractName);
  250. CodeTypeDeclaration callbackContract = null;
  251. CodeTypeReference callbackContractReference = null;
  252. if (isDuplex)
  253. {
  254. callbackContract = typeFactory.CreateInterfaceType();
  255. callbackContractReference = codeNamespaceScope.AddUnique(callbackContract, contractDescription.CodeName + Strings.CallbackTypeSuffix, Strings.DefaultContractName);
  256. }
  257. this.context = new ServiceContractGenerationContext(parent, contractDescription, contract, callbackContract);
  258. this.context.Namespace = codeNamespaceScope.CodeNamespace;
  259. this.context.TypeFactory = this.typeFactory;
  260. this.context.ContractTypeReference = contractReference;
  261. this.context.DuplexCallbackTypeReference = callbackContractReference;
  262. AddServiceContractAttribute(this.context);
  263. }
  264. void Visit(OperationDescription operationDescription)
  265. {
  266. bool isCallback = operationDescription.IsServerInitiated();
  267. CodeTypeDeclaration declaringType = isCallback ? context.DuplexCallbackType : context.ContractType;
  268. UniqueCodeIdentifierScope memberScope = isCallback ? this.callbackMemberScope : this.contractMemberScope;
  269. Fx.Assert(declaringType != null, "missing callback type");
  270. string syncMethodName = memberScope.AddUnique(operationDescription.CodeName, Strings.DefaultOperationName);
  271. CodeMemberMethod syncMethod = new CodeMemberMethod();
  272. syncMethod.Name = syncMethodName;
  273. declaringType.Members.Add(syncMethod);
  274. OperationContractGenerationContext operationContext;
  275. CodeMemberMethod beginMethod = null;
  276. CodeMemberMethod endMethod = null;
  277. if (asyncMethods)
  278. {
  279. beginMethod = new CodeMemberMethod();
  280. beginMethod.Name = ServiceReflector.BeginMethodNamePrefix + syncMethodName;
  281. beginMethod.Parameters.Add(new CodeParameterDeclarationExpression(context.ServiceContractGenerator.GetCodeTypeReference(typeof(AsyncCallback)), Strings.AsyncCallbackArgName));
  282. beginMethod.Parameters.Add(new CodeParameterDeclarationExpression(context.ServiceContractGenerator.GetCodeTypeReference(typeof(object)), Strings.AsyncStateArgName));
  283. beginMethod.ReturnType = context.ServiceContractGenerator.GetCodeTypeReference(typeof(IAsyncResult));
  284. declaringType.Members.Add(beginMethod);
  285. endMethod = new CodeMemberMethod();
  286. endMethod.Name = ServiceReflector.EndMethodNamePrefix + syncMethodName;
  287. endMethod.Parameters.Add(new CodeParameterDeclarationExpression(context.ServiceContractGenerator.GetCodeTypeReference(typeof(IAsyncResult)), Strings.AsyncResultArgName));
  288. declaringType.Members.Add(endMethod);
  289. operationContext = new OperationContractGenerationContext(parent, context, operationDescription, declaringType, syncMethod, beginMethod, endMethod);
  290. }
  291. else
  292. {
  293. operationContext = new OperationContractGenerationContext(parent, context, operationDescription, declaringType, syncMethod);
  294. }
  295. if (taskMethod)
  296. {
  297. if (isCallback)
  298. {
  299. if (beginMethod == null)
  300. {
  301. operationContext = new OperationContractGenerationContext(parent, context, operationDescription, declaringType, syncMethod);
  302. }
  303. else
  304. {
  305. operationContext = new OperationContractGenerationContext(parent, context, operationDescription, declaringType, syncMethod, beginMethod, endMethod);
  306. }
  307. }
  308. else
  309. {
  310. CodeMemberMethod taskBasedAsyncMethod = new CodeMemberMethod { Name = syncMethodName + ServiceReflector.AsyncMethodNameSuffix };
  311. declaringType.Members.Add(taskBasedAsyncMethod);
  312. if (beginMethod == null)
  313. {
  314. operationContext = new OperationContractGenerationContext(parent, context, operationDescription, declaringType, syncMethod, taskBasedAsyncMethod);
  315. }
  316. else
  317. {
  318. operationContext = new OperationContractGenerationContext(parent, context, operationDescription, declaringType, syncMethod, beginMethod, endMethod, taskBasedAsyncMethod);
  319. }
  320. }
  321. }
  322. operationContext.DeclaringTypeReference = operationDescription.IsServerInitiated() ? context.DuplexCallbackTypeReference : context.ContractTypeReference;
  323. context.Operations.Add(operationContext);
  324. AddOperationContractAttributes(operationContext);
  325. }
  326. void AddServiceContractAttribute(ServiceContractGenerationContext context)
  327. {
  328. CodeAttributeDeclaration serviceContractAttr = new CodeAttributeDeclaration(context.ServiceContractGenerator.GetCodeTypeReference(typeof(ServiceContractAttribute)));
  329. if (context.ContractType.Name != context.Contract.CodeName)
  330. {
  331. // make sure that decoded Contract name can be used, if not, then override name with encoded value
  332. // specified in wsdl; this only works beacuse our Encoding algorithm will leave alredy encoded names untouched
  333. string friendlyName = NamingHelper.XmlName(context.Contract.CodeName) == context.Contract.Name ? context.Contract.CodeName : context.Contract.Name;
  334. serviceContractAttr.Arguments.Add(new CodeAttributeArgument("Name", new CodePrimitiveExpression(friendlyName)));
  335. }
  336. if (NamingHelper.DefaultNamespace != context.Contract.Namespace)
  337. serviceContractAttr.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(context.Contract.Namespace)));
  338. serviceContractAttr.Arguments.Add(new CodeAttributeArgument("ConfigurationName", new CodePrimitiveExpression(NamespaceHelper.GetCodeTypeReference(context.Namespace, context.ContractType).BaseType)));
  339. if (context.Contract.HasProtectionLevel)
  340. {
  341. serviceContractAttr.Arguments.Add(new CodeAttributeArgument("ProtectionLevel",
  342. new CodeFieldReferenceExpression(
  343. new CodeTypeReferenceExpression(typeof(ProtectionLevel)), context.Contract.ProtectionLevel.ToString())));
  344. }
  345. if (context.DuplexCallbackType != null)
  346. {
  347. serviceContractAttr.Arguments.Add(new CodeAttributeArgument("CallbackContract", new CodeTypeOfExpression(context.DuplexCallbackTypeReference)));
  348. }
  349. if (context.Contract.SessionMode != SessionMode.Allowed)
  350. {
  351. serviceContractAttr.Arguments.Add(new CodeAttributeArgument("SessionMode",
  352. new CodeFieldReferenceExpression(
  353. new CodeTypeReferenceExpression(typeof(SessionMode)), context.Contract.SessionMode.ToString())));
  354. }
  355. context.ContractType.CustomAttributes.Add(serviceContractAttr);
  356. }
  357. void AddOperationContractAttributes(OperationContractGenerationContext context)
  358. {
  359. if (context.SyncMethod != null)
  360. {
  361. context.SyncMethod.CustomAttributes.Add(CreateOperationContractAttributeDeclaration(context.Operation, false));
  362. }
  363. if (context.BeginMethod != null)
  364. {
  365. context.BeginMethod.CustomAttributes.Add(CreateOperationContractAttributeDeclaration(context.Operation, true));
  366. }
  367. if (context.TaskMethod != null)
  368. {
  369. context.TaskMethod.CustomAttributes.Add(CreateOperationContractAttributeDeclaration(context.Operation, false));
  370. }
  371. }
  372. CodeAttributeDeclaration CreateOperationContractAttributeDeclaration(OperationDescription operationDescription, bool asyncPattern)
  373. {
  374. CodeAttributeDeclaration serviceOperationAttr = new CodeAttributeDeclaration(context.ServiceContractGenerator.GetCodeTypeReference(typeof(OperationContractAttribute)));
  375. if (operationDescription.IsOneWay)
  376. {
  377. serviceOperationAttr.Arguments.Add(new CodeAttributeArgument("IsOneWay", new CodePrimitiveExpression(true)));
  378. }
  379. if ((operationDescription.DeclaringContract.SessionMode == SessionMode.Required) && operationDescription.IsTerminating)
  380. {
  381. serviceOperationAttr.Arguments.Add(new CodeAttributeArgument("IsTerminating", new CodePrimitiveExpression(true)));
  382. }
  383. if ((operationDescription.DeclaringContract.SessionMode == SessionMode.Required) && !operationDescription.IsInitiating)
  384. {
  385. serviceOperationAttr.Arguments.Add(new CodeAttributeArgument("IsInitiating", new CodePrimitiveExpression(false)));
  386. }
  387. if (asyncPattern)
  388. {
  389. serviceOperationAttr.Arguments.Add(new CodeAttributeArgument("AsyncPattern", new CodePrimitiveExpression(true)));
  390. }
  391. if (operationDescription.HasProtectionLevel)
  392. {
  393. serviceOperationAttr.Arguments.Add(new CodeAttributeArgument("ProtectionLevel",
  394. new CodeFieldReferenceExpression(
  395. new CodeTypeReferenceExpression(typeof(ProtectionLevel)), operationDescription.ProtectionLevel.ToString())));
  396. }
  397. return serviceOperationAttr;
  398. }
  399. static bool IsDuplex(ContractDescription contract)
  400. {
  401. foreach (OperationDescription operation in contract.Operations)
  402. if (operation.IsServerInitiated())
  403. return true;
  404. return false;
  405. }
  406. }
  407. class ChannelInterfaceGenerator : IServiceContractGenerationExtension
  408. {
  409. void IServiceContractGenerationExtension.GenerateContract(ServiceContractGenerationContext context)
  410. {
  411. CodeTypeDeclaration channelType = context.TypeFactory.CreateInterfaceType();
  412. channelType.BaseTypes.Add(context.ContractTypeReference);
  413. channelType.BaseTypes.Add(context.ServiceContractGenerator.GetCodeTypeReference(typeof(IClientChannel)));
  414. new UniqueCodeNamespaceScope(context.Namespace).AddUnique(channelType, context.ContractType.Name + Strings.ChannelTypeSuffix, Strings.ChannelTypeSuffix);
  415. }
  416. }
  417. internal class CodeTypeFactory
  418. {
  419. ServiceContractGenerator parent;
  420. bool internalTypes;
  421. public CodeTypeFactory(ServiceContractGenerator parent, bool internalTypes)
  422. {
  423. this.parent = parent;
  424. this.internalTypes = internalTypes;
  425. }
  426. public CodeTypeDeclaration CreateClassType()
  427. {
  428. return CreateCodeType(false);
  429. }
  430. CodeTypeDeclaration CreateCodeType(bool isInterface)
  431. {
  432. CodeTypeDeclaration codeType = new CodeTypeDeclaration();
  433. codeType.IsClass = !isInterface;
  434. codeType.IsInterface = isInterface;
  435. RunDecorators(codeType);
  436. return codeType;
  437. }
  438. public CodeTypeDeclaration CreateInterfaceType()
  439. {
  440. return CreateCodeType(true);
  441. }
  442. void RunDecorators(CodeTypeDeclaration codeType)
  443. {
  444. AddPartial(codeType);
  445. AddInternal(codeType);
  446. AddDebuggerStepThroughAttribute(codeType);
  447. AddGeneratedCodeAttribute(codeType);
  448. }
  449. #region CodeTypeDeclaration decorators
  450. void AddDebuggerStepThroughAttribute(CodeTypeDeclaration codeType)
  451. {
  452. if (codeType.IsClass)
  453. {
  454. codeType.CustomAttributes.Add(new CodeAttributeDeclaration(parent.GetCodeTypeReference(typeof(DebuggerStepThroughAttribute))));
  455. }
  456. }
  457. void AddGeneratedCodeAttribute(CodeTypeDeclaration codeType)
  458. {
  459. CodeAttributeDeclaration generatedCodeAttribute = new CodeAttributeDeclaration(parent.GetCodeTypeReference(typeof(GeneratedCodeAttribute)));
  460. AssemblyName assemblyName = Assembly.GetExecutingAssembly().GetName();
  461. generatedCodeAttribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(assemblyName.Name)));
  462. generatedCodeAttribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(assemblyName.Version.ToString())));
  463. codeType.CustomAttributes.Add(generatedCodeAttribute);
  464. }
  465. void AddInternal(CodeTypeDeclaration codeType)
  466. {
  467. if (internalTypes)
  468. {
  469. codeType.TypeAttributes &= ~TypeAttributes.Public;
  470. }
  471. }
  472. void AddPartial(CodeTypeDeclaration codeType)
  473. {
  474. if (codeType.IsClass)
  475. {
  476. codeType.IsPartial = true;
  477. }
  478. }
  479. #endregion
  480. }
  481. internal static class ExtensionsHelper
  482. {
  483. // calls the behavior extensions
  484. static internal void CallBehaviorExtensions(ServiceContractGenerationContext context)
  485. {
  486. CallContractExtensions(EnumerateBehaviorExtensions(context.Contract), context);
  487. foreach (OperationContractGenerationContext operationContext in context.Operations)
  488. {
  489. CallOperationExtensions(EnumerateBehaviorExtensions(operationContext.Operation), operationContext);
  490. }
  491. }
  492. // calls a specific set of contract-level extensions
  493. static internal void CallContractExtensions(IEnumerable<IServiceContractGenerationExtension> extensions, ServiceContractGenerationContext context)
  494. {
  495. foreach (IServiceContractGenerationExtension extension in extensions)
  496. {
  497. extension.GenerateContract(context);
  498. }
  499. }
  500. // calls a specific set of operation-level extensions on each operation in the contract
  501. static internal void CallOperationExtensions(IEnumerable<IOperationContractGenerationExtension> extensions, ServiceContractGenerationContext context)
  502. {
  503. foreach (OperationContractGenerationContext operationContext in context.Operations)
  504. {
  505. CallOperationExtensions(extensions, operationContext);
  506. }
  507. }
  508. // calls a specific set of operation-level extensions
  509. static void CallOperationExtensions(IEnumerable<IOperationContractGenerationExtension> extensions, OperationContractGenerationContext context)
  510. {
  511. foreach (IOperationContractGenerationExtension extension in extensions)
  512. {
  513. extension.GenerateOperation(context);
  514. }
  515. }
  516. static IEnumerable<IServiceContractGenerationExtension> EnumerateBehaviorExtensions(ContractDescription contract)
  517. {
  518. foreach (IContractBehavior behavior in contract.Behaviors)
  519. {
  520. if (behavior is IServiceContractGenerationExtension)
  521. {
  522. yield return (IServiceContractGenerationExtension)behavior;
  523. }
  524. }
  525. }
  526. static IEnumerable<IOperationContractGenerationExtension> EnumerateBehaviorExtensions(OperationDescription operation)
  527. {
  528. foreach (IOperationBehavior behavior in operation.Behaviors)
  529. {
  530. if (behavior is IOperationContractGenerationExtension)
  531. {
  532. yield return (IOperationContractGenerationExtension)behavior;
  533. }
  534. }
  535. }
  536. }
  537. class FaultContractAttributeGenerator : IOperationContractGenerationExtension
  538. {
  539. static CodeTypeReference voidTypeReference = new CodeTypeReference(typeof(void));
  540. void IOperationContractGenerationExtension.GenerateOperation(OperationContractGenerationContext context)
  541. {
  542. CodeMemberMethod methodDecl = context.SyncMethod ?? context.BeginMethod;
  543. foreach (FaultDescription fault in context.Operation.Faults)
  544. {
  545. CodeAttributeDeclaration faultAttr = CreateAttrDecl(context, fault);
  546. if (faultAttr != null)
  547. methodDecl.CustomAttributes.Add(faultAttr);
  548. }
  549. }
  550. static CodeAttributeDeclaration CreateAttrDecl(OperationContractGenerationContext context, FaultDescription fault)
  551. {
  552. CodeTypeReference exceptionTypeReference = fault.DetailType != null ? context.Contract.ServiceContractGenerator.GetCodeTypeReference(fault.DetailType) : fault.DetailTypeReference;
  553. if (exceptionTypeReference == null || exceptionTypeReference == voidTypeReference)
  554. return null;
  555. CodeAttributeDeclaration faultContractAttr = new CodeAttributeDeclaration(context.ServiceContractGenerator.GetCodeTypeReference(typeof(FaultContractAttribute)));
  556. faultContractAttr.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(exceptionTypeReference)));
  557. if (fault.Action != null)
  558. faultContractAttr.Arguments.Add(new CodeAttributeArgument("Action", new CodePrimitiveExpression(fault.Action)));
  559. if (fault.HasProtectionLevel)
  560. {
  561. faultContractAttr.Arguments.Add(new CodeAttributeArgument("ProtectionLevel",
  562. new CodeFieldReferenceExpression(
  563. new CodeTypeReferenceExpression(typeof(ProtectionLevel)), fault.ProtectionLevel.ToString())));
  564. }
  565. // override name with encoded value specified in wsdl; this only works beacuse
  566. // our Encoding algorithm will leave alredy encoded names untouched
  567. if (!XmlName.IsNullOrEmpty(fault.ElementName))
  568. faultContractAttr.Arguments.Add(new CodeAttributeArgument("Name", new CodePrimitiveExpression(fault.ElementName.EncodedName)));
  569. if (fault.Namespace != context.Contract.Contract.Namespace)
  570. faultContractAttr.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(fault.Namespace)));
  571. return faultContractAttr;
  572. }
  573. }
  574. class MessageDescriptionComparer : IEqualityComparer<MessageDescription>
  575. {
  576. static internal MessageDescriptionComparer Singleton = new MessageDescriptionComparer();
  577. MessageDescriptionComparer() { }
  578. bool IEqualityComparer<MessageDescription>.Equals(MessageDescription x, MessageDescription y)
  579. {
  580. if (x.XsdTypeName != y.XsdTypeName)
  581. return false;
  582. // compare headers
  583. if (x.Headers.Count != y.Headers.Count)
  584. return false;
  585. MessageHeaderDescription[] xHeaders = new MessageHeaderDescription[x.Headers.Count];
  586. x.Headers.CopyTo(xHeaders, 0);
  587. MessageHeaderDescription[] yHeaders = new MessageHeaderDescription[y.Headers.Count];
  588. y.Headers.CopyTo(yHeaders, 0);
  589. if (x.Headers.Count > 1)
  590. {
  591. Array.Sort((MessagePartDescription[])xHeaders, MessagePartDescriptionComparer.Singleton);
  592. Array.Sort((MessagePartDescription[])yHeaders, MessagePartDescriptionComparer.Singleton);
  593. }
  594. for (int i = 0; i < xHeaders.Length; i++)
  595. {
  596. if (MessagePartDescriptionComparer.Singleton.Compare(xHeaders[i], yHeaders[i]) != 0)
  597. return false;
  598. }
  599. return true;
  600. }
  601. int IEqualityComparer<MessageDescription>.GetHashCode(MessageDescription obj)
  602. {
  603. return obj.XsdTypeName.GetHashCode();
  604. }
  605. class MessagePartDescriptionComparer : IComparer<MessagePartDescription>
  606. {
  607. static internal MessagePartDescriptionComparer Singleton = new MessagePartDescriptionComparer();
  608. MessagePartDescriptionComparer() { }
  609. public int Compare(MessagePartDescription p1, MessagePartDescription p2)
  610. {
  611. if (null == p1)
  612. {
  613. return (null == p2) ? 0 : -1;
  614. }
  615. if (null == p2)
  616. {
  617. return 1;
  618. }
  619. int i = String.CompareOrdinal(p1.Namespace, p2.Namespace);
  620. if (i == 0)
  621. {
  622. i = String.CompareOrdinal(p1.Name, p2.Name);
  623. }
  624. return i;
  625. }
  626. }
  627. }
  628. internal class NamespaceHelper
  629. {
  630. static readonly object referenceKey = new object();
  631. const string WildcardNamespaceMapping = "*";
  632. readonly CodeNamespaceCollection codeNamespaces;
  633. Dictionary<string, string> namespaceMappings;
  634. public NamespaceHelper(CodeNamespaceCollection namespaces)
  635. {
  636. this.codeNamespaces = namespaces;
  637. }
  638. public Dictionary<string, string> NamespaceMappings
  639. {
  640. get
  641. {
  642. if (namespaceMappings == null)
  643. namespaceMappings = new Dictionary<string, string>();
  644. return namespaceMappings;
  645. }
  646. }
  647. string DescriptionToCode(string descriptionNamespace)
  648. {
  649. string target = String.Empty;
  650. // use field to avoid init'ing dictionary if possible
  651. if (namespaceMappings != null)
  652. {
  653. if (!namespaceMappings.TryGetValue(descriptionNamespace, out target))
  654. {
  655. // try to fall back to wildcard
  656. if (!namespaceMappings.TryGetValue(WildcardNamespaceMapping, out target))
  657. {
  658. return String.Empty;
  659. }
  660. }
  661. }
  662. return target;
  663. }
  664. public CodeNamespace EnsureNamespace(string descriptionNamespace)
  665. {
  666. string ns = DescriptionToCode(descriptionNamespace);
  667. CodeNamespace codeNamespace = FindNamespace(ns);
  668. if (codeNamespace == null)
  669. {
  670. codeNamespace = new CodeNamespace(ns);
  671. this.codeNamespaces.Add(codeNamespace);
  672. }
  673. return codeNamespace;
  674. }
  675. CodeNamespace FindNamespace(string ns)
  676. {
  677. foreach (CodeNamespace codeNamespace in this.codeNamespaces)
  678. {
  679. if (codeNamespace.Name == ns)
  680. return codeNamespace;
  681. }
  682. return null;
  683. }
  684. public static CodeTypeDeclaration GetCodeType(CodeTypeReference codeTypeReference)
  685. {
  686. return codeTypeReference.UserData[referenceKey] as CodeTypeDeclaration;
  687. }
  688. static internal CodeTypeReference GetCodeTypeReference(CodeNamespace codeNamespace, CodeTypeDeclaration codeType)
  689. {
  690. CodeTypeReference codeTypeReference = new CodeTypeReference(String.IsNullOrEmpty(codeNamespace.Name) ? codeType.Name : codeNamespace.Name + '.' + codeType.Name);
  691. codeTypeReference.UserData[referenceKey] = codeType;
  692. return codeTypeReference;
  693. }
  694. }
  695. internal struct OptionsHelper
  696. {
  697. public readonly ServiceContractGenerationOptions Options;
  698. public OptionsHelper(ServiceContractGenerationOptions options)
  699. {
  700. this.Options = options;
  701. }
  702. public bool IsSet(ServiceContractGenerationOptions option)
  703. {
  704. Fx.Assert(IsSingleBit((int)option), "");
  705. return ((this.Options & option) != ServiceContractGenerationOptions.None);
  706. }
  707. static bool IsSingleBit(int x)
  708. {
  709. //figures out if the mode has a single bit set ( is a power of 2)
  710. return (x != 0) && ((x & (x + ~0)) == 0);
  711. }
  712. }
  713. static class Strings
  714. {
  715. public const string AsyncCallbackArgName = "callback";
  716. public const string AsyncStateArgName = "asyncState";
  717. public const string AsyncResultArgName = "result";
  718. public const string CallbackTypeSuffix = "Callback";
  719. public const string ChannelTypeSuffix = "Channel";
  720. public const string DefaultContractName = "IContract";
  721. public const string DefaultOperationName = "Method";
  722. public const string InterfaceTypePrefix = "I";
  723. }
  724. // ideally this one would appear on TransactionFlowAttribute
  725. class TransactionFlowAttributeGenerator : IOperationContractGenerationExtension
  726. {
  727. void IOperationContractGenerationExtension.GenerateOperation(OperationContractGenerationContext context)
  728. {
  729. System.ServiceModel.TransactionFlowAttribute attr = context.Operation.Behaviors.Find<System.ServiceModel.TransactionFlowAttribute>();
  730. if (attr != null && attr.Transactions != TransactionFlowOption.NotAllowed)
  731. {
  732. CodeMemberMethod methodDecl = context.SyncMethod ?? context.BeginMethod;
  733. methodDecl.CustomAttributes.Add(CreateAttrDecl(context, attr));
  734. }
  735. }
  736. static CodeAttributeDeclaration CreateAttrDecl(OperationContractGenerationContext context, TransactionFlowAttribute attr)
  737. {
  738. CodeAttributeDeclaration attrDecl = new CodeAttributeDeclaration(context.Contract.ServiceContractGenerator.GetCodeTypeReference(typeof(TransactionFlowAttribute)));
  739. attrDecl.Arguments.Add(new CodeAttributeArgument(ServiceContractGenerator.GetEnumReference<TransactionFlowOption>(attr.Transactions)));
  740. return attrDecl;
  741. }
  742. }
  743. }
  744. }