In a recent project, I was working on an MVC View that displayed a list of records with Edit and Delete buttons on them. This project was using the Blueprint CSS framework as well as Jquery-UI. Having both of my Edit and Delete buttons in the same cell was forcing one of them onto the next line, like so:
Here is the Html.Grid code I was using:
The problem here is evident, who wants buttons stacked like that? Now a simple fix may have been to just apply some new CSS properties to better control default width, margin, or padding of these elements. But that solution doesn't look as pretty in code and adds unneeded weight to the page. The Blueprint span classes do not go smaller than "span-1", so controlling the content through Blueprint was also out of the question. I wanted to give my 'Options' table header cell the attribute colspan a value of 2. At first I wasn't sure of the best way to do this and after a few searches through Google and Stackoverflow I still didn't know the best route. A coworker pointed out that I should probably start with the Attributes method, and a solution was born:
The solution was to set the HeaderAttribute with a colspan of 2 and then on the next column, override the Header() method with an empty string. Usually you would place a <th> tag in the Header string, leaving it empty keeps it from rendering that column header. Voila! You'll notice I did still have to add width attributes to the content cells, that was unfortunate however I still feel good about the solution.
Tracker is a free, award winning, agile project management tool that enables real time collaboration around a shared, prioritized backlog.
- http://www.pivotaltracker.com/
On top of that, its a refreshing break from our previous project management tool, Target Process. While Target Process is no doubt a powerful, robust, agile project management solution; it was a bit much sometimes. Pivotal Tracker is more asthetically pleasing as well as easy to use.
Unfortunately, there wasn't a lot of help out there for integrating our .Net websites with their API. The Pivotal Tracker API integration page does feature an exmple by Michael McKerlie here. This was a decent start, so I've added to it and in turn answered some of the questions posted in his comments. Behold, my first contribution to Google's Open Source project hosting - http://code.google.com/p/pivotal-tracker-api/. Here you will find some great wiki pages on how to integrate Pivotal Tracker API for .Net into your solution as well as planned features.
We currently use the API to integrate our user Feedback form with our Pivotal project. This way, when a user submits feedback, it enters our project queue as a bug so that we have to take action on it. It seems to work good in our enviornment, but thats probably because we have closed channel of users - I'm not sure I would recommend giving a public site this functionality. Opening your Project Management queue to spam-bots would probably be a bad thing.
Two things I look forward to adding in the near future include examples for using with Asp.Net MVC and the ability to Export project stories during a build (NUnit) for backup. Check back here or the project site often for updates.
Have you ever had a time when you were working with an ICriteria to return a specific group of data and found that it just wasn't returning what you wanted and you weren't sure why. In the past when I've had to troubleshoot troublesome SQL queries I would open up SQL Server Profiler to see what might be happening behind the scenes that could be messing up what is being returned. This can be a little bit troublesome when dealing with NHibernate. NHibernate generates the SQL in such a fashion that it becomes rather difficult and time consuming to make these queries readable after looking at their output withing SQL Profiler. Here is an example of a query from profiler, generated by NHibernate:
SELECT this_.id as id49_2_, this_.part_number as part2_49_2_, this_.quantity as quantity49_2_, this_.manufacturer as manufact4_49_2_, this_.part_name as part5_49_2_, this_.description as descript6_49_2_, this_.requested_date as requested7_49_2_, this_.notes as notes49_2_, this_.status as status49_2_, this_.cancelled_date as cancelled10_49_2_, this_.location_user_id as location11_49_2_, this_.customer_id as customer12_49_2_, locationus3_.person_id as person1_40_0_, locationus3_.name_first as name2_40_0_, locationus3_.name_middle as name3_40_0_, locationus3_.name_last as name4_40_0_, locationus3_.name_suffix as name5_40_0_, locationus3_.phone_number as phone6_40_0_, locationus3_.fax_number as fax7_40_0_, locationus3_.email_address as email8_40_0_, locationus3_.job_title as job9_40_0_, locationus3_1_.password as password41_0_, locationus3_1_.is_active as is3_41_0_, locationus3_1_.username as username41_0_, locationus3_1_.location_id as location5_41_0_, locationus3_1_.shopping_cart_id as shopping6_41_0_, customer1_.customer_id as customer1_72_1_, customer1_.name as name72_1_, customer1_.created_by as created3_72_1_, customer1_.dt_created as dt4_72_1_, customer1_.po_prefix as po5_72_1_, customer1_.is_active as is6_72_1_, customer1_.po_required as po7_72_1_, customer1_.enforce_order_multiples as enforce8_72_1_, customer1_.order_alert_recipient_id as order9_72_1_, customer1_.order_alert_dollar_amount as order10_72_1_, customer1_.show_on_hand as show11_72_1_, customer1_.is_central_billing as is12_72_1_, customer1_.display_promatch_number as display13_72_1_, customer1_.site_id as site14_72_1_ FROM quote_requests this_ left outer join people locationus3_ on this_.location_user_id=locationus3_.person_id left outer join dbs_users locationus3_1_ on locationus3_.person_id=locationus3_1_.person_id inner join customers customer1_ on this_.customer_id=customer1_.customer_id WHERE customer1_.site_id = @p0 and (this_.status = @p1 or (this_.status = @p2 and this_.cancelled_date < @p3)) and this_.status = @p4
My question was, what in the world is this query actually doing? In comes Instant SQL Formatter. Once you past the above generated SQL into Instant SQL Formatter and press 'Format SQL' it gives you this:
SELECT this_.id AS id49_2_, this_.part_number AS part2_49_2_, this_.quantity AS quantity49_2_, this_.manufacturer AS manufact4_49_2_, this_.part_name AS part5_49_2_, this_.DESCRIPTION AS descript6_49_2_, this_.requested_date AS requested7_49_2_, this_.notes AS notes49_2_, this_.status AS status49_2_, this_.cancelled_date AS cancelled10_49_2_, this_.location_user_id AS location11_49_2_, this_.customer_id AS customer12_49_2_, locationus3_.person_id AS person1_40_0_, locationus3_.name_first AS name2_40_0_, locationus3_.name_middle AS name3_40_0_, locationus3_.name_last AS name4_40_0_, locationus3_.name_suffix AS name5_40_0_, locationus3_.phone_number AS phone6_40_0_, locationus3_.fax_number AS fax7_40_0_, locationus3_.email_address AS email8_40_0_, locationus3_.job_title AS job9_40_0_, locationus3_1_.password AS password41_0_, locationus3_1_.is_active AS is3_41_0_, locationus3_1_.username AS username41_0_, locationus3_1_.location_id AS location5_41_0_, locationus3_1_.shopping_cart_id AS shopping6_41_0_, customer1_.customer_id AS customer1_72_1_, customer1_.name AS name72_1_, customer1_.created_by AS created3_72_1_, customer1_.dt_created AS dt4_72_1_, customer1_.po_prefix AS po5_72_1_, customer1_.is_active AS is6_72_1_, customer1_.po_required AS po7_72_1_, customer1_.enforce_order_multiples AS enforce8_72_1_, customer1_.order_alert_recipient_id AS order9_72_1_, customer1_.order_alert_dollar_amount AS order10_72_1_, customer1_.show_on_hand AS show11_72_1_, customer1_.is_central_billing AS is12_72_1_, customer1_.display_promatch_number AS display13_72_1_, customer1_.site_id AS site14_72_1_ FROM quote_requests this_ LEFT OUTER JOIN people locationus3_ ON this_.location_user_id = locationus3_.person_id LEFT OUTER JOIN dbs_users locationus3_1_ ON locationus3_.person_id = locationus3_1_.person_id INNER JOIN customers customer1_ ON this_.customer_id = customer1_.customer_id WHERE customer1_.site_id = @p0 AND (this_.status = @p1 OR (this_.status = @p2 AND this_.cancelled_date < @p3)) AND this_.status = @p4
Then, all you have to do is DECLARE your parameters, set their values and run the query and view the output.
This is going to save me a ton of time during development and troubleshooting troublesome queries.
I will be presenting at the West Michigan .NET University on April 4th about ASP.NET with my friend Carl Furrow. The goal of the .NET University is to educate beginners about .NET. Specifically our discussion will bring you up to speed on ASP.NET, Microsoft’s Web platform. But don’t miss out on some of the other great sessions by some really great people from West Michigan.
We even get a cool badge!
Do you use StructureMap? NHibernate? Good. Have you tried using them with WCF? Yeah, me too. It didn’t go so well for me either. StructureMap, NHibernate, and WCF all worked fine. They did nothing wrong. I guess it was my journey to find out how to make them work together that felt painful. So how did I do it? I Googled it, of course. This led me to read a few varying accounts of how others made it work. Each scenario was different than mine in some significant way (of course) so they each gave me a piece of the whole picture.
First, I tackled with getting StructureMap working with WCF. This wasn’t nearly as problematic as it could have been. Thanks to Jimmy Bogard for writing up a walkthrough on how to make it work.
Jimmy’s post: http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/07/29/integrating-structuremap-with-wcf.aspx
Second, I had to get NHibernate opening sessions per request, which is different than a typical request in an ASP.NET application. This is where the trouble started. I followed a second post by Jimmy discussing this very issue.
Jimmy’s Other Post: http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/09/16/integrating-structuremap-and-nhibernate-with-wcf.aspx
So this got me further but eventually I began having weird issues. As in things working and then not working out of nowhere. Eventually after some frustration I began to suspect a threading issue. Sure enough, In a discussion on the StructureMap User Group, Jimmy mentioned that the solution may have threading problems. Awesome.
So, Googling I found another post by Frank-Leonardo Quednau who described a different approach.
Frank-Leanardo’s Post: http://realfiction.net/?q=node/167
Using the OperationContext.Current.InstanceContext.Extensions collection to store the NHibernateSession because the instance context only lives once for the service request. The idea being that you can open the Session when the InstanceProvider starts and close the Session when it release the service instance.
I followed the post and nearly got it working pretty well, but my project structure didn’t work very well with how it was setup. I don’t know if it’s because of my UnitOfWork or FluentNHibernate or what, but things just didn’t line up for me.
So here is what I ended up doing. (Sorry for the images. It looked better than our style for inline code. Don’t worry though all the code will be posted at the end. And Here.)
1. In my WCF application I created my StructureMap Registry. The registry wires up all my object instances at runtime. You’ll notice that the ServiceinstanceContextBuildPolicy; this is a custom build policy (caching policy is a better term maybe) which instructs StructureMap to cache it’s objects in a special way for WCF (per service instance). More on that later. Everything else here is pretty run of the mill. I create an NHibernate Session from my SessionSource (fluentNHibernate) which sets up my UnitOfwork and creates an instance of my Repository to do the Querying, Loading, Saving of my domain objects.
2. To setup this registry, I needed a BootStrapper. The BooStrapper is initializing StructureMap with my registries, in this case I only have one, but I could add more later.
3. To get call the BootStrapper I needed a StructureMapServiceHostFactory.
4. The StructureMapServiceHostFactory creates instances of…StructureMapServiceHosts. The StructureMap Service Host isn’t necesarily the best name for it. The Service Host is responsible to add the InstanceCreationService Bahavior, when a Service Host is opened.
5. The InstanceCreationServiceBehavior wires up two things. First it wires up the InstanceProvider which does the “heavy lifting” for our UnitOfWork. Things like Initialize, Commit, RollBack, and Dispose.
Notice that after the finally block, I check for the InstanceContext.Extensions to Remove the InstanceCreationExtension. Doing so fires the Detach Event which calls the DisposesAndClear() method on our StructureMap Cache living inside of the InstanceCreationExtension. So where is that InstanceCreationExtension added? Well, in our InstanceCreationInitializer of course! :)
5.a. The InstanceCreationBehavior also wires up an InstanceCreationInitializer which enables the Custom Cache Policy from above, to work. The Initialize method adds a new InstanceCreationExtension which is being created with a null StructureMap InstanceCache as you see below.
6. So the InstanceCreationExtension is quite important to this whole thing working properly. Its job is to create and destroy the StructureMap InstanceCache when it is added and removed from the service instance.
7. Whew! so now that we have all that in place we still need StructureMap to use that Extensions collection to store the InstanceCache. The InstanceCache in structureMap is where it stores all of it’s Instantiated Objects. So in this case (refer to the Registry) the UnitOfWork and NHibernateUnitOfWork are being stored there but I want a new one every time a new Service Instance is started.
RED: This is where I check for an existing InstanceCreationExtension which stores our StructureMap InstanceCache. I learned the hard way that many of these things can be null so you’ll see I do a lot of checking for null instances of many things here. YELLOW: This is where the InstanceCache which will be returned to StructureMap is being set. GREEN: This is where I’m adding that InstanceCache back to our InstanceCreationExtension so that when StructureMap calls this Cache Policy again it will find it.
RED: This is where I check for an existing InstanceCreationExtension which stores our StructureMap InstanceCache. I learned the hard way that many of these things can be null so you’ll see I do a lot of checking for null instances of many things here.
YELLOW: This is where the InstanceCache which will be returned to StructureMap is being set.
GREEN: This is where I’m adding that InstanceCache back to our InstanceCreationExtension so that when StructureMap calls this Cache Policy again it will find it.
Eventually I have a Cache to return to StructureMap so it can grab the object that was being requested. For us, the requested instanceCache will have our NHibernateUnitOfwork on it.
So…Given a ServiceContract that looks like this:
I can do things like this:
My Repository will be injected by StructureMap and will have the UnitOfWork on it to execute the query against the database. The next time this Service is called a new UnitOfWork will be created and we’ll start the whole thing all over again.
If you’ve read this much I appreciate the time you’ve taken sitting through this lengthy post. So as a reward, please feel free to download the code and play around with it. I know it’s not perfect and I’m very open to the possibility that there is a better way to do this. If you have any suggestions please let me know. I appreciate any feedback. As long as it’s good feedback. :)
DOWNLOAD: GET THE CODE HERE!
Home
© Rapidparts, Inc 2008 | 2950 Walkent Ct. NW, Grand Rapids, MI 49544 | Phone 616.647.2500 | info@rpionline.com