966bba02bb
Former-commit-id: bb0468d0f257ff100aa895eb5fe583fb5dfbf900
931 lines
34 KiB
C#
931 lines
34 KiB
C#
//
|
|
// ServiceContractGenerator.cs
|
|
//
|
|
// Author:
|
|
// Atsushi Enomoto <atsushi@ximian.com>
|
|
//
|
|
// 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<System.ServiceModel.Description.IOperationContractGenerationExtension,System.ServiceModel.Description.OperationContractGenerationContext>;
|
|
|
|
namespace System.ServiceModel.Description
|
|
{
|
|
public class ServiceContractGenerator
|
|
{
|
|
CodeCompileUnit ccu;
|
|
ConfigurationType config;
|
|
CodeIdentifiers identifiers = new CodeIdentifiers ();
|
|
Collection<MetadataConversionError> errors
|
|
= new Collection<MetadataConversionError> ();
|
|
Dictionary<string,string> nsmappings
|
|
= new Dictionary<string,string> ();
|
|
Dictionary<ContractDescription,Type> referenced_types
|
|
= new Dictionary<ContractDescription,Type> ();
|
|
Dictionary<ContractDescription,ContractCacheEntry> generated_contracts
|
|
= new Dictionary<ContractDescription,ContractCacheEntry> ();
|
|
ServiceContractGenerationOptions options;
|
|
Dictionary<QName, QName> imported_names
|
|
= new Dictionary<QName, QName> ();
|
|
ServiceContractGenerationContext contract_context;
|
|
List<OPair> operation_contexts = new List<OPair> ();
|
|
|
|
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<MetadataConversionError> Errors {
|
|
get { return errors; }
|
|
}
|
|
|
|
public Dictionary<string,string> 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<ContractDescription,Type> 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<XmlSerializerMappingBehavior> () != 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<CodeExpression> args = new List<CodeExpression> ();
|
|
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<CodeVariableReferenceExpression> ();
|
|
|
|
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<CodeExpression> ().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<CodeParameterDeclarationExpression > ();
|
|
|
|
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<CodeExpression> (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<CodeExpression> ();
|
|
args.Add (beginOperDelegateRef);
|
|
args.Add (new CodeArrayCreateExpression (typeof (object), inArgs.ConvertAll<CodeExpression> (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;
|
|
}
|
|
|
|
|
|
}
|
|
}
|