Wednesday, August 15, 2012

Ensuring a Unique Name for User Entities



        public static string GetUniqueName(string[] existingNames, string targetName)
        {
            int copyCount = 0;
 
            Regex reg = new Regex("^" + targetName + "(\\s?COPY\\s?\\d+|\\s?COPY)$");
            foreach (var name in existingNames)
            {
                if (reg.IsMatch(name))
                {
                    copyCount++;
                }
            }
            string copyName = targetName + " COPY" + (copyCount == 0 ? "" : " " + copyCount);
            return copyName;
        }


As you can see, I've hard coded COPY and the number scheme in the code above. It does a decent job of finding all names that have been previously copied and adding on another copy. Doing it this way prevents us from iterating over the existingNames collection more than once.

Monday, August 6, 2012

nHibernate and Table Value Parameters

nHibernate is a great tool for interacting with data. However, much of our data involves pretty huge parameters. Some of our queries can have ID counts in the thousands, and sometimes they select all of them at once and want us to do stuff with them. I was working on just this issue with a section of an app, and found a neat solution. Typically we pass large ID lists using a table valued parameter. Unfortunately nHibernate doesn't know about those. My solution was to create an XML element with all of the id's as sub elements. Then, in the named SQL I query them out of the XML into a table. 

In your method, you want to build your XML string. I didn't create an XML doc and add it that way. This is simple, so I used a string builder to create the xml by hand.

            StringBuilder sb = new StringBuilder();
            sb.Append("<StoreIds>");
            foreach (int storeId in storeIds)
            {
                sb.AppendFormat("<Id>{0}</Id>", storeId);
            }
            sb.Append("</StoreIds>");
Then I passed the xml string into my GetNamedQuery call.

                var query = Session.GetNamedQuery("MyAwesomeNamedQuery")
                    .SetParameter("userId", userId)
                    .SetParameter("condition", whereClause)
                    .SetParameter("storeIds", sb.ToString(), NHibernateUtil.StringClob);
                result = query.List<MyObject>().AsQueryable();
NOTE: The key to this is defining the third parameter of the SetParameter method as NHibernateUtil.StringClob. If you don't, nHibernate automatically truncates your string to 4,000 characters.

​The SQL statement was changed from using 


​       INSERT INTO @storeIds2
​       SELECT StoreId FROM Client.Store
​       WHERE StoreId IN (:storeIds)


To

 INSERT INTO @storeIds2
 SELECT T.x.value('.', 'int') ID
 FROM @storeIds.nodes('/StoreIds/Id') T(x)​
And that's all the SQL it takes to extract the ID's from the xml string we passed into a sql @table variable!

When I tested this code, I tested it with a db that held 11,500+ stores. All stores were passed perfectly.

Monday, July 30, 2012

Making a div fill the page height

I overheard a co-worker complain about some of our HTML in the office today. There's a grid that we have in a div that is pretty much always too short for its content. It would be great if we could get a div to fill the entire page, excluding our header content.

I figured it out. It's okay, except there doesn't seem to be a nice way of automagically aligning to the header height. You pretty much have to set the "top" property to the height of your header.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=windows-1250">
  <meta http-equiv="x-ua-compatible" content="IE=8">
 <style type="text/css">
  *{
   box-sizing: border-box;
  }
  .container{
   border: 3px solid red;
   margin: 5px;
   position: absolute;
   bottom:0px;
   top:0px;
   left:0px;
   right:0px;
  }
  .header{
   border: 3px solid green;
   margin: 5px;
   height: 200px;
   position: relative;
  }
  .tableContainer{
   border: 3px solid blue;
   position: absolute;
   margin: 5px 5px 5px 5px;
   bottom:0px;
   top:210px; /* Required for positioning. Could tie this with javascript to make it work */
   left:0px;
   right:0px;
   overflow: auto;
  }
  .tableMock{
   height:400px;
   background-color: #3399ff;
   width: 100%;
   color: white;
   text-align: center;
   font-weight: bold;
   font-size: 30px;
   font-family: arial, helvetica, 'san-serif';
   padding: 30px;
  }
 </style>
</head>
<body>
 <div class="container">
  <div class="header"></div>
  <div class="tableContainer">
   <div class="tableMock">
    The table here could be as tall or short as you want. If it is larger than the containing div, scroll bars will appear (x and y). Otherwise no matter how large the screen gets, the containing div will always take up as much of it as it can.
   </div>
  </div>
 </div>
</body>
</html>

Thursday, July 26, 2012

Dates, Time Zones, JSON and MVC

Ran into an issue with sending a date to MVC. Needed to have a date within a certain range, and I'm using a Telerik date picker to do the date selection. Worked great, here in California. Didn't work so well in India.

When you create a date in Javascript, it appends the time zone.

Sat Jan 01 2000 00:00:00 GMT-0800 (Pacific Standard Time)

When you send it via ajax, it's converted to UTC, or 

"2000-01-01T08:00:00.000Z"

This works fine for time zones with a negative UTC offset. For time with a positive offset, this can set their selection outside of the date range,

"
1999-12-31T18:30:00.000Z"

I think this could be a fix...

myDate = new Date("1/1/2000")
Sat Jan 01 2000 00:00:00 GMT-0800 (Pacific Standard Time)
myDate.setMinutes(-myDate.getTimezoneOffset())
946684800000
myDate
Fri Dec 31 1999 16:00:00 GMT-0800 (Pacific Standard Time)
myDate.toJSON()
"2000-01-01T00:00:00.000Z"

And with my time set to IST


var myDate = new Date("1/1/2000")
undefined
myDate
Sat Jan 01 2000 00:00:00 GMT+0530 (India Standard Time)
myDate.setMinutes(-myDate.getTimezoneOffset())
946684800000
myDate
Sat Jan 01 2000 05:30:00 GMT+0530 (India Standard Time)
myDate.toJSON()
"2000-01-01T00:00:00.000Z"

Thursday, May 17, 2012

Telerik MVC 3 Controls Injected Dynamically via Template

So... I want to use the Telerik MVC controls (numeric textbox, datepicker, etc) in a template, but rendering the template breaks Telerik's code. Also, I don't want to use an ID to identify them, since I'm using them for each child item. What's a guy to do?

Well, I looked at the response from my partial view, and found that Telerik is initializing them each in their own javascript in the partial view.

So, I pulled out that logic and did it myself.


            $("input[name=editorDate]", control).tDatePicker({
                format: 'M/d/yyyy',
                minValue: $this._model.startDate,
                maxValue: $this._model.endDate
            });
 
            $("[name=percentValue]", control).tTextBox({
                val: 0,
                step: '1',
                minValue: 0,
                maxValue: 100,
                digits: 2,
                groupSize: 3,
                positive: 0,
                negative: 0,
                text: 'Enter value',
                type: 'percent'
            });
            $("[name=amountOffValue],[name=priceValue]", control).tTextBox({
                val: 0,
                step: '1',
                minValue: 0,
                maxValue: 1000000,
                digits: 2,
                groupSize: 3,
                positive: 0,
                negative: 0,
                text: 'Enter value',
                type: 'currency'
            });

 I call these functions after I instantiate a template, and it works great! There are some .css issues with the z index of Telerik's controls, but some finely applied css classes fixed that. 

I didn't see anything on the internet that solved this issue, so I posted this fix. Hope this helps someone!



F~

Tuesday, May 15, 2012

The difference between a good source control system and TFS 2010

So... We've switched to TFS from SVN. I'd heard good things about it, and was looking forward to using it. Years ago I'd used TFS and I really didn't have much of an issue. I liked the shelf set feature. It made it very easy to put aside changes I'd made to work on a different task, great for urgent bug fixes.

The company I work for implemented a new policy. Gated commits. I'd done automated code reviews when using Hg and FogBugz, and that was wonderful, so I was looking forward to TFS' implementation. I couldn't have been more wrong.

With gated checkins, you create a shelf set. Then you create a "code review" task. A part of the code review task creation process is to manually link your shelf set to the task. This consists of pasting the string name of the shelf set into the text field "shelf set name". Seriously. If you forgot to copy the name when you created the shelf set, go back and open its details to get its name.

Then you assign it to the reviewer and wait.

Or, continue working.

If you continue working, you must shelve your new changes to pull back your old changes. None of this is actually enforced. You just can't commit unless you have an approved code review task. So, you shelve your new changes, unshelve the approved set, get latest, test the code, and commit.

Now you see lots of changes that aren't even changes. Right-click the files in your Pending Changes window, and many of them show that they're identical. TFS can't tell when a file is identical. Wastes time, since I like to review my changes from time to time. Make sure I didn't change anything I didn't mean to change.

Here's a worst case scenario.

Say, a dev is working on code. They need you to take over. So, you shelve the work you're working on, take their shelf set, apply it. Fix it, then try and merge your previous shelf set.

Yeah, you're pretty much screwed.

It won't let you.

You do what I did, and search Google and find
http://blogs.infosupport.com/the-how-and-why-behind-tf203015-lt-file-gt-has-an-incompatible-change-while-unshelving-a-shelve-set/

Yep. You need a special tool to merge shelfsets when your working copy has changes. Every other (decent) source control has ways of doing this. So, you need to get a VS power tool, and hit the VS command line to do it in TFS. Hot, huh...

Even still, it only brings over your "merges" to your working copy. Your shelfset will always and forever be unuseable.

Shoulda stuck with FogBugz...

Monday, May 14, 2012

Posting a JSON object graph to MVC


Passing an entire json object graph via jQuery to an MVC action method.

This covers string arrays, int arrays, dates, etc. MVC is actually pretty good at this. You just can't use jQuery's post method, as it sends the JSON object as a form post.


<script language="javascript" type="text/javascript">
    var testModel = {
                    NumberList: [100, 200, 300, 400 ],
                    NumberArray: [101, 102, 103],
                    StringArray: ["a""b""c""d"],
                    Date: new Date(),
                    Boolean: true
                };
    $(function () {
        $("#submit").click(function () {
            var model = {
                Id: 123,
                Name: "foo",
                ProductIds: [1, 2, 3, 4, 5],
                StoreIds: [11, 12, 13, 14],
                TestModel: testModel
            };
 
            $.ajax({
                url: "/Test/SaveInvoice",
                data: JSON.stringify(model),
                success: function (data, xhr) {
                    alert(data);
                },
                dataType: "json",
                contentType: "application/json",
                type: "post"
            });
        });
    });
</script>