Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / ndp / fx / src / xsp / System / Web / Extensions / Compilation / WCFBuildProvider.cs / 2 / WCFBuildProvider.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Web.Compilation { using System; using System.Globalization; using System.CodeDom; using System.CodeDom.Compiler; using System.Collections.Specialized; using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Net; using System.Xml.Serialization; using System.Web.Hosting; using System.Web.UI; using System.Diagnostics; using System.Threading; using System.Text; using System.Web.Compilation.WCFModel; using System.Collections.Generic; using System.Web.Configuration; using System.Web.Resources; using System.Security.Permissions; using System.Data.Services.Design; using System.IO; using System.Xml; using System.Reflection; ////// A build provider for WCF service references in ASP.NET projects. /// (Note: the ASMX version is called WebReferencesBuildProvider). /// Due to compatibility requirements, as few changes as possible were made /// to System.Web.dll, which contains the build manager and WebReferencesBuildProvider. /// WebReferencesBuildProvider will call into us for the App_WebReferences folder and /// each of its subdirectories (recursively) if it finds our assembly and type on the /// machine. /// Note: for a normal BuildProvider, the input file is represented by the protected VirtualPath /// property of the base. But for this build provider (same as for WebReferencesBuildProvider, the /// asmx web references build provider), the input is an entire directory, which is still represented /// by the protected VirtualPath property. /// TO DEBUG: The easiest way to debug is to debug the aspnet_compiler.exe command-line tool /// while it compiles a website with a .svcmap file in it. /// As an example, you could debug aspnet_compiler.exe with this command-line to debug /// one of the suites: /// /// /v MiddleService -p {path}\ddsuites\src\vs\vb\IndigoTools\BuildProvider\WCFBuildProvider1\WebSite\MiddleService -c c:\temp\output /// /// Important: it will only call the build provider if the sources in the website have changed or if you delete /// the deleted output folder's contents. /// /// Data services (Astoria): in order to support Astoria "data services" we added code /// to scan for "datasvcmap" files in addition to the existing "svcmap" files. For data services /// we call into the Astoria code-gen library to do the work instead of the regular indigo path. /// /// [ AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal), AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal), PermissionSet(SecurityAction.LinkDemand, Name="FullTrust"), SuppressMessage("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased", Justification = "Too late to change in Orcas--the WCFBuildProvider is hard-coded in ASP.NET " + "build manager for app_webreferences folder. Build manager is part of redbits.") ] public class WCFBuildProvider : BuildProvider { internal const string WebRefDirectoryName = "App_WebReferences"; internal const string SvcMapExtension = ".svcmap"; internal const string DataSvcMapExtension = ".datasvcmap"; private const string TOOL_CONFIG_ITEM_NAME = "Reference.config"; ////// version number for 3.5 framework /// private const int FRAMEWORK_VERSION_35 = 0x30005; ////// Search through the folder represented by base.VirtualPath for .svcmap and .datasvcmap files. /// If any .svcmap/.datasvcmap files are found, then generate proxy code for them into the /// specified assemblyBuilder. /// /// Where to generate the proxy code ////// When this routine is called, it is expected that the protected VirtualPath property has /// been set to the folder to scan for .svcmap/.datasvcmap files. /// public override void GenerateCode(AssemblyBuilder assemblyBuilder) { // Go through all the svcmap files in the directory VirtualDirectory vdir = GetVirtualDirectory(VirtualPath); foreach (VirtualFile child in vdir.Files) { string extension = IO.Path.GetExtension(child.VirtualPath); if (extension.Equals(SvcMapExtension, StringComparison.OrdinalIgnoreCase)) { // .svcmap file found // NOTE: the WebReferences code requires a physical path, so this feature // cannot work with a non-file based VirtualPathProvider string physicalPath = HostingEnvironment.MapPath(child.VirtualPath); CodeCompileUnit codeUnit = GenerateCodeFromServiceMapFile(physicalPath); // Add the CodeCompileUnit to the compilation assemblyBuilder.AddCodeCompileUnit(this, codeUnit); } else if (extension.Equals(DataSvcMapExtension, StringComparison.OrdinalIgnoreCase)) { // NOTE: the WebReferences code requires a physical path, so this feature // cannot work with a non-file based VirtualPathProvider string physicalPath = HostingEnvironment.MapPath(child.VirtualPath); GenerateCodeFromDataServiceMapFile(physicalPath, assemblyBuilder); } } } ////// Generate code for one .datasvcmap file /// /// The physical path to the data service map file private void GenerateCodeFromDataServiceMapFile(string mapFilePath, AssemblyBuilder assemblyBuilder) { try { assemblyBuilder.AddAssemblyReference(typeof(System.Data.Services.Client.DataServiceContext).Assembly); DataSvcMapFileLoader loader = new DataSvcMapFileLoader(mapFilePath); DataSvcMapFile mapFile = loader.LoadMapFile(System.IO.Path.GetFileName(mapFilePath)); if (mapFile.MetadataList[0].ErrorInLoading != null) { throw mapFile.MetadataList[0].ErrorInLoading; } string edmxContent = mapFile.MetadataList[0].Content; System.Data.Services.Design.EntityClassGenerator generator = new System.Data.Services.Design.EntityClassGenerator(LanguageOption.GenerateCSharpCode); // the EntityClassGenerator works on streams/writers, does not return a CodeDom // object, so we use CreateCodeFile instead of compile units. using (TextWriter writer = assemblyBuilder.CreateCodeFile(this)) { // Note: currently GenerateCode never actually returns values // for the error case (even though it returns an IList of error // objects). Instead it throws on error. This may need some tweaking // later on. #if DEBUG object errors = #endif generator.GenerateCode( XmlReader.Create(new StringReader(edmxContent)), writer, GetGeneratedNamespace()); #if DEBUG Debug.Assert( errors == null || !(errors is ICollection) || ((ICollection)errors).Count == 0, "Errors reported through the return value. Expected an exception"); #endif writer.Flush(); } } catch (Exception ex) { string errorMessage = ex.Message; errorMessage = String.Format(CultureInfo.CurrentCulture, "{0}: {1}", IO.Path.GetFileName(mapFilePath), errorMessage); throw new InvalidOperationException(errorMessage, ex); } } ////// Generate code for one .svcmap file /// /// the path to the service map file ////// private CodeCompileUnit GenerateCodeFromServiceMapFile(string mapFilePath) { try { string generatedNamespace = GetGeneratedNamespace(); SvcMapFileLoader loader = new SvcMapFileLoader(mapFilePath); SvcMapFile mapFile = loader.LoadMapFile(System.IO.Path.GetFileName(mapFilePath)); HandleProxyGenerationErrors(mapFile.LoadErrors); // We always use C# for the generated proxy CodeDomProvider provider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("c#"); //Note: with the current implementation of the generator, it does all of its // work in the constructor. This may change in the future. VSWCFServiceContractGenerator generator = VSWCFServiceContractGenerator.GenerateCodeAndConfiguration( mapFile, GetToolConfig(mapFile, mapFilePath), provider, generatedNamespace, null, //targetConfiguration null, //configurationNamespace new ImportExtensionServiceProvider(), new TypeResolver(), FRAMEWORK_VERSION_35, typeof (System.Data.Design.TypedDataSetSchemaImporterExtensionFx35) //Always we are above framework version 3.5 ); // Determine what "name" to display to users for the service if there are any exceptions // If generatedNamespace is empty, then we display the name of the .svcmap file. string referenceDisplayName = String.IsNullOrEmpty(generatedNamespace) ? System.IO.Path.GetFileName(mapFilePath) : generatedNamespace; VerifyGeneratedCodeAndHandleErrors(referenceDisplayName, mapFile, generator.TargetCompileUnit, generator.ImportErrors, generator.ProxyGenerationErrors); #if DEBUG #if false IO.TextWriter writer = new IO.StringWriter(); CodeGeneratorOptions options = new CodeGeneratorOptions(); options.BlankLinesBetweenMembers=true; provider.GenerateCodeFromCompileUnit(generator.TargetCompileUnit, writer, options); Debug.WriteLine("Generated proxy code:\r\n" + writer.ToString()); #endif #endif return generator.TargetCompileUnit; } catch (Exception ex) { string errorMessage = ex.Message; errorMessage = String.Format(CultureInfo.CurrentCulture, "{0}: {1}", IO.Path.GetFileName(mapFilePath), errorMessage); throw new InvalidOperationException(errorMessage, ex); } } /// /// If there are errors passed in, handle them by throwing an appropriate error /// /// IEnumerable for easier unit test accessors private static void HandleProxyGenerationErrors(System.Collections.IEnumerable /**/ errors) { foreach (ProxyGenerationError generationError in errors) { // NOTE: the ASP.Net framework does not handle an error list, so we only give them the first error message // all warning messages are ignored today // // We treat all error messages from WsdlImport and ProxyGenerator as warning messages // The reason is that many of them are ignorable and doesn't block generating useful code. if (!generationError.IsWarning && generationError.ErrorGeneratorState != WCFModel.ProxyGenerationError.GeneratorState.GenerateCode) { throw new InvalidOperationException(ConvertToBuildProviderErrorMessage(generationError)); } } } /// /// Merge error message strings together /// /// /// ////// private static void CollectErrorMessages(System.Collections.IEnumerable errors, StringBuilder collectedMessages) { foreach (ProxyGenerationError generationError in errors) { if (!generationError.IsWarning) { if (collectedMessages.Length > 0) { collectedMessages.Append(Environment.NewLine); } collectedMessages.Append(ConvertToBuildProviderErrorMessage(generationError)); } } } /// /// Format an error reported by the code generator to message string reported to the user. /// /// ////// private static string ConvertToBuildProviderErrorMessage(ProxyGenerationError generationError) { string errorMessage = generationError.Message; if (!String.IsNullOrEmpty(generationError.MetadataFile)) { if (generationError.LineNumber < 0) { errorMessage = String.Format(CultureInfo.CurrentCulture, "'{0}': {1}", generationError.MetadataFile, errorMessage); } else if (generationError.LinePosition < 0) { errorMessage = String.Format(CultureInfo.CurrentCulture, "'{0}' ({1}): {2}", generationError.MetadataFile, generationError.LineNumber, errorMessage); } else { errorMessage = String.Format(CultureInfo.CurrentCulture, "'{0}' ({1},{2}): {3}", generationError.MetadataFile, generationError.LineNumber, generationError.LinePosition, errorMessage); } } return errorMessage; } /// /// Check the result from the code generator. /// By default we treat all error messages from WsdlImporter as warnings, /// and they will be ignored if valid code has been generated. /// We may hit other errors when we parse the metadata files. /// Those errors (which are usually because of a bad file) will not be ignored, because the user can fix them. /// If the WsdlImporter hasn't generated any code as we expect, we have to consider some of the error messages are fatal. /// We collect those messages and report to the user. /// /// The name of the generated reference /// Original Map File /// generated code compile unit /// /// ///private static void VerifyGeneratedCodeAndHandleErrors( string referenceDisplayName, SvcMapFile mapFile, CodeCompileUnit generatedCode, System.Collections.IEnumerable importErrors, System.Collections.IEnumerable generatorErrors) { // Check and report fatal error first... HandleProxyGenerationErrors(importErrors); HandleProxyGenerationErrors(generatorErrors); // if there is no fatal error, we expect valid type generated from the process // unless there is no metadata files, or there is a service contract type sharing if (mapFile.MetadataList.Count > 0 && mapFile.ClientOptions.ServiceContractMappingList.Count == 0) { if (!IsAnyTypeGenerated(generatedCode)) { StringBuilder collectedMessages = new StringBuilder(); // merge error messages CollectErrorMessages(importErrors, collectedMessages); CollectErrorMessages(generatorErrors, collectedMessages); throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_FailedToGenerateCode, referenceDisplayName, collectedMessages.ToString())); } } } /// /// Check whether we have generated any types /// /// ////// private static bool IsAnyTypeGenerated(CodeCompileUnit compileUnit) { if (compileUnit != null) { foreach (System.CodeDom.CodeNamespace codeNamespace in compileUnit.Namespaces) { if (codeNamespace.Types.Count > 0) { return true; } } } return false; } /// /// Retrieve a VirtualDirectory for the given virtual path /// /// ///private VirtualDirectory GetVirtualDirectory(string virtualPath) { return HostingEnvironment.VirtualPathProvider.GetDirectory(VirtualPath); } /// /// Caculate our namespace for current VirtualPath... /// ////// private string GetGeneratedNamespace() { // First, determine the namespace to use for code generation. This is based on the // relative path of the reference from its base App_WebReferences directory // ... Get the virtual path to the App_WebReferences folder, e.g "/MyApp/App_WebReferences" string rootWebRefDirVirtualPath = GetWebRefDirectoryVirtualPath(); // ... Get the folder's directory path, e.g "/MyApp/Application_WebReferences/Foo/Bar", // where we'll look for .svcmap files string currentSubfolderUnderWebReferences = this.VirtualPath; if (currentSubfolderUnderWebReferences == null) { Debug.Fail("Shouldn't be given a null virtual path"); throw new InvalidOperationException(); } return CalculateGeneratedNamespace(rootWebRefDirVirtualPath, currentSubfolderUnderWebReferences); } /// /// Determine the namespace to use for the proxy generation /// /// The path to the App_WebReferences folder /// The path to the current folder ///private static string CalculateGeneratedNamespace(string webReferencesRootVirtualPath, string virtualPath) { // ... Ensure both folders have trailing slashes webReferencesRootVirtualPath = VirtualPathUtility.AppendTrailingSlash(webReferencesRootVirtualPath); virtualPath = VirtualPathUtility.AppendTrailingSlash(virtualPath); Debug.Assert(virtualPath.StartsWith(webReferencesRootVirtualPath, StringComparison.OrdinalIgnoreCase), "We expected to be inside the App_WebReferences folder"); // ... Determine the namespace to use, based on the directory structure where the .svcmap file // is found. if (webReferencesRootVirtualPath.Length == virtualPath.Length) { Debug.Assert(string.Equals(webReferencesRootVirtualPath, virtualPath, StringComparison.OrdinalIgnoreCase), "We expected to be in the App_WebReferences directory"); // If it's the root WebReferences dir, use the empty namespace return String.Empty; } else { // We're in a subdirectory of App_WebReferences. // Get the directory's relative path from App_WebReferences, e.g. "Foo/Bar" virtualPath = VirtualPathUtility.RemoveTrailingSlash(virtualPath).Substring(webReferencesRootVirtualPath.Length); // Split it into chunks separated by '/' string[] chunks = virtualPath.Split('/'); // Turn all the relevant chunks into valid namespace chunks for (int i = 0; i < chunks.Length; i++) { chunks[i] = MakeValidTypeNameFromString(chunks[i]); } // Put the relevant chunks back together to form the namespace return String.Join(".", chunks); } } /// /// Returns the app domain's application virtual path [from HttpRuntime.AppDomainAppVPath]. /// Includes trailing slash, e.g. "/MyApp/" /// private static string GetAppDomainAppVirtualPath() { string appVirtualPath = HttpRuntime.AppDomainAppVirtualPath; if (appVirtualPath == null) { Debug.Fail("Shouldn't get a null app virtual path from the app domain"); throw new InvalidOperationException(); } return VirtualPathUtility.AppendTrailingSlash(VirtualPathUtility.ToAbsolute(appVirtualPath)); } ////// Gets the virtual path to the application's App_WebReferences directory, e.g. "/MyApp/App_WebReferences/" /// private static string GetWebRefDirectoryVirtualPath() { return VirtualPathUtility.Combine(GetAppDomainAppVirtualPath(), WebRefDirectoryName + @"\"); } ////// Return a valid type name from a string by changing any character /// that's not a letter or a digit to an '_'. /// /// ///internal static string MakeValidTypeNameFromString(string typeName) { if (String.IsNullOrEmpty(typeName)) throw new ArgumentNullException("typeName"); StringBuilder sb = new StringBuilder(); for (int i = 0; i < typeName.Length; i++) { // Make sure it doesn't start with a digit (ASURT 31134) if (i == 0 && Char.IsDigit(typeName[0])) sb.Append('_'); if (Char.IsLetterOrDigit(typeName[i])) sb.Append(typeName[i]); else sb.Append('_'); } // Identifier can't be a single underscore character string validTypeName = sb.ToString(); if (validTypeName.Equals("_", StringComparison.Ordinal)) { validTypeName = "__"; } return validTypeName; } /// /// Get the appropriate tool configuration for this service reference. /// /// If a reference.config file is present, the configuration object returned /// will be the merged view of: /// /// Machine Config /// ReferenceConfig /// /// If not reference.config file is present, the configuration object returned /// will be a merged view of: /// /// Machine.config /// web.config in application's physical path... /// /// /// SvcMapFile representing the service ///private System.Configuration.Configuration GetToolConfig(SvcMapFile mapFile, string mapFilePath) { string toolConfigFile = null; if (mapFile != null && mapFilePath != null) { foreach (ExtensionFile extensionFile in mapFile.Extensions) { if (String.Equals(extensionFile.Name, TOOL_CONFIG_ITEM_NAME, StringComparison.Ordinal)) { toolConfigFile = extensionFile.FileName; } } } System.Web.Configuration.WebConfigurationFileMap fileMap; fileMap = new System.Web.Configuration.WebConfigurationFileMap(); System.Web.Configuration.VirtualDirectoryMapping mapping; if (toolConfigFile != null) { // // If we've got a specific tool configuration to use, we better load that... // mapping = new System.Web.Configuration.VirtualDirectoryMapping(System.IO.Path.GetDirectoryName(mapFilePath), true, toolConfigFile); } else { // // Otherwise we fall back to the default web.config file... // mapping = new System.Web.Configuration.VirtualDirectoryMapping(HostingEnvironment.ApplicationPhysicalPath, true); } fileMap.VirtualDirectories.Add("/", mapping); return System.Web.Configuration.WebConfigurationManager.OpenMappedWebConfiguration(fileMap, "/", System.Web.Hosting.HostingEnvironment.SiteName); } /// /// Helper class to implement type resolution for the generator /// private class TypeResolver : IContractGeneratorReferenceTypeLoader { private System.Reflection.Assembly[] _referencedAssemblies; private IEnumerableReferencedAssemblies { get { if (_referencedAssemblies == null) { System.Collections.ICollection referencedAssemblyCollection = BuildManager.GetReferencedAssemblies(); _referencedAssemblies = new System.Reflection.Assembly[referencedAssemblyCollection.Count]; referencedAssemblyCollection.CopyTo(_referencedAssemblies, 0); } return _referencedAssemblies; } } System.Type IContractGeneratorReferenceTypeLoader.LoadType(string typeName) { // If the type can't be resolved, we need an exception thrown (thus the true argument) // so it can be reported as a build error. return BuildManager.GetType(typeName, true); } System.Reflection.Assembly IContractGeneratorReferenceTypeLoader.LoadAssembly(string assemblyName) { System.Reflection.AssemblyName assemblyToLookFor = new System.Reflection.AssemblyName(assemblyName); foreach (System.Reflection.Assembly assembly in ReferencedAssemblies) { if (System.Reflection.AssemblyName.ReferenceMatchesDefinition(assemblyToLookFor, assembly.GetName())) { return assembly; } } throw new System.IO.FileNotFoundException(String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_FailedToLoadAssembly, assemblyName)); } void IContractGeneratorReferenceTypeLoader.LoadAllAssemblies(out IEnumerable loadedAssemblies, out IEnumerable loadingErrors) { loadedAssemblies = ReferencedAssemblies; loadingErrors = new System.Exception[] {}; } } private class ImportExtensionServiceProvider : IServiceProvider { #region IServiceProvider Members public object GetService(Type serviceType) { // We don't currently provide any services to import extensions in the build provider context return null; } #endregion } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Web.Compilation { using System; using System.Globalization; using System.CodeDom; using System.CodeDom.Compiler; using System.Collections.Specialized; using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Net; using System.Xml.Serialization; using System.Web.Hosting; using System.Web.UI; using System.Diagnostics; using System.Threading; using System.Text; using System.Web.Compilation.WCFModel; using System.Collections.Generic; using System.Web.Configuration; using System.Web.Resources; using System.Security.Permissions; using System.Data.Services.Design; using System.IO; using System.Xml; using System.Reflection; ////// A build provider for WCF service references in ASP.NET projects. /// (Note: the ASMX version is called WebReferencesBuildProvider). /// Due to compatibility requirements, as few changes as possible were made /// to System.Web.dll, which contains the build manager and WebReferencesBuildProvider. /// WebReferencesBuildProvider will call into us for the App_WebReferences folder and /// each of its subdirectories (recursively) if it finds our assembly and type on the /// machine. /// Note: for a normal BuildProvider, the input file is represented by the protected VirtualPath /// property of the base. But for this build provider (same as for WebReferencesBuildProvider, the /// asmx web references build provider), the input is an entire directory, which is still represented /// by the protected VirtualPath property. /// TO DEBUG: The easiest way to debug is to debug the aspnet_compiler.exe command-line tool /// while it compiles a website with a .svcmap file in it. /// As an example, you could debug aspnet_compiler.exe with this command-line to debug /// one of the suites: /// /// /v MiddleService -p {path}\ddsuites\src\vs\vb\IndigoTools\BuildProvider\WCFBuildProvider1\WebSite\MiddleService -c c:\temp\output /// /// Important: it will only call the build provider if the sources in the website have changed or if you delete /// the deleted output folder's contents. /// /// Data services (Astoria): in order to support Astoria "data services" we added code /// to scan for "datasvcmap" files in addition to the existing "svcmap" files. For data services /// we call into the Astoria code-gen library to do the work instead of the regular indigo path. /// /// [ AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal), AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal), PermissionSet(SecurityAction.LinkDemand, Name="FullTrust"), SuppressMessage("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased", Justification = "Too late to change in Orcas--the WCFBuildProvider is hard-coded in ASP.NET " + "build manager for app_webreferences folder. Build manager is part of redbits.") ] public class WCFBuildProvider : BuildProvider { internal const string WebRefDirectoryName = "App_WebReferences"; internal const string SvcMapExtension = ".svcmap"; internal const string DataSvcMapExtension = ".datasvcmap"; private const string TOOL_CONFIG_ITEM_NAME = "Reference.config"; ////// version number for 3.5 framework /// private const int FRAMEWORK_VERSION_35 = 0x30005; ////// Search through the folder represented by base.VirtualPath for .svcmap and .datasvcmap files. /// If any .svcmap/.datasvcmap files are found, then generate proxy code for them into the /// specified assemblyBuilder. /// /// Where to generate the proxy code ////// When this routine is called, it is expected that the protected VirtualPath property has /// been set to the folder to scan for .svcmap/.datasvcmap files. /// public override void GenerateCode(AssemblyBuilder assemblyBuilder) { // Go through all the svcmap files in the directory VirtualDirectory vdir = GetVirtualDirectory(VirtualPath); foreach (VirtualFile child in vdir.Files) { string extension = IO.Path.GetExtension(child.VirtualPath); if (extension.Equals(SvcMapExtension, StringComparison.OrdinalIgnoreCase)) { // .svcmap file found // NOTE: the WebReferences code requires a physical path, so this feature // cannot work with a non-file based VirtualPathProvider string physicalPath = HostingEnvironment.MapPath(child.VirtualPath); CodeCompileUnit codeUnit = GenerateCodeFromServiceMapFile(physicalPath); // Add the CodeCompileUnit to the compilation assemblyBuilder.AddCodeCompileUnit(this, codeUnit); } else if (extension.Equals(DataSvcMapExtension, StringComparison.OrdinalIgnoreCase)) { // NOTE: the WebReferences code requires a physical path, so this feature // cannot work with a non-file based VirtualPathProvider string physicalPath = HostingEnvironment.MapPath(child.VirtualPath); GenerateCodeFromDataServiceMapFile(physicalPath, assemblyBuilder); } } } ////// Generate code for one .datasvcmap file /// /// The physical path to the data service map file private void GenerateCodeFromDataServiceMapFile(string mapFilePath, AssemblyBuilder assemblyBuilder) { try { assemblyBuilder.AddAssemblyReference(typeof(System.Data.Services.Client.DataServiceContext).Assembly); DataSvcMapFileLoader loader = new DataSvcMapFileLoader(mapFilePath); DataSvcMapFile mapFile = loader.LoadMapFile(System.IO.Path.GetFileName(mapFilePath)); if (mapFile.MetadataList[0].ErrorInLoading != null) { throw mapFile.MetadataList[0].ErrorInLoading; } string edmxContent = mapFile.MetadataList[0].Content; System.Data.Services.Design.EntityClassGenerator generator = new System.Data.Services.Design.EntityClassGenerator(LanguageOption.GenerateCSharpCode); // the EntityClassGenerator works on streams/writers, does not return a CodeDom // object, so we use CreateCodeFile instead of compile units. using (TextWriter writer = assemblyBuilder.CreateCodeFile(this)) { // Note: currently GenerateCode never actually returns values // for the error case (even though it returns an IList of error // objects). Instead it throws on error. This may need some tweaking // later on. #if DEBUG object errors = #endif generator.GenerateCode( XmlReader.Create(new StringReader(edmxContent)), writer, GetGeneratedNamespace()); #if DEBUG Debug.Assert( errors == null || !(errors is ICollection) || ((ICollection)errors).Count == 0, "Errors reported through the return value. Expected an exception"); #endif writer.Flush(); } } catch (Exception ex) { string errorMessage = ex.Message; errorMessage = String.Format(CultureInfo.CurrentCulture, "{0}: {1}", IO.Path.GetFileName(mapFilePath), errorMessage); throw new InvalidOperationException(errorMessage, ex); } } ////// Generate code for one .svcmap file /// /// the path to the service map file ////// private CodeCompileUnit GenerateCodeFromServiceMapFile(string mapFilePath) { try { string generatedNamespace = GetGeneratedNamespace(); SvcMapFileLoader loader = new SvcMapFileLoader(mapFilePath); SvcMapFile mapFile = loader.LoadMapFile(System.IO.Path.GetFileName(mapFilePath)); HandleProxyGenerationErrors(mapFile.LoadErrors); // We always use C# for the generated proxy CodeDomProvider provider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("c#"); //Note: with the current implementation of the generator, it does all of its // work in the constructor. This may change in the future. VSWCFServiceContractGenerator generator = VSWCFServiceContractGenerator.GenerateCodeAndConfiguration( mapFile, GetToolConfig(mapFile, mapFilePath), provider, generatedNamespace, null, //targetConfiguration null, //configurationNamespace new ImportExtensionServiceProvider(), new TypeResolver(), FRAMEWORK_VERSION_35, typeof (System.Data.Design.TypedDataSetSchemaImporterExtensionFx35) //Always we are above framework version 3.5 ); // Determine what "name" to display to users for the service if there are any exceptions // If generatedNamespace is empty, then we display the name of the .svcmap file. string referenceDisplayName = String.IsNullOrEmpty(generatedNamespace) ? System.IO.Path.GetFileName(mapFilePath) : generatedNamespace; VerifyGeneratedCodeAndHandleErrors(referenceDisplayName, mapFile, generator.TargetCompileUnit, generator.ImportErrors, generator.ProxyGenerationErrors); #if DEBUG #if false IO.TextWriter writer = new IO.StringWriter(); CodeGeneratorOptions options = new CodeGeneratorOptions(); options.BlankLinesBetweenMembers=true; provider.GenerateCodeFromCompileUnit(generator.TargetCompileUnit, writer, options); Debug.WriteLine("Generated proxy code:\r\n" + writer.ToString()); #endif #endif return generator.TargetCompileUnit; } catch (Exception ex) { string errorMessage = ex.Message; errorMessage = String.Format(CultureInfo.CurrentCulture, "{0}: {1}", IO.Path.GetFileName(mapFilePath), errorMessage); throw new InvalidOperationException(errorMessage, ex); } } /// /// If there are errors passed in, handle them by throwing an appropriate error /// /// IEnumerable for easier unit test accessors private static void HandleProxyGenerationErrors(System.Collections.IEnumerable /**/ errors) { foreach (ProxyGenerationError generationError in errors) { // NOTE: the ASP.Net framework does not handle an error list, so we only give them the first error message // all warning messages are ignored today // // We treat all error messages from WsdlImport and ProxyGenerator as warning messages // The reason is that many of them are ignorable and doesn't block generating useful code. if (!generationError.IsWarning && generationError.ErrorGeneratorState != WCFModel.ProxyGenerationError.GeneratorState.GenerateCode) { throw new InvalidOperationException(ConvertToBuildProviderErrorMessage(generationError)); } } } /// /// Merge error message strings together /// /// /// ////// private static void CollectErrorMessages(System.Collections.IEnumerable errors, StringBuilder collectedMessages) { foreach (ProxyGenerationError generationError in errors) { if (!generationError.IsWarning) { if (collectedMessages.Length > 0) { collectedMessages.Append(Environment.NewLine); } collectedMessages.Append(ConvertToBuildProviderErrorMessage(generationError)); } } } /// /// Format an error reported by the code generator to message string reported to the user. /// /// ////// private static string ConvertToBuildProviderErrorMessage(ProxyGenerationError generationError) { string errorMessage = generationError.Message; if (!String.IsNullOrEmpty(generationError.MetadataFile)) { if (generationError.LineNumber < 0) { errorMessage = String.Format(CultureInfo.CurrentCulture, "'{0}': {1}", generationError.MetadataFile, errorMessage); } else if (generationError.LinePosition < 0) { errorMessage = String.Format(CultureInfo.CurrentCulture, "'{0}' ({1}): {2}", generationError.MetadataFile, generationError.LineNumber, errorMessage); } else { errorMessage = String.Format(CultureInfo.CurrentCulture, "'{0}' ({1},{2}): {3}", generationError.MetadataFile, generationError.LineNumber, generationError.LinePosition, errorMessage); } } return errorMessage; } /// /// Check the result from the code generator. /// By default we treat all error messages from WsdlImporter as warnings, /// and they will be ignored if valid code has been generated. /// We may hit other errors when we parse the metadata files. /// Those errors (which are usually because of a bad file) will not be ignored, because the user can fix them. /// If the WsdlImporter hasn't generated any code as we expect, we have to consider some of the error messages are fatal. /// We collect those messages and report to the user. /// /// The name of the generated reference /// Original Map File /// generated code compile unit /// /// ///private static void VerifyGeneratedCodeAndHandleErrors( string referenceDisplayName, SvcMapFile mapFile, CodeCompileUnit generatedCode, System.Collections.IEnumerable importErrors, System.Collections.IEnumerable generatorErrors) { // Check and report fatal error first... HandleProxyGenerationErrors(importErrors); HandleProxyGenerationErrors(generatorErrors); // if there is no fatal error, we expect valid type generated from the process // unless there is no metadata files, or there is a service contract type sharing if (mapFile.MetadataList.Count > 0 && mapFile.ClientOptions.ServiceContractMappingList.Count == 0) { if (!IsAnyTypeGenerated(generatedCode)) { StringBuilder collectedMessages = new StringBuilder(); // merge error messages CollectErrorMessages(importErrors, collectedMessages); CollectErrorMessages(generatorErrors, collectedMessages); throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_FailedToGenerateCode, referenceDisplayName, collectedMessages.ToString())); } } } /// /// Check whether we have generated any types /// /// ////// private static bool IsAnyTypeGenerated(CodeCompileUnit compileUnit) { if (compileUnit != null) { foreach (System.CodeDom.CodeNamespace codeNamespace in compileUnit.Namespaces) { if (codeNamespace.Types.Count > 0) { return true; } } } return false; } /// /// Retrieve a VirtualDirectory for the given virtual path /// /// ///private VirtualDirectory GetVirtualDirectory(string virtualPath) { return HostingEnvironment.VirtualPathProvider.GetDirectory(VirtualPath); } /// /// Caculate our namespace for current VirtualPath... /// ////// private string GetGeneratedNamespace() { // First, determine the namespace to use for code generation. This is based on the // relative path of the reference from its base App_WebReferences directory // ... Get the virtual path to the App_WebReferences folder, e.g "/MyApp/App_WebReferences" string rootWebRefDirVirtualPath = GetWebRefDirectoryVirtualPath(); // ... Get the folder's directory path, e.g "/MyApp/Application_WebReferences/Foo/Bar", // where we'll look for .svcmap files string currentSubfolderUnderWebReferences = this.VirtualPath; if (currentSubfolderUnderWebReferences == null) { Debug.Fail("Shouldn't be given a null virtual path"); throw new InvalidOperationException(); } return CalculateGeneratedNamespace(rootWebRefDirVirtualPath, currentSubfolderUnderWebReferences); } /// /// Determine the namespace to use for the proxy generation /// /// The path to the App_WebReferences folder /// The path to the current folder ///private static string CalculateGeneratedNamespace(string webReferencesRootVirtualPath, string virtualPath) { // ... Ensure both folders have trailing slashes webReferencesRootVirtualPath = VirtualPathUtility.AppendTrailingSlash(webReferencesRootVirtualPath); virtualPath = VirtualPathUtility.AppendTrailingSlash(virtualPath); Debug.Assert(virtualPath.StartsWith(webReferencesRootVirtualPath, StringComparison.OrdinalIgnoreCase), "We expected to be inside the App_WebReferences folder"); // ... Determine the namespace to use, based on the directory structure where the .svcmap file // is found. if (webReferencesRootVirtualPath.Length == virtualPath.Length) { Debug.Assert(string.Equals(webReferencesRootVirtualPath, virtualPath, StringComparison.OrdinalIgnoreCase), "We expected to be in the App_WebReferences directory"); // If it's the root WebReferences dir, use the empty namespace return String.Empty; } else { // We're in a subdirectory of App_WebReferences. // Get the directory's relative path from App_WebReferences, e.g. "Foo/Bar" virtualPath = VirtualPathUtility.RemoveTrailingSlash(virtualPath).Substring(webReferencesRootVirtualPath.Length); // Split it into chunks separated by '/' string[] chunks = virtualPath.Split('/'); // Turn all the relevant chunks into valid namespace chunks for (int i = 0; i < chunks.Length; i++) { chunks[i] = MakeValidTypeNameFromString(chunks[i]); } // Put the relevant chunks back together to form the namespace return String.Join(".", chunks); } } /// /// Returns the app domain's application virtual path [from HttpRuntime.AppDomainAppVPath]. /// Includes trailing slash, e.g. "/MyApp/" /// private static string GetAppDomainAppVirtualPath() { string appVirtualPath = HttpRuntime.AppDomainAppVirtualPath; if (appVirtualPath == null) { Debug.Fail("Shouldn't get a null app virtual path from the app domain"); throw new InvalidOperationException(); } return VirtualPathUtility.AppendTrailingSlash(VirtualPathUtility.ToAbsolute(appVirtualPath)); } ////// Gets the virtual path to the application's App_WebReferences directory, e.g. "/MyApp/App_WebReferences/" /// private static string GetWebRefDirectoryVirtualPath() { return VirtualPathUtility.Combine(GetAppDomainAppVirtualPath(), WebRefDirectoryName + @"\"); } ////// Return a valid type name from a string by changing any character /// that's not a letter or a digit to an '_'. /// /// ///internal static string MakeValidTypeNameFromString(string typeName) { if (String.IsNullOrEmpty(typeName)) throw new ArgumentNullException("typeName"); StringBuilder sb = new StringBuilder(); for (int i = 0; i < typeName.Length; i++) { // Make sure it doesn't start with a digit (ASURT 31134) if (i == 0 && Char.IsDigit(typeName[0])) sb.Append('_'); if (Char.IsLetterOrDigit(typeName[i])) sb.Append(typeName[i]); else sb.Append('_'); } // Identifier can't be a single underscore character string validTypeName = sb.ToString(); if (validTypeName.Equals("_", StringComparison.Ordinal)) { validTypeName = "__"; } return validTypeName; } /// /// Get the appropriate tool configuration for this service reference. /// /// If a reference.config file is present, the configuration object returned /// will be the merged view of: /// /// Machine Config /// ReferenceConfig /// /// If not reference.config file is present, the configuration object returned /// will be a merged view of: /// /// Machine.config /// web.config in application's physical path... /// /// /// SvcMapFile representing the service ///private System.Configuration.Configuration GetToolConfig(SvcMapFile mapFile, string mapFilePath) { string toolConfigFile = null; if (mapFile != null && mapFilePath != null) { foreach (ExtensionFile extensionFile in mapFile.Extensions) { if (String.Equals(extensionFile.Name, TOOL_CONFIG_ITEM_NAME, StringComparison.Ordinal)) { toolConfigFile = extensionFile.FileName; } } } System.Web.Configuration.WebConfigurationFileMap fileMap; fileMap = new System.Web.Configuration.WebConfigurationFileMap(); System.Web.Configuration.VirtualDirectoryMapping mapping; if (toolConfigFile != null) { // // If we've got a specific tool configuration to use, we better load that... // mapping = new System.Web.Configuration.VirtualDirectoryMapping(System.IO.Path.GetDirectoryName(mapFilePath), true, toolConfigFile); } else { // // Otherwise we fall back to the default web.config file... // mapping = new System.Web.Configuration.VirtualDirectoryMapping(HostingEnvironment.ApplicationPhysicalPath, true); } fileMap.VirtualDirectories.Add("/", mapping); return System.Web.Configuration.WebConfigurationManager.OpenMappedWebConfiguration(fileMap, "/", System.Web.Hosting.HostingEnvironment.SiteName); } /// /// Helper class to implement type resolution for the generator /// private class TypeResolver : IContractGeneratorReferenceTypeLoader { private System.Reflection.Assembly[] _referencedAssemblies; private IEnumerableReferencedAssemblies { get { if (_referencedAssemblies == null) { System.Collections.ICollection referencedAssemblyCollection = BuildManager.GetReferencedAssemblies(); _referencedAssemblies = new System.Reflection.Assembly[referencedAssemblyCollection.Count]; referencedAssemblyCollection.CopyTo(_referencedAssemblies, 0); } return _referencedAssemblies; } } System.Type IContractGeneratorReferenceTypeLoader.LoadType(string typeName) { // If the type can't be resolved, we need an exception thrown (thus the true argument) // so it can be reported as a build error. return BuildManager.GetType(typeName, true); } System.Reflection.Assembly IContractGeneratorReferenceTypeLoader.LoadAssembly(string assemblyName) { System.Reflection.AssemblyName assemblyToLookFor = new System.Reflection.AssemblyName(assemblyName); foreach (System.Reflection.Assembly assembly in ReferencedAssemblies) { if (System.Reflection.AssemblyName.ReferenceMatchesDefinition(assemblyToLookFor, assembly.GetName())) { return assembly; } } throw new System.IO.FileNotFoundException(String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_FailedToLoadAssembly, assemblyName)); } void IContractGeneratorReferenceTypeLoader.LoadAllAssemblies(out IEnumerable loadedAssemblies, out IEnumerable loadingErrors) { loadedAssemblies = ReferencedAssemblies; loadingErrors = new System.Exception[] {}; } } private class ImportExtensionServiceProvider : IServiceProvider { #region IServiceProvider Members public object GetService(Type serviceType) { // We don't currently provide any services to import extensions in the build provider context return null; } #endregion } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- WebServiceParameterData.cs
- ActionItem.cs
- XmlProcessingInstruction.cs
- CasesDictionary.cs
- Rectangle.cs
- FormView.cs
- OdbcTransaction.cs
- SortedDictionary.cs
- MatrixCamera.cs
- SoapIncludeAttribute.cs
- HMACSHA512.cs
- TransformDescriptor.cs
- LocatorPartList.cs
- BuildProviderAppliesToAttribute.cs
- HttpHandlersSection.cs
- BaseServiceProvider.cs
- TableSectionStyle.cs
- RecordBuilder.cs
- CodeCastExpression.cs
- View.cs
- StyleXamlTreeBuilder.cs
- UTF7Encoding.cs
- PersonalizationProviderCollection.cs
- XhtmlConformanceSection.cs
- ObjectDataSourceDisposingEventArgs.cs
- XmlSchemaSimpleContentExtension.cs
- ProfilePropertySettings.cs
- Condition.cs
- HttpWriter.cs
- StaticContext.cs
- MemberPathMap.cs
- BaseParser.cs
- CellCreator.cs
- BufferModeSettings.cs
- GenericParameterDataContract.cs
- MouseDevice.cs
- ReachDocumentSequenceSerializerAsync.cs
- DataObjectSettingDataEventArgs.cs
- StylusPointDescription.cs
- DateTimePicker.cs
- documentsequencetextview.cs
- DataServices.cs
- TextBox.cs
- IgnoreFileBuildProvider.cs
- ContractNamespaceAttribute.cs
- NetworkInformationPermission.cs
- GetIndexBinder.cs
- GridViewCancelEditEventArgs.cs
- XmlBinaryReader.cs
- XmlNamespaceMappingCollection.cs
- RegexNode.cs
- SpAudioStreamWrapper.cs
- HttpProfileGroupBase.cs
- CultureMapper.cs
- SynchronizingStream.cs
- SymmetricKeyWrap.cs
- FontFamilyIdentifier.cs
- ProxyWebPart.cs
- SubMenuStyle.cs
- InsufficientExecutionStackException.cs
- OracleNumber.cs
- MobileControlPersister.cs
- OleDbMetaDataFactory.cs
- mediaeventshelper.cs
- InputBinding.cs
- RtfNavigator.cs
- FixedSOMTableRow.cs
- WebPartDisplayMode.cs
- RangeBase.cs
- StateRuntime.cs
- XmlSchemaType.cs
- RankException.cs
- CryptoConfig.cs
- TextServicesProperty.cs
- KeyNotFoundException.cs
- ChangeTracker.cs
- JoinElimination.cs
- EntitySetDataBindingList.cs
- InstallerTypeAttribute.cs
- RtfFormatStack.cs
- QuaternionRotation3D.cs
- TextBoxAutomationPeer.cs
- AssemblyNameProxy.cs
- rsa.cs
- TiffBitmapDecoder.cs
- RefType.cs
- _AcceptOverlappedAsyncResult.cs
- StyleXamlTreeBuilder.cs
- FeatureManager.cs
- CultureInfoConverter.cs
- ControlTemplate.cs
- DoubleLinkListEnumerator.cs
- EnumValAlphaComparer.cs
- GlobalDataBindingHandler.cs
- XmlConverter.cs
- errorpatternmatcher.cs
- Rectangle.cs
- DockPattern.cs
- TypeSystem.cs
- RTLAwareMessageBox.cs