Web Q&A

Virtual Directories, Releasing DB Connections, and More

Edited by Nancy Michell

Q I need to include files from a sibling directory under a virtual directory from two different sibling subdirectories, yet make sure parent paths stay disabled (see Figure 1). The virtual include is relative to the root, so I need to know the full path \vdirTwo\subInclude\MyInclude.asp. I want to be able to include just \subInclude\MyInclude.asp from either sibling directory so the include doesn't have to be duplicated in child directories, but I want to do it without parent paths turned on.

Q I need to include files from a sibling directory under a virtual directory from two different sibling subdirectories, yet make sure parent paths stay disabled (see Figure 1). The virtual include is relative to the root, so I need to know the full path \vdirTwo\subInclude\MyInclude.asp. I want to be able to include just \subInclude\MyInclude.asp from either sibling directory so the include doesn't have to be duplicated in child directories, but I want to do it without parent paths turned on.

Figure 1 Folder Hierarchy

Figure 1** Folder Hierarchy **

A To solve your problem, you have two options: turn ASPEnableParentPaths on or create a virtual directory, named inc, pointing to the physical directory that contains the include files. In IIS, turn off script/execute/read permissions for this virtual directory. From the applications in vdir1 and vdir2, you can now do this:

<!--#include virtual = '/inc/foo.asp' -->

A To solve your problem, you have two options: turn ASPEnableParentPaths on or create a virtual directory, named inc, pointing to the physical directory that contains the include files. In IIS, turn off script/execute/read permissions for this virtual directory. From the applications in vdir1 and vdir2, you can now do this:

<!--#include virtual = '/inc/foo.asp' -->

Q I'm having a problem with an ASP.NET site I worked on that's not releasing a Microsoft® Access database file. The file remains locked hours after the last site visitor, and the site owner can't upload a new MDB file to update his catalog. If I'm always careful to close OleDbConnection explicitly, do I gain anything additional by calling Dispose instead of, or in addition to, the Close method?

Q I'm having a problem with an ASP.NET site I worked on that's not releasing a Microsoft® Access database file. The file remains locked hours after the last site visitor, and the site owner can't upload a new MDB file to update his catalog. If I'm always careful to close OleDbConnection explicitly, do I gain anything additional by calling Dispose instead of, or in addition to, the Close method?

A Calling Dispose also saves the runtime from having to send the object to the finalize queue and then calling Dispose on it. If quite a few objects are ending up in the finalize queue, this can have a negative impact on performance.

A Calling Dispose also saves the runtime from having to send the object to the finalize queue and then calling Dispose on it. If quite a few objects are ending up in the finalize queue, this can have a negative impact on performance.

Using the Jet database engine from ASP.NET can be difficult. Jet works best if all threads in the same process use the same single instance of a connection (this is the exact opposite of SQL Server™).

As an alternative, you could write static functions called GetConnection and ReturnConnection. GetConnection would hand back to the caller a single instance of the OleDbConnection (stored in a static member variable) and bump up its reference count. GetConnection would take the calling ASP.NET page as a parameter as well, so you could track down which object is not releasing the connection. You should open the database exclusively as well. ReturnConnection would be called by the code when you are finished using the connection (Close would not be called), and this would bump down the reference count. You should also add locking code and a global flag to reject requests for connections. Then you could block callers from getting new connections while you compacted the database and performed other maintenance. Of course, to avoid all of this, you might consider using SQL Server.

Q Is it possible to securely perform SQL Merge replication between a workstation client running the Microsoft Data Engine (MSDE) and a machine running SQL Server across the Internet? If so, where can I find some documentation on how to do it? My main questions are how to enable authentication to occur properly, how to ensure that data is encrypted on the wire, and that the machine running SQL Server remain secure.

Q Is it possible to securely perform SQL Merge replication between a workstation client running the Microsoft Data Engine (MSDE) and a machine running SQL Server across the Internet? If so, where can I find some documentation on how to do it? My main questions are how to enable authentication to occur properly, how to ensure that data is encrypted on the wire, and that the machine running SQL Server remain secure.

A Merge replication in SQL Server 2000 does not currently support Web-based synchronization of incremental changes without using a virtual private network (VPN).

A Merge replication in SQL Server 2000 does not currently support Web-based synchronization of incremental changes without using a virtual private network (VPN).

SQL Server Books Online, in the topic called "Publishing Data Over the Internet Using Microsoft Proxy Server," says that integrating Microsoft SQL Server 2000 replication with Microsoft Proxy Server lets you perform replication over the Internet with security configured on Windows NT® 4.0 or Windows® 2000 Server, Proxy Server, and SQL Server 2000.

Using this approach, Proxy Server provides a connection between the Internet and the server running SQL Server 2000. The subscriber connects to Proxy Server over the Internet and uses a pull subscription to retrieve the data. Proxy Server is configured so that unauthorized Internet users are denied access to the internal network, and the subscriber must connect to a port on the Proxy Server that limits access to the services for which permissions have been granted.

For information about how to configure Microsoft Proxy Server for replication, read the white paper titled "Configuring Proxy Server for Replication Across the Internet".

Q How can I completely remove a node in my XML document using C#? For example, if I had some XML like the following

<Results>
    <Run id="1">
       <ID>
       <Name>
    </Run>
       <Run id="2">
          <ID>
          <Name>
       </Run>
</Results>

how would I remove id="2" so that I'd be left with:

<Results>
     <Run id="1">
       <ID>
       <Name>
     </Run>
</Results>

Q How can I completely remove a node in my XML document using C#? For example, if I had some XML like the following

<Results>
    <Run id="1">
       <ID>
       <Name>
    </Run>
       <Run id="2">
          <ID>
          <Name>
       </Run>
</Results>

how would I remove id="2" so that I'd be left with:

<Results>
     <Run id="1">
       <ID>
       <Name>
     </Run>
</Results>

A The easiest way to get rid of a node from an XML doc is to first select the node using an XPath expression; then call RemoveChild on its parent node:

XmlNode oNode = xmlDoc.SelectSingleNode("/Results/Run[@ID=" + 
    iRunIDs[i] + "]");
XmlNode oParentNode = oNode.ParentNode;
oParentNode.RemoveChild(oNode);

A The easiest way to get rid of a node from an XML doc is to first select the node using an XPath expression; then call RemoveChild on its parent node:

XmlNode oNode = xmlDoc.SelectSingleNode("/Results/Run[@ID=" + 
    iRunIDs[i] + "]");
XmlNode oParentNode = oNode.ParentNode;
oParentNode.RemoveChild(oNode);

Q I have an XML hierarchy that ideally looks like the following:

<?xml version="1.0" encoding="utf-8"?>
<ServiceOfferings xmlns="https://tempuri.org/Data.xsd">
    <ServiceOffering>
        <OfferingId>1</OfferingId>
        <Offering>Blah</Offering>
        <Description>Description</Description>
        <Features>
            <Feature>feature one</Feature>
            <Feature>feature two</Feature>
            <Feature>feature three</Feature>
      </Features>
    </ServiceOffering>
</ServiceOfferings>

I wrote an XML schema definition (XSD) that I thought would give me what I was hoping for (see Figure 2). But when I add elements in data view in Visual Studio® .NET and switch back to XML mode, it looks like the code in Figure 3. Why does the content inside those features tags get broken down that way, and how can I correct it so that it's formatted as follows:

<Features>
    <Feature>feature one</Feature>
    <Feature>feature two</Feature>
    <Feature>feature three</Feature>
</Features>

Q I have an XML hierarchy that ideally looks like the following:

<?xml version="1.0" encoding="utf-8"?>
<ServiceOfferings xmlns="https://tempuri.org/Data.xsd">
    <ServiceOffering>
        <OfferingId>1</OfferingId>
        <Offering>Blah</Offering>
        <Description>Description</Description>
        <Features>
            <Feature>feature one</Feature>
            <Feature>feature two</Feature>
            <Feature>feature three</Feature>
      </Features>
    </ServiceOffering>
</ServiceOfferings>

I wrote an XML schema definition (XSD) that I thought would give me what I was hoping for (see Figure 2). But when I add elements in data view in Visual Studio® .NET and switch back to XML mode, it looks like the code in Figure 3. Why does the content inside those features tags get broken down that way, and how can I correct it so that it's formatted as follows:

<Features>
    <Feature>feature one</Feature>
    <Feature>feature two</Feature>
    <Feature>feature three</Feature>
</Features>

Figure 3 XML Result

<?xml version="1.0" encoding="utf-8"?>
<ServiceOfferings xmlns="https://tempuri.org/Data.xsd">
    <ServiceOffering>
        <OfferingId>1</OfferingId>
        <Offering>Blah</Offering>
        <Description>Description</Description>
        <Features>
            <Feature>feature one</Feature>
        </Features>
        <Features>
            <Feature>feature two</Feature>
        </Features>
        <Features>
            <Feature>feature three</Feature>
        </Features>
    </ServiceOffering>
</ServiceOfferings>

Figure 2 XSD

<?xml version="1.0" encoding="utf-8"?>
<xsd:schema xmlns:xsd="https://www.w3.org/2001/XMLSchema"   
  elementFormDefault="qualified">
    <xsd:element name="ServiceOfferings" 
      type="ServiceOfferingsList" />
    <xsd:complexType name="ServiceOfferingsList">
        <xsd:sequence maxOccurs="unbounded">
            <xsd:element name="ServiceOffering" 
              type="OfferingType" />
        </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="OfferingType">
        <xsd:sequence>
            <xsd:element name="OfferingId" type="xsd:int" 
              minOccurs="1" maxOccurs="1" />
            <xsd:element name="Offering" type="xsd:string" 
              minOccurs="1" maxOccurs="1" />
            <xsd:element name="Description" type="xsd:string" 
              minOccurs="1" maxOccurs="1" />
            <xsd:element name="Features" maxOccurs="1">
                <xsd:complexType>
                    <xsd:sequence>
                       <xsd:element name="Feature" 
                         minOccurs="0" maxOccurs="unbounded" />
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
        </xsd:sequence>
    </xsd:complexType>
</xsd:schema>

A When a DataSet returns XML, it considers that this

<Features>
    <Feature>feature one</Feature>
    <Feature>feature two</Feature>
    <Feature>feature three</Feature>
</Features>

is a table called "Features" and that it has three fields called "Features." The following format says that there is a table called "Feature," that it has three records, and each record has a field called "Feature":

<Features>
    <Feature>feature one</Feature>
</Features>
<Features>
    <Feature>feature two</Feature>
</Features>
<Features>
    <Feature>feature three</Feature>
</Features>

A When a DataSet returns XML, it considers that this

<Features>
    <Feature>feature one</Feature>
    <Feature>feature two</Feature>
    <Feature>feature three</Feature>
</Features>

is a table called "Features" and that it has three fields called "Features." The following format says that there is a table called "Feature," that it has three records, and each record has a field called "Feature":

<Features>
    <Feature>feature one</Feature>
</Features>
<Features>
    <Feature>feature two</Feature>
</Features>
<Features>
    <Feature>feature three</Feature>
</Features>

So, the way the DataSet sees data is different from how XML sees it. (Remember that you are trying to build a text file out of something relational). You can try using the XmlDataDocument to pull it instead. You may never get the IDE to look they way you want it to. I would suggest sticking with one or the other, either DataSet to database, or XML to database.

Q I have an XML file like that looks like this:


<foo xmlns="urn:schemas-microsoft-com:xml-msdatasource">
    •••
    <bar>  ??? </bar>
    •••
 </foo>

I need to write code that, given an XmlReader that is positioned on the node "bar," will extract the entire node (and any subnodes) as a string. This seems simple enough, but when I try to do XmlReader.ReadOuterXml, the string returned has the namespace stuck in it (that is, <bar xmlns="urn:schemas-microsoft-com:xml-msdatasource"> ...).

Q I have an XML file like that looks like this:


<foo xmlns="urn:schemas-microsoft-com:xml-msdatasource">
    •••
    <bar>  ??? </bar>
    •••
 </foo>

I need to write code that, given an XmlReader that is positioned on the node "bar," will extract the entire node (and any subnodes) as a string. This seems simple enough, but when I try to do XmlReader.ReadOuterXml, the string returned has the namespace stuck in it (that is, <bar xmlns="urn:schemas-microsoft-com:xml-msdatasource"> ...).

How can I get a string corresponding to this node exactly as it appears in the file using an XmlReader?

A According to the XmlReader spec, you cannot pull the text without it being associated with the appropriate namespace. Basically, you will have to go through and take those out. Figure 4 shows how you can do it with a little function.

A According to the XmlReader spec, you cannot pull the text without it being associated with the appropriate namespace. Basically, you will have to go through and take those out. Figure 4 shows how you can do it with a little function.

Figure 4 GetNodeString

internal string GetNodeString(XmlNode node)
{
  NameTable nt = new NameTable();
  XmlNamespaceManager nsm = new XmlNamespaceManager(nt);
  Hashtable removeNamespaces = new Hashtable();
  if (node.NodeType != XmlNodeType.XmlDeclaration && 
    node.NodeType != XmlNodeType.DocumentType &&
    node.NodeType != XmlNodeType.Comment && node.NodeType != 
      XmlNodeType.Whitespace && node.NodeType != 
      XmlNodeType.ProcessingInstruction &&
    node.NodeType != XmlNodeType.SignificantWhitespace)
  {
    XmlNode tNode = node;
    if (node.NodeType != XmlNodeType.XmlDeclaration && 
      node.NodeType != XmlNodeType.DocumentType &&
      node.NodeType != XmlNodeType.Comment && node.NodeType != 
        XmlNodeType.Whitespace && node.NodeType != 
        XmlNodeType.ProcessingInstruction &&
      node.NodeType != XmlNodeType.SignificantWhitespace)
    {
      XPathNodeIterator xni = 
        tNode.CreateNavigator().Select("namespace::node()");
      while (xni.MoveNext())
      {
        if (xni.Current.Name != "xml") {
          if (nsm.HasNamespace(xni.Current.Name)) {
            removeNamespaces.Add(xni.Current.Name,xni.Current.Value);
          }
          else
          {
            nsm.AddNamespace(xni.Current.Name, xni.Current.Value);
          }
        }
      }
      tNode = tNode.ParentNode;
    }
    string outputText = node.OuterXml;
    IDictionaryEnumerator idenum = removeNamespaces.GetEnumerator();
    while (idenum.MoveNext())
    {
      if (((string)idenum.Key).Length == 0)
        outputText = System.Text.RegularExpressions.Regex.Replace(outputText, 
          " xmlns=." + (string)idenum.Value + ".","");
      else
        outputText = System.Text.RegularExpressions.Regex.Replace(outputText, 
         " xmlns:" + (string)idenum.Key + "=." + 
         (string)idenum.Value + ".","");
    }
    return outputText;
  }
  return node.OuterXml;
}

 

Q I have an application that uses System.Net.Sockets.Socket to make HTTP requests against an IIS server and get responses. How can I perform authentication with secure pages (using NTLM)? I know it is easy to do with WebRequest/NetworkCredential, but I have to use Socket. Typically you would not need to control that option (the default behavior is that the socket will remain in time_wait state for four minutes), but in my case I have a stress application which generates a very large number of requests concurrently on different sockets in order to simulate multiple clients being connected to the server. So unless I set each socket to not linger, I run out of ports. I would use WebRequest but I need to control keepalive and linger options, which WebRequest doesn't expose.

Q I have an application that uses System.Net.Sockets.Socket to make HTTP requests against an IIS server and get responses. How can I perform authentication with secure pages (using NTLM)? I know it is easy to do with WebRequest/NetworkCredential, but I have to use Socket. Typically you would not need to control that option (the default behavior is that the socket will remain in time_wait state for four minutes), but in my case I have a stress application which generates a very large number of requests concurrently on different sockets in order to simulate multiple clients being connected to the server. So unless I set each socket to not linger, I run out of ports. I would use WebRequest but I need to control keepalive and linger options, which WebRequest doesn't expose.

A You should reconsider using the WebRequest class (see HttpWebRequest Members). If you don't, then you need to read/write the HTTP headers yourself. You also need a good understanding of HTTP. There is a KeepAlive property in HttpWebRequest as well as a Timeout property, although the linger option you mentioned is a socket option, and not the same as timeout. Linger controls how long the socket remains in the time_wait state, waiting for out-of-band data after it is closed. You should really use HttpWebRequest.UnsafeAuthenticatedConnectionSharing supported in .NET Framework version 1.1 and higher. The behavior is similar to ReuseAddress in that it will reuse previous connections so you don't run out of available ports.

A You should reconsider using the WebRequest class (see HttpWebRequest Members). If you don't, then you need to read/write the HTTP headers yourself. You also need a good understanding of HTTP. There is a KeepAlive property in HttpWebRequest as well as a Timeout property, although the linger option you mentioned is a socket option, and not the same as timeout. Linger controls how long the socket remains in the time_wait state, waiting for out-of-band data after it is closed. You should really use HttpWebRequest.UnsafeAuthenticatedConnectionSharing supported in .NET Framework version 1.1 and higher. The behavior is similar to ReuseAddress in that it will reuse previous connections so you don't run out of available ports.

Q I have an app built on Visual Basic® 6.0 that performs a screen print (using Screen.ActiveForm.PrintForm). This screen capture ends up being an important legal document for the user. I'm worried about printer jams or out-of-paper situations, and I don't want to hold up the app waiting for printer issues to be resolved. What's the easiest way to capture the current screen to a file so it can be reprinted later?

Q I have an app built on Visual Basic® 6.0 that performs a screen print (using Screen.ActiveForm.PrintForm). This screen capture ends up being an important legal document for the user. I'm worried about printer jams or out-of-paper situations, and I don't want to hold up the app waiting for printer issues to be resolved. What's the easiest way to capture the current screen to a file so it can be reprinted later?

A You can use the code from Knowledge Base article 161299 "Capture and Print the Screen, a Form, or Any Window". You can then use the SavePicture method to save the picture in the PictureBox. With a little code modification, you could bypass the PictureBox control and just save the picture directly to a file.

A You can use the code from Knowledge Base article 161299 "Capture and Print the Screen, a Form, or Any Window". You can then use the SavePicture method to save the picture in the PictureBox. With a little code modification, you could bypass the PictureBox control and just save the picture directly to a file.

Got a question? Send questions and comments to  webqa@microsoft.com.

Thanks to the following Microsoft developers: Mike Abrahamson, Jeff Abrams, Ron Alberda, Mark Ashton, Bhavesh Doshi, AJ Giardullo, Mike Joe, Pranav Kandula, Larry Kuhn, Douglas Laudenschlager, Eric Lippert, Matt Neerincx, Chris Sano, Amber Sitko, Bruce Taimana, Alan Tchochiev, Kevin Williamson