Archive for the ‘.NET’ Category

Getting “localhost.” to work with IIS7

March 9, 2008

I’m not 100% sure why, but with IIS7, I am unable to get connect to my local IIS instance using Fiddler’s magic “localhost.” hostname.  Normally this hostname allows localhost traffic to be monitored within Fiddler. Out of the box, IIS7 was returning me the following error on requests to “localhost.”:

Bad Request - Invalid Hostname

Trying to add a secondary binding with “localhost.” explicitly set as the host yeilds another error, this time from the IIS Management Console:

Value does not fall within the expect range.

I found that editing Fiddler’s CustomRules.js provides a quick workaround while still allowing you to use the exact “localhost.” name.  Simply add the following line to the “onBeforeRequest” function:

if (oSession.host.ToUpper() == “LOCALHOST.”) { oSession.host = “localhost”; }

This fix of course only works when Fiddler is running, so please comment if you know of a workaround within IIS7.  (Alternatively, please comment if you have no issue in IIS7 — that my box simply has a case of the Vistas.)

ArcGIS Server Virtual Earth Tile Server vs. ArcIMS

February 4, 2008

Time to change the name Dave.

     

using System;
using System.Text;
using ArcDeveloper.TileServer.Interfaces;
using System.Net;
using System.IO;
using System.Xml;
using System.Drawing;
using System.Drawing.Imaging;   

namespace ArcDeveloper.TileServer.ArcIMS
{
    public class ArcImsTileProvider : ITileProvider
    {
        ///
        /// Private member containing the web service url
        ///
        private string _webServiceUrl = "";   

        ///
        /// Constructor that takes a web service Url
        ///
        ///
        public ArcImsTileProvider(string webServiceUrl)
        {
            _webServiceUrl = webServiceUrl;
        }   

        ///
        /// Get the tile image
        ///
        ///
        ///
        ///
        /// Tile data stream
        public System.IO.MemoryStream GetTile(ITileExtent extent, int tileHeight, int tileWidth)
        {
            MemoryStream returnStream = null;   

            try
            {
                string axl = getAXL(extent, tileHeight, tileWidth);
                Stream responseStream = PostData(_webServiceUrl, axl);
                XmlDocument xd = new XmlDocument();
                xd.Load(responseStream);
                string imgURL = xd.GetElementsByTagName("OUTPUT")[0].Attributes["url"].Value;
                responseStream.Close();   

                Bitmap bm = new Bitmap(new WebClient().OpenRead(imgURL));
                returnStream = new MemoryStream();
                bm.Save(returnStream, ImageFormat.Png);
            }
            catch(Exception e)
            {
                Bitmap bm = GetMessageTile(e.Message);
                returnStream = new MemoryStream();
                bm.Save(returnStream, ImageFormat.Png);
            }   

            return returnStream;
        }   

        ///
        /// Gets the ArcIMS GetImage AXL request.
        ///
        ///
        ///
        ///
        /// GetImage ArcIMS AXL request
        private string getAXL(ITileExtent extent, int tileHeight, int tileWidth)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
            sb.Append("<ARCXML version=\"1.1\">");
            sb.Append("<REQUEST>");
            sb.Append("<GET_IMAGE>");
            sb.Append("<PROPERTIES>");
            sb.Append("<ENVELOPE minx=\"" + extent.lon2 + "\" miny=\"" + extent.lat2 + "\" maxx=\"" + extent.lon + "\" maxy=\"" + extent.lat + "\" />");
            sb.Append("<IMAGESIZE height=\"" + tileHeight + "\" width=\"" + tileWidth + "\" />");
            sb.Append("</PROPERTIES>");
            sb.Append("</GET_IMAGE>");
            sb.Append("</REQUEST>");
            sb.Append("</ARCXML>");
            return sb.ToString();
        }   

        ///
        /// Gets a tile with an error message burned into it.
        ///
        /// The message.
        /// An error message PNG
        private Bitmap GetMessageTile(string message)
        {
            Bitmap tile = new Bitmap(Properties.Resources.blankTile);
            Graphics graphics = Graphics.FromImage(tile);
            Font drawFont = new Font("Arial", 12);
            SolidBrush drawBrush = new SolidBrush(System.Drawing.Color.Black);
            SizeF len = graphics.MeasureString(message, drawFont);
            graphics.DrawString(message, drawFont, drawBrush, new PointF(10, 10));
            graphics.Dispose();
            return tile;
        }   

        ///
        /// Does an HTTP Post, returning a stream.
        ///
        /// The URL to post to
        /// The data to post
        /// The Http response stream
        private Stream PostData(string sURL, string postData)
        {
            HttpWebRequest webReq = null;
            HttpWebResponse response = null;
            Stream requestStream = null;
            StreamWriter writer = null;
            Stream responseStream = null;
            try
            {
                webReq = (HttpWebRequest)WebRequest.Create(sURL);
                webReq.Method = "POST";
                webReq.ContentType = "application/x-www-form-urlencoded";
                webReq.Timeout = 0xea60;
                webReq.AllowAutoRedirect = true;
                requestStream = webReq.GetRequestStream();
                writer = new StreamWriter(requestStream);
                writer.Write(postData);
                writer.Flush();
                writer.Close();
                requestStream.Close();
                response = (HttpWebResponse)webReq.GetResponse();
                responseStream = ((HttpWebResponse)webReq.GetResponse()).GetResponseStream();
            }
            finally
            {
                if (writer != null) writer.Close();
            }
            return responseStream;
        }   

    }
}   

Winners: Python-fearing .NET folks still using ArcIMS.  All six of you!

Losers: “ArcGIS Server Virtual Earth Tile Server” as a name.  Die.

LINQ vs LINQ

December 19, 2007

I’m just getting into LINQ, and experimenting with what goes on behind the scenes.  LINQ uses the idea of a “DataContext” as an interface to the database, much like the older “DataSet” acts to encapsulate multiple table structures.   One notably lacking feature of LINQ’s database-derived classes is the ability to traverse many-to-many relationships without touching the intermediate cross-reference table. 

Assuming an existing “vsNetwork” entity, you can query a many-to-many relationship directly against the ”DataContext” ala:

from f in myDataContext.ScienceFocus
join vsf in vsDc.VitalSignFocus on f.FocusId equals vsf.FocusIdf
where vsf.VitalSignIdf == vsNetwork.VitalSignId
select f;

Again assuming an existing “vsNetwork” entity, you can also query by walking the database-derived class’s table-relations, ala:

vsNetwork.FXrefVitalSignFocusList.Select(vsf => vsf.FTluScienceFocus).ToList();

The magic of LINQ exists within all things that implement the IQueriable interface.  This allows the keywords “select”, “where”, “join”, etc. to be applied to a collection of objects.  Note, however, that there are differences in how “IQueriable” is implemented.  The examples above, indeed generate markedly different SQL.  Respectively,

SELECT [t0].* FROM [tlu_Science_Focus] AS [t0] INNER JOIN [xref_Vital_Sign_Focus] AS [t1] ON [t0].[Focus_ID] = [t1].[Focus_IDF] WHERE [t1].[Vital_Sign_IDF] = vsNetwork.Vital_Sign_ID

versus

SELECT [Focus_IDF] FROM [xref_Vital_Sign_Focus]  WHERE [Vital_Sign_IDF] = vsNetwork.Vital_Sign_ID

*For Each Focus_IDF…*

SELECT * FROM [tlu_Science_Focus] WHERE [Focus_ID] = Focus_IDF

We see that the latter runs multiple queries instead of using a SQL join.  The source of this inefficiency is described by The Wayward Weblog:

“[EntitySet's] ToQueryable() wraps your IEnumerable<T> in IQueryable<T> clothing, uses the Queryable infrastructure to let you build up your own expression tree queries, and then when you enumerate it, the expression is rebound to refer to your IEnumerable<T> directly”

In this case, “vsNetwork.FXrefVitalSignFocusList” is of type EntitySet, which does not implement IQueriable directly, but rather through IEnumerable.  So not only does this shorter syntax lose efficiency by issuing multiple and superfluous queries, it also loses efficiency by iterating through each xref table in memory.  In the case of large sets, using LINQ against IEnumerables can be an issue.  It has been enough of an issue that people are adding indexing for in-memory LINQ objects, rather than simply iterating collections.

In earlier Visual Studio 2008 CTP’s, LINQ EntitySets implemented the iQueriable interface (directly?).  I’m not sure if at that point, this was implemented to elegantly generate SQL, I simply know that O’Reilly’s C# in a Nutshell author Joseph Albahari made some fuss about the change. 

It appears that Mr. Albahari is behind a library called LINQKit designed to mitigate his qualms with the LINQ’s recently-neutered EntitySet classes.  While this library appears interesting, it critically doesn’t make EntitySets natively IQueriable.  Hence, I don’t see a way where LINQKit allows effortless, short syntax, efficient SQL queries to be built from existing database-derived classes.

Winners: Tautologically, LINQ totally won this battle.

Losers: LINQ EntitySets, for failing to combine short syntax and efficient SQL generation.