How to use a Stopwatch in C# incorrectly

Here’s something I did wrong this week. And, after realizing I’d done it wrong, I remembered that I’d made the same exact mistake in the past. So I’m going to write up a blog post in the hopes that maybe it’ll help me hammer the right way into my head, so I don’t screw up next time.

If you want to do a little quick and dirty performance logging in an application, you can use the Stopwatch class. Just create a new Stopwatch, then stop it when you’re done and check the elapsed time. Easy, right?

Well, here’s what I did wrong: The Elapsed property is a TimeSpan structure, which has a lot of nice properties on it, like Days, Hours, Milliseconds, and so on. For my purposes, I wanted to know how many milliseconds had elapsed. So I showed sw.Elapsed.Milliseconds. Looks good, right? Except that the Milliseconds property is not the whole span in milliseconds. It’s the milliseconds part of the span. If you want total milliseconds, then you need to get sw.Elapsed.TotalMilliseconds.

This is one of those pernicious little bugs that doesn’t cause anything to fail, but instead causes the programmer to make bad decisions. “Hey, it looks like all my web service calls execute in less than a second! Great! I can go home early!” Nope. Turns out some of them were taking more like 20 seconds.

Here’s a little code snippet showing the wrong way and the right way.

Stopwatch sw = Stopwatch.StartNew();
TimeSpan elapsed;
Thread.Sleep(1500);
sw.Stop();
elapsed = sw.Elapsed;
Console.WriteLine("Wrong: {0}", elapsed.Milliseconds);
Console.WriteLine("Right: {0}", elapsed.TotalMilliseconds);

Simple enough, but I’ve done this wrong at least twice so far in my life. Here’s hoping I can remember to do it right next time!

C# source code analysis

Something came up at work today that got me thinking about source code analysis tools. Since I’m currently working on two C# projects, both of which are close to done and working reasonably well, I decided that maybe it would be cool to try running some source code analysis tools against them and see if there was anything I could clean up.

I started with something fairly simple: StyleCop. I installed the Visual Studio extension for it, ran it and went through the results. It found a ton of stuff, much of which I didn’t entirely agree with. But it did find quite a few things that made sense to me, so I cleaned them all up. With the VS extension, StyleCop only identifies issues; it doesn’t do any automated fixes. And it’s not doing any deep analysis; it’s just finding stuff like issues with naming conventions, missing comment headers, too much or too little whitespace, and similar style issues. But I’m a sucker for that stuff, and I like my code to be consistent with accepted conventions (for the most part).

I also looked at CodeMaid, which looks like it does a lot of stuff that’s similar to StyleCop, but it also automates fixing the issues. I didn’t get around to trying it, but I’d like to play with it when I get a chance. It’s open source, so I can try it without having to worry about spending any money.

I’ve been aware of some of the fancier commercial tools for a long time. Specifically, ReSharper and CodeRush. I’m curious about them, but they’re both too expensive for me to really justify. If I ever find myself in a job where I’m doing a lot more C# work than I’m doing now, and I have a budget to work with, I’ll try one of those.

Fun with TLS 1.2

At work, I’ve somehow wound up being the “credit card expert” in my group. I don’t mind, really, since the work is reasonably interesting, most of the time. I had occasion to go down a bit of a rabbit hole this week that I thought might make for a good blog post.

PayPal, starting in mid-2017, is going to require that all communication with their APIs happen via TLS 1.2 and HTTP/1.1. TLS 1.1, at minimum, is a PCI requirement, so I’m sure that’s what motivated PayPal to make these changes. (Further info on the PCI requirement can be found here and here.)

I’ve been working on a project that uses PayPal’s Payflow Pro API. There is a .NET library for this API that hasn’t been updated by PayPal in years, but (for various reasons) it’s the only one we can use right now. So PayPal is requiring TLS 1.2, but apparently not updating this library accordingly or really offering any guidance about using it. So it’s been up to me to research this and figure out if we’re in trouble or not.

The library itself is offered as a DLL only. PayPal has been posting a lot of their source code to GitHub lately, but this particular API is only downloadable in binary format. It’s a non-obfuscated .Net DLL, though, so I’ve been able to poke around inside of it with JetBrains dotPeek. I can see that they’re using the standard HttpWebRequest class in the .NET Framework, so that’s a good start.

I also tried looking at the actual calls being made from this DLL, using Fiddler, but I had some problems with that. I thought about trying Wireshark instead, but it looks like I won’t have to bother with that.

Looking at several Stack Overflow questions led me to add the following line to my code, prior to calling the PayPal API:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

And I found a web site that has a simple API that can give you an indication of your SSL/TLS status. So I plugged in some calls to this API (using simple HttpWebRequest calls), and I think that the above line does, indeed, fix things for me.

Here’s some sample code to call that API (which I found here):

//ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var response = WebRequest.Create("https://www.howsmyssl.com/a/check").GetResponse();
var responseData = new StreamReader(response.GetResponseStream()).ReadToEnd();
Console.WriteLine(responseData);

The API returns a block of JSON, which I’m just dumping to the console here, but you could also use JSON.NET and do something fancy with it.

PayPal is going to change their “pilot” endpoint over to support only TLS 1.2 in mid-February. So, at that time, I can run some tests and see if my guesswork holds up, or if there’s something I missed. I won’t be at all surprised if I do run into a “gotcha” or three. My understanding of this stuff is really not that deep, and who knows if PayPal is going to do something weird in their server implementation that breaks my code.

Interactive C# REPLs

I was working on a C# program today, and wanted to test a small code snippet out. I used to use Snippet Compiler for that sort of thing, but it hasn’t been updated in a very long time.

Visual Studio 2015 now has a built-in REPL, but I’m using VS 2013.

I decided to try installing ScriptCS, which I’d read about before, but never actually tried. It was near the end of the work day when I started trying to install it, and I hit a couple of minor snags. So I don’t have it working yet, but I plan on getting it straightened out tomorrow.

In the past, I’ve also looked at CShell, which appears to be similar to ScriptCS, but with an actual lightweight IDE included.

I’ve also looked at CS-Script, which is interesting because it can be used with Notepad++ via a plugin.

And if you just want to try something out in a web browser, there’s .NET Fiddle.

Scott Hanselman has a good blog post on C# REPLs here. He mentions ScriptCS and the VS 2015 C# and F# REPLs.

I don’t have anything terribly useful to say about any of these products, since I haven’t gotten around to using them yet, but I wanted to write this up, just so I’d have a consolidated list of links to all of them.

This whole thing has side-tracked me enough from the actual task I was trying to accomplish, that I almost forgot what it was. I’m pretty sure I was trying to check how WebUtility.HtmlDecode would treat a certain input string, but I could be wrong. Well, tomorrow’s another day!

 

a bit more on Visual Studio 2012

I feel a little bad about yesterday’s screed on the VS 2012 UI. (But not bad enough to delete it or anything. I still wish they hadn’t mucked with the UI so much.) So today I thought I’d try to write a more positive post about VS 2012.

First, I’d like to link to this blog post on how cool it is that Microsoft has kept so much of the functionality of the full VS product in the Express editions. I do agree with him on this, and I am glad that Microsoft is willing to release such a full-featured product for free. Having said that, though, I’d also love to see a $99 “standard” version that comes a bit closer to the $499 “pro” version. I think my biggest issue with the Express product will be lack of support for extensions. I’ve gotten quite used to DPack, for instance, so it’ll be hard to do without that.

Here’s a good article on “Simple but Interesting Features of VS2012“. Some of these features should be pretty useful. I’m glad that Microsoft is still adding little things like this to Visual Studio. It’s easy to let small, useful, features get lost in favor of grand initiatives, and I’m happy to see that someone at MS still thinks about stuff like this.

Finally, here’s a post about some really great new features in VS2012. I was pretty stoked about a few of these, until I realized that it was an April Fool’s post. (Actually, a couple of these *would* be useful, and not that hard to implement…)

Visual Studio 2012, take two

So I managed to get VS 2012 installed. (See previous post for details on my first failed attempt.) I’d love to write up a blog post detailing some weird issue and how I worked around it, but I don’t really have anything useful to offer along those lines. I basically just installed some pending Windows Updates, had a cup of coffee, then tried again.

After the install, I was prompted to install a patch that apparently fixes some compatibility issue. Then, I was prompted to install VS 2012 update 2. I did both of those things, and now have a usable VS 2012 install. I’m still not sure why Microsoft can’t post updated installers for their products when they release patches and updates, but I’m used to the silliness now, so I just grin and bear it.

I had read a good bit of negative feedback about the UI changes in VS 2012, and I have to say that I agree with most of it, now that I’ve seem the product up close. It’s much less pleasant to look at, compared to VS 2010. First, the upper-case menus are ridiculous. Whoever thought that was a good idea has hopefully been fired by now. (Who am I kidding, he probably got promoted!) You can fix that pretty easily with this NuGet package. And the guy who put it together gets extra points for the instructions: “YOU NO LIKE NO SHOUTING?! Run Disable-AllCaps”.

The next easily-fixed interface blunder is the color scheme. The default is called “light”, and it’s kind of an all-grey mess, with a little bit of white, black, and blue.  If you switch to the “blue” theme, you get something a little like VS 2010, and much more usable.

The general flatness of the interface, though, is still pretty blah. There was really nothing wrong with the VS 2010 interface, and no reason to arbitrarily change stuff for the worse like this, and it’s so hard to believe that anybody really thought they were making things better here.

There’s a blog entry on the VS team blog that discusses the all-caps thing in specific. If you read it, you’ll get a good picture of how a very large company can make really poor decisions about specific products, based on big-picture corporate strategies and directions, and how they can be (apparently) clueless about what they’re doing. They talk about how the use of uppercase text is a “strong signature element” of MS user interfaces, including Zune and Bing. Now, really, how much thought does it take to figure out that the menu bar for a complex programming IDE has nothing to do with the user interface on a failed MP3 player or a web search engine? They end the blog post by saying that “we will enable you to customize the casing, and we are exploring options for how to expose that choice.” Well, the blog post is about a year old, VS 2012 has had two update releases, and still no option in the product itself to change the menu casing.

Alright, so that was way too much grumbling about fairly trivial user interface stuff. I guess I’m just in a bit of a cranky mood today! I still look forward to trying out VS 2012, and seeing what useful new features have been added to the product, and to C#!

Visual Studio 2012

I haven’t bothered with VS2012 yet, but today I decided to try to install VS Express 2012 for Windows Desktop on my laptop. I really only want it, at this point, for developing console apps. I wanted to take a shot at using it for the Project Euler stuff that I’ve been playing around with, and I was also interested in trying out some of the async stuff in C# 5. So nothing fancy; I just wanted to get familiar with it.

Well, no luck. The install got about halfway through (judging by the progress bars), then got no further. I know some of the VS installs in the past have been notoriously slow. (I’m looking at *you* VS 2005 SP 1! Or was it VS 2008 SP1…?) But this one just stalled at the same spot for 2 or 3 hours, with no change, so I gave up on it.

I’m starting to wonder if I need to do a fresh install of Windows 8 on that laptop, just to clear up the cruft from previous VS installs. I really wish Microsoft could make an IDE that didn’t cause so much grief just to install…

Bullseye

The main product of the company I currently work for is a hosted store locator and lead manager service, named Bullseye. We’ve been concentrating on both improving it and marketing it recently, so there’s some fairly interesting things going on that I thought I would write up. I’ve mentioned some of these things previously, but I thought it might be useful to have a single consolidated post with some pointers.

We have a Facebook store locator, which you can find at https://www.facebook.com/BullseyeStoreLocator. The Facebook locator was an interesting project. The first iteration was outsourced to an external developer, and was completed before I started working for the company. Due to some major changes in the way Facebook supported app development, we had to do a lot of work on the Facebook locator in mid-2011. I did a pretty major rewrite of it, using the Facebook C# SDK (which seems to have moved to Github, and gotten a nicely redesigned website since I last looked at it). Since that rewrite, one of our other developers has been working on it, mostly adding new features. The most recent new feature is one that allows you to attach ‘like’ buttons to individual locations in the returned results, linking them to individual Facebook pages for each location. It’s described in a blog post here.

We also now have a Drupal module that allows you to easily implement a store locator in any Drupal 7 site. (I’ve blogged about this previously.) It’s still a sandbox project under my drupal.org account. Cleaning it up and getting it ready to be promoted to a full project is on my rainy-day to-do list. (I’ve done a bit of work towards that goal, but I still need to do some more.)

And we now have a way to import and sync locations from Salesforce into Bullseye. This was another component that was initially outsourced. I’ve done some maintenance work on the project, and it’s pretty interesting. We’ve implemented a way to filter the results that we pull in from Salesforce, using serialized LINQ expressions. I was initially leery about this approach, and there are still some things I don’t like about it, but if you need to abstract out and save complex filter conditions to a database, I guess there isn’t a better way to do it than this. The one difficulty is that it’s fairly difficult to read and comprehend the expressions, once they’re serialized, so it’s important to keep track of the original LINQ expressions, for reference.

Selenium

OK, one more post for tonight. (This is another one I suspected that I may have previously written up, but apparently not.)

I’ve known about Selenium for awhile now, mostly because one of our clients has a “testing guy” and he uses it. I’ve always wanted to be able to do some automated testing of web site projects, but it always seemed like the tools for doing so were too limited or complex. I’ll admit I put off downloading & learning Selenium, largely because I thought it would be a hassle and eat up a lot of time before I could really do anything useful with it. When I finally gave it a chance, though, I was surprised how easy it was to use.

I initially started with WebDriver, which is basically a couple of DLLs that let you “drive” Firefox (or another browser), sending keystrokes and click events, and looking for certain responses. You can get started with WebDriver quickly by grabbing it via NuGet. My first project with WebDriver was a simple console program that launches Firefox, then goes to several of the store locator web sites that use our Bullseye API, does a search at each one, and checks to see if it gets results. Nothing big, but just a useful program that I can run any time I roll out code changes to the API. Previously, I’d been checking this stuff by hand after each rollout.

Today, I took another step, and downloaded Selenium IDE. This is a Firefox plugin that lets you record a series of actions as you do them, then save them to a script. There are plugins allowing you save the script in several languages, including C#. So, I can record some steps, export some C# code, then fix it up to do some reasonable testing. My main purpose today was to record the steps involved in a fairly complex workflow on one of our client sites. It’s a multi-step process (around 20 steps, I think). Just in and of itself, the script is useful to have, as I often need to step through it to establish a new test account, so now I can just “play” it instead of clicking through the whole process myself. But, I would also like to use it to automate some testing of this process. Now that I have a base script, I can go in and replace the values I entered today with variables, so I can abstract things out in such a way that I can run the code repeatedly, testing multiple scenarios. And since I can do this all in C#, I can also then check the database, and see if the values I entered were interpreted and stored in the database correctly.

This may all seem pretty routine to some people, but I have to admit that I’ve never really had a chance to do this kind of testing before. It’s kind of cool!

I think my next project is going to have to be trying WebDriver with browsers other than Firefox.  I’d like to be able to test the same workflow in IE, Firefox, and Chrome, at least. (And if I get really ambitious, maybe I’ll see about iOS browser automation…)

Mandrill REST API

OK, here’s another REST API example. In the same system I blogged about yesterday, I also had to use the Mandrill API to send out some transactional emails. Mandrill is a relatively new service from MailChimp that can be used to send out e-mail via simple API calls. It’s pretty nice, and free to use, up to a certain point.

You can actually use it via regular old SMTP, with some control info added into the headers, or you can go ahead and use their REST API. I decided to use the API. Initially, I had no luck with that at all. After thrashing around a bit, I eventually figured out that I had to update to the newest version of EasyHttp. Then, it started working fine, no problem. I didn’t bother looking into the internals of EasyHttp to try and figure out what was causing the problem. I’m kind of curious, but, as usual, it’s more important to just get the thing working.

So, here’s another Gist with some code:

/*
 * Code/Mandrill.cs
 * These are calls to the Mandrill email service.
 * Reference: https://mandrillapp.com/api/docs/
 * 
 * ajh 2012-08-09: new
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;
using System.Dynamic;
using EasyHttp.Http;
using System.Net;
using log4net;

namespace Sample.Web
{
    enum MandrillError
    {
        OK,
        WebException,
        HttpNotOk,
        Invalid,
        Rejected,
        Unknown
    }

    public class Mandrill
    {
        static string MandrillBaseUrl = ConfigurationManager.AppSettings["MandrillBaseUrl"];
        static Guid MandrillKey = new Guid(ConfigurationManager.AppSettings["MandrillKey"]);

        public static bool SendActivationEMail(BLL.TrialSignup ts, out string errorMsg)
        {
            string activationLink =
                HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority) + "/Register/Activation.aspx?id=" + ts.Id;
            
            //send-template(string key, string template_name, array template_content, struct message) 
            dynamic sendParams = new ExpandoObject();
            sendParams.key = MandrillKey;
            sendParams.template_name = "Secret Project Trial Activation";

            sendParams.template_content = new List();
            
            sendParams.message = new ExpandoObject();
            sendParams.message.subject = "Here's your Secret Project activation email";
            sendParams.message.from_email = "info@SecretProject.com";
            sendParams.message.from_name = "Secret Project";

            sendParams.message.to = new List();
            sendParams.message.to.Add(new ExpandoObject());
            sendParams.message.to[0].email = ts.EMail;
            sendParams.message.to[0].name = ts.Name;

            sendParams.message.track_opens = true;
            //sendParams.message.track_clicks = true;

            sendParams.message.global_merge_vars = new List();
            sendParams.message.global_merge_vars.Add(new ExpandoObject());
            sendParams.message.global_merge_vars[0].name = "NAME";
            sendParams.message.global_merge_vars[0].content = ts.Name;

            sendParams.message.global_merge_vars.Add(new ExpandoObject());
            sendParams.message.global_merge_vars[1].name = "LINK";
            sendParams.message.global_merge_vars[1].content = activationLink;

            errorMsg = string.Empty;
            
            MandrillError merr = SendMessage(sendParams);

            switch (merr)
            {
                case MandrillError.OK:
                    return true;
                case MandrillError.WebException:
                case MandrillError.HttpNotOk:
                    errorMsg = "There was an issue sending your activation e-mail. Please try again later or call us directly.";
                    break;
                case MandrillError.Invalid:
                    errorMsg = "Your email address appears to be invalid. Please try again with a valid address, or call us directly.";
                    break;
                case MandrillError.Rejected:
                    errorMsg = "Your activation email was rejected. Please try again with a valid address, or call us directly.";
                    break;
                case MandrillError.Unknown:
                    errorMsg = "There was an unknown problem sending your activation email. Please try again, or call us directly.";
                    break;
            }
            return false;
        }

        public static bool SendSalesNotification(BLL.TrialSignup ts)
        {
            dynamic sendParams = new ExpandoObject();
            sendParams.key = MandrillKey;
            sendParams.template_name = "Secret Project Trial Sales Notification";

            sendParams.template_content = new List();

            sendParams.message = new ExpandoObject();
            sendParams.message.subject = "Secret Project Trial Account Notification";
            sendParams.message.from_email = "info@SecretProject.com";
            sendParams.message.from_name = "Secret Project";

            sendParams.message.to = new List();
            sendParams.message.to.Add(new ExpandoObject());
            sendParams.message.to[0].email = ConfigurationManager.AppSettings["SalesEmail"];
            sendParams.message.to[0].name = "Secret Project Sales";

            //sendParams.message.track_opens = true;
            //sendParams.message.track_clicks = true;

            sendParams.message.global_merge_vars = new List();
            sendParams.message.global_merge_vars.Add(new ExpandoObject());
            sendParams.message.global_merge_vars[0].name = "NAME";
            sendParams.message.global_merge_vars[0].content = ts.Name;

            sendParams.message.global_merge_vars.Add(new ExpandoObject());
            sendParams.message.global_merge_vars[1].name = "COMPANY";
            sendParams.message.global_merge_vars[1].content = ts.CompanyName;
            
            sendParams.message.global_merge_vars.Add(new ExpandoObject());
            sendParams.message.global_merge_vars[2].name = "EMAIL";
            sendParams.message.global_merge_vars[2].content = ts.EMail;

            MandrillError merr = SendMessage(sendParams);

            switch (merr)
            {
                case MandrillError.OK:
                    return true;
                case MandrillError.WebException:
                case MandrillError.HttpNotOk:
                case MandrillError.Invalid:
                case MandrillError.Rejected:
                case MandrillError.Unknown:
                    break;
            }
            return false;
        }

        private static MandrillError SendMessage(dynamic sendParams)
        {
            ILog _log = log4net.LogManager.GetLogger("Mandrill/SendMessage");

            string url = MandrillBaseUrl + "/messages/send-template.json";

            var http = new HttpClient
            {
                Request = { Accept = HttpContentTypes.ApplicationJson }
            };

            EasyHttp.Http.HttpResponse response;
            try
            {
                response = http.Post(url, sendParams, HttpContentTypes.ApplicationJson);
            }
            catch (WebException ex)
            {
                _log.ErrorFormat("Error: WebException - {0}", ex.Message);
                return MandrillError.WebException;
            }

            if (response.StatusCode != HttpStatusCode.OK)
            {
                _log.InfoFormat("Response = {0} - {1}", response.StatusCode, response.StatusDescription);
                _log.Info(response.RawText);
                return MandrillError.HttpNotOk;
            }

            dynamic rv = response.DynamicBody;
            _log.InfoFormat("email: {0}, status: {1}", rv[0].email, rv[0].status);

            string send_status = rv[0].status;
            if (send_status == "sent" || send_status == "queued")
                return MandrillError.OK;

            // otherwise, it should be "rejected" or "invalid"
            if (send_status == "invalid")
            {
                return MandrillError.Invalid;
            }
            if (send_status == "rejected")
            {
                return MandrillError.Rejected;
            }

            // unexpected...
            return MandrillError.Unknown;
        }
        
    }
}

I hope someone finds it useful and/or entertaining.
In this code, I’m sending out two e-mails, one is a trial account activation email, and the other is just a notification to sales. On the first, I’m looking at the errors closely and trying to return a useful error message, so the customer knows that something has gone wrong and has a clue about how to handle it. On the second, I’m less worried about that, as it’s just a notification to our salesperson.