Future Of the .NET Stack from a Developers Point of View

So I have been to two presentations about the future of the .NET stack: Rocky’s Llocka’s presentation to the TRINUG’s main meeting and Billy Hollis’s sales pitch presentation at Tech Ed in NOLA last week.  I have also read some of the articles about the demise of the PC in Barron’s and in Wired.

A couple of themes have been to be emerging that I have framed my decisions about technology:

1) Moore’s law is dead – PC sales are slowing not because of other device sales but because the hardware capabilities of the average PC does not need to change.

2) The PC doesn’t need to change because the apps that once drove hardware upgrades (games, etc…) are not being written.  Instead the software development is focusing on writing lighter-weight clients.  I wonder how many copies of Windows Doom sold?  I still am waiting for the game that takes advantage of my 3 monitor (Front, Side, Side) setup – could you imagine Starcraft across 3 27” monitors?

3) As a company, I own my device.  I own the operating system on the device.  I write my own software to run on that device.  I can’t side-load it?  WTF? MSFT is about enterprise computing and needs to stop this nonsense ASAP.  Stop trying to be the third horse in a two-horse race.  And in a related idea:

4) My 2 favorite quotes from Rocky and Billy about the iPAD:

“Window’s biggest competitor is not the iPAD.  It is the browser” (RL)

“My grandmother loves her iPad.  I am sure she does.  Your 2 year old likes her speak and spell too.  Neither of them have any work to do” (BH)

4B) The tablet is not made for the business – it is made for consumers.  Business leaders that use iPads probably manage people – it is very hard to do any other kind of work on it.  Apple could pivot and drive towards business (they have the $$ and smarts to do it) but it would be a very long and hard road to follow.  And they probably wouldn’t get 50% margin selling to large companies.

5) Rocky thinks BYOD is nonsense and will not stick.  Billy and the rest of the popular press thinks it is a very real change.  I don’t know.  On one hand, I agree it is impossible to do any kind of analytical work or manage multiple systems and process with that kind of screen real estate, keyboard, and processing power.  However, if you only manage people so all you need is email, Lync and don’t need to print anything, then you can get away with using a tablet as your primary machine.  I wonder if managers/executives that only use a tablet are less recession-proof than managers/executives that have a more analytical/process skill set?  Perhaps in the next recession, the BYOD will mean “Bullseye For Downsizing.”   In any event, I am skeptical that BYOD will stick – but it still has some time to become more popular before the coolness wears off.

6)  Business software developers can still get away with writing crappy UIs for the business.  They don’t need to out form factor consumer-facing apps.  However, they need to have UIs that are device-intelligent to accomplish the business task at hand.

7) Every speaker/article is making a big deal about how our industry is going a seismic, once every generation change.  If you have been doing this for a bit, it is just par for the course.

8) My own non-empirical observation is that we are seeing the high-water mark of BSCS students.  Kids that I have watched grow up in my neighborhood are going to college to get a BSCS.  Kids of business associates are getting their BSCS. Kids that don’t like writing computer code are getting a BSCS.

I guess every article that shows the salary of a BA major versus a BS major pushes more kids into our field.  When I ask them why they want the BSCS, it is pretty much the same “I want the money.”  I feel bad because on a per hour, the average software dev starting out is lower than that clerk at Starbucks that has a BA in History (that is the typical story I hear).  Not only that, that clerk might have other, larger dreams and enjoys what s/he does.  If the BSCS kid is in our profession for the money, they are going to be disappointed – very quickly. 

On the flip side, more junior developers means more code the be refactored later, which is a boon to consultants and people who have been in the business for awhile.

Geocoding APIs

I am building my first Windows Phone 7 Application. As part of the applications requirements, I need to find the geo coordinates of cross streets. Unlike many phone apps, I do not need to gather that information real-time. Rather, I have a database of 700 cross streets (key locations) that I will load into the phone at app start-up and then use the built in GeocordinateWatcher class of the .NET phone framework to compare to this list when the PositionChanged  event is raised. The problem is that the initial list of 700 addresses do not have geocoordinates. I built an application that hits the major map APIs (Yahoo, Bing, Google) to see how well they can create geocoordinates from cross street information that can be used by my phone application.

Bing

I started with Microsoft because this is a .NET application. The really cool thing about the Microsoft API is that I can consume the WCF service so there is no HTTP requests to code or XML/JSON responses to parse.

string queryString = String.Format("{0} AT {1} {2}, NC", intersection.RoadA, intersection.RoadB, crash.City); string results = string.Empty; GeocodeRequest geocodeRequest = new GeocodeRequest(); geocodeRequest.Credentials = new GeocodeService.Credentials(); geocodeRequest.Credentials.ApplicationId = "XXXXXXXXXX"; geocodeRequest.Query = queryString; ConfidenceFilter[] filters = new ConfidenceFilter[1]; filters[0] = new ConfidenceFilter(); filters[0].MinimumConfidence = GeocodeService.Confidence.High; GeocodeOptions geocodeOptions = new GeocodeOptions(); geocodeOptions.Filters = filters; geocodeRequest.Options = geocodeOptions; GeocodeServiceClient geocodeServiceClient = new GeocodeServiceClient("BasicHttpBinding_IGeocodeService"); GeocodeResponse geocodeResponse = geocodeServiceClient.Geocode(geocodeRequest); if (geocodeResponse.Results.Length > 0) { if (geocodeResponse.Results[0].Locations[0].Latitude >= 33 && geocodeResponse.Results[0].Locations[0].Latitude <= 36 && geocodeResponse.Results[0].Locations[0].Longitude >= -84 && geocodeResponse.Results[0].Locations[0].Longitude <= -76) { intersection.Latitude = geocodeResponse.Results[0].Locations[0].Latitude; intersection.Longitude = geocodeResponse.Results[0].Locations[0].Longitude; intersection.GeoCodeSource = "Bing"; } }

Getting a developer key was a snap because I already had a liveID.

· Search Result Rating = Medium

· Developer Experience = High

Yahoo

I then went to Yahoo to supplement the data that was not found by Bing. Coding the Yahoo API was a straight Web Request/Response:

StringBuilder queryString = new StringBuilder(); queryString.Append("http://where.yahooapis.com/geocode?"); queryString.Append("?street="); queryString.Append(intersection.RoadA); queryString.Append("&xstreet="); queryString.Append(intersection.RoadB); queryString.Append("&city="); queryString.Append(intersection.City); queryString.Append("&state=NC"); queryString.Append("&appid="); queryString.Append("XXXXXX--"); HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(queryString.ToString()); WebResponse webResponse = webRequest.GetResponse(); Stream stream = webResponse.GetResponseStream(); XmlDocument xmlDocument = new XmlDocument(); xmlDocument.Load(stream); if(xmlDocument != null) { XmlNode rootNode = xmlDocument.DocumentElement; string returnCode = rootNode.SelectSingleNode("Error").InnerText; if (returnCode == "0") { double latitude = Double.Parse(rootNode.SelectSingleNode("Result/latitude").InnerText); double longitude = Double.Parse(rootNode.SelectSingleNode("Result/longitude").InnerText); if (latitude >= 33 && latitude <= 36 && longitude >= -84 && longitude <= -76) { intesection.Latitude = latitude; intesection.Longitude = longitude; intesection.GeoCodeSource = "Yahoo"; } } }

Getting the applicationId was a snap because I already had a YahooId. The one amusing thing to me is that when I went to Yahoo Search and tped in “Maps”, Google’s Map page came up before Yahoo.

clip_image002[4]

· Search Result Rating = Medium

· Developer Experience = Medium

Google

I then tried Google’s API. Coding Google was the same as Yahoo (using HTTP Request and Response) with a slightly less verbose query string:

StringBuilder queryString = new StringBuilder(); queryString.Append("http://maps.googleapis.com/maps/api/geocode/xml?"); queryString.Append("?address="); queryString.Append(intersection.RoadA); queryString.Append(" + and + "); queryString.Append(intersection.RoadB); queryString.Append("&city="); queryString.Append(intersection.City); queryString.Append("&state=NC"); queryString.Append("&sensor="); queryString.Append("false");

The problem was that the data coming back is all wrong. For example, I tried to geocode a major street intersection near my house (you can put this into your browser to see the results):

http://maps.googleapis.com/maps/api/geocode/xml?address=I 440+&+Wake Forest Road+Raleigh,+NC&sensor=false

Check out what I got back:

clip_image001

I then tried this:

http://maps.googleapis.com/maps/api/geocode/xml?address=I 440+and+Wake Forest Road+Raleigh,+NC&sensor=false

clip_image003

At least I am in the country now, but not anywhere near the intersection.  It appears that Bing and Yahaoo have a much better way of geocoding cross streets. After too many false-positives (and no correct hits), I gave up on the Google API. The nice thing about the Google API is that I didn’t have to register for an app id. The not-so-nice thing is that I am limited to 2,500 a day unless I joined something called Premium Developer.

· Search Result Rating = Low

· Developer Experience = Medium

For my project then I am using Bing and supplementing with Yahoo. I am not using Google.

WCF Callback Functions

I am working though Lab 6658 for my 70-569 exam (due in January).  I am having a problem getting the Callback functions to work.  Granted there are more moving parts using a callback function that a request/response pattern, but I think I have isolated the problem.  The start kit that Microsoft provides does not use a Duplex Chanel – which I thought was necessary for callback functions.  I will investigate on my local machine.

Pragmatic Unit Tests

I am preparing a Slide Deck on Unit Testing for my employer’s .NET User Group.  The deck is pretty vanilla but it does help me crystallize my thoughts around dogmatic versus pragmatic unit tests.  The purist argues that the unit test should be in complete isolation – it has no dependencies across layers and, therefore, needs a mocking framework to be used correctly.  I take a more pragmatic approach – I am less worried about writing correct unit tests as much as writing effective unit tests.

I recently added some unit testing to a project for which I am tech lead.  We implemented some unit tests that call an API that, in turn, calls a lower layer.  The tests are clean and they are very effective at identifying problems when the code changes.  Interesting, even though the unit test depends on some data in a database – they have yet to fail because of that dependency.  It has failed for some failed logic introduced when refactoring though – proving the usefulness of the tests. If the tests do fail, the developers know that the error might be in the dependency and can investigate appropriately.

 I think this example points to the heart of the unit testing – you either take ownership and use them effectively as part of the way you code, or you don’t.  If you are in the former category, you can get away with dependencies in your unit tests.  If you are in the latter category, you shouldn’t even bother with unit tests at all – correctly formed or otherwise.

It All Comes Down To Infrastructure Redux

I was pulled into an IRT over the weekend.  A development team was moving their application from DEV to QA and experiencing an unexpected error.  They could not connect to the database from their web application.  Turns out they had two databases on the same server and they could connect to one using a connection string like this:

connectionString="Data Source=DIXON08;Initial Catalog=VideoGames;Integrated Security=True"

but not the other one.  After going through the typical algorithm  of ports open, impersonation set correctly, etc…, it was found that the connection string needed to fully qualify the server name and the port like this (I didn’t come up with the right answer, Rob Seder did):

connectionString="Data Source=Com.Tff.DIXON08:8001;Initial Catalog=VideoGames;Integrated Security=True"

Why it worked on one database without fully qualifying versus another that did need it, I don’t know.

Microsoft Labs – Some QA would help

I am getting ready  for Microsoft exam 70-569 by going through their on-line learning courses found here.  Last night, I did the lab for 6654.  Things were going great until the last step – where you actually call there WCF Service, which then makes a database call.  I received the following error:

Could not open database “Appointments” requested by login.  The login failed.  Login failed for user ‘LON-DEV/Student’

The problem is that the connection string is hard-coded into the supporting class that Microsoft provides and there is no other way to login.  I am surprised Microsoft didn’t QA their applications better.  I am also surprised that no one else hasn’t found this for Microsoft to fix…

Partial Page Post-Back and The Property Proxy Validator – CTD

As a quick follow-up to this post, I disabled the PropertyProxyValidator on the RowUpdating Event Handler of the other control and it worked fine

 

        protected void GridViewFamily_RowUpdating(object sender, GridViewUpdateEventArgs e)

        {

            PropertyProxyValidator propertyProxyValidator =

                GenericUtilities.FindAChildControl(this.DetailsViewInsertFamily, "PropertyProxyValidatorInsertPersonName") as PropertyProxyValidator;

            propertyProxyValidator.Enabled = false;

 

        }

 

FindaChildControl is a just a recursive method to find a control on a web page:

        public static Control FindAChildControl(Control control, string controlId)

        {

            Control targetControl = null;

            foreach (Control childControl in control.Controls)

            {

                if (childControl.ID == controlId)

                {

                    targetControl = childControl;

                }

                if (targetControl == null && childControl.Controls.Count > 0)

                {

                    targetControl = FindAChildControl(childControl, controlId);

                }

            }

            return targetControl;

        }

 

The fact that I have to do this is very annoying – it adds more code to the project, therefore increasing the cost to develop and maintain it.  I noticed that the out of the box ASP.NET validators do not have this problem – they only fire when the control that they are associated is updating/inserting…   I would argue that the Ent Lib validators need to add this feature.

 

Partial Page Post-Back and The Property Proxy Validator

I ran into a problem recently that had me scratching my head.  I have a DTO with Enterprise Library Validation like so:

    public class Person

    {

       

        [RangeValidator(0,RangeBoundaryType.Inclusive,10,RangeBoundaryType.Inclusive,MessageTemplate="{0} is an invalid Person Id")]

        public int PersonId { get; set; }

        [StringLengthValidator(10, MessageTemplate = "Name is 10 characters")]

        [NotNullValidator(MessageTemplate="{1} Can’t be null}")]

        public string PersonName { get; set; }

        public DateTime DateOfBirth { get; set; }

        public DateTime? DateOfDeath { get; set; }

 

This person class was served up by a Factory (Builder, really) like so.  I threw some test data into the class just so I can inspect value in the round trip

 

    [DataObject(true)]

    public class PersonFactory

    {

        static Collection<Person> family;

 

        static PersonFactory()

        {

            family = new Collection<Person>();

            family.Add(new Person(0, "Jackie", DateTime.Parse("05/04/1951"), null));

            family.Add(new Person(1, "Tito", DateTime.Parse("10/15/1953"), null));

            family.Add(new Person(2, "Jermaine", DateTime.Parse("12/11/1954"), null));

            family.Add(new Person(3, "Marlon", DateTime.Parse("03/12/1957"), null));

            family.Add(new Person(4, "Michael", DateTime.Parse("08/29/1958"), DateTime.Parse("06/25/2009")));

        }

 

        public static Collection<Person> GetFamily()

        {

            return family;

        }

 

        public static void UpdatePerson(Person person)

        {

            . . .

        }

 

 

        public static void InsertPerson(Person person)

        {

            . . .

        }

 

I then created a simple Web Form to handle the updating using an Object Data Source and a Grid View:

<asp:GridView ID="GridViewFamily" runat="server" AutoGenerateColumns="False"

    DataSourceID="ObjectDataSourceFamily">

    <Columns>

            <EditItemTemplate>

                <asp:TextBox ID="TextBoxPersonId" runat="server" Text=’<%# Bind("PersonId") %>‘></asp:TextBox>

                <cc1:PropertyProxyValidator ID="PropertyProxyValidatorUpdatePersonId" runat="server"

                    ControlToValidate="TextBoxPersonId" PropertyName="PersonId"

                    SourceTypeName="TestWebProject.Person" Display="None"></cc1:PropertyProxyValidator>

                <cc2:ValidatorCalloutExtender ID="PropertyProxyValidator_UpdatePersonId_ValidatorCalloutExtender"

                    runat="server" Enabled="True" TargetControlID="PropertyProxyValidatorUpdatePersonId">

                </cc2:ValidatorCalloutExtender>

            </EditItemTemplate>

            <ItemTemplate>

                <asp:Label ID="Label1" runat="server" Text=’<%# Bind("PersonId") %>‘></asp:Label>

            </ItemTemplate>

        </asp:TemplateField>

        <asp:TemplateField HeaderText="PersonName" SortExpression="PersonName">

      . . .

 

    </Columns>

</asp:GridView>

 

So far, so good

 

 

I then added a Details View for inserting and associated it with the same Object Data Source:

<asp:DetailsView ID="DetailsViewInsertFamily" runat="server" AutoGenerateRows="False"

DataSourceID="ObjectDataSourceFamily" DefaultMode="Insert" Height="50px"

Width="125px">

    <Fields>

            <InsertItemTemplate>

                <asp:TextBox ID="TextBoxInsertPersonId" runat="server"

            Text=’<%# Bind("PersonId") %>‘></asp:TextBox>

                <cc1:PropertyProxyValidator ID="PropertyProxyValidatorInsertPersonId" runat="server"

            ControlToValidate="TextBoxInsertPersonId" PropertyName="PersonId"

            SourceTypeName="TestWebProject.Person" Display="None"></cc1:PropertyProxyValidator>

                <cc2:ValidatorCalloutExtender ID="PropertyProxyValidator_InsertPersonId_ValidatorCalloutExtender"

            runat="server" Enabled="True"

            TargetControlID="PropertyProxyValidatorInsertPersonId">

                </cc2:ValidatorCalloutExtender>

            </InsertItemTemplate>

            <ItemTemplate>

                <asp:Label ID="Label1" runat="server" Text=’<%# Bind("PersonId") %>‘></asp:Label>

            </ItemTemplate>

        </asp:TemplateField>

            . . .

 

 

    </Fields>

</asp:DetailsView>

 

Things go awry.   When I try and run an update on the screen, I get the following error:

Index was outside the bounds of the array.

After a little thought, I realized that the Insert Template is firing off the validation on the update postback.  I handled the error by adding a ValueConvert event handler to the PropertyProxyValidator on the insert:

        protected void PropertyProxyValidatorInsertPersonId_ValueConvert(object sender, Microsoft.Practices.EnterpriseLibrary.Validation.Integration.ValueConvertEventArgs e)

        {

            int result = 0;

            bool ok = Int32.TryParse(e.ValueToConvert.ToString(), out result);

            if (!ok)

            {

                e.ConvertedValue = -1;

            }

        }

However, handling the error then gives me this:

 

Notice how the Validator is firing for Details View – even though the Updating is from the Grid View.

I then put the details view into an Update Panel to see if that will isolate the GridView’s Update – no luck.  I then thought of two programic solutions:

·         On the Updating Event, disable the Insert Validators

·         Adding a RuleSet for updating and inserting

Both solutions seem less than ideal to me – Adding a ruleset uses duplicates code and makes the code base harder to maintain – as well as coupling the DTO to the User Layer – which is never good.  Using some event to enable/disable the validators seems to be better from a coding perspective so I will implement that

 

 

 

WCF – My “Duh!” moment of the week

In preparation for my 70-569 exam, I have been following the Learning Plan for WCF found here
The first step was this webcast found here

I attempted to build a Hello-WCF solution based on what as presented in the webcast and I <thought> I ran into Vista problems.  I set my endpoint like this in the Hosting Service:

serviceHost.AddServiceEndpoint(typeof(IPersonFactory), new NetTcpBinding(), "net.tcp://localhost:9000/PersonFactory");

And I set a reference to that endpoint in the client like this:

IPersonFactory proxy = ChannelFactory<IPersonFactory>.CreateChannel(new

      NetTcpBinding(),new

EndpointAddress("net.tcp://localhost:9000/PersonFactory"));

 

Console.WriteLine(proxy.Echo("Hello World"));

 

I got the following message:


No connection could be made because the target machine actively refused it 127.0.0.1:9000

I tried running VS2008 as an administrator but I got the same message.  I then opened the port in Windows Firewall and then disabling the firewall completely.

I then posted to the MSDN forums here

 

Turns out I was barking up the wrong tree.  By using the “Using” statement, I was closing the port before attempting to have the client make the call.  I quess I was too quick to blame Vista…

MCPD Upgrade

I passed Exam 70-568 on Friday.  I was surprised about how little of the new features of 3.5 were actually in the exam – only a few questions on LINQ, none on entity framework, etc….  I guess the 4.0 exam might have it?  In any event, I am planning to finish the 3.5 upgrade this Decemeber so I am jumping into WCF with both feet.