Coordinating AJAX Requests with jQuery

I was working on a requirement where I had to run the same AJAX post request multiple times, in this case to close out multiple trades in a system. I then had to route to another page if successful, or display an error message if an error occurred.

I started by looping through the AJAX calls using jQuery.each, but I quickly realized that this wasn’t going to work without using a global boolean within the .ajax success callback. I then found a Stack Overflow post that highlighted that jQuery.when can be used to group AJAX calls as “deferred objects,” allowing you to coordinate multiple calls and then run a function after they are all complete.

First I wrapped my jQuery.ajax call in a function.

function postClose(closeObj, reroute)
{
    return $.ajax({
        type: "POST",
        url: "/Trades/Close",
        data: closeObj,
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            alert(errorThrown);
        },
        success: function (data) {
            if (reroute) {
                window.location.href = "/Trades";
            }
        }
    });
}

Then I added the following code to a click handler for the “Close Selected” button on my page. This builds up the defers array with deferred objects, which are essentially calls to the postClose method, above. Then it uses .when to execute the AJAX calls. Once they are all complete, the function in .done executes the final AJAX call, which routes the user to another page upon success.

    
$('#multi-close-btn').click(function () {
    var trades = closeCombinedTrades.GetSelectedItems();
    var defers = [];
    var close;
    $.each(trades, function (i, val) {
        close = new Close(val.value, closeMethod.GetValue(), closeCloseDt.GetDate(), closeTradeStatus.GetValue());
        defers.push(postClose(close,false));
    });
    $.when.apply($, defers).done(function () {
        close = new Close($('#Trade_TradeId').val(), closeMethod.GetValue(), closeCloseDt.GetDate(), closeTradeStatus.GetValue());
        postClose(close, true);
    }).fail(function () {
        alert("An issue occurred while closing the trades. Please contact the administrator.");
    });
});
Advertisements

Dear Git, Stop Tracking My Bin Folders

Visual Studio 2013 was the first version to come pre-packaged with Git. When you create a new solution and elect Git as source control, it kindly places a new .gitignore file in your solution root folder. This tells Git to ignore a bunch of different files that you don’t want it tracking, including the bin and obj folders.

Any Visual Studio version prior to 2013 does not do this for you. So when I created a new solution in VS 2012 and used Git as source control manually through the command line, Git kept tracking all of the bin and obj folders. I ended up pushing a bunch of unnecessary files with every update. A trivial issue, but super annoying. Here’s how to fix it:

  1. Copy the .gitignore file from another solution that uses Visual Studio 2013 or higher, or copy the text from GitHub.
  2. Paste the file into your solution root folder, or, if you copied from GitHub, use the command line to create a new .gitignore file in your solution root and append the text to it.
  3. Finally, enter the following commands. This will reset your repo and tell Git to start ignoring the files in .gitignore.
git rm --cached -r .
git add .

NHibernate: The Upsides and Downsides of Cascading and Not Lazyloading

I’m nearing the end of my first MVC project using NHibernate. It’s a powerful ORM with a ton a features, but it was hard to figure out how to implement simple, safe, and performance-friendly reads & writes to the database, which is funny, considering that’s what an ORM is supposed to do. I attribute this difficulty to NHibernate’s one obvious flaw: it has no official documentation. The two new features/extensions that we used the most, Fluent NHibernate and QueryOver, have no official resources, so learning was often a matter of both searching through Stack Overflow posts for answers and trial & error.

Cascade.SaveUpdate()

One NHibernate feature, Cascade.SaveUpdate, was very effective but also had dire consequences if used incorrectly. In the HR section of our application, we use two classes, Employee and User. User has properties like name, username, password, etc.–pretty much a standard system user class. Employee contains User as a property, as well as details like hired date, address, and emergency contact. We had a view with form fields that encompassed both Employee and User properties, so I decided to use the Employee class as the model for the view, and then use Cascade.SaveUpdate in the Employee map to persist changes to the User table, like this.

Employee class:


public class Employee { 
public Employee() { } 
public virtual int EmployeeId { get; set; } 
public virtual User User { get; set; } 
[Required(ErrorMessage = "Hire date is required.")] 
public virtual DateTime? HiredDate { get; set; } 
public virtual DateTime? TerminatedDate { get; set; } 
public virtual bool? IsAttorney { get; set; } 
public virtual DateTime? ModifiedDate { get; set; } 
public virtual DateTime? CreatedDate { get; set; } 
public virtual IList<Rate> Rates { get; set; } 
//etc.
}

Employee map:


public EmployeeMap() { 
Table("employee"); 
LazyLoad(); 
Id(x => x.EmployeeId).GeneratedBy.Identity().Column("employee_id"); 
References(x => x.User).Column("user_id").Not.LazyLoad().Cascade.SaveUpdate(); 
Map(x => x.HiredDate).Column("hired_date"); 
Map(x => x.IsAttorney).Column("is_attorney"); 
Map(x => x.ModifiedDate).Column("modified_date"); 
Map(x => x.CreatedDate).Column("created_date"); 
HasMany(x => x.Rates).KeyColumn("employee_id").Not.LazyLoad();
//etc.
} 

Saving data to both the Employee and User tables was then very simple: just do an update with your NHibernate database session. It’s very important, though, that you think through every use of Cascade.SaveUpdate. In our application, one of the developers (it could have been me) copied and pasted a property from a map that contained Cascade.SaveUpdate into another map. Later on in QA, we experienced a bug where Trades in the system were losing their respective Clients. This was not good; it broke one of the most critical links for database integrity. We discovered it was a result of that incorrectly copied Cascade.SaveUpdate. Changes were cascading on our Client entity in an area of the application where they should not have been, and as a result NHibernate was doing rogue updates to the database, including nulling out foreign keys for Client in our Trades table. (As a side note, this foreign key field should have been set to not null, which we then corrected.)

When determining if you should use a cascade, at least in terms of an MVC application, you need to look at the associated view for that class. Do you actually need to persist changes for that class, based on your view? Is there any chance that the object will be instantiated, but it will contain no actual data? This happened to us as well. We had a ContactInfo class, which held details for clients like phone number and email, but it was not required in our form. If no contact details were entered, NHibernate would save a row in the associated table with all null values, and soon we had hundreds of null rows clogging up that table.

Not.LazyLoad()

In the Employee map above, you can see that I have several properties set to Not.LazyLoad, including User. If I didn’t have User set to Not.LazyLoad, I would get a lazyload exception when I pull the Employee from the database and try to access the properties of User in my view. Using Not.LazyLoad makes sense here; whenever we select an Employee, we also want its associated User details. However, an important caveat is that NHibernate pulls properties with Not.LazyLoad using separate select statements. Normally you would write a SQL statement that had an inner join between the Employee and User tables, but NHibernate performs one select from Employee, and then another Select from User.

This is not a big deal in our example, but it quickly becomes a problem when you retrieve a collection of entities that have many nested properties with Not.LazyLoad. We had this occur on a view that provides the list of active Clients in the application. When I opened SQL Profiler and ran the NHibernate query that pulled back the collection of Clients, I saw over a hundred select statements! Again, this was because multiple nested classes under Client had properties with Not.LazyLoad set in their maps.

In order to avoid all of the separate selects and simulate the proper SQL join, I ended up using QueryOver. Again, this is where NHibernate gets weird, because there are several options for querying (QueryOver, Criteria, HQL), and not a single option is officially recommended or documented. QueryOver is nice because it very LINQ-like and, as another developer on the project put it, doesn’t litter the project with “magic strings.” The query for Employee plus its User in QueryOver looks like this.


IEnumerable<Employee> employees;

//establish a session using your factory manager class and wrap it in a using statement
using (ISession session = NHibernateSessionFactoryManager.Factory.OpenSession())
{
User userAlias = null;
employees = session.QueryOver<Employee>()
.JoinAlias(e => e.User, () => userAlias)
.List();
}

//do stuff with employees, like return it as a model

In the example above, I use JoinAlias to specify an inner join from Employee to User. This brings up a final import note on QueryOver. If you have a property in a map with Not.LazyLoad, and you do not provide a JoinAlias for it in your query, NHibernate will perform a separate select. Taking the query above as an example, say my User class has a nested class of its own, PasswordHistory. If, in the User map, I place a Not.LazyLoad on the LoginHistory property, NHibernate will perform a separate select for it. In profiler, you will end up seeing two select statements: one for the Employee with a join to User, and another select from the LoginHistory table for the specific User. To combine all of this into one select statement, you would do the following.

User userAlias = null;
LoginHistory lhAlias = null;
employees = session.QueryOver<Employee>()
.JoinAlias(e => e.User, () => userAlias)
.JoinAlias(e => e.User.LoginHistory, () => lhAlias)
.List();

In summary, I have mixed feelings about NHibernate. I would probably use it on another application, but that’s mainly because I have invested the time into understanding its features. If I were new to all of the major (non-micro) ORMs for .NET, I might choose Entity Framework instead, because it has official documentation and it is not open source like NHibernate. But that is a subject for a different post.

The Dangers of Digital Ephemera

westmyspace

I logged into Myspace recently. It had been years, but I still got my username and password on the first try. The site had completely changed, and although several of my photos were still there, I couldn’t find my old messages. In fact, I couldn’t even find the inbox link.

This was a sad event. Painful, in a nostalgic way. Not because I was expecting new messages from friends on a long forgotten social media site, but because I was on the hunt for old messages from my sister, who died six years ago. Previously I had searched Gmail for her various nicknames (including “sushi,” which brought up a ton of spam) but found nothing. I realized that we didn’t really communicate via email. Most of our letters back and forth were through Myspace. This was before Facebook got big, and besides, we both didn’t like Facebook that much.

When Myspace rebranded after losing the battle to the new generation of social media giants, they deleted all the old messages and posts. So those letters to and from my sister are gone forever. This is also true of my old love letters to my girlfriend. I didn’t bother to forward them from my college email account to Gmail.

The vast majority of people do not write letters anymore, nor do they print real photographs. Technology has made these things much easier, and we communicate more, but it comes with a hidden cost, or danger. All the important artifacts of our lives now exits as bits, hidden on servers owned by companies that are, in the big picture, tenuous. Myspace may have had a shorter shelf life than Facebook, but they both ultimately have a shelf life. Even the free service upon which I write this post, WordPress, may go under at some point.

Maybe free is the key word there. All of the services mentioned thus far are free, and when things are free, you can’t trust that they will be preserved. I have bit more faith in cloud storage, like Dropbox, since I actually pay for it. I guess one lesson I’ve learned from losing those Myspace messages is to back up your data. Even if I had to copy them as text, and even if they were stupid, silly, or banal, it would have been worth it.

Something doesn’t sit right with the transfer of our memories from physical materials to bits. I have a leather-bound book of photos, letters, and other ephemera from my grandfather. I open it up now and then and can actually touch the old, faded letters and hold the photos up to the light. I wonder what I will leave behind. A WordPress page with a “blog not found” message on it? A Facebook profile with the majority of friends deceased? We will have Facebook graveyards.

I think I’ll take my film camera out of its box, and start printing out some old emails.

Mia & Feline Kidney Disease

mia-cat-vermont

Mia, enjoying the outdoors in Vermont this summer

It just so happened that I caught Miley Cyrus’ performance on SNL last night, which focused expressly on mourning for her dead pets. While this was likely bizarre for most viewers, it was eerily poignant for my girlfriend and I. On Monday, we had to put down our cat, Mia, whom we had for 13 years. We hardly ever watch SNL, and definitely would not tune in especially for Miley, so it was a uncanny coincidence to say the least.

Every cat owner probably says this, but Mia was a special cat. She did all the good things and none of the bad: she greeted visitors by jumping on their laps (especially boys–she was a flirt), she would let us hold her like a baby without a fuss, and she never once scratched or swiped at anyone. She also had an amazing ability to seek you out and comfort you when you were feeling depressed.

So it was especially difficult when, two and a half years ago, she was diagnosed with kidney disease. Up until that point, she had been a normal, healthy cat, whose only major health problem was a result of a brawl with another cat in the alleys of Berkeley, CA. The vet said that, due to the state of her kidneys, she would live anywhere from a few weeks to maybe six months, and we were devastated. It’s amazing how it easy it is to take a healthy cat or dog for granted.

Mia’s daily treatment consisted of three pills as well as a subcutaneous injection of electrolyte-infused fluids. At first, it was daunting to think of doing this every day. I think most cats hate taking pills, and I can say without a doubt that they despise getting poked with needles. Mia quickly learned to run from the sound of the plastic needle cover cracking open, and we had to kind of pin her down to do the injection while she grabbed the edge of the couch like a rock climber.

Things got easier as the months went on, though. We learned better ways of giving her her pills, like diluting them in a touch of warm water and mixing them into her food. She also got used to the injections, and I think started to associate them with feeling better. It helped to wait until a calm time later in the night, when we were all in bed, and then poke her when she was relaxed. One guy we met who had a cat with kidney disease swore that putting on 90’s hip-hop and R&B helped the process. This was actually the only person we met who had cared for a cat with kidney disease, highlighting how it can be an isolating experience.

Mia continued on, living for the most part as her normal self, for nearly five times her original six month estimate. While this should give hope to owners who just received the diagnosis, it’s important to note a few things. We spent thousands of dollars on visits to the vet, medication, and supplies to make it possible for Mia to live over two years. We also both worked from home, which made it much easier to give her the attention she needed, especially when one person traveled. Our only trips together were to upstate New York and Vermont, where we could take Mia with us.

Orienting our life around Mia made it all of the more difficult when she died. Losing a pet is always a terrible experience, mostly because our relationship with pets is almost always all positive and filled with love, as opposed to human relationships, which are almost always more complicated and mixed. Since our daily routine and to extent careers were dictated by Mia’s condition, we have a big “now what” that we are dealing with after her death. It’s a bit like losing a child, having watched my mother go through that process.

I wanted to give a shout out to a few people who helped us along the way. The first is Tanya’s site dedicated to feline kidney disease, which is the best internet resource on FKD. It’s one of those awesome plain HTML sites with no gimmicks that the internet was created to support. The second is the Bidawee animal hospital here in NYC, who cared for Mia throughout her illness, and even sent us a condolence card filled with sincere messages from the staff. Finally, Doctor Foster and Smith is a great option for getting more affordable medical supplies and medications for cats and dogs.

For all those with a cat diagnosed with kidney disease: fear not, as with the right care, you will hopefully have your cat for as long as we had Mia!

The Beauty of Skyrim

I think I’ve always loved RPGs. It was just that 20 years went by without playing one.

It started with King’s Quest. I remember booting it up from DOS. Amazing a world could emerge from all that darkness and solitary blinking.

kings-quest

With the help of my teenage stepbrother, I was able to understand how games are played “these days,” and I downloaded Steam. He recommended Skyrim, and around 1am one night, I finally purchased it. I remember starting the game, and, after the initial scene, realizing that I could go wherever I wanted to. I was completely blown away. Things had come so far in 20 years. During this initial period I actually woke up in the morning excited for the day because of the game.

I’ve now played Skyrim for 65 hours, and along the way I’ve been collecting screenshots. I don’t have the best gaming laptop, nor can I even run the game with anti-aliasing, but I still admire the beauty of Skyrim.

2015-06-24_00001

2015-06-26_00001

2015-09-11_000012015-09-14_000012015-09-16_000022015-09-16_000042015-09-21_00001

Mapping NYC Bike Accidents

Not me. I'm not that dapper

Not me. I’m not that dapper

I was riding my bike through downtown Brooklyn, at the intersection of Jay and Tillary, when a large SUV made a right turn into me. Due to the angle of impact and maybe just luck, I came away with just some minor bruises. It was the first time I had been hit by a car while riding in NYC, though, and it changed the way I moved through the city streets. I am more cautious and a little paranoid now, especially since the SUV driver decided to be an asshole and just drive away.

About a month later I pulled up to the same intersection and saw a guy on the ground, his bike next to him, front wheel still spinning, and people gathering around to help him. He had been hit literally in the same exact spot I had. As I rode away, I thought that it would be great to see a map of dangerous biking zones in the city.

Fortunately, NYC OpenData has a dataset from the NYPD on motor vehicle collisions. I hooked it up to Google Maps and voila! Check it out here.

My goal for this was to provide a way for people to evaluate their daily commutes and maybe find safer routes. Data can be weird, though, and what I see a lot on the map are crashes along the common bike paths, which is probably because that’s where the bikers are (duh). I’m not sure if taking alternate routes that are off the bike paths is any safer. Maybe there is still some valuable info in there though.

I’d like to expand on this map and add functionality. It’s on GitHub, if you’re interested in helping.