DataServiceBuildProvider.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Design / system / Data / EntityModel / DataServiceBuildProvider.cs / 1305376 / DataServiceBuildProvider.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

// Putting the build provider in the System.Data.Services.Design namespace forces DataSvcUtil.exe to also take dependency on System.Web.dll 
// Hence putting this in a different namespace. 
[module: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Data.Services.BuildProvider")]
namespace System.Data.Services.BuildProvider 
{
    using System;
    using System.Collections;
    using System.Data.Services.Design; 
    using System.Diagnostics;
    using System.Globalization; 
    using System.IO; 
    using System.Text;
    using System.Web; 
    using System.Web.Compilation;
    using System.Web.Hosting;
    using System.Xml;
    using System.Xml.Schema; 

    ///  
    /// A build provider for data service references in website projects. 
    /// 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.
    ///
    /// 
    [FolderLevelBuildProviderAppliesTo(FolderLevelBuildProviderAppliesTo.WebReferences)] 
    [System.Security.SecurityCritical]
    public class DataServiceBuildProvider : BuildProvider 
    { 
        private const string WebRefDirectoryName = "App_WebReferences";
        private const string DataSvcMapExtension = ".datasvcmap"; 

        private const string UseCollectionParameterName = "UseDataServiceCollection";
        private const string VersionParameterName = "Version";
        private const string Version1Dot0 = "1.0"; 
        private const string Version2Dot0 = "2.0";
 
        ///  
        /// 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. 
        ///  
        [System.Security.SecuritySafeCritical]
        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(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
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "XmlReader over StringReader doesn't need to be disposed")] 
        private void GenerateCodeFromDataServiceMapFile(string mapFilePath, AssemblyBuilder assemblyBuilder)
        { 
            try 
            {
                MapFileLoader loader = new MapFileLoader(mapFilePath); 
                loader.LoadDataSvcMapFile();
                MapFileLoader.AddAssemblyReferences(assemblyBuilder, loader.UseDataServiceCollection);
                string edmxContent = loader.GetEdmxContent();
                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))
                { 
                    generator.UseDataServiceCollection = loader.UseDataServiceCollection;
                    generator.Version = loader.Version;

                    // 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);
            } 
        }
 
        ///  
        ///  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; 
        }

        private class MapFileLoader
        { 
            /// Namespace for the dataSvcMap file.
            private const string dataSvcSchemaNamespace = "urn:schemas-microsoft-com:xml-dataservicemap"; 
 
            /// xsd Schema for the datasvcmap file
            private static XmlSchemaSet serviceMapSchemaSet; 

            /// xsd Schema for the datasvcmap file
            private static XmlNamespaceManager namespaceManager;
 
            /// full path of the data svc map file.
            private string dataSvcMapFilePath; 
 
            /// Name of the edmx schema file.
            private string edmxSchemaFileName; 

            /// True if DataServiceCollection needs to be used, otherwise false.
            private bool useDataServiceCollection;
 
            /// Version as specified in the svcmap file.
            private DataServiceCodeVersion version; 
 
            /// 
            /// Creates the new instance of the dataSvcMapFileLoader. 
            /// 
            /// filePath for the dataSvcMap file.
            internal MapFileLoader(string mapFilePath)
            { 
                Debug.Assert(!String.IsNullOrEmpty(mapFilePath), "mapFilePath cannot be null or empty");
                this.dataSvcMapFilePath = mapFilePath; 
            } 

            /// True if the binding code needs to be generated, otherwise false. 
            internal bool UseDataServiceCollection
            {
                get { return this.useDataServiceCollection; }
            } 

            /// Returns the version as specified in the svcmap file. 
            internal DataServiceCodeVersion Version 
            {
                get { return this.version; } 
            }

            /// 
            /// Returns the XMlSchemaSet against which the dataSvcMap needs to be validated against. 
            /// 
            private static XmlSchemaSet ServiceMapSchemaSet 
            { 
                get
                { 
                    if (serviceMapSchemaSet == null)
                    {
                        XmlSchema serviceMapSchema;
                        System.IO.Stream fileStream = typeof(WCFBuildProvider).Assembly.GetManifestResourceStream(@"System.Web.Compilation.WCFModel.Schema.DataServiceMapSchema.xsd"); 
                        System.Diagnostics.Debug.Assert(fileStream != null, "Couldn't find ServiceMapSchema.xsd resource");
 
                        serviceMapSchema = XmlSchema.Read(fileStream, null); 
                        serviceMapSchemaSet = new XmlSchemaSet();
                        serviceMapSchemaSet.Add(serviceMapSchema); 
                    }

                    return serviceMapSchemaSet;
                } 
            }
 
            ///  
            /// Returns the namespace manager for the dataSvcMap file.
            ///  
            private static XmlNamespaceManager NamespaceManager
            {
                get
                { 
                    if (namespaceManager == null)
                    { 
                        namespaceManager = new XmlNamespaceManager(new NameTable()); 
                        namespaceManager.AddNamespace("ds", dataSvcSchemaNamespace);
                    } 

                    return namespaceManager;
                }
            } 

            ///  
            /// Read the contents from the edmx file. 
            /// 
            /// a string instance containing all the contents of the edmx file. 
            internal string GetEdmxContent()
            {
                string edmxFilePath = IO.Path.Combine(IO.Path.GetDirectoryName(this.dataSvcMapFilePath), this.edmxSchemaFileName);
                return IO.File.ReadAllText(edmxFilePath); 
            }
 
            ///  
            /// Load the DataSvcMapFile from the given location
            ///  
            internal void LoadDataSvcMapFile()
            {
                using (System.IO.TextReader mapFileReader = IO.File.OpenText(this.dataSvcMapFilePath))
                { 
                    XmlDocument document = LoadAndValidateFile(mapFileReader);
                    this.edmxSchemaFileName = GetMetadataFileNameFromMapFile(document); 
                    ValidateParametersInMapFile(document, out this.version, out this.useDataServiceCollection); 
                }
            } 

            /// 
            /// Add the assembly references to the assembly builder depending on whether the databinding is enabled or not.
            ///  
            /// instance of assemblyBuilder where we need to add assembly references.
            /// indicates whether databinding is enabled or not. 
            internal static void AddAssemblyReferences(AssemblyBuilder assemblyBuilder, bool useDataServiceCollection) 
            {
                assemblyBuilder.AddAssemblyReference(typeof(System.Data.Services.Client.DataServiceContext).Assembly); 

                if (useDataServiceCollection)
                {
                    // TODO: SQLBUDT 707098 - Use the helper method which returns the FrameworkName instance as defined in System.Runtime.Versioning namespace. 
                    // When generating data binding code for .Net framework 3.5, we need to load the right version of windows base here.
                    // 3.5 Framework version: WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 
                    // In 4.0, ObservableCollection<> class was moved into System.dll. Hence no need to do anything. 
                    if (BuildManager.TargetFramework.Version.Major < 4)
                    { 
                        assemblyBuilder.AddAssemblyReference(System.Reflection.Assembly.Load("WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"));
                    }
                }
            } 

            ///  
            /// Validates the given map file. 
            /// 
            /// instance of text reader containing the map file. 
            /// returns an instance of XmlDocument containing the map file.
            private static XmlDocument LoadAndValidateFile(TextReader mapFileReader)
            {
                XmlReaderSettings readerSettings = new XmlReaderSettings(); 

                readerSettings.Schemas = ServiceMapSchemaSet; 
                readerSettings.ValidationType = ValidationType.Schema; 
                readerSettings.ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings;
 
                // Validation Handler
                ValidationEventHandler handler = delegate(object sender, System.Xml.Schema.ValidationEventArgs e)
                {
                    if (e.Severity == XmlSeverityType.Error) 
                    {
                        throw e.Exception; 
                    } 
                };
 
                // Create an xmlDocument and load the map file
                XmlDocument document = new XmlDocument();
                document.Load(mapFileReader);
 
                // validate the map file against the xsd
                document.Schemas = ServiceMapSchemaSet; 
                document.Validate(handler); 

                return document; 
            }

            /// 
            /// Gets the metadata file name from the map file. 
            /// 
            /// instance of xml document which refers to the map file. 
            /// returns the metadata file path as specified in the map file. 
            private static string GetMetadataFileNameFromMapFile(XmlDocument document)
            { 
                // The xsd already validates that there should be exactly one MetadataFile element present.
                XmlNode node = document.SelectSingleNode("/ds:ReferenceGroup/ds:Metadata/ds:MetadataFile", NamespaceManager);
                Debug.Assert(node != null, "MetadataFile node must be present");
 
                // Both the attributes FileName and MetadataType must be present,
                // since the xsd already does this validation. 
                // xsd also does the validation that these attributes cannot be empty. 
                string edmxSchemaFileName = node.Attributes["FileName"].Value;
                string metadataType = node.Attributes["MetadataType"].Value; 

                // Ideally, this check must be moved in the xsd, so that user gets all the information about where
                // exactly the error is - file information, line number etc. We currently only have file information
                // with us 
                if (metadataType != "Edmx")
                { 
                    throw new ArgumentException(Strings.InvalidMetadataType(metadataType, "MetadataType", "Edmx")); 
                }
 
                return edmxSchemaFileName;
            }

            ///  
            /// Validate the parameters list in the map file. Currently only EnableDataBinding parameter is supported.
            ///  
            /// instance of xml documents which contains the map file. 
            /// returns true if the data binding parameter is specified and its value is true, otherwise returns false.
            private static void ValidateParametersInMapFile(XmlDocument document, out DataServiceCodeVersion version, out bool useDataServiceCollection) 
            {
                bool isSentinalTypePresent = BuildManager.GetType("System.Data.Services.Client.DataServiceCollection`1", false /*throwOnError*/) != null;

                // Validate all the parameters specified in the map file 
                XmlNodeList parameters = document.SelectNodes("/ds:ReferenceGroup/ds:Parameters/ds:Parameter", NamespaceManager);
                bool? useCollectionParamValue = null; 
                DataServiceCodeVersion? versionParamValue = null; 

                if (parameters != null && parameters.Count != 0) 
                {
                    // currently only one parameter can be specified. EnableDataBinding - whose valid values are 'true and 'false'
                    foreach (XmlNode p in parameters)
                    { 
                        // Name and value attributes must be present, since they are verified by the xsd
                        XmlAttribute nameAttribute = p.Attributes["Name"]; 
                        XmlAttribute valueAttribute = p.Attributes["Value"]; 
                        if (nameAttribute.Value == VersionParameterName)
                        { 
                            ValidateVersionParameter(ref versionParamValue, valueAttribute.Value);
                        }
                        else if (nameAttribute.Value == UseCollectionParameterName)
                        { 
                            ValidateBooleanParameter(ref useCollectionParamValue, valueAttribute.Value, UseCollectionParameterName);
                        } 
                        else 
                        {
                            throw new ArgumentException( 
                                Strings.InvalidParameterName(
                                    nameAttribute.Value,
                                    String.Format(CultureInfo.InvariantCulture, "'{0}', '{1}'", VersionParameterName, UseCollectionParameterName)));
                        } 
                    }
                } 
 
                if (!isSentinalTypePresent && useCollectionParamValue.HasValue && useCollectionParamValue == true)
                { 
                    throw new ArgumentException(Strings.UseDataServiceCollectionCannotBeSetToTrue(UseCollectionParameterName));
                }

                version = versionParamValue.HasValue ? versionParamValue.Value : DataServiceCodeVersion.V1; 
                useDataServiceCollection = useCollectionParamValue.HasValue ? useCollectionParamValue.Value : false;
            } 
 
            /// 
            /// Validate the boolean parameter value as specified in the .svcmap file. 
            /// 
            /// parameter value as specified in the svcmap file.
            /// name of the parameter whose value is getting validated.
            /// boolean value as specified in the svcmap file. 
            private static void ValidateBooleanParameter(ref bool? paramValue, string value, string parameterName)
            { 
                if (paramValue == null) 
                {
                    try 
                    {
                        paramValue = XmlConvert.ToBoolean(value);
                    }
                    catch (FormatException e) 
                    {
                        throw new ArgumentException(Strings.InvalidBooleanParameterValue(value, parameterName), e); 
                    } 
                }
                else 
                {
                    throw new ArgumentException(Strings.ParameterSpecifiedMultipleTimes(UseCollectionParameterName));
                }
            } 

            ///  
            /// Validate the version parameter value is valid. 
            /// 
            /// version parameter value as specified in the .svcmap file. 
            /// instance of DataServiceCodeVersion, which contains the value of the version parameter as specified in the svcmap file.
            private static void ValidateVersionParameter(ref DataServiceCodeVersion? version, string parameterValue)
            {
                if (version == null) 
                {
                    if (parameterValue == Version1Dot0) 
                    { 
                        version = DataServiceCodeVersion.V1;
                    } 
                    else if (parameterValue == Version2Dot0)
                    {
                        version = DataServiceCodeVersion.V2;
                    } 
                    else
                    { 
                        throw new ArgumentException( 
                            Strings.InvalidVersionParameterValue(
                                parameterValue, 
                                VersionParameterName,
                                String.Format(CultureInfo.InvariantCulture, "'{0}', '{1}'", Version1Dot0, Version2Dot0)));
                    }
                } 
                else
                { 
                    throw new ArgumentException(Strings.ParameterSpecifiedMultipleTimes(VersionParameterName)); 
                }
            } 
        }
    }
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK