custom software development to help your business take flight™

Push to AppHarbor on every push to GitHub

Posted by Joe Wilson on Tuesday, October 11, 2011 1:21 PM

If you're using GitHub as your shared, online, git repository and you're using AppHarbor for continuous integration and deployment, you really have two git repositories - the GitHub one and the AppHarbor one.

AppHarbor instructions help you set up a remote to this git repository so you can push to it whenever you like, but that means you have two steps on most commits:

git push origin master

to push your code up to your GitHub repo, and:

git push appharbor master

to push your code up to the AppHarbor repo.  Because AppHarbor doesn't support SSH yet (only HTTPS is supported now), you also have to enter your password.  Not a giant hassle, but what if you could get rid of that last little bit of friction?

GitHub Service Hooks

GitHub Service Hooks run after a push to GitHub, and there is one to fire off your AppHarbor build/test/deploy.  It's under your GitHub repo > Admin > Service Hooks.

image

Once you're there, the instructions are pretty simple.

  1. Go to your application's main page on AppHarbor and find the "Create build URL". Example: https://appharbor.com/application/{application_slug}/build?authorization={token}
  2. "token" is the value of the "authorization" parameter.
  3. "application_slug" is your application's unique identifier.
  4. If your GitHub repository is private you need to add the "apphb" GitHub user as a collaborator. This enables AppHarbor to download the source code.

Here's where you can find the values for steps 1-3 in AppHarbor:

image

Step #4 can be skipped if you have a public repo.  If you're using private repos, add the "apphb" GitHub user to a GitHub team with "pull" grants.

All done!

Next time you do

git push origin master

your code will be pushed up to GitHub and AppHarbor will grab the latest code from GitHub (not from the AppHarbor repo anymore), build it, test it, and if the tests pass, deploy it.  All in one step!

Tags: , ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Sessionless MVC without losing TempData

Posted by Joe Wilson on Thursday, June 30, 2011 11:32 PM

I've blogged before about making your MVC controllers sessionless.  The knock on sessionless controllers is that TempData is off the table because it uses Session as its data store.

If you see the TempData name and dismiss it, thinking it's another dictionary like Session[] and ViewData[] and you're waaaay past that since your mister type-safe, take another look.  The neat thing about TempData is that it lasts for one more page load and then "poof" - it's gone.  It self destructs as soon as it has been read.

Sure, it's a dictionary with magic string keys, but it's a great place to store message like "Your changes were saved" when you redirect to another page.

So how do we get the benefits of TempData as an ephemeral application message store, but go all-in on the web farm with sessionless controllers?

Cookies!

I can hear you groaning.  Cookies are old school!  They can't hold much information!  They aren't secure!

Fine, don't put much in there, and don't put your secret fried chicken recipe in there.  We're just going to use this as a little string to tell the user everything is OK or it's not OK.

TempData uses a provider model for the data store, and Session is the default data store.  But you can get a cookie data store, CookieTempDataProvider, with Mvc3Futures NuGet package:

image

This should work as a TempData provider, but now it has to be wired into the application.  Here's how you can do that:

    public class BaseController : Controller
    {
        protected override ITempDataProvider CreateTempDataProvider()
        {
            return new CookieTempDataProvider(HttpContext);
        }
    }

Tags: , , ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Running SpecFlow and WatiN tests with TeamCity and AppHarbor

Posted by Joe Wilson on Thursday, June 2, 2011 10:44 PM

If you're using SpecFlow and WatiN to automate your web testing, things work great on localhost, but tend to fall apart on the continuous integration server.  It's a "chicken or the egg" paradox.  If the new code hasn't been deployed yet, it can't be externally tested with WatiN, but it shouldn't be deployed until all the tests have passed.  What are you supposed to do?

TeamCity

If your SpecFlow and WatiN tests are in their own assembly (something like MyProject.AcceptanceTests.dll), and you're using TeamCity, it's easy to run your unit tests and integration tests, then deploy the code to a web server, then run your acceptance tests in a separate build step.  It's just a matter of breaking up the steps and designating which test assemblies will be run at which time.

This step in the screen shot below is running the unit tests and integration tests.  A separate, later step will run the tests in the **\*AcceptanceTests.dll assembly after the code has been deployed to the web server.

image

AppHarbor

imageIf you're using AppHarbor to run your tests on check in with "git push appharbor master", (and you should - it's super easy), you don't want to run your web tests until the code is deployed, but AppHarbor doesn't deploy anything until all the tests pass.

What we want is for AppHarbor to ignore the web automation tests until the code is deployed.  AppHarbor flips a *.config appSetting key named "Environment" to "Test" when it's testing and to "Release" when the code is deployed.  So while your tests are running, your App.config in your test project should look something like this:

<appSettings>
  <add key="Environment" value="Test"/>
</appSettings>

We can take advantage of this appSetting test runner change to tell SpecFlow to ignore this set of tests, which allows AppHarbor to continue running other tests and deploying the code.

This is not be the most elegant way to do this, but I got this working today:

[Binding]
public class MyStepDefinition
{
    [BeforeFeature]
    public void BeforeFeature()
    {
        if (ConfigurationManager.AppSettings["Environment"] == "Test")
            Assert.Ignore();
    }

    // Rest of step definition code...
}

The BeforeFeature attribute tells the SpecFlow test runner to execute that method before each feature.  With a quick check on the Environment appSetting value, we can tell AppHarbor to ignore this test and continue processing.  This will ignore all scenarios in all feature files.  Since all my scenarios are web automation tests, that works for me.

But what if you have some scenarios that rely on web automation testing and others that don't?  A more targeted approach would be to set a SpecFlow tag, like @web over the SpecFlow scenarios that require web automation testing to pass.  Then in the [BeforeScenario("web")] handlers for that tag, you could set Assert.Ignore to skip those scenarios while executing all other scenarios.

[BeforeScenario("web")]
public void BeforeScenario()
{
    if (ConfigurationManager.AppSettings["Environment"] == "Test")
        Assert.Ignore();
}

Once your non-web automation tests have passed and the code is deployed, you can point your local test runner at the AppHarbor URL and test against the newly deployed web app.

Tags: , , , , ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Why I'm using Moq instead of Rhino Mocks these days

Posted by Joe Wilson on Thursday, March 10, 2011 10:29 AM

On my last greenfield project I got to pick the unit testing and mocking tools.  I've been a regular Rhino Mocks user for a long time, but I've seen lots of code samples with Moq and liked the syntax, so I gave it a try.

I have to say, I did prefer Moq slightly to Rhino Mocks.  The newer Arrange, Act, Assert (AAA) style syntax in Rhino Mocks is a huge improvement over the old Record/Replay mess.  But Moq has the benefit of being born at the right time and has AAA style calls without the burden of supporting deprecated syntax.  This means Moq has cleaner documentation, fewer options, and fewer ways to get confused by the API.  I really couldn't find anything I needed to do in Moq that I couldn't do.  I did get a little tripped up having to get the object out of the mock, with myMock.Object calls, but that wasn't a big deal.

Here's the NuGet Moq install package syntax to add Moq to your unit test project:

image

I'm using mocking frameworks less these days.  Instead, I'll write a fake object myself or just try to avoid interaction testing altogether.  But if I needed to pull one off the shelf today, I'd grab Moq first.

Tags: , , ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Fixing screen saver wait time of 999 minutes on a Dell laptop

Posted by Joe Wilson on Wednesday, March 9, 2011 11:12 PM

I have a Dell Precision M4500 with Windows 7 and ran into a problem with the screen saver not coming on.  Rebooting didn't fix it and calling Dell technical support didn't help ("You could reformat, I guess."). 

My screen saver dialog looked like this with 999 minutes in the wait time:

SNAGHTML1e594b9

If I clicked Preview, the screen would go blank like it was supposed to, so it wasn't a video card thing.  When I set the wait time to 10 minutes and clicked OK, then opened the dialog box again, my value of 10 minutes was saved, but the screen saver never turned on.

Finally, I found this dialog in the Dell System and Manager under Display Settings:

SNAGHTML1e9726b

See the "Disable alarms and timers for Presentations" check box?  I had that checked, so I unchecked it and the screen saver started working normally again.  I wasn't in Presentation mode, but this Dell app didn't seem to know that.

I hope this is helpful to someone stuck on the same issue.

Tags:
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Using CRM auto filtering in custom reports with complex SQL

Posted by Joe Wilson on Sunday, February 13, 2011 4:55 PM

Report prefiltering in Microsoft CRM 4.0 is a very cool feature - when it works.  The normal ways of setting up this auto filtering feature in your custom CRM reports are 1) special aliases, and 2) SQL strings.

Special aliases

The alias approach is the simplest one to use.  If you have a report that needs prefiltering, change your SQL from this:

select firstname, lastname 
from FilteredContact

to this:

select firstname, lastname 
from FilteredContact AS CRMAF_FilteredContact
The trick is the alias.  All you need to do is add an alias with"CRMAF_" in front of the view name, and CRM will rip out your SQL at report runtime and replace it with a custom SQL statement.  I think the acronym must stand for CRM Auto Filter.  By using a special alias CRM recognizes, the actual SQL run on your server will be similar to this, where contacts are prefiltered for city = 'Denver'.

select firstname, lastname 
from (select contact0.* 
      from FilteredContact as contact0 
      where address1_city  = 'Denver') AS CRMAF_FilteredContact

SQL strings

The other route you can go is mash together SQL strings and get a parameter from CRM that is the prefilter SQL statement.  You've probably done this kind of this kind of thing before:

declare @sqlstring varchar(max)
set @sqlstring = 'select firstname, lastname 
from (' + @CRM_FilteredContact + ') AS MyFilteredContacts '
exec (@sqlstring)

This really isn't bad when you have a small SQL statement, but you probably wouldn't need to create a custom CRM report if it was a simple SQL statement, right?  The CRM users could create their own simple report inside CRM for that.

When the SQL statement gets complex, you have quote and string bugs.  When the SQL gets lengthy, you can't have over a certain number of characters in the SQL string or you have to split it and you end up with:

exec (@sqlstring1 + @sqlstring2 + @sqlstring3)

You can get it to work, but it's pretty yucky. 

The problem

I much prefer the alias approach.  It's much easier to read, and the aliases are not too much trouble to add in and get prefiltering in CRM for free.

The problem is, the alias technique breaks down when the queries get more complex or you need multiple datasets in your report using the same prefiltering.  Here are the cases I've found and the workarounds.

Dataset based on a union query

If you have a query with a union statement, like:

select firstname, lastname 
from FilteredContact AS CRMAF_FilteredContact
where address1_city = 'Denver'
union
select firstname, lastname 
from FilteredContact AS CRMAF_FilteredContact
where address1_city = 'Highlands Ranch'

CRM will do the alias filtering trick, but only for the first select.  The second select in this query will not be filtered at all.

The fix I've come up with uses SQL temp tables.  Maybe not the best approach, but I had a lot of reports that were not auto filtering as expected, and this has worked so far:

select * 
into #FilteredContact
from FilteredContact AS CRMAF_FilteredContact

select firstname, lastname 
from #FilteredContact
where address1_city = 'Denver'
union
select firstname, lastname 
from #FilteredContact
where address1_city = 'Highlands Ranch'

drop table #FilteredContact

This let's CRM do what it wants to, and swaps out your SQL where it finds the CRMAF_FilteredContact alias the first time.  It also let's you leave the rest of your SQL pretty much intact.

Dataset based on multiple queries selecting into a temp table

I had another query that was suffering the same problem, but this query was a little different.  I needed the data displayed in columns, so I created a temp table and populated it with selects.  There were multiple selects in the query filling up the columns in this temp table.  Everything ran great without auto filtering.

But with auto filtering, the same thing happened.  The first select statement got filtered.  The ones after that were untouched.  The fix I used was the same.  I created a temp table, populated it with the auto filtered data, then selected off that temp table throughout. 

To be a good TempDB citizen, I dropped the temp table at the end of the query.  SQL Server does the temp table drop for you when the table is out of scope, but it helps to have the drop statement in there when testing the query in SQL Server Management Studio and running it multiple times in the same query window.

Reports with multiple datasets

This technique works if you have a complex query that needs auto filtering.  But what if you have multiple datasets in your report, and they all need to be auto filtered?

In one report, the queries were very similar.  I had two datasets that were identical expect one used report parameter A and one used report parameter B.  This was to compare two funnels for two different time periods.  Auto filtering worked fine on the first dataset and funnel, but didn't do anything on the second dataset or funnel.

The fix in this case was to create one dataset that didn't filter on either report parameter A or parameter B.  Then in the report, have the funnel controls themselves do the filtering based on the report parameters.  Most SQL Server Reporting Services data controls have a filter property.  I've never really used them before now, since it almost never makes sense to pull all the data from the database and then filter inside the report.  I'd rather filter on the database first.  However, in this case, the control filters helped and I got the result I wanted because there was one dataset with one CRMAF_ alias.

On another report, there were three charts built using three different datasets.  The datasets were the same except for the group by clause.  I was able to use the same approach and condense these to one dataset with no group by clause, then do the grouping in the chart controls.  Again, it made me feel a little bad that the data wasn't already summed and grouped when it got to the chart control, but at least it was working.

Tags: , ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Autocomplete dropdown with jQuery UI and MVC

Posted by Joe Wilson on Friday, February 4, 2011 11:03 AM

You know the autocomplete dropdown.  It has become ubiquitous, and customers now expect them in their web apps.  "Can't you just set autocomplete=true or something?"  It's not quite that easy with ASP.NET MVC, but it's pretty simple.

We want to set up a text box on a web page that uses jQuery to call back to a controller action as the user types.  The action method will return a JSON array of values that match what the user has typed so far.  Each time they type and pause for a second, the screen should show the new matching records.

The view

First, we'll work on the view.  Set a reference to jQuery and to jQuery UI.  These are included with new MVC 3 projects (look in the Scripts folder), or you can find a CDN and reference them there.  I've got this in my view's head section:

<head>
    <title>Autocomplete Test </title>
    <script src="<%: Url.Content("~/Scripts/jquery-1.4.4.min.js")%>" type="text/javascript"></script>
    <script src="<%: Url.Content("~/Scripts/jquery-ui.min.js")%>" type="text/javascript"></script>
</head>

Then you need a text box to tie all this too.  The search results will be shown under this text box to give the appearance of a dropdown.  This simple form should do:

<form action="/Search/ProcessTheForm" method="post">
    <input id="searchTerm" name="searchTerm" type="text" />
    <input type="submit" value="Go" />
</form>

Next, you'll need to use the autocomplete function in jQuery UI like this:

<script type="text/javascript">
    $(function () {
        $("#searchTerm").autocomplete({
            source: "/Search/AutocompleteSuggestions",
            minLength: 3,
            select: function (event, ui) {
                if (ui.item) {
                    $("#searchTerm").val(ui.item.value);
                    $("form").submit();
                }
            }
        });
    });
</script>

Here we've got a jQuery selector binding the autocomplete function to the searchTerm input box("#searchTerm").  We then set the URL for the source for the autocomplete suggestions, which in this case, will be our controller (Search) and action method (AutocompleteSuggestions).  Then we set the number of characters the user has to type before we make the first call.  It's set to three here to prevent pulling back meaningless matches from searching too soon.

Finally, we set up the autocomplete function's select event to set the selected value from the dropdown list as the value in the searchTerm input box and submit the form to the Search controller and the ProcessTheForm action method.

Here's the entire view for reference:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>

<!DOCTYPE html>
<html>
<head>
    <title>Autocomplete Test</title>
    <script src="<%: Url.Content("~/Scripts/jquery-1.4.4.min.js")%>" type="text/javascript"></script>
    <script src="<%: Url.Content("~/Scripts/jquery-ui.min.js")%>" type="text/javascript"></script>
</head>
<body>
    <form action="/Search/ProcessTheForm" method="post">
        <input id="searchTerm" name="searchTerm" type="text" />
        <input type="submit" value="Go" />
    </form>
    <script type="text/javascript">
        $(function () {
            $("#searchTerm").autocomplete({
                source: "/Search/AutocompleteSuggestions",
                minLength: 3,
                select: function (event, ui) {
                    if (ui.item) {
                        $("#searchTerm").val(ui.item.value);
                        $("form").submit();
                    }
                }
            });
        });
    </script>
</body>
</html>

The controller

Now you need to write something that will return the records that match what the user has entered so far.  This might be a database call with a LIKE searchTerm + "%" (watch out for SQL injection, though) or SOUNDEX or a web service call.  Be sure you limit your returned search results to maybe the top 10 matches so you don't bog things down with too much data moving over the wire.

Next, set up a controller action to invoke this back-end call and JSON up the returned list.  We've already names the controller (Search) and action (AutocompleteSuggestions) in our view.  Something like this should work, where _searchRepository is my repository call that finds matching strings, which are then converted to JSON:

public JsonResult AutocompleteSuggestions(string term)
{
    var suggestions = _searchRepository.GetAutoCompleteSearchSuggestion(term);
    return Json(suggestions, JsonRequestBehavior.AllowGet);
}

Don't forget the JsonRequestBehavior.AllowGet parameter in the Json call.  You won't get anything back from the action method if you leave it off.

Also, the name of the parameter passed into the action method is important.  The jQuery UI autocomplete method will call your source URL with a query string like "?term=foo".  If you set the parameter name to a string named "term", the MVC model binder will grab the value from the query string for you.

The jQuery UI Autocomplete function has several other options and events you can use in your app, and the demos are worth checking out too.

Tags: ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

HtmlEncode and UrlEncode aren't the same thing

Posted by Joe Wilson on Saturday, January 29, 2011 5:43 PM

The other day I was trying to pass an encoded version of "id=123" to a customer's search API.  I tried

var result = HttpUtility.HtmlEncode("id=123");

but it returned "id=123".  I stared at the screen, scratched my head, and jumped on Google.

A quick search reminded me that HtmlEncode is for escaping HTML, but it leaves everything else alone.  So

var result = HttpUtility.HtmlEncode("<br/>");

returns "&lt;br/&gt;".  All the less thans and greater thans get escaped, but the rest is pretty much the same.

UrlEncode is for cases where you want to escape a URL, so

var result = HttpUtility.UrlEncode("id=123");

returns "id%3d123", which is what I wanted in the first place.  By comparison, if you UrlEncode the "<br/>" tag, like

var result = HttpUtility.UrlEncode("<br/>");

you'll get back "%3cbr%2f%3e".  It's encoding the less than, the slash, and the greater than, but it's encoding them with URL-type escapes instead of HTML-style ones.

I guess it's been a while since I paid attention to encoding.  Not all encoding methods are equal in the ASP.NET framework, so be careful which one you use.

Tags:
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Sessionless MVC 3

Posted by Joe Wilson on Thursday, January 20, 2011 12:02 PM

If you've done much ASP.NET development, you know you shouldn't use session for high performance web sites that require web farms and load balancers.  You can centralize session to be on one web server or move it to the database, but what if you want to avoid it altogether?

MVC 3 introduces a new controller attribute called SessionStateAttribute.  You can decorate your controller with this attribute to disable session, make it read-only, or turn it on for full read-write access.  Note that TempData, which uses session to store values between requests, is also disabled if session is disabled.  That makes sense, but since it's called TempData[] instead of Session[], it's not obvious.

Here's how you can use this on a controller.  Note this attribute is at the controller level, not the action level.

using System.Web.Mvc;
using System.Web.SessionState;

namespace MvcApplication1.Controllers
{
    [SessionState(SessionStateBehavior.Disabled)]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Message = "Welcome to ASP.NET MVC!";

            return View();
        }

        public ActionResult About()
        {
            return View();
        }
    }
}

And here's how you can set it as a Global Filter to turn it off for all controllers.  The attribute is being set in the RegisterGloablFilters method in the Global.asax and is called in Application_Start.

UPDATE: This doesn't work as a global action filter.  However, you can turn off session for your whole application with code in your web.config like this:

<system.web>
    ...
    <sessionState mode="Off" />
    ...
</system.web>

Tags: ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

SpecFlow isn't just a WatiN Wrapper

Posted by Joe Wilson on Tuesday, January 4, 2011 1:23 PM

I have been using SpecFlow with WatiN for about 9 months now.  It's a great combination for driving the features you want in a web site and verifying they work as expected.  SpecFlow takes care of the BDD framework and application features side, while WatiN drives the browser to verify behavior.

I've gotten used to the the pattern of writing a SpecFlow scenario, then writing the WatiN code in the step definition to open a browser and perform the task or look for the expected HTML.  Once you get the hang of the tools and the process, it's a very sustainable workflow for the developer.

But today I ran into something that I couldn't test with WatiN.  I wanted to check that an XML sitemap was coming up correctly on a web site.  WatiN was able to open the XML document in the browser, but there was no Body tag because the XML is not an HTML document, so WatiN couldn't read the XML.  All I needed to do was verify that an expected value was in the XML sitemap.  If the value was found, the test would pass.

I spent about 15 minutes trying out different WatiN commands to find the text, but with no Body tag, there was nothing loaded into the WatiN object model, so nothing was working.  I also tried looking through the WatiN Elements collection and a few other dead ends, but it was only showing me the previous web page or null.

Finally, the obvious answer occurred to me.  I'm looking at an XML document and I want to verify that it has a value in it.  I'm in a SpecFlow step definition and I've got the entire .NET framework at my disposal, not just WatiN.  Five minutes later, I've got it coded to load and examine an XDocument for the value I needed so the test can pass.

This was a good lesson for me.  Being on a development workflow roll is no excuse to not keep your head up for alternative strategies that would better solve your problem.  SpecFlow isn't just a wrapper for WatiN calls.  It can be used to verify any behavior it can invoke.

Tags: , ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Image Details