Cymen Vig

Software Craftsman

ASP.NET MVC and DropDownList: One approach...

One thing that I’m not particularly fond of in ASP.NET MVC is figuring out where to put additional data one needs for views. If one is using a “model per view” approach it is simple – stick it in that model. Otherwise you can choose to add it to a model that won’t always need it or pass it via ViewBag/ViewState. I currently lean towards the last option.

The other annoyance is how best to transform a list of objects into a IEnumerable<SelectListItem> collection cleanly with the special case of handling a default value. I have started to approach this with extension methods and am very happy with how it is working.

Transforming your List<MyObject> to IEnumerable<SelectListItem>

Our current data tier has a manager class for each object type. When that object type is going to be used in a select list, I add an extension to IEnumerable<MyObject> like so:

To support the option of a default value, I have an extension method that applies to IEnumerable:

Here is an example of an actually call to this methods in:

Comments and Reactions

SSL Certificates, OCSP and CRLs: How to troubleshoot

Recently, I ran into an interesting problem: a website that my workplace was going to start hosting was about to be cut over however the SSL certificate was failing. By disabling OCSP in Firefox, I determined it was likely due to the certificate authority (GoDaddy) revoking the certificate.

The warning displayed by Google Chrome is:

The server’s security certificate is revoked! You attempted to reach www.xyz.org, but the certificate that the server presented has been revoked by its issuer. This means that the security credentials the server presented absolutely should not be trusted. You may be communicating with an attacker. You should not proceed.

The warning displayed by Mozilla Firefox is:

Secure Connection Failed

An error occurred during a connection to www.xyz.org. Peer’s Certificate has been revoked.

(Error code: sec_error_revoked_certificate)

  • The page you are trying to view can not be shown because the authenticity of the received data could not be verified.
  • Please contact the web site owners to inform them of this problem. Alternatively, use the command found in the help menu to report this broken site.

At first, we thought there was perhaps an issue with the intermediary certificate bundle. However that bundle matched GoDaddy’s current bundle so it was time to take a closer look at the certificate. Firefox wasn’t much help however Chrome could display all the metadata attached to the failing certificate. The two essential parts for to check the status for OCSP are the serial number and the CRL list.

The CRL list was located at http://crl.godaddy.com/gds1-18.crl and I was able to retrieve this file however I couldn’t find a decent way to view a CRL file on Windows in a searchable way quickly. I found that openssl can dump a CRL to text and I did so with the GoDaddy CRL using this command:

1
openssl crl -inform DER -in gds1-58.crl -text > list.txt

I then opened up list.txt in a text editor and was able to find a revocation entry similar to this one:

1
2
3
4
5
    Serial Number: 049A85BD85BE83
        Revocation Date: Oct 26 18:56:06 2011 GMT
        CRL entry extensions:
            X509v3 CRL Reason Code:
                Cessation Of Operation

Of course, if you have login access to the registrar for the certificate it may be quicker to simply login and see if there is some sort of mention on the account about the failing certificate. But if you need to determine why a certificate is failing OCSP hopefully the above will help. I suspect there are additional CRL to check however I’m not sure how to determine what they are.

Comments and Reactions

jQuery JCrop plugin, Chrome/Webkit: fails to initialize after the first attempt

I recently ran into a bug when using recent versions of Chrome and the jQuery JCrop plugin. I am adding some HTML to a jQuery UI modal window that contains the image I want to crop. I am initializing JCrop using the onload event of the image within this HTML. That worked fine in the past but stopped working sometime in the past year or so.

According to issue 7731 on chromium, the cause of this problem is that Webkit is more strict – the onload event is only triggered the first time the image is loaded. So the issue was the onload event triggered immediately when the modal window was shown – it triggered before the image was displayed (except for the first time). This is a tricky thing to work around. I verified that this was my issue by appending to the image URL the current timestamp (so + ‘?’ + new Date().getTime()) in order to force the browser to reload the image each time. That did fix the problem but it also introduced UI lag as the image had to be fetched each time a crop was attempted.

I put in this short term fix: put my JCrop initialization code in a function named crop and then (still bound to the load event), attempt to initialize it. If the image isn’t loaded, try again in 25ms up to 5 times. I can tell if the image is loaded by checking for a height > 0. The code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
    $('#cropbox').load(function (event) {
        var boxHeight = Math.floor($('#crop').closest('.ui-dialog').height() * 0.8);

        var $img = $(event.target);
        var productDiv = $('div#' + id);
        var sku = $('div.sku', productDiv).text();
        var product = productsData[sku];
        var height, width;

        // put crop loading code into function -- see comment below about chrome hack
        var crop = function () {
            height = $img.height();
            width = $img.width();

            var jcrop = $.Jcrop(
                ...
                });

            ...
        }

        // Ugly hack for chrome -- the load event triggers before the image is actually displayed/in DOM
        // but only on crop attempts after the initial one. One way to detect this is to check if the image
        // height is 0 -- if so, retry.
        // Update: issue probably this: http://code.google.com/p/chromium/issues/detail?id=7731#c12

        var worked = false;
        var attempts = 0;
        var attempt = function () {
            if (worked) return;

            height = $img.height();
            if (typeof height === 'number' && height > 0) {
                crop();
                worked = true;
            }
            else {
                attempts++;
                if (attempts < 5) {
                    // try again in 25 milliseconds
                    setTimeout(attempt, 25);
                }
                else {
                    alert('Bug with cropping image.');
                }
            }
        }

        attempt();
    });

I reported this problem in issue 63 for the JCrop plugin. Hopefully, there is a better way to do this however if you need a quick work around now…

Comments and Reactions

SQL Server - Ranking names for search results by position of query within name

SQL Server using PATINDEX() and LEFT()

When searching names there are some assumptions we can make (based on first and last name being in separate columns):

  • A match in the last name is more important than a match in the first name

  • The position of the match within the last name is important: an earlier match is a better match

  • The first name should still be search

  • If present, a middle name is least important

It is possible to do this with SQL Server using the following proprietary extensions:

  • PATINDEX(needle, haystack): returns position of needle within haystack and (unfortunately in our use case) 0 if not present.

  • LEFT(string, count): returns substring of string up to length of count (note: will truncate string if length greater than count!)

    SELECT TOP 10 firstName + ‘ ‘ + middleName + ‘ ‘ + lastName FROM Member WHERE [firstName] + ‘ ‘ + [middleName] + ‘ ‘ + [lastName] LIKE @query ORDER BY PATINDEX ( @query, LEFT([lastName] + ‘ ‘, 90) + LEFT([firstName] + ‘ ‘, 90) + [middleName] ), [lastName], [firstName], [middleName] – Note: the ‘ …. ‘ above is a string of spaces of length 90

We are making a big assumption: none of the name fields will have a length > 90. You may need to adjust this value for your use case. The reason we need to do this is that PATINDEX() will return 0 if the value is not present so we can’t simply due a nice ORDERBY PATINDEX(@query, lastName), PATINDEX(@query, firstName), PATINDEX(@query, middleName). Instead, we have to concatenate the name fields into one long string but pad them so that variable length of the names will not affect the rank they are put in.

MySQL using LOCATE() and LEFT()

The same method should work in MySQL using the LOCATE() and LEFT() functions. Both appear to be identical in usage to the SQL Server functions.

Comments and Reactions

Mime types for ASP.NET

One of the annoyances working on the Windows/IIS stack is that getting mime types is a pain. They are located in multiple places and there is no really ideal “best practice” method to get mime types without what I consider overly-complicated solutions. In light of this observation I wrote a basic C# program that fetches the mime.types file from the Apache project and converts it to a C# Dictionary<string, string> keyed by file extension. It is a basic program but might be useful for others wondering why in the world this is so complicated.

ApacheMimeTypesToDotNet on github

The output looks like this: ApacheMimeTypes.cs

Comments and Reactions

Idea: enhancing msdeploy with .skip, .skip-production, etc...

At work we use Jenkins (formerly Hudson) for continuous integration and for pushing out releases to our staging and production servers. It works well for this however the configuration for msdeploy is a bit of a cluster due to it being in a batch script – adding or removing directories to skip for deployment is a pain.

I realized today one way to solve this might be to support creating a .skip or .skip-TARGET (so in our case, .skip-production and .skip-staging) files. Than add a batch or simple program to scan the project directory (for script, can use “dir /S /B FILENAME” seems to be equivalent of UNIX “find FILENAME”) and add a skip for the directory. That way anyone can add the skip option and it is clear in source control what is and what is not getting pushed out.

It seems like a simple enough idea to implement…

Comments and Reactions

Yet another round on the ModelState PercentComplete() extension

So there were a few issues with the previous version – at least when I wanted to extend it for some custom calculations so here is yet another version:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
        public static int PercentComplete(this ModelStateDictionary modelStateDictionary, int? ScalePercentWithValueAsZero = null, int? MaxValue = 100)
        {
            int totalItems = 0;
            int validItems = 0;
            int percentComplete = 0;

            if (MaxValue.HasValue && (MaxValue.Value < 0 || MaxValue.Value > 100))
                throw new ArgumentOutOfRangeException("MaxValue must between 0 and 100!");

            if (modelStateDictionary.IsValid)
            {
                percentComplete = MaxValue.Value;
            }
            else
            {
                foreach (var item in modelStateDictionary)
                {
                    totalItems++;
                    if (item.Value.Errors.Count == 0)
                        validItems++;
                }

                if (totalItems > 0)
                    percentComplete = (100 * validItems) / totalItems;

                if (ScalePercentWithValueAsZero.HasValue)
                {
                    if (ScalePercentWithValueAsZero.Value >= percentComplete)
                    {
                        percentComplete = 0;
                    }
                    else
                    {
                        percentComplete = Convert.ToInt32(Math.Ceiling((double)(percentComplete - ScalePercentWithValueAsZero.Value) / (100 - ScalePercentWithValueAsZero.Value) * 100));
                    }
                }

                if (MaxValue.HasValue)
                {
                    percentComplete = percentComplete * MaxValue.Value / 100;
                }
            }

            return percentComplete;
        }

And if a model has a particularly complicated percentage complete calculation in which one needs to manually check some things and add to the total that can be done:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
        public int CustomPercentComplete(Func<int?, int?, int> PercentComplete)
        {
            // scale percent complete 50-100% as 0-100%
            int basePercentAsZero = 50;

            // actually, scale 50-100% as 0-91%
            int max = 91;            

            // call default PercentComplete
            int percentComplete = PercentComplete(basePercentAsZero, max);

            // implement here your custom percente complete for the remaining 9%

            return percentComplete;
        }

An example of calling the custom percentage complete calculator on your model:

1
        myModelInstance.CustomPercentComplete(ModelState.PercentComplete);

And if you need to call either calculator but your controller action doesn’t bind to an instance of the model you need to calculate the percentage on, you can add a method in your controller like this one (hopefully there is a better way – let me know if you know of one):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
        // Work around for getting percentage complete when in another action where the model is not the application
        // like so:
        // int percentComplete = MyPercentComplete(application);
        private int MyPercentComplete(MyApplication application)
        {
            return Convert.ToInt32(MyPercentCompleteAction(application).Content);
        }

        // Work around for getting percentage complete when in another action where the model is not the application
        // like so:
        // int percentComplete = Convert.ToInt32(MyPercentComplete(application).Content);
        private ContentResult MyPercentCompleteAction(MyApplication application)
        {
            return Content(application.CustomPercentComplete(ModelState.PercentComplete).ToString());
        }
Comments and Reactions

Another round on the ModelState percentage complete calculator

Update: The mathematics is wrong – not sure what I was thinking. See the updated version here.

So the prior version has a bug – it works fine if the scaling value is 50 however it fails at other values. Whoops! That has been fixed and now we have a MaxValue scaler too. The MaxValue scaler is useful if you want to calculate the percentage done as maxing out at say 91% because you want to manually calculate the remaining 9%.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
    public static class ModelStatePercentCompleteCalculator
    {
        // Very rough but reusable % complete calculator that is an extension so can be
        // called simply as ModelState.PercentComplete() in a controller action. Iterates
        // over the items in ModelState returns percentage complete where a complete field
        // is seen as having no errors. If the ModelState.IsValid returns true, the
        // model is 100% complete.
        //
        // ScalePercentWithValueAsZero: if you calculate the % done on a model and it says say 40%
        // really it is 0% than set ScalePercentWithValueAsZero=40 and it'll treat 40% as 0% and
        // scale it appropriately to 100 or maxValue
        //
        // MaxValue: if you want the maximum value to be less than 100 than set it here so you can
        // accomodate calculating to 100% based on some other criteria too (which you're
        // responsible for)
        public static int PercentComplete(this ModelStateDictionary modelStateDictionary, int? ScalePercentWithValueAsZero = null, int? MaxValue = 100)
        {
            int totalItems = 0;
            int validItems = 0;
            int percentComplete = 0;

            if (MaxValue.HasValue && (MaxValue.Value < 0 || MaxValue.Value > 100))
                throw new ArgumentOutOfRangeException("MaxValue must between 0 and 100!");

            if (modelStateDictionary.IsValid)
            {
                percentComplete = MaxValue.Value;
            }
            else
            {
                foreach (var item in modelStateDictionary)
                {
                    totalItems++;
                    if (item.Value.Errors.Count == 0)
                        validItems++;                   
                }

                if (totalItems > 0)
                    percentComplete = (MaxValue.Value * validItems) / totalItems;
            }

            if (ScalePercentWithValueAsZero.HasValue && ScalePercentWithValueAsZero.Value > 0 && percentComplete >= ScalePercentWithValueAsZero)
            {
                percentComplete = (percentComplete - ScalePercentWithValueAsZero.Value) / (MaxValue.Value - ScalePercentWithValueAsZero.Value) * 100;
            }
            else
            {
                percentComplete = percentComplete / 100 * MaxValue.Value;
            }

            return percentComplete;
        }
Comments and Reactions

When not to use AutoMapper

AutoMapper is great but it is both specialized, heavy duty and lacking in documentation. The lack of documentation can bite you: if you need to map two objects in different ways in different places things may not work as you expect. For example, say I have Mapper.CreateMap<MyObject, MyObject>() with a number of .ForMember(x => x.Something, opt => opt.Ignore()). Now if I create the same mapping elsewhere but with different .ForMember options the prior .ForMember mapping options persist into my current map. To fix this, one can do a Mapper.Reset() which will blow away the global mappings. But creating mappings is expensive. Food for thought.

Comments and Reactions

Updated percentage complete calculator for ASP.NET MVC

Update: there is a bug with this code. Please see this new post with updated code.

This version adds a baseline value so the percentage is scaled from that baseline (as 0) to 100%. This is to accommodate the case where the form starts out at say 50% complete. In this case, the call in the controller would be:

1
    x.PercentComplete = ModelState.PercentComplete(50);

Note this will cause some odd results if you pick a base value that isn’t truly a base value. In that case, the percentage calculation as the model is “completed” will increase, dip to zero at the false base value and then increase to 100 from there.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    public static class ModelStatePercentCompleteCalculator
    {
        // Very rough but reusable % complete calculator that is an extension so can be
        // called simply as ModelState.PercentComplete() in a controller action. Iterates
        // over the items in ModelState returns percentage complete where a complete field
        // is seen as having no errors. If the ModelState.IsValid returns true, the
        // model is 100% complete.
        public static int PercentComplete(this ModelStateDictionary modelStateDictionary, int? ScalePercentWithValueAsZero = null)
        {
            int totalItems = 0;
            int validItems = 0;
            int percentComplete = 0;

            if (modelStateDictionary.IsValid)
            {
                percentComplete = 100;
            }
            else
            {
                foreach (var item in modelStateDictionary)
                {
                    totalItems++;
                    if (item.Value.Errors.Count == 0)
                        validItems++;                   
                }

                if (totalItems > 0)
                    percentComplete = (100 * validItems) / totalItems;
            }

            if (ScalePercentWithValueAsZero.HasValue && ScalePercentWithValueAsZero.Value > 0 && percentComplete >= ScalePercentWithValueAsZero)
            {
                percentComplete = 100 / ScalePercentWithValueAsZero.Value * (percentComplete - ScalePercentWithValueAsZero.Value);
            }

            return percentComplete;
        }
    }
Comments and Reactions

AutoMapper and Mapper.Reset()

Did you know that AutoMapper’s Mapper.CreateMap<T1, T2>() persists even if you use different options on the mapping? So if you have this in one place in your code:

And then later this:

That second usage won’t map BrainSize! This is quite confusing… The cache makes sense but it would be more intuitive if the cache was sensitive to .ForMember() usage and other options so that the exact same mapping is cached not any mapping.

The solution is to use this (potentially both before and after your use of AutoMapper):

I haven’t read the source yet but this is a very non-intuitive aspect of AutoMapper. Basically on the level of a deal breaker almost in my opinion.

Comments and Reactions

ASP.NET MVC, ModelState and Simple Generic Percent Complete Helper Method

Say you have a number of models that are mostly comprised of plain old CLR objects and you need a rough percent complete calculator. Assuming you are using the standard ASP.NET MVC validation rules (via attributes), here is a rough helper method that can calculate the percentage complete for the model based on the number of fields with validation errors divided by the total number of fields.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
        public static int PercentComplete(this ModelStateDictionary modelStateDictionary)
        {
            int totalItems = 0;
            int validItems = 0;
            int percentComplete = 0;

            if (modelStateDictionary.IsValid)
            {
                percentComplete = 100;
            }
            else
            {
                foreach (var item in modelStateDictionary)
                {
                    totalItems++;
                    if (item.Value.Errors.Count == 0)
                        validItems++;                   
                }

                if (totalItems > 0)
                    percentComplete = (100 * validItems) / totalItems;
            }
            
            return percentComplete;
        }
Comments and Reactions