Esempio n. 1
0
        public void CommitOperationThrowsExceptionWhenKeysAreMissingInConfigFile()
        {
            var xml               = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<?xml-stylesheet type=""text/xsl"" href=""style1.xsl""?>
<settings>
    <?xml-stylesheet type=""text/xsl"" href=""style2.xsl""?>
    <Data>
        <DefaultConnection>
            <ConnectionString>TestConnectionString</ConnectionString>
            <Provider>SqlClient</Provider>
        </DefaultConnection>
        <Inventory>
            <ConnectionString>AnotherTestConnectionString</ConnectionString>
            <Provider>MySql</Provider>
        </Inventory>
    </Data>
</settings>";
            var modifiedXml       = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<?xml-stylesheet type=""text/xsl"" href=""style1.xsl""?>
<settings>
    <?xml-stylesheet type=""text/xsl"" href=""style2.xsl""?>
    <Data>
        <DefaultConnection>
            <ConnectionString>TestConnectionString</ConnectionString>
        </DefaultConnection>
        <Inventory>
            <Provider>MySql</Provider>
        </Inventory>
    </Data>
</settings>";
            var xmlConfigSrc      = new XmlConfigurationSource(ArbitraryFilePath);
            var outputCacheStream = new MemoryStream();

            xmlConfigSrc.Load(StringToStream(xml));

            var exception = Assert.Throws <InvalidOperationException>(
                () => xmlConfigSrc.Commit(StringToStream(modifiedXml), outputCacheStream));

            Assert.Equal(
                Resources.
                FormatError_CommitWhenKeyMissing("Data:DefaultConnection:Provider, Data:Inventory:ConnectionString"),
                exception.Message);
        }
        // Use the original file as a template while generating new file contents
        // to make sure the format is consistent and comments are not lost
        internal void Commit(Stream inputStream, Stream outputStream)
        {
            var dataCopy = new Dictionary <string, string>(Data, StringComparer.OrdinalIgnoreCase);

            var writerSettings = new XmlWriterSettings()
            {
                Indent           = false,
                ConformanceLevel = ConformanceLevel.Auto
            };

            var outputWriter = XmlWriter.Create(outputStream, writerSettings);

            var readerSettings = new XmlReaderSettings()
            {
                DtdProcessing                = DtdProcessing.Prohibit,
                IgnoreWhitespace             = false,
                IgnoreComments               = false,
                IgnoreProcessingInstructions = false
            };

            using (var inputReader = XmlReader.Create(inputStream, readerSettings))
            {
                var prefixStack = new Stack <string>();

                CopyUntilRootElement(inputReader, outputWriter);

                // We process the root element individually since it doesn't contribute to prefix
                outputWriter.WriteStartElement(inputReader.LocalName);
                ProcessAttributes(inputReader, prefixStack, dataCopy, AddNamePrefix);
                ProcessAttributes(inputReader, prefixStack, dataCopy, CommitAttributePair, outputWriter);

                var preNodeType = inputReader.NodeType;
                while (inputReader.Read())
                {
                    switch (inputReader.NodeType)
                    {
                    case XmlNodeType.Element:
                        prefixStack.Push(inputReader.LocalName);
                        outputWriter.WriteStartElement(inputReader.LocalName);

                        ProcessAttributes(inputReader, prefixStack, dataCopy, AddNamePrefix);
                        ProcessAttributes(inputReader, prefixStack, dataCopy, CommitAttributePair, outputWriter);

                        // If current element is self-closing
                        if (inputReader.IsEmptyElement)
                        {
                            outputWriter.WriteEndElement();
                            prefixStack.Pop();
                        }
                        break;

                    case XmlNodeType.EndElement:
                        if (prefixStack.Any())
                        {
                            // If this EndElement node comes right after an Element node,
                            // it means there is no text/CDATA node in current element
                            if (preNodeType == XmlNodeType.Element)
                            {
                                var key = string.Join(Constants.KeyDelimiter, prefixStack.Reverse());
                                if (!dataCopy.ContainsKey(key))
                                {
                                    throw new InvalidOperationException(Resources.FormatError_CommitWhenNewKeyFound(key));
                                }
                                outputWriter.WriteValue(dataCopy[key]);
                                dataCopy.Remove(key);
                            }
                            outputWriter.WriteFullEndElement();
                            prefixStack.Pop();
                        }
                        break;

                    case XmlNodeType.CDATA:
                    case XmlNodeType.Text:
                    {
                        var key = string.Join(Constants.KeyDelimiter, prefixStack.Reverse());

                        if (!dataCopy.ContainsKey(key))
                        {
                            throw new InvalidOperationException(Resources.FormatError_CommitWhenNewKeyFound(key));
                        }

                        if (inputReader.NodeType == XmlNodeType.CDATA)
                        {
                            outputWriter.WriteCData(dataCopy[key]);
                        }
                        else
                        {
                            outputWriter.WriteValue(dataCopy[key]);
                        }
                        dataCopy.Remove(key);
                        break;
                    }

                    case XmlNodeType.ProcessingInstruction:
                        outputWriter.WriteProcessingInstruction(inputReader.LocalName, inputReader.Value);
                        break;

                    case XmlNodeType.Comment:
                        outputWriter.WriteComment(inputReader.Value);
                        break;

                    case XmlNodeType.Whitespace:
                        outputWriter.WriteWhitespace(inputReader.Value);
                        break;

                    default:
                        throw new FormatException(Resources.FormatError_UnsupportedNodeType(inputReader.NodeType,
                                                                                            GetLineInfo(inputReader)));
                    }
                    preNodeType = inputReader.NodeType;
                    // If this element is a self-closing element,
                    // we pretend that we just processed an EndElement node
                    // because a self-closing element contains an end within itself
                    if (preNodeType == XmlNodeType.Element &&
                        inputReader.IsEmptyElement)
                    {
                        preNodeType = XmlNodeType.EndElement;
                    }
                }

                // Close the root element
                outputWriter.WriteEndElement();
                outputWriter.Flush();
            }

            if (dataCopy.Any())
            {
                var missingKeys = string.Join(", ", dataCopy.Keys);
                throw new InvalidOperationException(Resources.FormatError_CommitWhenKeyMissing(missingKeys));
            }
        }