19 June 2011

C# In Depth 2nd. Ed.: A (partial) Review

I’m currently reading C# In Depth: 2nd Edition (Jon Skeet, Manning Publications), and I thought I’d share my thoughts about it so far.

I would say I’ll do this in three parts: the good, the bad, an my take, but really there’s only “the good.” 

I consider myself to be a mediocre-to-good developer.  I still have lots to learn and my code can always get better.  Hanging out on StackOverflow.com, I get to see Jon’s casual mastery of C# nearly every day.  I can only thank him for the help he’s been to me- even if he’s never actually answered one of my questions (or, at least, I don’t remember that he has, and I think that would stick with me…)

C# In Depth is the work of a master.  They say that you’ve truly mastered something- not when you understand it, but when you can communicate that understanding clearly.  By that standard, Mr. Skeet has truly mastered C#.

The book begins with a look, a retrospective if you will, of C# 1.  I, for one, noticed the fondness Mr. Skeet still holds for the old language (and, as he would point out, the BCL and CLR that went with it).  Even as he points out its flaws- which it must have, or why even bother with C#2, 3, & 4- he also takes care to explain things so clearly that one can see a huge proponent of the language.

From pointing out the flaws in C#1, the book proceeds through C#2 and its enhancements to C#3.5 (where I currently am) and will proceed to C#4.  At each level, the detail he provides about how the language actually works is quite comprehensive- or, as comprehensive as a book meant for actual consumption can be.  Moreover, his explanations and examples pointed out, quite clearly, places I could improve my own code, or explained to me the “why” of things I had taken for granted.

As a C# developer who considers myself in the “Coding 201” camp (Okay, I may have graduated to Coding 202), I cannot imagine a better technical book to have at one’s fingertips.  Where other books range (within the same book) from simple (and sometimes misguided) pedantry in text to oversimplification and hand-waving in examples, C# In Depths seems to have hit that perfect sweet-spot of the text being concise enough not to put me to sleep, and examples being robust enough to see what would actually happen.

I highly recommend you read the book, if you haven’t already.  I can virtually guarantee that it will improve your coding game as you begin to understand why some things work the way they do.

I also recommend you go (and subscribe) to his coding blog.  It’s on my must-read list.

14 June 2011

A New Project: DoctorConnect

Welcome back!

Today I’m introducing a new project- again, mostly to help me learn stuff.  This one is a little more complex and will actually involve at least 2 and probably 3 sub-projects.  I call it DoctorConnect.

The theory is this: Assume that a Doctor’s office wanted to enable its patients to do some basic tasks online without having to call and wait for a nurse to call them back.  We’ve all had to do that, right?  You’re puking your guts out, and you have to wait for the nurse to call you back before you know if they can see you today (saving you $$$) or if you have to go to some Doc-in-a-Box Urgent Care place.

So: DoctorConnect.

It will be a system in 3 parts: A database (SQL Server 2008 R2), an internal web application (haven’t decided on MVC or Web Forms, yet) and an external web page.  After all that’s done, I may also design a mobile-app for the project: so users could schedule an appointment on-the-go.

This is a big project for me, and I’m going to have to master some things that I haven’t mastered yet before I get it right.

First: The DB.  I’ll need to map my DB tables to Objects.  This is complex enough a data model (more on that in another post) that I don’t fully trust EF, and I don’t trust my own skills at Linq-2-SQL enough.  So I’ll be looking at various ORM options to determine the best way to go.

Second: Membership.  Unlike the Issue Tracker, this will require a custom membership provider: I’ll need to know not only if someone is an authenticated user, but exactly what things they should be able to see.  On the internal application, that may be role-based, but on the external application, it will have to be something else- some hierarchy model that allows for a Responsible Party to see all the accounts for which he (or she) is responsible, but let other patients see only their own accounts.

Third: Polymorphic Data.  What I mean is this:  A patient, when scheduling an appointment, doesn’t need to know all the information about a specific doctor, just their name, specialty (so they don’t go see an OB/GYN about their prostate), and availability.  On the other hand, the Office Staff need to know just about everything about a patient.  How should I handle that?

Fourth: Scheduling.  I’ll need a way to set an appointment for a doctor and validate it against other scheduled events: we don’t want patients to be able to schedule an appointment during lunch, for instance, or while that doctor is on vacation.  So I’ll need to figure that out.

All in all, this is shaping up to be a big project.  So let’s start eating this elephant: one bite at a time.  Next up: the data model.

09 June 2011

"I'd like to thank all the Little People..."

I'm a member of the D/FW Connected Systems User Group.  Last night we held officer elections and I was honored by being selected as the Vice President of Meetings (not as big a deal as it sounds, but nice nonetheless).  So expect to see a lot more content specifically related to DFWCSUG on the blog for the next year or so.

You can find us here if you're interested.

08 June 2011

On Professionalism

Yesterday, two of my co-workers, a fellow consultant and one of the client’s FTEs, were having a dispute about some procedural issues.  I’m not going to discus specifics, nor who was “right,” that’s not the point of this post.

In the course of the dispute, both sent fairly preemptory emails, and exchanged harsh- though still mostly civil- words verbally as well.  For the rest of us in the area, it was fairly uncomfortable, and I hope they thought so as well, once they each cooled down and thought about it.

In this particular case, the major issue was that neither side was actually listening to the other.   Had either been willing to set their assumptions aside for thirty seconds while the other completed a statement, I believe the issue would have been resolved much sooner than it was.

So, for the record, let’s set out some rules about workplace disagreements.

1: Don’t have public arguments.  Ever. 

This is highly important.  When you argue with a colleague, nothing good comes from it.  More-over, you will probably hurt the group by causing it to divide into factions over who is right.  Finally, you hurt the group in the eyes of any other observer who may be walking by.

When you have a disagreement, take the person with whom you disagree aside and have a private discussion.  If you can secure an office or conference room, feel free to yell.

2: Listening doesn’t just mean waiting for the other person to finish speaking.

Every “conflict resolution” class ever in the history of ever has agreed on this point.  When the other person is speaking, you should not be thinking about what you’re going to say next, you should be paying attention to them.

Most people don’t do things that don’t make sense- when viewed from their perspective.  So, when someone is saying something that doesn’t make sense to you, you’re probably holding a different set of assumptions than the person to whom you’re speaking.  The correct answer, at that point, is to check your own assumptions at the door, and try to see things from a viewpoint where what the other person is saying is reasonable.  Once you’re able to do that- even if you disagree- you’ll be much more able to remain polite.

Politeness helps speed the resolution of the issue.

3: Never assume.

I mean that.  Never make assumptions beyond those necessary to hold a conversation.

The argument yesterday could have been shortened considerably had either party, let alone both, stopped assuming that the other was being unreasonable (see point 2).  The issue was over a particular process and whether it had been followed in a particular case.  One party assumed the process hadn’t been followed, the other assumed it had, and there was no clear evidence one way or the other.

Had each party checked their assumptions at the door, the entire exchange would have gone something like this:

“Hey, when [Resource X] did such-and-such, did they make sure to follow [Procedure Y]?”

“I’m sure they did, but I’ll send an email {the other resource is off-shore} to make sure of that.”

So, the next time you have a disagreement with a co-worker, whether you or they are a consultant, contractor, FTE, PTE, or whatever, remember that the rest of the office would much prefer it if you keep things professional and civil.  If you’re unable to comply with either of those, take the argument somewhere the rest of us don’t have to hear it.

06 June 2011

MVC Issue Tracker: Where We've Been

I should have the final code zipped and pushed to CodePlex as a download sometime today or tomorrow.  This post is sort of a retrospective on what we've already done.

If you missed the posts, here they are in order
Introduction
Part 1- Setting Up
Part 2- Controllers
Part 2.5- Ancillary Issues
Part 3- Cleaning Up the Views
Part 4- Finishing Up

So, some impressions to wrap up:

First off, while I would have liked to implement my own authorization system, MVC3's provided version met all my needs quite well, and so there was no reason to handle that.  So that got cut out at the end.  I do like the fact that, for almost all scenarios, the MVC3 provided authorization model works well enough.  Perhaps someone with exceptionally high security needs would be required to create their own model, but I doubt anyone with such needs would be using MVC (the product) anyway.

Which leads me to my second point.  ASP.NET MVC3 seems to me like something a small company (even big for a small company, but small nonetheless) would use to get a website up and running relatively quickly.  I can see that it has plenty of power for the mid-sized business as well, but once you get up to larger enterprises, I see things I believe would cause some problems.

First, I don't like the lack of separation of the code.  MVC3, by default at least, throws all your code into one project.  I believe that my Model code (let's face it, it's the Data Layer) should be in a separate project, at least, and possibly a separate solution.  The Controller code- being the Business Layer- should, to my mind, be likewise separated- either in a separate project or separate solution.

Now, I don't see any reason you couldn't use MVC like methodology and still have those layers of separation, but MVC3, in itself, is fairly dependent upon having all that code in the same project- at the very least for auto-generating views and controllers.  I may be wrong, but it seemed to me that MVC was built on the idea that all of this code would be in the same project.  That's fine for fairly small and/or encapsulated applications, but a larger enterprise with multiple web applications may very well have need to separate those layers better simply because they want people to interact very differently (beyond simple authorization issues) with the same data.

Take, for instance, the sample project the good folks at MS use of Contoso University.  Their MVC3 tutorial walks you through setting up a way to enroll students, or add classes, or whatnot.  In a real life situation, that enrollment, and most of the administrative functions, would be handled (if they used MS Technologies at all) on an internal only application- whether an intranet application or a windows application- and all anyone logging in would be able to do is some basic maintenance of their own student account and request enrollment (with the actual enrollment being handled by some other application to ensure class sizes were set properly, etc.).  This would lead to it making more sense to have that level of separation.

Now, my thought is that MS would be saying, "You're right- so you don't use MVC for that."  That's a completely reasonable expectation- but it is something to be aware of.

All in all, however, I was impressed by the power of MVC and the ease with which the application came together.  Certainly it would need a lot of cleaning and polishing before anyone used it as an actual production application, but I can see the seeds in there for just such a thing.  Add to that the lightweight nature of the newest version of SQL Compact for the database, and a small company could easily use such a thing- instead of some big and expensive tracking system- to track their trouble tickets.  And, as with anything that contacts the database, all that really needs to happen if the ticket database grows too big is to create the same db schema in some more robust database (say, full on SQL Server, or whatever) and change the web.config connection string.

I hope this exploration has been as beneficial for you as it was for me.

03 June 2011

MVC Issue Tracker Part 4

This is the penultimate entry in this series.  I’ve completed the code to the level I intend (for the moment) and I’ve verified things work as I expect (for the moment).

So, let’s look at what we had left to do:

  • Wire up WorkNote/Create so that it would redirect back to the underlying issue instead of to the index of WorkNote.
  • Provide Security.

The first was easy, instead of using “RedirectToAction(“Index”) I changed the code for "Create” to this:

   1: [HttpPost]
   2:         public ActionResult Create(WorkNote worknote)
   3:         {
   4:             if (ModelState.IsValid)
   5:             {
   6:                 var issueId = db.Issues.Where(i => i.IssueId == worknote.IssueId).SingleOrDefault();
   7:                 worknote.EnteredBy = this.HttpContext.User.Identity.Name;
   8:                 worknote.LoggedDate = DateTime.Now;
   9:                 db.WorkNotes.Add(worknote);
  10:                 db.SaveChanges();
  11:                 return RedirectToAction("Details", "Issue", new { id = issueId.IssueId });   
  12:             }
  13:  
  14:             ViewBag.IssueId = new SelectList(db.Issues, "IssueId", "Title", worknote.IssueId);
  15:             return View(worknote);
  16:         }

Ignore the “IssueId” and “EnteredBy” stuff for now.  The important part is at the end: “return RedirectToAction(“Details”, “Issue”, new {id = issueId.IssueId})';  This tells the controller that, on a successful insert, it should redirect to the “Details” view of the Issue with a specified issue id.

See?  Easy.

The next part required some extensive refactoring.  I was going to implement my own security model, but that’s really fairly silly.  MVC3 provides us with a perfectly acceptable method of Role based security, so we’ll just hook into that.

First, I removed the User class and Role enum, and all the references to same.  Then, using the configuration button, I used the built-in web-based configuration to set up roles, and add a couple of users.

From there, it was all markup.  Some samples follow:

   1: [Authorize(Roles="Administrators,Manager,Developer,User")]
   2:     public class WorkNoteController : Controller

This piece just says that only someone logged in as an Admin, Manager, Dev, or User can get to the WorkNoteController.  I have similar authorization on Issue.

More significantly, on Issue, I marked up some of the specific methods for the IssueController:

   1: [Authorize(Roles = "Administrators,Manager,Developer")]
   2:         [HttpPost]
   3:         public ActionResult Edit(Issue issue)
   4:         {
   5:             if (ModelState.IsValid)
   6:             {
   7:                 if (issue.ClosedDate != null) issue.ClosedBy = this.HttpContext.User.Identity.Name;
   8:                 db.Entry(issue).State = EntityState.Modified;
   9:                 db.SaveChanges();
  10:                 return RedirectToAction("Index");
  11:             }
  12:             return View(issue);
  13:         }
   1: [Authorize(Roles = "Administrators,Manager")]
   2: [HttpPost, ActionName("Delete")]
   3: public ActionResult DeleteConfirmed(int id)
   4: {            
   5:     Issue issue = db.Issues.Find(id);
   6:     db.Issues.Remove(issue);
   7:     db.SaveChanges();
   8:     return RedirectToAction("Index");
   9: }

These are specific authorizations required to access specific views: Edit and Delete, respectively.

So, now, an Administrator would have to create a new user (as one would expect in a business scenario) and provide that user with the correct credentials.  That user would then only be able to access the parts available to the assigned Role.  Basically, a basic user can add issues and can look at issue details/status.  A Developer can do that, but can also edit issues (specifically to close them, when necessary) and add work notes.  Managers and Administrators have the additional authority to delete issues.

Additionally, since I’m able to use the provided Role based security, I’m also able to avoid the necessity of a “User” object, and just grab the UserName from the Session.  This allows tagging that is just as accurate as building role-based security from scratch, and that is more intuitive, since I can just grab the username instead of the user’s Id, or going through some hoops to take that Id and turn it into their name.

The final code push is up on CodePlex now, and I’ll zip it all up and add it to the downloads folder soon as well.

Next Up (In a couple of days): A look back, and my impressions from working with MVC3.

And now, for Something Completely Different

I'm changing lap-tops, which means porting code.  To do that, I'm taking the things I think are worth keeping and throwing them up on CodePlex.  One in particular, being close to my table-top-rpg-playing heart is my Campaign Portfolio Manager.  Go check it out, if such things interest you.

02 June 2011

MVC Issue Tracker: Part 3

For this installment of the Issue tracker, I mostly just clean up our views a little bit.  I also added a little functionality for a snap-shot page and provided the ability to add new work notes to an issue.

Let’s dive in.

First off, I added a Snap-Shot report to the ‘About’ Page, like so:

image

This required very little code, actually, but adds a nice little bit of polish- along with helping me get my head around exactly where we’re going here…

 

   1: public ActionResult About()
   2:         {
   3:             var allIssues = ctx.Issues;
   4:             var closedIssues = ctx.Issues.Where(i => i.ClosedBy != null);
   5:             var openIssues = ctx.Issues.Where(i => i.ClosedBy == null);
   6:  
   7:             var oneWeek = GetClosedInOneWeek(closedIssues);
   8:             var twoWeeks = GetOpenGreaterThanOneWeek(openIssues);
   9:             double oneWeekCount = oneWeek.Count();
  10:             double closedCount = closedIssues.Count();
  11:             var pctOneWeek = oneWeekCount.PercentOf(closedCount);
  12:  
  13:             ViewBag.CountAllIssues = allIssues.Count();
  14:             ViewBag.CountClosedIssues = closedIssues.Count();
  15:             ViewBag.CountOpenIssues = openIssues.Count();
  16:             ViewBag.OneWeek = oneWeek.Count();
  17:             ViewBag.TwoWeeks = twoWeeks.Count();
  18:             ViewBag.PctOneWeek = pctOneWeek;
  19:  
  20:             return View();
  21:         }

First, I added this to the About action of the HomeController.  It just grabs the issues, then collates the ones that are closed vs. the ones that are open, and runs some quick analysis.

Heading over to the View, we added this:

   1: <h2>Issue Tracker Snap-Shot</h2>
   2: <p />
   3: <table>
   4:     <tr>
   5:         <td><b>Total Issues Logged</b></td>
   6:         <td>@ViewBag.CountAllIssues</td>
   7:     </tr>
   8:     <tr>
   9:         <td><b>Closed Issues</b></td>
  10:         <td>@ViewBag.CountClosedIssues</td>
  11:     </tr>
  12:     <tr>
  13:         <td><b>Open Issues</b></td>
  14:         <td>@ViewBag.CountOpenIssues</td>
  15:     </tr>
  16: </table>
  17:  
  18: <h3>Issue Closure Details</h3>
  19:  
  20: <table>
  21:     <tr>
  22:         <td><b>Issues Closed In One Week or Less</b></td>
  23:         <td>@ViewBag.OneWeek</td>
  24:     </tr>
  25:     <tr>
  26:         <td><b>Percent Issues Closed in One Week</b></td>
  27:         <td>@ViewBag.PctOneWeek</td>
  28:     </tr>
  29:     <tr>
  30:         <td><b>Issues Open More than Two Weeks</b></td>
  31:         <td>@ViewBag.TwoWeeks</td>
  32:     </tr>
  33: </table>

And that’s really all it took.

The Percentage part was kind of tricky, though: since there are no closed issues (as might actually happen in the first several days to couple of weeks if this were ever someone’s actual ticket tracking system) the Percentage wanted to return NaN.  So I created a little extension method off of Double:

   1: public static double PercentOf(this double numerator, double denominator)
   2:         {
   3:             var temp = numerator / denominator;
   4:             if (Double.NaN.CompareTo(temp) == 0) return 0;
   5:             return temp;
   6:         }

It just goes ahead and divides anyway, and if the result returns NaN, it passes back 0.  Not mathematically accurate, but business accurate.

Next, I turned to the WorkNote.  Specifically, I turned to making the edit a little more accurate, and linking it to the Issue.  I modified the controller and view to handle the LoggedDate automatically, and then I modified the Detail view of the Issue to provide a list of WorkNotes and the ability to add a new one.  Like So:

   1: </fieldset>
   2: <table>
   3:  <thead>
   4:    <tr>
   5:      <th>Work Detail</th>
   6:      <th>Entered By</th>
   7:      <th>Date</th>
   8:    </tr>
   9:  </thead>
  10:  <tbody>
  11:    @foreach (var item in Model.WorkNotes)
  12:    {
  13:        <tr>
  14:          <td>@item.Detail</td>
  15:          <td>@item.UserId</td>
  16:          <td>@item.LoggedDate.ToShortDateString()</td>
  17:        </tr> 
  18:    }
  19:  </tbody>
  20: </table>
  21: <p>
  22:     @Html.ActionLink("Add Notes", "Create", "WorkNote") |
  23:     @Html.ActionLink("Edit", "Edit", new { id=Model.IssueId }) |
  24:     @Html.ActionLink("Back to List", "Index")
  25: </p>

So, now, when we navigate to the detail of an issue, we get:

image

And clicking on the “Add Notes” link gives us:

image

Currently, when we add a new note, it takes us to the WorkNotes Index… we’ll work on that for the next post.