Thursday, September 1, 2011

Versioning JavaScript Files

A common problem web developers face is dealing with versions of JavaScript files (and other resources like CSS file, images etc.)  On the one hand we want to cache the file so that it doesn't get downloaded every time, but on the other hand we need to be able to force the browser to get a new version if the file changes.

My solution is to treat my own JavaScript files differently from the files I include from frameworks such as jQuery.

For framework files I add a version number to the file name e.g. jQuery-1.5.1.min.js. When the framework is updated, I will use a new file with the new version number in its name and the browser will fetch the new file.

For my own files I append a query string to the end of the url that contains a version number. This version number is fetched from the DLL that the controller code runs from. The effect is that each time a new build is deployed, the browser will refetch the JavaScript file, but in between builds it will cache it.

To this end I have two different URL helpers that I use when referencing files as seen below.

<script type="text/javascript" language="javascript" src="<%: Url.Content("~/Content/Scripts/jquery-1.5.1.min.js") %>"></script>
<script type="text/javascript" language="javascript" src="<%: Url.VersionedLink("~/Content/Scripts/General.js") %>"></script>

Thursday, July 28, 2011

Formatting Dates in MVC

When you want to display a value in MVC, for instance a date, that needs to be formatted and may even be null, it can result in a lot of code in the view. For example.
<%: benefit.BenefitCancelledDt.HasValue ? benefit.BenefitCancelledDt.Value.ToString(Constants.SHORTDATE_FORMAT) : "" %>
There is a better alternative. By using data annotations we can do this it in a much shorter for.

Add the following annotation to the property on the model.
[DisplayFormat (DataFormatString = "{0:" + Constants.SHORTDATE_FORMAT + "}")]
public DateTime? BenefitCancelledDt { get; set; }
The view can then be rewritten like this.
<%: Html.DisplayFor (x => x.PolicyBenefits[i].BenefitCancelledDt)%>
Note that I used Html.DisplayFor. The formatting annotation is not used by the other methods like Html.DisplayTextFor. Also note that the expression needs to bind directly to the model and not to a intermediate variable like benefit in the inital code.

Monday, July 25, 2011

Chucking Out the Registry

I have refactored away from the registry I used previously to store the DataAccessAdaptor, UnitOfWork, ServiceHome, etc. I am now using a DataContext class that has references to the other objects (DataAccessAdapterm etc.) but is itself managed by the DI container. So to access the DataAccessAdapter, instead of using

Registry.GetDataAccessAdapter();
you now use

ObjectFactory.GetInstance<IDataContext> ().Adapter;
All the connection and transaction management is now handled by the DataContext as well.

Here are some pieces of the DataContext class.

public class DataContext
    : IDataContext
{
    public ActionProcedures ActionProcedures { get; set; }
    public IRetrievalProcedures RetrievalProcedures { get; set; }
    public DataAccessAdapterBase Adapter { get; set; }

    ...



    public void Clear ()
    {
        // If clear is called while there is still a transaction, the client code has a bug
        if (Adapter != null && Adapter.IsTransactionInProgress)
        {
            throw new Exception ("There is still an active transaction.");
        }

        // First save any changes in UnitOfWork
        if (UnitOfWork != null)
        {
            UnitOfWork.Commit (Adapter, true);
        }

        // Dispose and remove adapter
        if (Adapter != null)
        {
            Adapter.Dispose ();
        }

        ActionProcedures = null;
        RetrievalProcedures = null;
        Adapter = null;

        ...
    }

    public void Initialize (
        DataAccessAdapterBase adapter)
    {
        Adapter = adapter;
        ActionProcedures = new ActionProcedures ();
        RetrievalProcedures = new RetrievalProcedures ();
        
        ...
    }

    public virtual void StartTransaction (
        IsolationLevel isolationLevel,
        string transactionName)
    {
        Adapter.StartTransaction (isolationLevel, transactionName);
    }


    ...
}
The next step would now be to get rid of the DataAccessorHome and ServiceHome completely and simply get the data accessors and services from the DI container directly.

Thanks to Francois and Schalk for their inputs.

Sunday, July 24, 2011

Setting the Date for Testing

I've added the following code to the remoting sink on the remoting server side to enable us to set the date on the server for automated ui testing.
if (!string.IsNullOrWhiteSpace (ConfigurationManager.AppSettings["Date"]))
{
    ObjectFactory.GetInstance<IDataContext> ().ServiceHome.DateService.SetDate (Utils.ParseDateTime (ConfigurationManager.AppSettings["Date"]));
}
Please make sure this setting never gets set on the production environment.

Thursday, July 14, 2011

Automatic Database Script Execution

We are using an automatic database script execution mechanism such as the one recommended by Scott Ambler.  The basic working of it is that every database refactoring is placed in its own script file and that each file has a sequence number.  We also store the sequence number of the last run script in a table called Version along with a column called DatabaseName that we can query to find out which database we are running the script against.

When the automated deployment takes place, our PowerShell script will automatically run all the scripts since the last one that was run against that database and update the version number.

During an automated build, we actually create the entire unit test database from scratch with a few initialization scripts and then apply all the update scripts in sequence to arrive at the latest database schema.

We also have a script that we us when we download a production database to our development machines, to bring that database up to speed with the latest scripts to match our code.

I recently had an issue where I needed to create a stored procedure on the unit test database that I didn't want in production.  This procedure wipes all the data in the database except lookup data.  I needed this when using recorded tests from Selenium, because unlike our other tests, we can't do the entire test in one transaction and I needed to clean up after the test.

In order to achieve this I used some dynamic SQL like so (thanks for the tip Schalk):
if dbo.GetDatabaseName () = 'policy-unittests'
BEGIN

    DECLARE @Sql NVARCHAR(MAX)
    SET @Sql = 
    'CREATE PROCEDURE CleanDatabase
    AS

        -- Lots of delete statements
        DELETE FROM Policy
        DELETE FROM Person'
        
    EXECUTE sp_executesql @Sql

END

Adding Convenience Methods to Classes

Code can often be simplified by adding a few convenience methods to our classes. For instance, one set of classes were littered with code like this:
CommandInitDataEntity data = CommandEntity.InitDatas.SingleOrDefault (x => x.Key == InitDataKeys.MoiBliscyUpgradeType.ToString ());
if (data != null)
{
    this.UpgradeType = (UpgradeType) Enum.Parse (typeof (UpgradeType), data.Value);
}
I added the following convenience methods to the CommandEntity class:
public bool HasInitData (
    string key)
{
    return this.InitDatas.ToList ().Exists (x => x.Key == key);
}

public bool HasInitData (
    Enum key)
{
    return HasInitData (key.ToString ());
}

public string GetValueFromInitData (
    string key)
{
    return this.InitDatas.Single (x => x.Key == key).Value;
}

public string GetValueFromInitData (
    Enum key)
{
    return GetValueFromInitData (key.ToString ());
}

public T GetValueFromInitData (
    Enum key)
    where T : struct, IConvertible
{
    Type type = typeof (T);
    if (type.IsEnum)

    {
        return (T) Enum.Parse (typeof (T), GetValueFromInitData (key));
    }

    throw new NotImplementedException ("Unsupported type");
 }
I could then rewrite the original code like this:
if (commandEntity.HasInitData (InitDataKeys.MoiBliscyUpgradeType))
{
    this.UpgradeType = commandEntity.GetValueFromInitData (InitDataKeys.MoiBliscyUpgradeType);
}
 
It may not seem like a big difference, but well designed software is achieved though hundreds of little decisions that all add up.

Tuesday, July 12, 2011

How to Make Selenium Wait for Ajax Calls

I needed to be able to wait for Selenium to complete an Ajax call before checking the effects.  After looking at many different solutions, I came up with this one.

Add a hidden field to the master page to store the number of Ajax requests that have been done.
<input type="hidden" id="NumAjaxRequests" value="0" />
Hook into the jQuery Ajax framework to automatically increment the field for each request that completes.
$('.ajaxindicator').ajaxStop(function ()
{
    $('#NumAjaxRequests').val(parseInt($('#NumAjaxRequests').val() + 1));
});
You can then reset the field before performing the action that causes the Ajax call, perform that action and tell Selenium to wait until the expected number of Ajax calls have taken place.
| type         | NumAjaxRequests | 0                 |
| select       | Mb_SpouseId     | label=Spouse Name |
| waitForValue | NumAjaxRequests | 1                 |