MVC 3, Technical

Using ASP.net MVC remote validation in the real world – AdditionalFields

Implementing remove validation in MVC 3 is very trivial. Typical examples found on the web after a quick search will usually demonstrate the remove validation required when creating a new user. The typical “Is my user name available” check.

However, on putting together a real world MVC 3 application that made full use of models that could be re-used and partials, I hit a pretty obvious issue. In this application, we were using the same model for creating new users and updating existing users. Why wouldn’t we? The model represents the same entity, and will need the same validation. This was a perfect fit and was ideal until we decided to put some remote validation in against the email address field.

Our email address property in our model, after initially adding remote validation, looked something like this:

        [DisplayName("Email Address")]
        [Required]
        [RegularExpression("[\\w\\.-]+@[\\w-]+\\.(\\w{3}|\\w{2}\\.\\w{2})")]
        [Remote("IsUserNameAvailable", "UserManagement")]
        public string ContactEmailPrimary { get; set; }

This worked fantastically on the create user page. When the user entered an email address that was already registered, the client made an Ajax request to our following controller action, that checked if the email address was not already in the database:

[HttpGet]
        public virtual JsonResult IsUserNameAvailable(string contactEmailPrimary)
        {
                int results = 0;
                var searchResults = _userServiceGateway.SearchUserLogin(contactEmailPrimary);

                if (searchResults.Count > 0)
                    return Json("This Email Address has already been registered", JsonRequestBehavior.AllowGet);

                return Json(result, JsonRequestBehavior.AllowGet);
        }

Which if it was registered, would return false.

“Great” we thought. Feature complete – and it went off to the testers. Only for another bug to have come up. We now couldn’t update the user information of an existing user. This was because the remote validation was firing and checking that the email address of the existing users hadn’t already been registered – which, it obviously had. This resulted in our page failing client side validation, and the client being unable to save any changes to the entity.

So we needed a solution. We first explored the possibility of disabling the Remote Validation somehow for this one page. This proved fruitless. We then looked at possibly splitting the models used, and having one model for the User Create, and one model for the User Update. We resisted this idea for obvious reasons.

We then looked at the possibility of passing additional parameters to the Remote Validation action. Sure enough, we discovered the AdditionalFields attribute of the Remote validation object.

The AdditionalFields attribute allows you to specify the name of another form element that will appear within the same form. ASP.NET MVC3 remote validation will then pass the value of this additional attribute to your controller action that is performing the validation. So to fix our issue, we just altered the code to pass the AdditionalField of “UserAccountId”, which we knew would be greater than zero if the user entity was being edited and not created.

Here’s how our finished working code looked, making use of the AdditionalFields attribute:

Model:

        [DisplayName("Email Address")]
        [Required]
        [RegularExpression("[\\w\\.-]+@[\\w-]+\\.(\\w{3}|\\w{2}\\.\\w{2})")]
        [Remote("IsUserNameAvailable", "UserManagement", AdditionalFields = "UserAccountId")]
        public string ContactEmailPrimary { get; set; }
        public int UserAccountId { get; set; }

View:

</pre>
<div class="columns">
 @Html.LabelFor(model => model.ContactEmailPrimary)
 <span class="relative">
 @Html.TextBoxFor(model => model.ContactEmailPrimary)
 @Html.HiddenFor(model => model.UserAccountId)

 </span></div>
<pre>

And finally our controller, which now gets an AdditionalField of “UserAccountId”:

[HttpGet]
        public virtual JsonResult IsUserNameAvailable(string contactEmailPrimary, int userAccountId)
        {
            if (userAccountId == 0)
            {
                int results = 0;
                var searchResults = _userServiceGateway.SearchUserLogin(contactEmailPrimary);
                bool result = true;

                //We need to check for an exact match here as the search only does a "like"
                foreach (var userAccountDto in searchResults)
                {
                    if (userAccountDto.UserLogin == contactEmailPrimary)
                    {
                        result = false;
                        break;
                    }
                }
                if (!result)
                    return Json("This Email Address has already been registered", JsonRequestBehavior.AllowGet);

                return Json(result, JsonRequestBehavior.AllowGet);
            }
            return Json(true, JsonRequestBehavior.AllowGet);
}

We were now back to create and update functionality working securely.

Recommended Reading:

Pro ASP.NET MVC 3 Framework

Technical

Script to Access Subscription Data in SQL Server Reporting Services

This script will find out exactly what reports a particular email address is setup to recieve. Simply run it against your ReportServer database:

DECLARE @EmailAddress NVARCHAR(250)

SET @EmailAddress = 'test@test.com'
SELECT
C.Name,
S.[Description] AS [Description],
S.LastRunTime
FROM
Subscriptions S
INNER JOIN [Catalog] C
ON S.Report_OID = C.ItemID
WHERE
S.ExtensionSettings LIKE '%' + @EmailAddress + '%'

You will then need to manually filter through the results, looking at the description in order to see which report is going to who, and when it was last run. This is because you may have a reply to email address set as the email that you are looking for, which would also be returned by the above query.

I wrote this because it was much easier and quicker than trawling through hundreds of subscriptions. Enjoy.

If anyone has any better ways of manipulating the XML contained within the “ExtensionSettings” field on the Subscription table, then please share.

 

Technical

Why Eukhost are bad

Finding good affordable hosting is a very difficult task. At work, we have several solutions hosted across a cluster of machines at Rackspace. These guys are by far the best hosting company I’ve ever had to deal with, and have an amazing level of support.

I have also always paid a yearly amount for some personal web hosting. As I currently deal with largely asp.net, I need to work with Windows Servers.  So about two years ago, I came across Eukhost.

I went for Eukhost because of their prices, and the available technologies ticked the right boxes – I even paid a little more to have Sql Server 2008, which is available for a lot cheaper than I could find elsewhere.

However it must be said that the service provided by Eukhost is poor. Below are a list of my grievances with Eukhost, accompanied with evidence.

Technical staff don’t take care and lack professionalism

One afternoon I discovered that one of my websites hosted at Eukhost had just stopped working. It was spitting out an asp.net runtime error, and had just come out of nowhere without me updating any of the files on the server. After initiating a support ticket with Eukhost’s Windows support, I was amazed to learn that a “technician” had accidentally changed the version of asp.net to 1.1 after doing some routine maintenance

You need to notice that your hosting isn’t working

Eukhost don’t proactively check that your hosting is available. It’s up to you to notice that its down and to contact them and to get them to fix it.

When there is a major problem and they have actually noticed it, don’t expect them to tell you

Recently, the server that my hosting lives on came under a massive DDOS attack (according to Eukhost). I only found this out because I discovered that several of my websites were down. I did the usual thing – contact Eukhost with a snotagram and get them to resolve the issue. I was then told about the situation, and told to read the forum. Silly me, not watching the forum for anouncements about the state of the service that I pay them for!

Support staff will get you to clean up their mess where possible

When Eukhost eventually worked out a solution for the DDOS attack, it involved the updating of the DNS records for all websites hosted on that machine. Rather than do it themselves by means of automation or just by putting all hands to the pump, they informed their customers, through means of forum post, that they needed to go and change their DNS settings.

Shared Hosting should be called cattle hosting

During the mess that was the DDOS attack, one of Eukhost’s staff got cornered in a forum and asked why nobody at Eukhost felt the need to bother updating the DNS settings themselves. In a rude and inconsiderate response, one the Eukhost’s staff claimed that there were “Over 3000 websites hosted on the box and that it would be impossible to update them all”. Eukhost have forgotten that as somebody that pays an annual fee for a service, I don’t give a monkeys about anyone else on my hosting, it just needs to work.

You can read through the said forum post here, although its contents have been butchered by Eukhost staff in what looks like a PR exercise. I can understand the difficulties in dealing with a DDOS attack, but I would have been a lot more sympatheic and understanding if it had been dealt with to the customers properly.

Anyway, I’m currently looking for a new asp.net hosting company. I’m willing to pay a little more if I can get a better service.

Technical

How to avoid cell merging when exporting to Excel in SSRS 2008

Lets be completely direct here. Cell merging in Sql Server Reporting services after exporting to Excel, is a common nightmare.

It happens because the engine that transforms the report tries to do so on a presentation basis.

I have been developing reports in SSRS for a few years now, and here are the best ways around the issue that I have found:

1. Don’t use standalone textboxes for titles, or any non-data elements.
Rather than fiddle with these for hours trying to get them to line up, just insert another row or two as headers above your data driven report element (e.g. table). You can then play with the presentation of the cells to make it look like it isn’t part of the same table. This can be done by colouring certain borders white to give the impression that there is nothing there.

2. Use points and not centimetres when specifying sizes.
The renderer converts all measurements into points anyway, so converting from centimetres can often lead to rounding errors. This is why you still get cell merging sometimes when you have two table opposite each other, with exactly the same sizes. I appreciate that this can be a bit of a hassle, especially if you already have a report that already specifies everything in centimetres. I’d recommend using a hefty bit of search and replacing in the source rdl fie.

I use both techniques in almost all reports that I develop. It keeps the clients happy.

Technical

How To: Debug Javascript in Visual Studio 2008

Debugging Javascript in Visual Studio 2008 is very simple.

It does however need a bit of a “kick” to get going. This is how you can do it.

1. Fire up your application in debug mode. Don’t bother setting a breakpoint in any of your Javascript source, as it wont work.

2. Once its running, open Solution Explorer. To do this while debugging, select View and then Solution Explorer. It should look something like this:

Solution Explorer - Javascript Debug

Note that this is now showing you what is actually being delivered to the browser. This is important because you might have javascript being added dynamically in your code behind. As you browse around your site you will see the items underneath Windows Internet Exporer (or whichever browser you are using), change.

4. You can then open any of the files underneath your browser node in Solution Explorer by double clicking on them. The files named “anonymous code” will contain the Javascript that is embedded into the page itself. you can then debug this Javascript code by putting a breakpoint in it and waiting for it to be hit.

Very useful and powerful stuff.