e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
1850 lines
89 KiB
C#
1850 lines
89 KiB
C#
#region Copyright (c) Microsoft Corporation
|
|
/// <copyright company='Microsoft Corporation'>
|
|
/// Copyright (c) Microsoft Corporation. All Rights Reserved.
|
|
/// Information Contained Herein is Proprietary and Confidential.
|
|
/// </copyright>
|
|
#endregion
|
|
|
|
using System.CodeDom;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Globalization;
|
|
using System.Reflection;
|
|
using System.Runtime.Serialization;
|
|
using System.ServiceModel;
|
|
using System.ServiceModel.Channels;
|
|
using System.ServiceModel.Configuration;
|
|
using System.ServiceModel.Description;
|
|
using System.Xml;
|
|
using System.Xml.Schema;
|
|
using System.Security.Permissions;
|
|
using System.Linq;
|
|
|
|
#if WEB_EXTENSIONS_CODE
|
|
using System.Security;
|
|
using System.Web.Resources;
|
|
#else
|
|
using Microsoft.VSDesigner.WCF.Resources;
|
|
#endif
|
|
|
|
///
|
|
/// The VSWCFServiceContractGenerator takes a SvcMap file and it's associated metadata,
|
|
/// imports the metadata using a WsdlImporter and System.ServiceModel.ServiceContractGenerator
|
|
/// that are configured according to the options set in the SvcMap file
|
|
///
|
|
using Debug = System.Diagnostics.Debug;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
#if WEB_EXTENSIONS_CODE
|
|
namespace System.Web.Compilation.WCFModel
|
|
#else
|
|
namespace Microsoft.VSDesigner.WCFModel
|
|
#endif
|
|
{
|
|
/// <summary>
|
|
/// Proxy and configuration generator
|
|
/// </summary>
|
|
#if WEB_EXTENSIONS_CODE
|
|
[PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust")]
|
|
[SecurityCritical]
|
|
internal class VSWCFServiceContractGenerator
|
|
#else
|
|
// We only check for CLS compliant for the public version of this class since the
|
|
// compiler will complain about CLS compliance not being checked for non-public classes
|
|
[CLSCompliant(true)]
|
|
[PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
|
|
[PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
|
|
public class VSWCFServiceContractGenerator
|
|
#endif
|
|
{
|
|
|
|
#region Private backing fields
|
|
private const string VB_LANGUAGE_NAME = "vb";
|
|
|
|
/// <summary>
|
|
/// Collection to hold all bindings generated by this generator
|
|
/// </summary>
|
|
private IEnumerable<System.ServiceModel.Channels.Binding> bindingCollection;
|
|
|
|
/// <summary>
|
|
/// Collection to hold all contracts generated by this generator
|
|
/// </summary>
|
|
private IEnumerable<ContractDescription> contractCollection;
|
|
|
|
/// <summary>
|
|
/// Collection to hold all endpoints generated by this generator
|
|
/// </summary>
|
|
private List<ServiceEndpoint> serviceEndpointList;
|
|
|
|
/// <summary>
|
|
/// Map from service endpoint to the channel endpoint that was actually imported
|
|
/// </summary>
|
|
private Dictionary<ServiceEndpoint, ChannelEndpointElement> serviceEndpointToChannelEndpointElementMap;
|
|
|
|
/// <summary>
|
|
/// List of contract types generate by this generator
|
|
/// </summary>
|
|
private List<GeneratedContractType> proxyGeneratedContractTypes;
|
|
|
|
/// <summary>
|
|
/// The target compile unit that contains the proxy and data contracts
|
|
/// </summary>
|
|
private CodeCompileUnit targetCompileUnit;
|
|
|
|
/// <summary>
|
|
/// Configuration object that we inserterd bindings and endpoints from the
|
|
/// current service into. May be Null/Nothing.
|
|
/// </summary>
|
|
private System.Configuration.Configuration targetConfiguration;
|
|
|
|
/// <summary>
|
|
/// Errors encountered while generating the proxy
|
|
/// </summary>
|
|
private IEnumerable<ProxyGenerationError> proxyGenerationErrors;
|
|
|
|
/// <summary>
|
|
/// Errors encountered while importing the metadata..
|
|
/// </summary>
|
|
private IList<ProxyGenerationError> importErrors;
|
|
|
|
/// <summary>
|
|
/// Helper property that is added to Out parameters for VB
|
|
/// </summary>
|
|
private static CodeAttributeDeclaration outAttribute;
|
|
|
|
/// <summary>
|
|
/// version number for 3.5 framework
|
|
/// </summary>
|
|
private const int FRAMEWORK_VERSION_35 = 0x30005;
|
|
|
|
/// <summary>
|
|
/// list of types which are new in the 3.5 framework.
|
|
/// </summary>
|
|
private static Type[] unsupportedTypesInFramework30 = new Type[] {
|
|
typeof(DateTimeOffset),
|
|
};
|
|
|
|
|
|
#endregion
|
|
|
|
#region Public read-only properties
|
|
|
|
/// <summary>
|
|
/// The collection of bindings generated by this generator
|
|
/// </summary>
|
|
/// <value></value>
|
|
/// <remarks>
|
|
/// </remarks>
|
|
public IEnumerable<System.ServiceModel.Channels.Binding> BindingCollection
|
|
{
|
|
get
|
|
{
|
|
System.Diagnostics.Debug.Assert(bindingCollection != null);
|
|
return bindingCollection;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The collection of generated contract types
|
|
/// </summary>
|
|
public IEnumerable<GeneratedContractType> ProxyGeneratedContractTypes
|
|
{
|
|
get
|
|
{
|
|
System.Diagnostics.Debug.Assert(proxyGeneratedContractTypes != null);
|
|
return proxyGeneratedContractTypes;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The collection of errors encountered while generating the
|
|
/// proxy. For errors related to the metadata import, use the
|
|
/// ImportErrors property
|
|
/// </summary>
|
|
public IEnumerable<ProxyGenerationError> ProxyGenerationErrors
|
|
{
|
|
get
|
|
{
|
|
System.Diagnostics.Debug.Assert(proxyGenerationErrors != null);
|
|
return proxyGenerationErrors;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The collection of errors encountered while importing metadata.
|
|
/// For errors related to the proxy and config generation, use the
|
|
/// ProxyGenerationErrors property
|
|
/// </summary>
|
|
public IEnumerable<ProxyGenerationError> ImportErrors
|
|
{
|
|
get
|
|
{
|
|
System.Diagnostics.Debug.Assert(importErrors != null);
|
|
return importErrors;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The collection of contracts imported by this generator
|
|
/// </summary>
|
|
/// <value></value>
|
|
/// <remarks></remarks>
|
|
public IEnumerable<ContractDescription> ContractCollection
|
|
{
|
|
get
|
|
{
|
|
System.Diagnostics.Debug.Assert(contractCollection != null);
|
|
return contractCollection;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Collection of Endpoints in the service model generated by
|
|
/// this generator
|
|
/// </summary>
|
|
/// <value></value>
|
|
/// <remarks></remarks>
|
|
public IEnumerable<ServiceEndpoint> EndpointCollection
|
|
{
|
|
get
|
|
{
|
|
System.Diagnostics.Debug.Assert(serviceEndpointList != null);
|
|
return serviceEndpointList;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Map from service endpoints to its corresponding channel endpoint configuration
|
|
/// element
|
|
/// </summary>
|
|
public Dictionary<ServiceEndpoint, ChannelEndpointElement> EndpointMap
|
|
{
|
|
get
|
|
{
|
|
System.Diagnostics.Debug.Assert(serviceEndpointToChannelEndpointElementMap != null);
|
|
return serviceEndpointToChannelEndpointElementMap;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The configuratin into which we inject the bindings and endpoints. May be null/Nothing
|
|
/// if no target configuration was provided.
|
|
/// </summary>
|
|
public System.Configuration.Configuration TargetConfiguration
|
|
{
|
|
get
|
|
{
|
|
// Note: it is valid for this to be NULL. Caller beware!
|
|
return targetConfiguration;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// CodeCompileUnit containing the generated data contracts, service contracts
|
|
/// and WCF client.
|
|
/// </summary>
|
|
public CodeCompileUnit TargetCompileUnit
|
|
{
|
|
get
|
|
{
|
|
System.Diagnostics.Debug.Assert(targetCompileUnit != null);
|
|
return targetCompileUnit;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Cached instance of an Out attribute that we use to patch up
|
|
/// the codegen for VB projects (the VB code generates out parameters
|
|
/// as ByRef)
|
|
/// </summary>
|
|
private static CodeAttributeDeclaration OutAttribute
|
|
{
|
|
get
|
|
{
|
|
if (outAttribute == null)
|
|
{
|
|
outAttribute = new CodeAttributeDeclaration(typeof(System.Runtime.InteropServices.OutAttribute).FullName);
|
|
}
|
|
return outAttribute;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// protected constructor to block creating instance directly.
|
|
/// </summary>
|
|
/// <param name="importErrors"></param>
|
|
/// <param name="targetCompileUnit"></param>
|
|
/// <param name="targetConfiguration">May be null</param>
|
|
/// <param name="bindingCollection"></param>
|
|
/// <param name="contractCollection"></param>
|
|
/// <param name="serviceEndpointList"></param>
|
|
/// <param name="serviceEndpointToChannelEndpointElementMap"></param>
|
|
/// <param name="proxyGeneratedContractTypes"></param>
|
|
/// <param name="proxyGenerationErrors"></param>
|
|
protected VSWCFServiceContractGenerator(
|
|
List<ProxyGenerationError> importErrors,
|
|
CodeCompileUnit targetCompileUnit,
|
|
System.Configuration.Configuration targetConfiguration,
|
|
IEnumerable<System.ServiceModel.Channels.Binding> bindingCollection,
|
|
IEnumerable<ContractDescription> contractCollection,
|
|
List<ServiceEndpoint> serviceEndpointList,
|
|
Dictionary<ServiceEndpoint, ChannelEndpointElement> serviceEndpointToChannelEndpointElementMap,
|
|
List<GeneratedContractType> proxyGeneratedContractTypes,
|
|
IEnumerable<ProxyGenerationError> proxyGenerationErrors)
|
|
{
|
|
if (importErrors == null) throw new ArgumentNullException("importErrors");
|
|
if (targetCompileUnit == null) throw new ArgumentNullException("targetCompileUnit");
|
|
// Please note - target configuration may be NULL
|
|
if (bindingCollection == null) throw new ArgumentNullException("bindingCollection");
|
|
if (contractCollection == null) throw new ArgumentNullException("contractCollection");
|
|
if (serviceEndpointList == null) throw new ArgumentNullException("serviceEndpointList");
|
|
if (serviceEndpointToChannelEndpointElementMap == null) throw new ArgumentNullException("serviceEndpointToChannelEndpointElementMap");
|
|
if (proxyGeneratedContractTypes == null) throw new ArgumentNullException("proxyGeneratedContractTypes");
|
|
if (proxyGenerationErrors == null) throw new ArgumentNullException("proxyGenerationErrors");
|
|
|
|
this.importErrors = importErrors;
|
|
this.targetCompileUnit = targetCompileUnit;
|
|
this.targetConfiguration = targetConfiguration;
|
|
this.bindingCollection = bindingCollection;
|
|
this.contractCollection = contractCollection;
|
|
this.serviceEndpointList = serviceEndpointList;
|
|
this.serviceEndpointToChannelEndpointElementMap = serviceEndpointToChannelEndpointElementMap;
|
|
this.proxyGeneratedContractTypes = proxyGeneratedContractTypes;
|
|
this.proxyGenerationErrors = proxyGenerationErrors;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Factory method: generate code and return the resulting VSWCFServiceContractGenerator.
|
|
/// </summary>
|
|
/// <param name="svcMapFile">
|
|
/// The SvcMapFile that lists the metadata and generation options for the service reference.
|
|
/// </param>
|
|
/// <param name="toolConfiguration">
|
|
/// Configuration from which we are going to pick up WSDL and policy importer extensions as well
|
|
/// as custom MEX bindings for metadata download. May be Null/Nothing.
|
|
/// </param>
|
|
/// <param name="codeDomProvider">
|
|
/// CodeDom provider that is to be used to generate the client code.
|
|
/// </param>
|
|
/// <param name="proxyNamespace">
|
|
/// CLR namespace in which to generate the client code.
|
|
/// </param>
|
|
/// <param name="targetConfiguration">
|
|
/// The configuration into which we will put bindings/endpoints for this service
|
|
/// reference. May be Null/Nothing.
|
|
/// </param>
|
|
/// <param name="configurationNamespace">
|
|
/// The namespace that is to be used in configuration for this service reference.
|
|
/// </param>
|
|
/// <param name="serviceProviderForImportExtensions">
|
|
/// Service provider that we'll pass on to import extensions that accept our site:ing
|
|
/// mechanism
|
|
/// </param>
|
|
/// <param name="typeLoader">
|
|
/// Type loader that can be used to find reference assemblies and/or resolve shared service and
|
|
/// data contract types.
|
|
/// </param>
|
|
/// <param name="targetFrameworkVersion">
|
|
/// The target framework version number. The higher 16 bits contains the major version number, and low 16 bits contains minor version number.
|
|
/// </param>
|
|
/// <param name="typedDataSetSchemaImporterExtension">
|
|
/// Schema importer extension to be used for typed datasets.
|
|
/// </param>
|
|
/// <returns>
|
|
/// A VSWCFServiceContractGenerator instance that contains the result of the generation. To get
|
|
/// hold of the generated information, you can query it's properties.
|
|
/// </returns>
|
|
public static VSWCFServiceContractGenerator GenerateCodeAndConfiguration(SvcMapFile svcMapFile,
|
|
System.Configuration.Configuration toolConfiguration,
|
|
System.CodeDom.Compiler.CodeDomProvider codeDomProvider,
|
|
string proxyNamespace,
|
|
System.Configuration.Configuration targetConfiguration,
|
|
string configurationNamespace,
|
|
IServiceProvider serviceProviderForImportExtensions,
|
|
IContractGeneratorReferenceTypeLoader typeLoader,
|
|
int targetFrameworkVersion,
|
|
System.Type typedDataSetSchemaImporterExtension)
|
|
{
|
|
if (svcMapFile == null) throw new ArgumentNullException("svcMapFile");
|
|
if (codeDomProvider == null) throw new ArgumentNullException("codeDomProvider");
|
|
if (typedDataSetSchemaImporterExtension == null) throw new ArgumentNullException("typedDataSetSchemaImporterExtension");
|
|
|
|
List<ProxyGenerationError> importErrors = new List<ProxyGenerationError>();
|
|
List<ProxyGenerationError> proxyGenerationErrors = new List<ProxyGenerationError>();
|
|
|
|
CodeCompileUnit targetCompileUnit = new CodeCompileUnit();
|
|
|
|
WsdlImporter wsdlImporter = CreateWsdlImporter(svcMapFile,
|
|
toolConfiguration,
|
|
targetCompileUnit,
|
|
codeDomProvider,
|
|
proxyNamespace,
|
|
serviceProviderForImportExtensions,
|
|
typeLoader,
|
|
targetFrameworkVersion,
|
|
importErrors,
|
|
typedDataSetSchemaImporterExtension);
|
|
|
|
ServiceContractGenerator contractGenerator = CreateContractGenerator(svcMapFile.ClientOptions,
|
|
wsdlImporter,
|
|
targetCompileUnit,
|
|
proxyNamespace,
|
|
targetConfiguration,
|
|
typeLoader,
|
|
targetFrameworkVersion,
|
|
importErrors);
|
|
|
|
try
|
|
{
|
|
List<ServiceEndpoint> serviceEndpointList = new List<ServiceEndpoint>();
|
|
IEnumerable<System.ServiceModel.Channels.Binding> bindingCollection;
|
|
IEnumerable<ContractDescription> contractCollection;
|
|
|
|
ImportWCFModel(wsdlImporter,
|
|
targetCompileUnit,
|
|
importErrors,
|
|
out serviceEndpointList,
|
|
out bindingCollection,
|
|
out contractCollection);
|
|
|
|
Dictionary<ServiceEndpoint, ChannelEndpointElement> serviceEndpointToChannelEndpointElementMap;
|
|
List<GeneratedContractType> proxyGeneratedContractTypes;
|
|
|
|
GenerateProxy(wsdlImporter,
|
|
contractGenerator,
|
|
targetCompileUnit,
|
|
proxyNamespace,
|
|
configurationNamespace,
|
|
contractCollection,
|
|
bindingCollection,
|
|
serviceEndpointList,
|
|
proxyGenerationErrors,
|
|
out serviceEndpointToChannelEndpointElementMap,
|
|
out proxyGeneratedContractTypes);
|
|
|
|
if (IsVBCodeDomProvider(codeDomProvider))
|
|
{
|
|
PatchOutParametersInVB(targetCompileUnit);
|
|
}
|
|
|
|
return new VSWCFServiceContractGenerator(importErrors,
|
|
targetCompileUnit,
|
|
targetConfiguration,
|
|
bindingCollection,
|
|
contractCollection,
|
|
serviceEndpointList,
|
|
serviceEndpointToChannelEndpointElementMap,
|
|
proxyGeneratedContractTypes,
|
|
proxyGenerationErrors);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// fatal error... (workaround for bug #135242)
|
|
// We want to convert fatal error exception to a normal code generator error message,
|
|
// so the user could find information from pervious errors to find KB topic.
|
|
proxyGenerationErrors.Add(new ProxyGenerationError(
|
|
ProxyGenerationError.GeneratorState.GenerateCode,
|
|
String.Empty,
|
|
ex,
|
|
false));
|
|
|
|
return new VSWCFServiceContractGenerator(importErrors,
|
|
new CodeCompileUnit(),
|
|
targetConfiguration,
|
|
new List<System.ServiceModel.Channels.Binding>(),
|
|
new List<ContractDescription>(),
|
|
new List<ServiceEndpoint>(),
|
|
new Dictionary<ServiceEndpoint, ChannelEndpointElement>(),
|
|
new List<GeneratedContractType>(),
|
|
proxyGenerationErrors);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Instantiate and configure a ServiceContractGenerator to be used for code and config
|
|
/// generation.
|
|
/// </summary>
|
|
/// <param name="proxyOptions">
|
|
/// Options set in the SvcMap file to control the code/config generation.
|
|
/// </param>
|
|
/// <param name="wsdlImporter">
|
|
/// The WsdlImporter that is to be used to import the metadata for this service reference.
|
|
/// </param>
|
|
/// <param name="targetCompileUnit">
|
|
/// Compile unit into which we will generate the client code
|
|
/// </param>
|
|
/// <param name="proxyNamespace">
|
|
/// The CLR namespace into which we will generate the client code.
|
|
/// </param>
|
|
/// <param name="targetConfiguration">
|
|
/// Optional configuration into which we will generate the endpoints/bindings corresponding
|
|
/// to this service reference. May be Null/Nothing, in which case we will not generate config.
|
|
/// </param>
|
|
/// <param name="typeLoader">
|
|
/// Type loader that can be used to find reference assemblies and/or resolve shared service and
|
|
/// data contract types.
|
|
/// </param>
|
|
/// <param name="targetFrameworkVersion">
|
|
/// The target framework version number. The higher 16 bits contains the major version number, and low 16 bits contains minor version number.
|
|
/// </param>
|
|
/// <param name="importErrors">
|
|
/// The list into which we will add any errors while importing the metadata.
|
|
/// </param>
|
|
/// <returns></returns>
|
|
protected static ServiceContractGenerator CreateContractGenerator(ClientOptions proxyOptions,
|
|
WsdlImporter wsdlImporter,
|
|
CodeCompileUnit targetCompileUnit,
|
|
string proxyNamespace,
|
|
System.Configuration.Configuration targetConfiguration,
|
|
IContractGeneratorReferenceTypeLoader typeLoader,
|
|
int targetFrameworkVersion,
|
|
IList<ProxyGenerationError> importErrors)
|
|
{
|
|
ServiceContractGenerator contractGenerator = new ServiceContractGenerator(targetCompileUnit, targetConfiguration);
|
|
|
|
// We want to generate all types into the proxy namespace CLR namespace. We indicate
|
|
// this by adding a namespace mapping from all XML namespaces ("*") to the namespace
|
|
// the caller told us to generate the client code in.
|
|
contractGenerator.NamespaceMappings.Add("*", proxyNamespace);
|
|
|
|
if (proxyOptions.GenerateInternalTypes)
|
|
{
|
|
contractGenerator.Options |= ServiceContractGenerationOptions.InternalTypes;
|
|
}
|
|
else
|
|
{
|
|
contractGenerator.Options &= ~ServiceContractGenerationOptions.InternalTypes;
|
|
}
|
|
|
|
// Make sure at most one of the async options will be set: AsynchronousMethods | TaskBasedAsynchronousMethod.
|
|
contractGenerator.Options &= ~ServiceContractGenerationOptions.AsynchronousMethods &
|
|
~ServiceContractGenerationOptions.EventBasedAsynchronousMethods &
|
|
~ServiceContractGenerationOptions.TaskBasedAsynchronousMethod;
|
|
|
|
if (proxyOptions.GenerateTaskBasedAsynchronousMethod)
|
|
{
|
|
contractGenerator.Options |= ServiceContractGenerationOptions.TaskBasedAsynchronousMethod;
|
|
}
|
|
else if (proxyOptions.GenerateAsynchronousMethods)
|
|
{
|
|
contractGenerator.Options |= ServiceContractGenerationOptions.AsynchronousMethods;
|
|
if (targetFrameworkVersion >= FRAMEWORK_VERSION_35)
|
|
{
|
|
contractGenerator.Options |= ServiceContractGenerationOptions.EventBasedAsynchronousMethods;
|
|
}
|
|
}
|
|
|
|
if (proxyOptions.GenerateMessageContracts)
|
|
{
|
|
contractGenerator.Options |= ServiceContractGenerationOptions.TypedMessages;
|
|
}
|
|
else
|
|
{
|
|
contractGenerator.Options &= ~ServiceContractGenerationOptions.TypedMessages;
|
|
}
|
|
|
|
// If we have a type loader, we tell the contract generator and wsdl importer about
|
|
// all shared types and assemblies that we've specified in the proxy options...
|
|
if (typeLoader != null)
|
|
{
|
|
foreach (ContractMapping mapping in proxyOptions.ServiceContractMappingList)
|
|
{
|
|
try
|
|
{
|
|
Type sharedType = typeLoader.LoadType(mapping.TypeName);
|
|
|
|
// verify that the type is shareable - if not, we generate an error...
|
|
if (!IsTypeShareable(sharedType))
|
|
{
|
|
importErrors.Add(
|
|
new ProxyGenerationError(
|
|
ProxyGenerationError.GeneratorState.GenerateCode,
|
|
String.Empty,
|
|
new FormatException(String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_SharedTypeMustBePublic, mapping.TypeName)))
|
|
);
|
|
continue;
|
|
}
|
|
|
|
// Get a contract description corresponding to the type we wanted to share
|
|
ContractDescription contract = ContractDescription.GetContract(sharedType);
|
|
|
|
if (!String.Equals(mapping.Name, contract.Name, StringComparison.Ordinal) ||
|
|
!String.Equals(mapping.TargetNamespace, contract.Namespace, StringComparison.Ordinal))
|
|
{
|
|
// mismatch
|
|
importErrors.Add(
|
|
new ProxyGenerationError(
|
|
ProxyGenerationError.GeneratorState.GenerateCode,
|
|
String.Empty,
|
|
new FormatException(String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_ServiceContractMappingMissMatch, mapping.TypeName, contract.Namespace, contract.Name, mapping.TargetNamespace, mapping.Name)))
|
|
);
|
|
}
|
|
|
|
XmlQualifiedName qname = new XmlQualifiedName(contract.Name, contract.Namespace);
|
|
wsdlImporter.KnownContracts.Add(qname, contract);
|
|
contractGenerator.ReferencedTypes.Add(contract, sharedType);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
importErrors.Add(new ProxyGenerationError(
|
|
ProxyGenerationError.GeneratorState.GenerateCode,
|
|
String.Empty,
|
|
ex));
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (NamespaceMapping namespaceMapping in proxyOptions.NamespaceMappingList)
|
|
{
|
|
contractGenerator.NamespaceMappings.Add(namespaceMapping.TargetNamespace, namespaceMapping.ClrNamespace);
|
|
}
|
|
|
|
return contractGenerator;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate Proxy Code and (if available) configuration
|
|
/// </summary>
|
|
/// <param name="contractGenerator">The contract generator to use</param>
|
|
/// <param name="targetCompileUnit">Compile unit into which we should generate the client code</param>
|
|
/// <param name="proxyNamespace">CLR namespace into which we should generate the client code</param>
|
|
/// <param name="configurationNamespace">Namespace to use in configuration</param>
|
|
/// <param name="contractCollection">The contracts for which we should generate code and optionally config</param>
|
|
/// <param name="bindingCollection">The bindings we should generate config for</param>
|
|
/// <param name="serviceEndpointList">The endpoints we should generate config for</param>
|
|
/// <param name="proxyGenerationErrors">A list of errors encountered while generating the client</param>
|
|
/// <param name="serviceEndpointToChannelEndpointElementMap">Map from service endpoint to the configuration element for the endpoint</param>
|
|
/// <param name="proxyGeneratedContractTypes">The generated contract types</param>
|
|
protected static void GenerateProxy(WsdlImporter importer,
|
|
ServiceContractGenerator contractGenerator,
|
|
CodeCompileUnit targetCompileUnit,
|
|
string proxyNamespace,
|
|
string configurationNamespace,
|
|
IEnumerable<ContractDescription> contractCollection,
|
|
IEnumerable<System.ServiceModel.Channels.Binding> bindingCollection,
|
|
List<ServiceEndpoint> serviceEndpointList,
|
|
IList<ProxyGenerationError> proxyGenerationErrors,
|
|
out Dictionary<ServiceEndpoint, ChannelEndpointElement> serviceEndpointToChannelEndpointElementMap,
|
|
out List<GeneratedContractType> proxyGeneratedContractTypes)
|
|
{
|
|
// Parameter checking
|
|
if (serviceEndpointList == null) throw new ArgumentNullException("serviceEndpointList");
|
|
if (bindingCollection == null) throw new ArgumentNullException("bindingCollection");
|
|
if (contractCollection == null) throw new ArgumentNullException("contractCollection");
|
|
if (proxyGenerationErrors == null) throw new ArgumentNullException("proxyGenerationErrors");
|
|
|
|
proxyGeneratedContractTypes = new List<GeneratedContractType>();
|
|
serviceEndpointToChannelEndpointElementMap = new Dictionary<ServiceEndpoint, ChannelEndpointElement>();
|
|
|
|
try
|
|
{
|
|
HttpBindingExtension httpBindingEx = importer.WsdlImportExtensions.Find<HttpBindingExtension>();
|
|
|
|
foreach (ContractDescription contract in contractCollection)
|
|
{
|
|
if (httpBindingEx == null || !httpBindingEx.IsHttpBindingContract(contract) || serviceEndpointList.Any(endpoint => endpoint.Contract == contract))
|
|
{
|
|
CodeTypeReference typeReference = contractGenerator.GenerateServiceContractType(contract);
|
|
if (typeReference != null)
|
|
{
|
|
// keep the (targetNamespace, portType) -> CLR type map table...
|
|
|
|
string baseType = typeReference.BaseType;
|
|
|
|
GeneratedContractType generatedType = new GeneratedContractType(contract.Namespace, contract.Name, baseType, baseType);
|
|
proxyGeneratedContractTypes.Add(generatedType);
|
|
}
|
|
}
|
|
}
|
|
|
|
// We should only import the Binding & Endpoints if there is a configuration storage...
|
|
if (contractGenerator.Configuration != null)
|
|
{
|
|
foreach (ServiceEndpoint endpoint in serviceEndpointList)
|
|
{
|
|
ChannelEndpointElement endpointElement = null;
|
|
contractGenerator.GenerateServiceEndpoint(endpoint, out endpointElement);
|
|
serviceEndpointToChannelEndpointElementMap[endpoint] = endpointElement;
|
|
}
|
|
|
|
foreach (System.ServiceModel.Channels.Binding bindingDescription in bindingCollection)
|
|
{
|
|
string bindingSectionName = null;
|
|
string bindingConfigurationName = null;
|
|
// Generate binding will change the state of the contractGenerator...
|
|
contractGenerator.GenerateBinding(bindingDescription, out bindingSectionName, out bindingConfigurationName);
|
|
}
|
|
|
|
}
|
|
|
|
PatchConfigurationName(proxyNamespace,
|
|
configurationNamespace,
|
|
proxyGeneratedContractTypes,
|
|
serviceEndpointToChannelEndpointElementMap.Values,
|
|
targetCompileUnit);
|
|
}
|
|
finally
|
|
{
|
|
foreach (MetadataConversionError error in contractGenerator.Errors)
|
|
{
|
|
proxyGenerationErrors.Add(new ProxyGenerationError(error));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create appropriate XmlSerializerImportOptions for the generator
|
|
/// </summary>
|
|
/// <param name="proxyOptions">Options for the code/config generation</param>
|
|
/// <param name="targetCompileUnit">Compile unit we are going to generate the client code in</param>
|
|
/// <param name="codeDomProvider">CodeDom provider for the language we are using</param>
|
|
/// <param name="proxyNamespace">CLR namespace we'll put the client code in</param>
|
|
/// <returns></returns>
|
|
protected static XmlSerializerImportOptions CreateXmlSerializerImportOptions(
|
|
ClientOptions proxyOptions,
|
|
CodeCompileUnit targetCompileUnit,
|
|
System.CodeDom.Compiler.CodeDomProvider codeDomProvider,
|
|
string proxyNamespace,
|
|
System.Type typedDataSetSchemaImporterExtension)
|
|
{
|
|
System.ServiceModel.Channels.XmlSerializerImportOptions xmlSerializerOptions = new XmlSerializerImportOptions(targetCompileUnit);
|
|
System.Web.Services.Description.WebReferenceOptions webReferenceOptions = new System.Web.Services.Description.WebReferenceOptions();
|
|
|
|
webReferenceOptions.CodeGenerationOptions = System.Xml.Serialization.CodeGenerationOptions.GenerateProperties | System.Xml.Serialization.CodeGenerationOptions.GenerateOrder;
|
|
|
|
if (proxyOptions.EnableDataBinding)
|
|
{
|
|
webReferenceOptions.CodeGenerationOptions |= System.Xml.Serialization.CodeGenerationOptions.EnableDataBinding;
|
|
}
|
|
|
|
webReferenceOptions.SchemaImporterExtensions.Add(typedDataSetSchemaImporterExtension.AssemblyQualifiedName);
|
|
webReferenceOptions.SchemaImporterExtensions.Add(typeof(System.Data.DataSetSchemaImporterExtension).AssemblyQualifiedName);
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
xmlSerializerOptions.WebReferenceOptions = webReferenceOptions;
|
|
xmlSerializerOptions.CodeProvider = codeDomProvider;
|
|
|
|
xmlSerializerOptions.ClrNamespace = proxyNamespace;
|
|
|
|
return xmlSerializerOptions;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create an appropriate XsdDataContractImporter for the generator
|
|
/// </summary>
|
|
/// <param name="proxyOptions">Code/config generation options to use</param>
|
|
/// <param name="targetCompileUnit">CodeCompileUnit into which we will generate the client code</param>
|
|
/// <param name="codeDomProvider">CodeDomProvider for the language we will use to generate the client</param>
|
|
/// <param name="proxyNamespace">CLR namespace in which the client code will be generated</param>
|
|
/// <param name="typeLoader">Service used to resolve type/assembly names (strings) to actual Types and Assemblies</param>
|
|
/// <param name="targetFrameworkVersion">Targetted Framework version number</param>
|
|
/// <param name="importErrors">List of errors encountered. New errors will be added to this list</param>
|
|
/// <returns></returns>
|
|
protected static XsdDataContractImporter CreateDataContractImporter(
|
|
ClientOptions proxyOptions,
|
|
CodeCompileUnit targetCompileUnit,
|
|
System.CodeDom.Compiler.CodeDomProvider codeDomProvider,
|
|
string proxyNamespace,
|
|
IContractGeneratorReferenceTypeLoader typeLoader,
|
|
int targetFrameworkVersion,
|
|
IList<ProxyGenerationError> importErrors)
|
|
{
|
|
System.Runtime.Serialization.XsdDataContractImporter xsdDataContractImporter = new System.Runtime.Serialization.XsdDataContractImporter(targetCompileUnit);
|
|
System.Runtime.Serialization.ImportOptions options = new System.Runtime.Serialization.ImportOptions();
|
|
|
|
options.CodeProvider = codeDomProvider;
|
|
|
|
// We specify that we want to generate all types from all XML namespaces into
|
|
// our proxy namespace. By default, each XML namespace get's its own CLR namespace
|
|
options.Namespaces.Add("*", proxyNamespace);
|
|
options.GenerateInternal = proxyOptions.GenerateInternalTypes;
|
|
options.GenerateSerializable = proxyOptions.GenerateSerializableTypes;
|
|
options.EnableDataBinding = proxyOptions.EnableDataBinding;
|
|
options.ImportXmlType = proxyOptions.ImportXmlTypes;
|
|
|
|
if (typeLoader != null)
|
|
{
|
|
IEnumerable<Type> referencedTypes = LoadSharedDataContractTypes(proxyOptions, typeLoader, targetFrameworkVersion, importErrors);
|
|
if (referencedTypes != null)
|
|
{
|
|
foreach (Type sharedType in referencedTypes)
|
|
{
|
|
options.ReferencedTypes.Add(sharedType);
|
|
}
|
|
}
|
|
|
|
IEnumerable<Type> referencedCollectionTypes = LoadSharedCollectionTypes(proxyOptions, typeLoader, importErrors);
|
|
if (referencedCollectionTypes != null)
|
|
{
|
|
foreach (Type collectionType in referencedCollectionTypes)
|
|
{
|
|
options.ReferencedCollectionTypes.Add(collectionType);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
foreach (NamespaceMapping namespaceMapping in proxyOptions.NamespaceMappingList)
|
|
{
|
|
options.Namespaces.Add(namespaceMapping.TargetNamespace, namespaceMapping.ClrNamespace);
|
|
}
|
|
|
|
xsdDataContractImporter.Options = options;
|
|
|
|
return xsdDataContractImporter;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load DataContract types which could be used in the code generator (shared types)
|
|
/// </summary>
|
|
/// <param name="proxyOptions">Options controlling the generation</param>
|
|
/// <param name="typeLoader">Type loader to resolve referenced assemblies and types</param>
|
|
/// <param name="targetFrameworkVersion">Targetted Framework version number</param>
|
|
/// <param name="importErrors">Errors encountered while loading the shared data contracts</param>
|
|
/// <return>
|
|
/// A list of CLR types from referenced assemblies and/or specific types that we want to share
|
|
/// </return>
|
|
/// <remarks></remarks>
|
|
[SuppressMessage("Microsoft.Usage", "CA2301:EmbeddableTypesInContainersRule", MessageId = "sharedTypeTable", Justification = "This is used within VS to get the types from reference assemblies i.e., the reference assembly for a given assembly but not across assemblies - so com interop is not an issue.")]
|
|
protected static IEnumerable<Type> LoadSharedDataContractTypes(
|
|
ClientOptions proxyOptions, IContractGeneratorReferenceTypeLoader typeLoader, int targetFrameworkVersion, IList<ProxyGenerationError> importErrors)
|
|
{
|
|
if (typeLoader == null) throw new ArgumentNullException("typeLoader");
|
|
|
|
// the value in sharedTypeTable is why we add this type in the shared type list.
|
|
// if it is only added because it is in the referenced assembly, the value will be null, otherwise it contains the entry in the type inclusion list
|
|
// if the type is also in the exclusion list, we will report an error if the type is comming from the inclusion list, but no error if it comes from a referenced assembly only.
|
|
Dictionary<Type, ReferencedType> sharedTypeTable = new Dictionary<Type, ReferencedType>();
|
|
|
|
// load all types in referencedAssemblies
|
|
IEnumerable<Assembly> referencedAssemblies = LoadReferenedAssemblies(proxyOptions, typeLoader, importErrors);
|
|
if (referencedAssemblies != null)
|
|
{
|
|
foreach (Assembly referencedAssembly in referencedAssemblies)
|
|
{
|
|
var typeLoader2 = typeLoader as IContractGeneratorReferenceTypeLoader2;
|
|
if (typeLoader2 != null)
|
|
{
|
|
foreach (Type sharedType in typeLoader2.LoadExportedTypes(referencedAssembly))
|
|
{
|
|
sharedTypeTable.Add(sharedType, null);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Fall back to the original approach using IContractGeneratorReferenceTypeLoader.LoadType().
|
|
foreach (Type typeInAssembly in referencedAssembly.GetExportedTypes())
|
|
{
|
|
try
|
|
{
|
|
// Do multi-targeting check by calling IContractGeneratorReferenceTypeLoader.LoadType().
|
|
if (typeLoader.LoadType(typeInAssembly.FullName) != null)
|
|
{
|
|
sharedTypeTable.Add(typeInAssembly, null);
|
|
}
|
|
}
|
|
catch (NotSupportedException)
|
|
{
|
|
// NotSupportedException is thrown by multi-targeting check. It's normal if some types not existing in the current FX.
|
|
// So we can safely ---- it.
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// fail to load one type in an assembly: warning message
|
|
importErrors.Add(
|
|
new ProxyGenerationError(
|
|
ProxyGenerationError.GeneratorState.GenerateCode,
|
|
String.Empty,
|
|
ex,
|
|
true));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// load types in DataContractTypeList
|
|
foreach (ReferencedType referencedType in proxyOptions.ReferencedDataContractTypeList)
|
|
{
|
|
try
|
|
{
|
|
Type sharedType = typeLoader.LoadType(referencedType.TypeName);
|
|
|
|
// verify...
|
|
if (!IsTypeShareable(sharedType))
|
|
{
|
|
importErrors.Add(
|
|
new ProxyGenerationError(
|
|
ProxyGenerationError.GeneratorState.GenerateCode,
|
|
String.Empty,
|
|
new FormatException(String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_SharedTypeMustBePublic, referencedType.TypeName)))
|
|
);
|
|
continue;
|
|
}
|
|
|
|
sharedTypeTable[sharedType] = referencedType;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
importErrors.Add(new ProxyGenerationError(
|
|
ProxyGenerationError.GeneratorState.GenerateCode,
|
|
String.Empty,
|
|
ex));
|
|
}
|
|
}
|
|
|
|
// remove excluded types
|
|
foreach (ReferencedType excludedType in proxyOptions.ExcludedTypeList)
|
|
{
|
|
try
|
|
{
|
|
Type sharedType = typeLoader.LoadType(excludedType.TypeName);
|
|
|
|
if (sharedTypeTable.ContainsKey(sharedType))
|
|
{
|
|
if (sharedTypeTable[sharedType] != null)
|
|
{
|
|
// error message
|
|
importErrors.Add(new ProxyGenerationError(
|
|
ProxyGenerationError.GeneratorState.GenerateCode,
|
|
String.Empty,
|
|
new Exception(String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_DataContractExcludedAndIncluded, excludedType.TypeName))));
|
|
}
|
|
sharedTypeTable.Remove(sharedType);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// waring message for excludedTypes
|
|
importErrors.Add(new ProxyGenerationError(
|
|
ProxyGenerationError.GeneratorState.GenerateCode,
|
|
String.Empty,
|
|
ex,
|
|
true));
|
|
}
|
|
}
|
|
|
|
// remove unsupported types
|
|
foreach (Type unsupportedType in GetUnsupportedTypes(targetFrameworkVersion))
|
|
{
|
|
sharedTypeTable.Remove(unsupportedType);
|
|
}
|
|
|
|
return sharedTypeTable.Keys;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get list of types which are not supported in the targetted framework.
|
|
/// </summary>
|
|
/// <param name="targetFrameworkVersion">Targetted Framework version number</param>
|
|
/// <return></return>
|
|
/// <remarks></remarks>
|
|
private static IEnumerable<Type> GetUnsupportedTypes(int targetFrameworkVersion)
|
|
{
|
|
|
|
if (targetFrameworkVersion < FRAMEWORK_VERSION_35)
|
|
{
|
|
// NOTE: do we need load those types with typeLoader?
|
|
return unsupportedTypesInFramework30;
|
|
}
|
|
|
|
return new Type[0];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensure that the ConfigurationName attribute on service contracts and the channel endpoint elements all agree on the
|
|
/// name of the service contract.
|
|
/// We want to avoid having root/default namespace values persisted in config, since that would require us
|
|
/// to update config whenever the default/root namespace changes, so we make sure that we exclude it
|
|
/// from the ConfigurationName attribute and the channel endpoint element we generate.
|
|
///
|
|
/// For VB, the root namespace is not actually present in the generated code, so we typically don't have to
|
|
/// do anything (the configuration and proxy namespaces are equal) but for C#, we need to strip out the
|
|
/// default namespace.
|
|
/// </summary>
|
|
/// <param name="proxyNamespace">
|
|
/// CLR namespace into which we will generate the service in the CodeCompileUnit
|
|
/// </param>
|
|
/// <param name="configNamespace">
|
|
/// The namespace that we expect to use in configuration.
|
|
/// </param>
|
|
/// <param name="generatedContracts">
|
|
/// The contracts that we have generated
|
|
/// </param>
|
|
/// <param name="endpoints">
|
|
/// All channel endpoints we have generated
|
|
/// </param>
|
|
/// <param name="targetCompileUnit">
|
|
/// The compile unit into which we generated the client
|
|
/// </param>
|
|
private static void PatchConfigurationName(
|
|
string proxyNamespace,
|
|
string configNamespace,
|
|
IEnumerable<GeneratedContractType> generatedContracts,
|
|
IEnumerable<ChannelEndpointElement> endpoints,
|
|
CodeCompileUnit targetCompileUnit
|
|
)
|
|
{
|
|
|
|
// Since the name has to match between configuration and the name we put in the ConfigurationName
|
|
// attribute in code, we may have some patching to do - but only if the proxy namespace is not equal
|
|
// to the configuration namespace...
|
|
if (configNamespace != null && !configNamespace.Equals(proxyNamespace, StringComparison.Ordinal))
|
|
{
|
|
string proxyNamespaceHead = MakePeriodTerminatedNamespacePrefix(proxyNamespace);
|
|
string configNamespaceHead = MakePeriodTerminatedNamespacePrefix(configNamespace);
|
|
|
|
// We need to fix up the configuration name for all generated contracts...
|
|
foreach (GeneratedContractType contract in generatedContracts)
|
|
{
|
|
contract.ConfigurationName = ReplaceNamespace(proxyNamespaceHead, configNamespaceHead, contract.ConfigurationName);
|
|
}
|
|
|
|
// ..and we need to fix up all elements in config...
|
|
foreach (ChannelEndpointElement endpoint in endpoints)
|
|
{
|
|
endpoint.Contract = ReplaceNamespace(proxyNamespaceHead, configNamespaceHead, endpoint.Contract);
|
|
}
|
|
|
|
// ...and all ConfigurationName values in service contract attributes in the generated code as well...
|
|
PatchConfigurationNameInServiceContractAttribute(targetCompileUnit, proxyNamespace, configNamespace);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// If the type name begins with the namespace specified in originalNamespace, replace the namespace
|
|
/// for the given type name with the namespace specified in the replacementNamespace parameter.
|
|
/// </summary>
|
|
/// <param name="originalNamespace">
|
|
/// Original namespace to look for.
|
|
/// Must either be an empty string ("") or end with a period (".")
|
|
/// </param>
|
|
/// <param name="replacementNamespace">
|
|
/// Namespace to replace original namespace with
|
|
/// Muse either be an empty string ("") or end with a period...
|
|
/// </param>
|
|
/// <param name="typeName">Typename on which to do the replacement</param>
|
|
/// <returns>
|
|
/// The new type name (potentially the same as passed in)
|
|
/// </returns>
|
|
private static string ReplaceNamespace(string originalNamespace, string replacementNamespace, string typeName)
|
|
{
|
|
Debug.Assert(originalNamespace.Length == 0 || originalNamespace.EndsWith(".", StringComparison.Ordinal));
|
|
Debug.Assert(replacementNamespace.Length == 0 || replacementNamespace.EndsWith(".", StringComparison.Ordinal));
|
|
Debug.Assert(typeName != null);
|
|
|
|
if (typeName.StartsWith(originalNamespace, StringComparison.Ordinal))
|
|
{
|
|
// Strip out the original namespace and replace it with the new namespace
|
|
return replacementNamespace + typeName.Substring(originalNamespace.Length);
|
|
}
|
|
else
|
|
{
|
|
return typeName;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Given the namespace, return either an empty string (if the given namespace was NULL or emtpy)
|
|
/// or a period-terminated (".") string.
|
|
/// </summary>
|
|
/// <param name="ns"></param>
|
|
/// <returns>
|
|
/// Either an empty string or a string that ends with a period (".")
|
|
/// Can never return Null...
|
|
/// </returns>
|
|
private static string MakePeriodTerminatedNamespacePrefix(string ns)
|
|
{
|
|
if (String.IsNullOrEmpty(ns))
|
|
{
|
|
return "";
|
|
}
|
|
else if (!ns.EndsWith(".", StringComparison.Ordinal))
|
|
{
|
|
return ns + ".";
|
|
}
|
|
else
|
|
{
|
|
return ns;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determine if a type can be shared.
|
|
///
|
|
/// In order for a type to be shareable for service references, it has to be a
|
|
/// public type...
|
|
/// </summary>
|
|
/// <param name="t"></param>
|
|
/// <returns></returns>
|
|
private static bool IsTypeShareable(Type t)
|
|
{
|
|
if (t == null)
|
|
{
|
|
System.Diagnostics.Debug.Fail("Why are you asking if a NULL type is shareable?");
|
|
return false;
|
|
}
|
|
|
|
return t.IsPublic || t.IsNestedPublic;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load referenced assemblies
|
|
/// </summary>
|
|
/// <param name="proxyOptions"></param>
|
|
/// <param name="typeLoader"></param>
|
|
/// <param name="importErrors"></param>
|
|
/// <return></return>
|
|
/// <remarks></remarks>
|
|
private static IEnumerable<Assembly> LoadReferenedAssemblies(ClientOptions proxyOptions, IContractGeneratorReferenceTypeLoader typeLoader, IList<ProxyGenerationError> importErrors)
|
|
{
|
|
List<Assembly> referencedAssemblies = new List<Assembly>();
|
|
if (proxyOptions.ReferenceAllAssemblies)
|
|
{
|
|
try
|
|
{
|
|
IEnumerable<Exception> loadingErrors = null;
|
|
IEnumerable<Assembly> allAssemblies = null;
|
|
typeLoader.LoadAllAssemblies(out allAssemblies, out loadingErrors);
|
|
if (loadingErrors != null)
|
|
{
|
|
// treat as warning messages
|
|
foreach (Exception ex in loadingErrors)
|
|
{
|
|
importErrors.Add(new ProxyGenerationError(
|
|
ProxyGenerationError.GeneratorState.GenerateCode,
|
|
String.Empty,
|
|
ex,
|
|
true));
|
|
}
|
|
}
|
|
|
|
if (allAssemblies != null)
|
|
{
|
|
referencedAssemblies.AddRange(allAssemblies);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
importErrors.Add(new ProxyGenerationError(
|
|
ProxyGenerationError.GeneratorState.GenerateCode,
|
|
String.Empty,
|
|
ex));
|
|
}
|
|
}
|
|
|
|
foreach (ReferencedAssembly referencedAssembly in proxyOptions.ReferencedAssemblyList)
|
|
{
|
|
try
|
|
{
|
|
Assembly refAssembly = typeLoader.LoadAssembly(referencedAssembly.AssemblyName);
|
|
if (refAssembly != null && !referencedAssemblies.Contains(refAssembly))
|
|
{
|
|
referencedAssemblies.Add(refAssembly);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
importErrors.Add(new ProxyGenerationError(
|
|
ProxyGenerationError.GeneratorState.GenerateCode,
|
|
String.Empty,
|
|
ex));
|
|
}
|
|
}
|
|
return referencedAssemblies;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load the list of types that we have specified as collection types in our client options.
|
|
/// The collection types will be used to generate collections in the data contracts.
|
|
/// </summary>
|
|
/// <param name="proxyOptions">Options specifying the list of collection types</param>
|
|
/// <param name="typeLoader">Type loader that resolves type names to actual CLR types</param>
|
|
/// <param name="importErrors">Errors encountered while loading the collection types</param>
|
|
/// <return></return>
|
|
/// <remarks></remarks>
|
|
protected static IEnumerable<Type> LoadSharedCollectionTypes(ClientOptions proxyOptions, IContractGeneratorReferenceTypeLoader typeLoader, IList<ProxyGenerationError> importErrors)
|
|
{
|
|
List<Type> referencedCollectionTypes = new List<Type>();
|
|
foreach (ReferencedCollectionType referencedCollectionMapping in proxyOptions.CollectionMappingList)
|
|
{
|
|
try
|
|
{
|
|
Type collectionType = typeLoader.LoadType(referencedCollectionMapping.TypeName);
|
|
|
|
// verify...
|
|
if (!IsTypeShareable(collectionType))
|
|
{
|
|
importErrors.Add(
|
|
new ProxyGenerationError(
|
|
ProxyGenerationError.GeneratorState.GenerateCode,
|
|
String.Empty,
|
|
new FormatException(String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_SharedTypeMustBePublic, referencedCollectionMapping.TypeName)))
|
|
);
|
|
continue;
|
|
}
|
|
|
|
referencedCollectionTypes.Add(collectionType);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
importErrors.Add(new ProxyGenerationError(
|
|
ProxyGenerationError.GeneratorState.GenerateCode,
|
|
String.Empty,
|
|
ex));
|
|
}
|
|
}
|
|
return referencedCollectionTypes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create an appropriate WsdlImporter for the generator
|
|
/// </summary>
|
|
/// <param name="svcMapFile"></param>
|
|
/// <param name="toolConfiguration"></param>
|
|
/// <param name="targetCompileUnit"></param>
|
|
/// <param name="codeDomProvider"></param>
|
|
/// <param name="targetNamespace"></param>
|
|
/// <param name="typeLoader"></param>
|
|
/// <param name="targetFrameworkVersion">Targetted Framework version number</param>
|
|
/// <param name="importErrors"></param>
|
|
/// <param name="typedDataSetSchemaImporterExtension"></param>
|
|
/// <returns></returns>
|
|
protected static WsdlImporter CreateWsdlImporter(SvcMapFile svcMapFile,
|
|
System.Configuration.Configuration toolConfiguration,
|
|
CodeCompileUnit targetCompileUnit,
|
|
System.CodeDom.Compiler.CodeDomProvider codeDomProvider,
|
|
string targetNamespace,
|
|
IServiceProvider serviceProviderForImportExtensions,
|
|
IContractGeneratorReferenceTypeLoader typeLoader,
|
|
int targetFrameworkVersion,
|
|
IList<ProxyGenerationError> importErrors,
|
|
System.Type typedDataSetSchemaImporterExtension)
|
|
{
|
|
List<MetadataSection> metadataSections = CollectMetadataDocuments(svcMapFile.MetadataList, importErrors);
|
|
|
|
WsdlImporter importer = null;
|
|
|
|
ClientOptions.ProxySerializerType serializerType = svcMapFile.ClientOptions.Serializer;
|
|
if (serializerType == ClientOptions.ProxySerializerType.Auto && ContainsHttpBindings(metadataSections))
|
|
{
|
|
// NOTE: HTTP Get/Post binding indicates an old web service. We use XmlSerializer to prevent generating dup classes.
|
|
// Please check devdiv bug 94078
|
|
serializerType = ClientOptions.ProxySerializerType.XmlSerializer;
|
|
}
|
|
|
|
if (toolConfiguration != null)
|
|
{
|
|
ServiceModelSectionGroup serviceModelSection = ServiceModelSectionGroup.GetSectionGroup(toolConfiguration);
|
|
|
|
if (serviceModelSection != null)
|
|
{
|
|
Collection<IWsdlImportExtension> wsdlImportExtensions = serviceModelSection.Client.Metadata.LoadWsdlImportExtensions();
|
|
Collection<IPolicyImportExtension> policyImportExtensions = serviceModelSection.Client.Metadata.LoadPolicyImportExtensions();
|
|
|
|
// If we have specified a specific serializer to use, we remove
|
|
// the other serializer...
|
|
switch (serializerType)
|
|
{
|
|
case ClientOptions.ProxySerializerType.DataContractSerializer:
|
|
RemoveExtension(typeof(XmlSerializerMessageContractImporter), wsdlImportExtensions);
|
|
break;
|
|
case ClientOptions.ProxySerializerType.XmlSerializer:
|
|
RemoveExtension(typeof(DataContractSerializerMessageContractImporter), wsdlImportExtensions);
|
|
break;
|
|
case ClientOptions.ProxySerializerType.Auto:
|
|
break;
|
|
default:
|
|
System.Diagnostics.Debug.Fail("Unknown serializer");
|
|
break;
|
|
}
|
|
|
|
ProvideImportExtensionsWithContextInformation(svcMapFile, serviceProviderForImportExtensions, wsdlImportExtensions, policyImportExtensions);
|
|
|
|
wsdlImportExtensions.Add(new HttpBindingExtension());
|
|
|
|
// Create Importer...
|
|
importer = new WsdlImporter(new MetadataSet(metadataSections), policyImportExtensions, wsdlImportExtensions);
|
|
}
|
|
}
|
|
|
|
if (importer == null)
|
|
{
|
|
importer = new WsdlImporter(new MetadataSet(metadataSections));
|
|
}
|
|
|
|
// DevDiv 124333 - Always add DataContract importer (even if we are in XmlSerializerMode) to
|
|
// enable importing Fault contracts...
|
|
importer.State.Add(typeof(System.Runtime.Serialization.XsdDataContractImporter),
|
|
CreateDataContractImporter(svcMapFile.ClientOptions, targetCompileUnit, codeDomProvider, targetNamespace, typeLoader, targetFrameworkVersion, importErrors));
|
|
|
|
if (serializerType != ClientOptions.ProxySerializerType.DataContractSerializer)
|
|
{
|
|
importer.State.Add(typeof(System.ServiceModel.Channels.XmlSerializerImportOptions),
|
|
CreateXmlSerializerImportOptions(svcMapFile.ClientOptions,
|
|
targetCompileUnit,
|
|
codeDomProvider,
|
|
targetNamespace,
|
|
typedDataSetSchemaImporterExtension));
|
|
}
|
|
|
|
// Read the UseSerializerForFaults from Reference.svcmap, create a FaultImportOptions using this information
|
|
// and pass it to WSDL Importer.
|
|
FaultImportOptions faultOptions = new FaultImportOptions();
|
|
faultOptions.UseMessageFormat = svcMapFile.ClientOptions.UseSerializerForFaults;
|
|
importer.State.Add(typeof(System.ServiceModel.FaultImportOptions), faultOptions);
|
|
|
|
// Read the WrappedOptions from Reference.svcmap, create a WrappedOptions using this information
|
|
// and pass it to WSDL Importer.
|
|
WrappedOptions wrappedOptions = new WrappedOptions();
|
|
wrappedOptions.WrappedFlag = svcMapFile.ClientOptions.Wrapped;
|
|
importer.State.Add(typeof(System.ServiceModel.Channels.WrappedOptions), wrappedOptions);
|
|
|
|
return importer;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Look through all the import extensions to see if any of them want access to
|
|
/// the service reference's extension files. They tell us this by implementing
|
|
/// the interface IWcfReferenceReceiveContextInformation.
|
|
/// </summary>
|
|
/// <param name="svcMapFile"></param>
|
|
/// <param name="serviceProviderForImportExtensions"></param>
|
|
/// <param name="wsdlImportExtensions"></param>
|
|
/// <param name="policyImportExtensions"></param>
|
|
internal static void ProvideImportExtensionsWithContextInformation(SvcMapFile svcMapFile, IServiceProvider serviceProviderForImportExtensions, IEnumerable<IWsdlImportExtension> wsdlImportExtensions, IEnumerable<IPolicyImportExtension> policyImportExtensions)
|
|
{
|
|
// Only make this copy if we need to (not the mainline case)
|
|
Dictionary<string, byte[]> extensionFileContents = null;
|
|
|
|
foreach (IWsdlImportExtension wsdlImportExtension in wsdlImportExtensions)
|
|
{
|
|
System.Web.Compilation.IWcfReferenceReceiveContextInformation receiveContext =
|
|
wsdlImportExtension as System.Web.Compilation.IWcfReferenceReceiveContextInformation;
|
|
if (receiveContext != null)
|
|
{
|
|
if (extensionFileContents == null)
|
|
{
|
|
extensionFileContents = CreateDictionaryOfCopiedExtensionFiles(svcMapFile);
|
|
}
|
|
receiveContext.ReceiveImportContextInformation(
|
|
extensionFileContents,
|
|
serviceProviderForImportExtensions);
|
|
}
|
|
}
|
|
foreach (IPolicyImportExtension policyImportExtension in policyImportExtensions)
|
|
{
|
|
System.Web.Compilation.IWcfReferenceReceiveContextInformation receiveContext =
|
|
policyImportExtension as System.Web.Compilation.IWcfReferenceReceiveContextInformation;
|
|
if (receiveContext != null)
|
|
{
|
|
if (extensionFileContents == null)
|
|
{
|
|
extensionFileContents = CreateDictionaryOfCopiedExtensionFiles(svcMapFile);
|
|
}
|
|
receiveContext.ReceiveImportContextInformation(
|
|
extensionFileContents,
|
|
serviceProviderForImportExtensions);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove specific wsdl importer extension
|
|
/// </summary>
|
|
/// <param name="extensionType">
|
|
/// The extension to remove
|
|
/// </param>
|
|
/// <param name="wsdlImportExtensions">
|
|
/// The collection to remove the extension from
|
|
/// </param>
|
|
/// <return></return>
|
|
/// <remarks></remarks>
|
|
private static void RemoveExtension(Type extensionType, Collection<IWsdlImportExtension> wsdlImportExtensions)
|
|
{
|
|
Debug.Assert(wsdlImportExtensions != null);
|
|
|
|
for (int i = 0; i < wsdlImportExtensions.Count; i++)
|
|
{
|
|
if (wsdlImportExtensions[i].GetType() == extensionType)
|
|
wsdlImportExtensions.RemoveAt(i);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a dictionary containing a copy of the contents of all of the extension files
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private static Dictionary<string, byte[]> CreateDictionaryOfCopiedExtensionFiles(SvcMapFile svcMapFile)
|
|
{
|
|
Dictionary<string, byte[]> extensionFileContents = new Dictionary<string, byte[]>();
|
|
foreach (ExtensionFile extensionFile in svcMapFile.Extensions)
|
|
{
|
|
// If the extension file was not successfully loaded, do not include it in the
|
|
// collection. Users are more likely to expect that the extension file won't
|
|
// be in the collection on an error than they are to assume they have to check
|
|
// if the byte array we return is null or not.
|
|
if (extensionFile.ContentBuffer != null && extensionFile.IsBufferValid)
|
|
{
|
|
extensionFileContents.Add(extensionFile.Name, (byte[])extensionFile.ContentBuffer.Clone());
|
|
}
|
|
}
|
|
|
|
return extensionFileContents;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Merge metadata files to prepare code generation
|
|
/// </summary>
|
|
/// <returns>metadata collection</returns>
|
|
/// <remarks></remarks>
|
|
protected static List<MetadataSection> CollectMetadataDocuments(IEnumerable<MetadataFile> metadataList, IList<ProxyGenerationError> importErrors)
|
|
{
|
|
List<MetadataSection> metadataCollection = new List<MetadataSection>();
|
|
|
|
foreach (MetadataFile metadataItem in metadataList)
|
|
{
|
|
if (!metadataItem.Ignore)
|
|
{
|
|
try
|
|
{
|
|
MetadataSection metadataSection = metadataItem.CreateMetadataSection();
|
|
if (metadataSection != null)
|
|
{
|
|
metadataCollection.Add(metadataSection);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
importErrors.Add(ConvertMetadataErrorToProxyGenerationError(metadataItem, ex));
|
|
}
|
|
}
|
|
}
|
|
|
|
RemoveDuplicatedSchemaItems(metadataCollection, importErrors);
|
|
CheckDuplicatedWsdlItems(metadataCollection, importErrors);
|
|
|
|
return metadataCollection;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert metadata loading errors into proxy generation error messages
|
|
/// </summary>
|
|
/// <return></return>
|
|
/// <remarks></remarks>
|
|
internal static ProxyGenerationError ConvertMetadataErrorToProxyGenerationError(MetadataFile metadataItem, Exception ex)
|
|
{
|
|
ProxyGenerationError generationError = null;
|
|
if (ex is XmlSchemaException)
|
|
{
|
|
generationError = new ProxyGenerationError(ProxyGenerationError.GeneratorState.LoadMetadata, metadataItem.FileName, (XmlSchemaException)ex);
|
|
}
|
|
else if (ex is XmlException)
|
|
{
|
|
generationError = new ProxyGenerationError(ProxyGenerationError.GeneratorState.LoadMetadata, metadataItem.FileName, (XmlException)ex);
|
|
}
|
|
else if (ex is InvalidOperationException)
|
|
{
|
|
System.Xml.Schema.XmlSchemaException schemaException = ex.InnerException as System.Xml.Schema.XmlSchemaException;
|
|
if (schemaException != null)
|
|
{
|
|
generationError = new ProxyGenerationError(ProxyGenerationError.GeneratorState.LoadMetadata, metadataItem.FileName, schemaException);
|
|
}
|
|
else
|
|
{
|
|
System.Xml.XmlException xmlException = ex.InnerException as System.Xml.XmlException;
|
|
if (xmlException != null)
|
|
{
|
|
generationError = new ProxyGenerationError(ProxyGenerationError.GeneratorState.LoadMetadata, metadataItem.FileName, xmlException);
|
|
}
|
|
else
|
|
{
|
|
generationError = new ProxyGenerationError(ProxyGenerationError.GeneratorState.LoadMetadata, metadataItem.FileName, (InvalidOperationException)ex);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
generationError = new ProxyGenerationError(ProxyGenerationError.GeneratorState.LoadMetadata, metadataItem.FileName, ex);
|
|
}
|
|
return generationError;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove duplicated schema items from the metadata collection
|
|
/// </summary>
|
|
/// <remarks></remarks>
|
|
private static void RemoveDuplicatedSchemaItems(List<MetadataSection> metadataCollection, IList<ProxyGenerationError> importErrors)
|
|
{
|
|
Dictionary<XmlSchema, MetadataSection> schemaList = new Dictionary<XmlSchema, MetadataSection>();
|
|
|
|
// add independent schema files...
|
|
foreach (MetadataSection metadataSection in metadataCollection)
|
|
{
|
|
if (metadataSection.Dialect == MetadataSection.XmlSchemaDialect)
|
|
{
|
|
XmlSchema schema = (XmlSchema)metadataSection.Metadata;
|
|
schemaList.Add(schema, metadataSection);
|
|
}
|
|
}
|
|
|
|
// add embedded files...
|
|
foreach (MetadataSection metadataSection in metadataCollection)
|
|
{
|
|
if (metadataSection.Dialect == MetadataSection.ServiceDescriptionDialect)
|
|
{
|
|
System.Web.Services.Description.ServiceDescription wsdl = (System.Web.Services.Description.ServiceDescription)metadataSection.Metadata;
|
|
foreach (XmlSchema schema in wsdl.Types.Schemas)
|
|
{
|
|
schema.SourceUri = wsdl.RetrievalUrl;
|
|
schemaList.Add(schema, metadataSection);
|
|
}
|
|
}
|
|
}
|
|
|
|
IEnumerable<XmlSchema> duplicatedSchemas;
|
|
SchemaMerger.MergeSchemas(schemaList.Keys, importErrors, out duplicatedSchemas);
|
|
|
|
if (duplicatedSchemas != null)
|
|
{
|
|
foreach (XmlSchema schema in duplicatedSchemas)
|
|
{
|
|
Debug.Assert(schemaList.ContainsKey(schema), "The schema list should not contain any of the schemas returned in the duplicateSchemas...");
|
|
|
|
MetadataSection metadataSection = schemaList[schema];
|
|
if (metadataSection.Dialect == MetadataSection.XmlSchemaDialect)
|
|
{
|
|
metadataCollection.Remove(metadataSection);
|
|
}
|
|
else if (metadataSection.Dialect == MetadataSection.ServiceDescriptionDialect)
|
|
{
|
|
System.Web.Services.Description.ServiceDescription wsdl = (System.Web.Services.Description.ServiceDescription)metadataSection.Metadata;
|
|
wsdl.Types.Schemas.Remove(schema);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// check all wsdl files, and generate error messages if one contract have multiple different specifications
|
|
/// </summary>
|
|
/// <remarks></remarks>
|
|
private static void CheckDuplicatedWsdlItems(IList<MetadataSection> metadataCollection, IList<ProxyGenerationError> importErrors)
|
|
{
|
|
List<System.Web.Services.Description.ServiceDescription> wsdlFiles = new List<System.Web.Services.Description.ServiceDescription>();
|
|
foreach (MetadataSection metadataSection in metadataCollection)
|
|
{
|
|
if (metadataSection.Dialect == MetadataSection.ServiceDescriptionDialect)
|
|
{
|
|
System.Web.Services.Description.ServiceDescription wsdl = (System.Web.Services.Description.ServiceDescription)metadataSection.Metadata;
|
|
wsdlFiles.Add(wsdl);
|
|
}
|
|
}
|
|
|
|
WsdlInspector.CheckDuplicatedWsdlItems(wsdlFiles, importErrors);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Given a WSDL importer, a set of metadata files and a compile unit, import the metadata
|
|
/// files.
|
|
/// </summary>
|
|
/// <param name="importer"></param>
|
|
/// <param name="compileUnit"></param>
|
|
/// <param name="generationErrors">
|
|
/// Errors encountered whie importing the model. Any new errors will be appended to this list.
|
|
/// </param>
|
|
/// <param name="serviceEndpointList">List of endpoints imported</param>
|
|
/// <param name="bindingCollection">The collection of bindings imported</param>
|
|
/// <param name="contractCollection">The collection of contracts imported</param>
|
|
protected static void ImportWCFModel(WsdlImporter importer,
|
|
System.CodeDom.CodeCompileUnit compileUnit,
|
|
IList<ProxyGenerationError> generationErrors,
|
|
out List<ServiceEndpoint> serviceEndpointList,
|
|
out IEnumerable<System.ServiceModel.Channels.Binding> bindingCollection,
|
|
out IEnumerable<ContractDescription> contractCollection)
|
|
{
|
|
// We want to remove soap1.2 endpoints for ASMX references, but we can't use the "normal" way
|
|
// of using a IWsdlImportExtension to do so since BeforeImport is called too late (DevDiv 7857)
|
|
// If DevDiv 7857 is fixed, we can remove the following two lines and instead add the
|
|
// AsmxEndpointPickerExtension to the importer's wsdl import extensions...
|
|
IWsdlImportExtension asmxFixerUpper = new AsmxEndpointPickerExtension();
|
|
asmxFixerUpper.BeforeImport(importer.WsdlDocuments, null, null);
|
|
|
|
// NOTE: we should import Endpoint before Contracts, otherwise some information (related to binding) will be lost in the model (devdiv: 22396)
|
|
serviceEndpointList = new List<ServiceEndpoint>();
|
|
|
|
//
|
|
// First we import all the endpoints (ports). This is required so that any WsdlImportExtension's BeforeImport
|
|
// gets called before we actually try to import anything from the WSDL object model.
|
|
// If we don't do this, we run into problems if any wsdl import extensions want to delete a specific port
|
|
// and this port happens to be the first port we try to import (you can't interrupt the import)
|
|
importer.ImportAllEndpoints();
|
|
|
|
//
|
|
// We need to go through each endpoint element and "re-import" it in order to get the mapping
|
|
// between the wsdlPort and the ServiceEndpoint... Importing the same endpoint twice is a no-op
|
|
// as far as the endpoint collection is concerned - it is simply a hashtable lookup to retreive
|
|
// the already generated information...
|
|
//
|
|
foreach (System.Web.Services.Description.ServiceDescription wsdlServiceDescription in importer.WsdlDocuments)
|
|
{
|
|
foreach (System.Web.Services.Description.Service wsdlService in wsdlServiceDescription.Services)
|
|
{
|
|
foreach (System.Web.Services.Description.Port servicePort in wsdlService.Ports)
|
|
{
|
|
try
|
|
{
|
|
ServiceEndpoint newEndpoint = importer.ImportEndpoint(servicePort);
|
|
serviceEndpointList.Add(newEndpoint);
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
// Invalid operation exceptions should already be in the errors collection for the importer, so we don't
|
|
// need to add another generationError. The most probable cause for this is that the we failed to import
|
|
// the endpoint...
|
|
}
|
|
catch (Exception ex)
|
|
{ // It is bad, because WsdlImporter.WsdlImportException is a private class
|
|
generationErrors.Add(new ProxyGenerationError(ProxyGenerationError.GeneratorState.GenerateCode, wsdlServiceDescription.RetrievalUrl, ex));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bindingCollection = importer.ImportAllBindings();
|
|
System.Diagnostics.Debug.Assert(bindingCollection != null, "The importer should never return a NULL binding collection!");
|
|
|
|
contractCollection = importer.ImportAllContracts();
|
|
System.Diagnostics.Debug.Assert(contractCollection != null, "The importer should never return a NULL contract collection!");
|
|
|
|
foreach (MetadataConversionError error in importer.Errors)
|
|
{
|
|
generationErrors.Add(new ProxyGenerationError(error));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// This function patches ServiceContractAttribute in the generated proxy code. It replaces all proxyNamespace in the attribute
|
|
/// to configNamespace. The configNamespace does not depend on defaultNamespace of the project.
|
|
/// </summary>
|
|
/// <param name="proxyNamespace"></param>
|
|
/// <param name="configNamespace"></param>
|
|
/// <return></return>
|
|
/// <remarks></remarks>
|
|
private static void PatchConfigurationNameInServiceContractAttribute(CodeCompileUnit proxyCodeUnit, string proxyNamespace, string configNamespace)
|
|
{
|
|
if (proxyNamespace == null)
|
|
{
|
|
proxyNamespace = String.Empty;
|
|
}
|
|
|
|
string proxyNamespaceHead = MakePeriodTerminatedNamespacePrefix(proxyNamespace);
|
|
string configNamespaceHead = MakePeriodTerminatedNamespacePrefix(configNamespace);
|
|
|
|
if (proxyCodeUnit != null)
|
|
{
|
|
foreach (CodeNamespace proxyCodeNamespace in proxyCodeUnit.Namespaces)
|
|
{
|
|
// Find the namespace we are patching...
|
|
if (String.Equals(proxyNamespace, proxyCodeNamespace.Name, StringComparison.Ordinal))
|
|
{
|
|
// ...and all types in each namespace...
|
|
foreach (CodeTypeDeclaration typeDeclaration in proxyCodeNamespace.Types)
|
|
{
|
|
if (typeDeclaration.IsInterface)
|
|
{
|
|
// ...and each attribute on each interface...
|
|
foreach (CodeAttributeDeclaration codeAttribute in typeDeclaration.CustomAttributes)
|
|
{
|
|
// find System.ServiceModel.ServiceContractAttribute attribute.
|
|
if (String.Equals(codeAttribute.AttributeType.BaseType, typeof(System.ServiceModel.ServiceContractAttribute).FullName, StringComparison.Ordinal))
|
|
{
|
|
foreach (CodeAttributeArgument argument in codeAttribute.Arguments)
|
|
{
|
|
if (String.Equals(argument.Name, "ConfigurationName", StringComparison.Ordinal))
|
|
{
|
|
// we only fix the string here
|
|
CodePrimitiveExpression valueExpression = argument.Value as CodePrimitiveExpression;
|
|
if (valueExpression != null && valueExpression.Value is string)
|
|
{
|
|
valueExpression.Value = ReplaceNamespace(proxyNamespaceHead, configNamespaceHead, (string)valueExpression.Value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Patch VB code for output parameters.
|
|
///
|
|
/// Visual Basic doesn't support Out parameters - they are all generated as ByRef.
|
|
/// Unfortunately, the CodeDom provider doesn't add an Out attribute to the ByRef
|
|
/// parameters, so we have to do that ourselves...
|
|
/// </summary>
|
|
/// <param name="codeCompileUnit"></param>
|
|
/// <remarks></remarks>
|
|
private static void PatchOutParametersInVB(CodeCompileUnit codeCompileUnit)
|
|
{
|
|
foreach (CodeNamespace codeNamespace in codeCompileUnit.Namespaces)
|
|
{
|
|
foreach (CodeTypeDeclaration codeClass in codeNamespace.Types)
|
|
{
|
|
PatchTypeDeclaration(codeClass);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Patch TypeDeclaration in VB code for output parameters
|
|
/// </summary>
|
|
/// <param name="codeClass"></param>
|
|
/// <remarks></remarks>
|
|
private static void PatchTypeDeclaration(CodeTypeDeclaration codeClass)
|
|
{
|
|
foreach (CodeTypeMember member in codeClass.Members)
|
|
{
|
|
if (member is CodeTypeDeclaration)
|
|
{
|
|
// Recurse down in nested types...
|
|
PatchTypeDeclaration((CodeTypeDeclaration)member);
|
|
}
|
|
else if (member is CodeMemberMethod)
|
|
{
|
|
CodeMemberMethod method = member as CodeMemberMethod;
|
|
foreach (CodeParameterDeclarationExpression parameter in method.Parameters)
|
|
{
|
|
if (parameter.Direction == FieldDirection.Out)
|
|
{
|
|
// Make sure that all Out parameters have an <Out> attribute
|
|
//
|
|
// First check for explicit <OutAttribute> declaration to avoid adding duplicate attributes.
|
|
if (!IsDefinedInCodeAttributeCollection(typeof(System.Runtime.InteropServices.OutAttribute), parameter.CustomAttributes))
|
|
{
|
|
parameter.CustomAttributes.Add(OutAttribute);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// check whether code attribuate has already been declared.
|
|
/// </summary>
|
|
/// <param name="type"></param>
|
|
/// <param name="metadata"></param>
|
|
/// <return></return>
|
|
/// <remarks></remarks>
|
|
private static bool IsDefinedInCodeAttributeCollection(Type type, CodeAttributeDeclarationCollection metadata)
|
|
{
|
|
foreach (CodeAttributeDeclaration attribute in metadata)
|
|
{
|
|
if (String.Equals(attribute.Name, type.FullName, StringComparison.Ordinal) || String.Equals(attribute.Name, type.Name, StringComparison.Ordinal))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check whether it is VB language
|
|
/// </summary>
|
|
/// <param name="codeDomProvider"></param>
|
|
/// <return></return>
|
|
/// <remarks></remarks>
|
|
private static bool IsVBCodeDomProvider(System.CodeDom.Compiler.CodeDomProvider codeDomProvider)
|
|
{
|
|
string fileExtension = codeDomProvider.FileExtension;
|
|
try
|
|
{
|
|
string language = System.CodeDom.Compiler.CodeDomProvider.GetLanguageFromExtension(fileExtension);
|
|
return String.Equals(language, VB_LANGUAGE_NAME, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
catch (System.Configuration.ConfigurationException)
|
|
{
|
|
// not defined extension
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// check whether HTTP Binding is used in those metadata files
|
|
/// </summary>
|
|
/// <param name="metadataCollection">metadata files</param>
|
|
/// <return></return>
|
|
/// <remarks></remarks>
|
|
private static bool ContainsHttpBindings(IEnumerable<MetadataSection> metadataCollection)
|
|
{
|
|
foreach (MetadataSection metadataSection in metadataCollection)
|
|
{
|
|
if (metadataSection.Dialect == MetadataSection.ServiceDescriptionDialect)
|
|
{
|
|
System.Web.Services.Description.ServiceDescription wsdlFile = (System.Web.Services.Description.ServiceDescription)metadataSection.Metadata;
|
|
if (ContainsHttpBindings(wsdlFile))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// check whether HTTP Binding is used in one wsdl file
|
|
/// </summary>
|
|
/// <param name="wsdlFile">one wsdl</param>
|
|
/// <return></return>
|
|
/// <remarks></remarks>
|
|
internal static bool ContainsHttpBindings(System.Web.Services.Description.ServiceDescription wsdlFile)
|
|
{
|
|
foreach (System.Web.Services.Description.Binding binding in wsdlFile.Bindings)
|
|
{
|
|
foreach (object extension in binding.Extensions)
|
|
{
|
|
System.Web.Services.Description.HttpBinding httpBinding = extension as System.Web.Services.Description.HttpBinding;
|
|
if (httpBinding != null)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|