Software developers aren’t freakin’ construction crews

I swear, if I hear that damn “designing software is like building a house” analogy again, I’m going to shoot someone in the face (probably myself).

Software is organic. It evolves. It changes. When you build a house, it’s set in stone – literally. All the floors depend on one another and build on one another. You can’t radically change one below it without disrupting those above it. If you’re designing your software to be like a house is built – you are doing it WRONG. Your software shouldn’t be so rigid and brittle that it’s a great pyramid of dependencies, and changing one thing brings the whole thing crumbling down. We have methodologies and technologies to keep us from doing that exact problem – and they’re not new! They’ve been around for a decade or more. Coping with and embracing change is exactly what good software is about. The only constant in software you can rely on is that things will need to be changed. If you’re designing your software so that it can’t meet that requirement, then it fails at it’s most fundamental level.

If you still don’t agree with me, google some of these terms: Dependency Inversion (also known as Inversion of Control) (DI / IoC), Agile software, Single Responsibility Principal (SRP), Open-Closed Principle (OCP), Liskov Substitution Principle (LSP), or just buy (and read) this book – Agile Software by Robert Martin.

That’s not to say that you can write software that will be able to gracefully handle every change ever. You can’t (not without an unlimited budget/time that is, which doesn’t exist in The Real World). But most business domains have areas that are more volatile than others, and you can reasonably predict which areas you need to be more careful and purposeful when adhering to proper software design principles. You want to be able to say that you can handle most changes and refactoring easily, but sometimes things will require change in an unexpected way. So you implement those changes, and adjust your architecture to adhere to similar changes of that nature in the future. That’s why we, as software developers, get paid what we get paid – to learn from our mistakes, to improve our practices, and help provide more business value in the future by providing the ability to handle owner’s requests more efficiently. When a change request / new feature comes down the pipe that we didn’t anticipate, we explain to them the cost (time/money/effort/resources) to handle that request, and we do it just like we do any other work. We don’t bitch and moan that doing our job is hard because we fucked up the initial design, or to whine about how software is exactly like masonry work.

Maybe no one uses that analogy anymore, everyone agrees with me, and I’m overreacting. But I did just see it again the other day. Maybe that’s the freak occurrence though? I hope so.

Quick Resource Helper Class

The production app I’m currently working on involves reading a client-provided extract (on the magnitude of hundreds of thousands of lines), transforming that data, and importing it into our system for processing. This happens once a day. The current process involves importing this information into staging tables on the DB side, then running stored procedures to transform the data.

As part of an effort to both improve efficiency as well as add in scalability, the DB team has decided this process should import into session-level temporary tables. This will allow multiple imports to go on at once if need be, as well as give some performance enhancements. The software side of this requires that the importing software opens a connection, and then creates the temporary tables on that connection. From there, the software reads the client-provided extract in about one thousand lines batches, and uses SqlBulkCopy to transfer it to SQL server.

Most of the architecture in this problem was laid out already, and not having any previous experience dealing with data sets this large, I followed the rest of the team’s leads. The temporary table creation needed to be done on the software side, and the scripts themselves needed to be part of the project. Not wanting to rely on folder paths to read in the data, and really not wanting to have string variables that held the scripts, I opted to leave the scripts intact, but use them as embedded resources. I haven’t worked with embedded resources before, honestly, so I did find out a couple of things.

The main call you’ll need is:

Assembly.GetAssembly(yourAssembly).GetManifestResourceStream(fullResourceName)

The tricky thing can be the file path. It uses periods to denote folder paths to load your project. Not difficult, but not the most obvious thing either. I wanted to “hide” that from my consuming apps, as they really don’t care about how the resource accessor likes to make it’s names. They just want their file. They do know about the project structure of the assembly that has their resource, however.

The next thing that I accidentally did wrong was the assembly – you need to provide the assembly that the resource lives in, for obvious reasons. Given that this helper class lives in a “Utilities” type of project, it’s no shock that as soon as I refactored my class out, my tests broke.

So, with those two thoughts in mind, here’s the API I wound up with, along with the full class:

public static class ResourceHelper
    {
        public static string GetResourceAsString(Type typeOfCallingObject, string namespaceName, string fileName)
        {
            return GetResourceAsString(typeOfCallingObject, string.Format("{0}.{1}", namespaceName, fileName));
        }

        public static string GetResourceAsString(Type typeOfCallingObject, string namespaceName, string folderName, string fileName)
        {
            return GetResourceAsString(typeOfCallingObject, string.Format("{0}.{1}.{2}", namespaceName, folderName, fileName));
        }

        public static string GetResourceAsString(Type typeOfCallingObject, string namespaceName, string[] folderPaths, string fileName)
        {
            var fullFolderPath = new StringBuilder();

            if(folderPaths != null && folderPaths.Count() > 0)
            {
                foreach (var folderPath in folderPaths)
                {
                    fullFolderPath.AppendFormat("{0}.", folderPath);
                }

                // Remove the trailing period
                fullFolderPath.Remove(fullFolderPath.Length -1, 1);
            }

            return GetResourceAsString(typeOfCallingObject, string.Format("{0}.{1}.{2}", namespaceName, fullFolderPath, fileName));
        }

        public static string GetResourceAsString(Type typeOfCallingObject, string fullResourceName)
        {
            var fileStream = Assembly.GetAssembly(typeOfCallingObject).GetManifestResourceStream(fullResourceName);

            if (fileStream == null)
            {
                throw new Exception(string.Format("Resource '{0}' not found!", fullResourceName));
            }

            var sqlStreamReader = new StreamReader(fileStream);

            return sqlStreamReader.ReadToEnd();
        }
    }

Note that most times, the first parameter is liable to just be “GetType()”. I could’ve maybe made it an extension method, or made the type a generic parameter, but it really felt like overkill for such a small class. Here’s some examples of calling it (from my unit tests):

ResourceHelper.GetResourceAsString(GetType(), "UtilitiesTests.RootResource.txt");
ResourceHelper.GetResourceAsString(GetType(), "UtilitiesTests", "RootResource.txt");
ResourceHelper.GetResourceAsString(GetType(), "UtilitiesTests", "ResourcesFolder", "FolderResource.txt");
ResourceHelper.GetResourceAsString(GetType(), "UtilitiesTests", new[] { "ResourcesFolder" }, "FolderResource.txt");
ResourceHelper.GetResourceAsString(GetType(), "UtilitiesTests", new[] { "ResourcesFolder", "NestedFolder" }, "NestedResource.txt");
 

Yeah, this is all extremely basic, but thought it still might be useful for someone. I had fun doing it, none-the-less.

New job, new challenges!

I know. It’s crazy. I had been dissatisfied with my old position for an extremely long time, and it eventually came to a boiling point, leading me to switch jobs in the middle of THE RECESSION!!! ZOMGS!!!!!

Things worked out amazingly, though. I had my new job before my severance even ran out. Nothing like getting paid to nurse my crack addiction play World of Warcraft.

I’m the start of what is basically a new team. I’ve been here a little over 3 weeks now. The previous team consisted of a single developer, who has been here for about eight years. He’s moving to more of a management position, while me and another developer (a Web/UI focused developer) will be working for him. There’s also 2 DBAs that we work closely with, but aren’t technically on this team. We steal a lot of their time though.

My role is “Senior .NET Software Engineer”. What do they mean by “Engineer” exactly, you ask? Well, that’s a good question that took me a bit to find out myself. I think I can explain it best by thinking of what an engineer by any definition really does – makes things work! My job is to make it work, however I feel that should be accomplished. My boss helps critique my proposed solutions, typically by giving me information about how many of the external systems interact with what I’m working on. That’s where a lot of the complexity is currently. The company deals with the health care supply chain. Not only is it my first foray into the health care arena, which is complex in and of itself, but the supply chain has many unique factors and forces at play at the same time. It’s a lot to take in. I’m finally getting a good base grounding (although not very in-depth yet) of how the entire system works.

My current “day-to-day” focus involves refactoring an existing system made for a single client to make it extendable for the second client it needs to support. I’m finally getting a chance to put to use a lot of the great information I had read recently. The code is pretty straight forward, so while it’s not hard to understand what it’s doing, it’s very hard to break up and make extendable. I’ve been referencing some books pretty frequently to help me make the right decisions, however, and I’m excited about how it’s turning out. My boss is liking my work as well (which is very important to the checkbook).

The work here is also extremely data heavy. Collecting and processing the data is a huge concern. Working with tables that contain millions of records, and importing, exporting, and converting them. So I’m learning a lot of information about how to write efficient SQL and designing secure databases.

On top of that, the company is fully committed to doing Scrum in a proper fashion. That’s great – but of course, each company will have it’s own “spin” on it. This is an extremely large company, and we’re actually a remote office, so it involves a lot of phone conferencing. So that’s been another challenge to learn how to work with properly.

All in all, however, this is going pretty great. The work is challenging. It’s a new business domain, which is a great change of pace, if nothing else. I’m being allowed to propose architecture improvements, and implement them. My boss is committed to making sure we have good hardware and the latest development tools. He’s also always looking to the future – we want to wrap up the current project, then move on to the next. Then the next. Then the next.

So, I hope to blog a little here and there about what I’m doing. I figure most people will find it mind-numbingly boring or introductory. Hopefully not though. A lot of it will be difficult to explain or may not make sense. We’ll see how it goes.

Oh, and a freaking huge 11.5% raise. It was over the top end of the range I was asking for. And a bonus plan that actually pays about 10% of your salary a year (and yes, people actually get their bonus). Take that recession!

Keeping scroll position between redirects

Had need of this on a project, and while I thought it’d be a large hassle, Elijah Manor was able to point me to a very simple to us javascript to do it – Scrollsaver.

All you do is include it, and it does the rest. It uses a cookie and an ID of your element to keep track of your last position. Very nice!

Silverlight – YOUR WORLD IS OVER

So, Sliveright THREE is in beta now, and it’s set to take over the entire web! If your site isn’t made in Silverlight, guess what? IT SUCKS. IT COMPLETLEY FAILS AS A WEBSITE. PERIOD.

This site? SUCKS.

Amazon.com? SUCKS.

eBay? SUCKS.

Google? SUCKS

“You’re obviously right, Rob. But I can’t put my finger on why you’re so right! Please tell me!”

Okay, I’ll tell you. But you keep your filthy fingers away from me. You sound poor, and I don’t like it when poor people touch me.

 

Gradients

You can make gradients with it. HOT. SHIT. You might have THOUGHT you could make gradients before, but guess what? YOU DIDN’T. NOW you can finally make them.

I know, you really did think you were making gradients with Photoshop or Paint.NET or something, and using them on your site, but you didn’t. You just made colors. They were garish and horrible.

And you certainly didn’t make a single grayscale transparent gradient, then use CSS to set that as the background image with a background color so you could have multiple gradients from image. Nope, you’ve never done that.

See, all those would be way too much work and time investment. Why open up image software, and make a gradient in 3 or 4 clicks? Now you can open up Blend, and make a REAL GRADIENT WITH XAML in 3 or 4 clicks!

So, now you can finally create gradients. You’re on your way to being a real developer. Right now there is only one real developer in the world, me, because I’m the only one who’s used gradients in Silverlight.

But why are they good? Because the world is nothing but ONE BIG GRADIENT. Next time you’re in the bathroom, and you feel your insides explode and issue forth an eruption of epic proportions, take a look at what you see – it’s JUST like a gradient. It’s not one solid color. It’s a rainbow, softly blending from one color to the next. Just like Silverlight!

 

Animations

You can FINALLY animate stuff! I know, once again, you THOUGHT you animated stuff before.

BUT.

YOU.

DIDN’T.

“Pretty sure I use jQuery to easily animate some stuff.”

Listen, I don’t know what jQuery is, so it doesn’t exist. Got it? It was never possible to animate UI before.

“Well, pretty sure I used Flash to animate a site like a decade ago.”

Will you quit making shit up? I’ve never used Flash either, so I’m pretty sure it doesn’t exist either.

Finally, with Silverlight, using Blend, you can make animations! It uses this great thing that the Silverlight team invented, called a storyboard, which consists of timelines, that have keyframes! It’s pretty simple!

 

Hover effects

This is a bit of a combination of Gradients and Animations (capitalized because they’re so important)! Now, when a user hovers over important things, like menu items, you can give a subtle gradient to pop into place to let the user know they’re over something sensitive!

“That’s been possible with CSS for yea—“

SHUT UP!

“I could do it with javascript even longer than t—“

WHAT PART OF SHUT UP ARE YOU MISSING?! YOU COULD NEVER HAVE A HOVER EFFECT FOR A WEBSITE BEFORE SILVERLIGHT!!!! SHUT THE HELL UP!!!!!

 

It’s one huge ass object

Well, I know FOR A FACT this one is new! Silverlight is now one single element on the page! This is super great, and really where Silverlight shines.

This really makes it, finally, the perfect solution for public sites. I mean, who needs that “web” crap. Now, it’s one solid object. You no longer have to worry about your public site being pilfered by nasty search spiders. I don’t even know what those are, but they’re called spiders, so I assume they have eight legs and crawl all over you. I have enough night terrors about them as it is.

Plus, people now can’t link to somewhere deep in your site. I hate it when people link to direct portions of my site, and skip all my advertisements or cool pictures of myself or something. Now, they have to see all that EVERY TIME. Hell yes.

Also, you can’t copy & paste my text now, jerks! You’re just going to have to paraphrase it. I’ll get you started:

“Then Rob was awesome”.

Done.

“Uh, I made sites entirely in Flash a decade ago, and it was a horrible idea, and that’s why only marketing sites do it now, You see—“

I’M GOING TO SHOTGUN YOU IN THE FACE. I’M BUSY TALKING ABOUT HOW BRILLANT SILVERLIGHT IS, AND HOW EVERYTHING IT DOES IS COMPLETLEY NEW AND BETTER THAN ANYTHING THAT EXISTED FOR THE WEB BEFORE, EVEN THOUGH I’M PURPOSEFULLY IGNORANT OF ANY AND ALL WEB TECHNOLOGY!!!

 

The Bad Stuff

Well, the main bad stuff is that all these non-Silverlight websites haven’t been converted yet. Hopefully soon browsers will support xaml natively. At least the One True Browser should do it soon.

But there’s really one thing wrong with it:

Case Sensitivity. Seriously. WTF. How freaking retarded is that. I mean, in this day and Age, why is ANYThinG case Sensitive? i can’T think WHY it would Matter at alL. AnD whiLE I’M on tHiS subJecT, I shOUlnD’t hAve to spEll thINGs correCTly eiTheR. it shouLd havE a DictIOnaRy of poSSiBle ValUEs, aND pIck teH nErEst wRd B-Cuz e dUn hAf tIm oR sKil 2 lErn al tHeeS wurDz i meN u nO hw eT s wHN u gTz so Mny wRd tt its impsB fr tO sTp fern rEd F dgeR DqoeR gler!!!!!

Google hates IE

And therefore, I hate Chrome a little less.

google-hates-ie

Top is Firefox 3, bottom is Google. I was exploring some jQuery stuff and decided to see if the behavior was any different in IE (suspecting it wasn’t), and noticed a slight difference. I signed into my google account on both to see if that caused a difference

While in my perfect world, everyone would use FFX, I’d still rather them use Chrome then IE.

I then did a search on google to see if anyone else has noticed this (or so I could see how long it’s been around), and while I didn’t see anything relating to this, it’s nice to know that google has been pushing for an alternative browser for a while now. Too bad they switched their recommendations though.

And no, I don’t know why I blacked out my email address. Everyone who visits this blog knows it anyways.

A better way to organize your CSS/HTML

It’s tough being a genius. It really is. Here’s my new guidelines for CSS/HTML:

  1. All tags are divs
  2. Use standard html tags as class/id names whenever possible

So, with our new awesomeness, we might get something like this:

   1: <div class="body">
   2:  
   3:     <div class="h1">My Title</div>
   4:     <div id="p">This is my awesome <div id="span">stuff</div></div>
   5:  
   6: </div>

 

Pretty freaking slick, eh? I’m hoping it can be integrated into Kobe soon!

Installing Live Writer

So, I just got a new computer, and I had an amazing idea of outstanding excellence (forthcoming), but needed to install Live Writer first.

First, it tries to get you to install FIVE HUNDRED other programs first. Piece of crap.

But, then, in the installer, it has a picture of a girl smiling at her bewbz. I mean, I smile at bewbz too, but I don’t see why it’s in an installer.

Proof:

image

Auditing Part 4 - Quit being a slob

Before I start, I'd like to think my colleagues Alex Robson and Craig Israel for helping with the design. It'd have ended up containing 200% more suck without their assistance. Also, the finished product is part of Nvigorate, so whenever Alex updates that project, it'll be there. What is currently up there however is very out of date. It's improved greatly.

Welcome to Part 4, the last in my auditing series. Here's the previous ones, you drunken slob, Mr. I'm-Too-Lazy-To-Click-In-The-Archive:

Everything is almost wrapped up! Our list-o-crap has now turn into a sentence-o-crap, not even worthy of a list anymore! What is that sentence, you ask? Why, it's coming up! Pay attention! Here it comes! Right now! Firing our auditor is still noisy, as it takes place in our functions that are handling unrelated tasks. So let's clean it up!

I can't carry it for you Mr. Frodo, but I can carry you

Auditing is more of something that the function should support, but it'd be nice to not have to muddle up our business code with it, even if it is just one line.

So what's a way we could do this? Why, attributes of course! If you're not familiar with adding attributes to a function/class, here's the quick version. If you know this, tough. I'm not going to identify when I'm not talking about it, so you have to wade through these words anyways. Deal.

First, an attribute is represented (in C# that is - noone cares what it looks like in VB) by square brackets above your function/class. Like this:

   1: [MyClassAttribute]
   2: public MyClass
   3: {
   4:     [MyFuncAttribute]
   5:     public void MyFunc()
   6:     {
   7:         /* do stuff */
   8:     }
   9: }

See? Easy. Applying attributes is called "decorating" your class or function. So, we're going to go about creating some attributes that will fire up our AuditManager when our function is accessed. This is known as Aspect Oriented Programming, or AOP. But how do we do this? We're going to use an AOP framework. There are several out there, but Nvigorate already makes use of PostSharp, so we're going to use that one. Why PostSharp? Most AOP frameworks have the attributes being evaluated at run-time. This can, unfortunately, lead to some serious run-time speed hits. PostSharp, however, is run pre-compilation, and actually injects your code in the appropriate places. This means no run-time overhead, but more setup on the developers side. To do this, it has to be physically installed on the machine doing the compilation. Also, any project that makes use of the attributes (or aspect, as I'll refer to it from here on), even if not directly using PostSharp libraries, will need a reference to the PostSharp DLLs from the GAC.

So, you've installed PostSharp, and added a reference to the DLLs in your consuming project. Let the fun begin!

Insert witty subtitle here

We'll start with the basic information our aspect needs - the action, the username, the object to be audited, and the date. Aka, the same 4 things we've been looking at this entire series. One of the restrictions though about making this an aspect, is that they can't be generic! Oh noes! Well, the good news is we can still fire our AuditManager, using Reflection, and get our generic call! Yay! There's another issue too, but we took care of it when we wrote AuditManager. Remember how I had us make TWO TrackAction calls? We ended up with TrackAction for instances, and TrackActionEnumerable for collections? Well, if we hadn't done that, our reflection calls would've actually failed, as it wouldn't have been able to distinguish between a generic type and a collection of a generic type. But we're so smart, we knew that ahead of time, so we're good. What we'll do is check if our information object implements IEnumerable or not, then we'll know which one to call.

Now, I don't feel like writing raw Reflection code. Yet again, I'll be using the Reflector portion of Nvigorate to handle that for me. One of things we'll have to pass to the call is a collection of arguments. Let's think about how we'll retrieve each of those:

  • Action - well, this is just an enum type, so we can pass that right in the attribute itself. We'll add it to the constructor, then save it in a private field.
  • Date - we'll just use DateTime.Now, of course. If the auditors themselves wish to use a different timestamp, they can.
  • Information - I'm going to force a restriction here. I'm saying that consumers have to have this passed in on the function call. You're welcome to change this behavior if you desire. But I think separating your DAL from the rest of your code will end up with a cleaner design, and then this won't be an issue!
  • Username - hmmm....this one is tricky. And gee, it's like I did them out of order for a reason! Guess what, I did, suckers! That way I can make this it's own topic. Let's discuss it more.

What's in a name?

Ah, getting the username. And the system username is useless. We need to know the user who's logged in and making the call. There's a LOT of options here. Maybe it's the current user principal. That could be in a windows app, or in a web app. Maybe it's passed along the function call. Maybe it's in your request header.

The point is, we have NO idea where it could be. And trying to define every possibility is asking for failure. This becomes pretty obvious pretty quick that it's an area we need to leave open for the consuming application to define.

We should make a base class for our aspect then. Since it'll be incomplete, lacking an actual way to get the user name, it should be abstract too. "AuditAspectBase" sounds good. We'll define an abstract function then, let's call it "GetUser", that developers will have to implement to use our system. Now, we don't have to care! Yay!

The last thing our class needs to do is make sure that our concrete aspect definitions can get to the information it needs. Lucky enough, PostSharp allows us to grab all kinds of good information about the function call. From it, we grab the instance the function was called on, the parameters the function has, and the values of those. There's more too, but those are the important ones. All of this is passed through a "MethodExecutionEventArgs" object, which is defined in the PostSharp assembly. In the interest of making the consuming developer know as little about PostSharp as possible, we're going to take out the important bits, and store them in our base aspect for easier consumption.

Give me bits, dammit!

Alright, alright, alright. I've been making with lots of the werdz and none of the codez. The good news is, there's nothing left to discuss! Here's our AuditAspectBase:

   1: [Serializable]
   2: public abstract class AuditAspectBase : OnMethodBoundaryAspect
   3: {
   4:     private AuditAction _action;
   5:     private string _functionArgumentName;
   6:     private object _information;
   7:  
   8:     protected object Instance { get; set; }
   9:     protected object[] FunctionArguments { get; set; }
  10:     protected ParameterInfo[] FunctionParameters { get; set; }
  11:  
  12:     protected AuditAspectBase(AuditAction action, string functionArgumentName)
  13:     {
  14:         _action = action;
  15:         _functionArgumentName = functionArgumentName;
  16:     }
  17:  
  18:     public sealed override void OnEntry(MethodExecutionEventArgs eventArgs)
  19:     {
  20:         Instance = eventArgs.Instance;
  21:         FunctionArguments = eventArgs.GetReadOnlyArgumentArray();
  22:         FunctionParameters = eventArgs.Method.GetParameters();
  23:  
  24:         _information = GetArgumentValueByName(_functionArgumentName);
  25:  
  26:         CallAudit();
  27:     }
  28:  
  29:     public object GetArgumentValueByName(string name)
  30:     {
  31:         foreach (ParameterInfo info in FunctionParameters)
  32:         {
  33:             if (info.Name == name)
  34:             {
  35:                 return FunctionArguments[info.Position];
  36:             }
  37:         }
  38:  
  39:         return null;
  40:     }
  41:  
  42:  
  43:     private void CallAudit()
  44:     {
  45:         object[] args = new object[]
  46:                             {   _action,
  47:                                 GetUser(),
  48:                                 _information,
  49:                                 DateTime.Now
  50:                             };
  51:  
  52:         if(_information is IEnumerable)
  53:         {
  54:             Reflector.CallMethod(typeof(AuditManager), "TrackActionEnumerable", args, _information.GetType().GetElementType());
  55:         }
  56:         else
  57:         {
  58:             Reflector.CallMethod(typeof(AuditManager), "TrackAction", args, _information.GetType());
  59:         }
  60:  
  61:     }
  62:  
  63:     public abstract string GetUser();
  64: }

But I'm such a nice guy, that even though you're an ungrateful jerk, I'm going to go through this with you anyways.

First you'll notice it's marked as Serializable. This is required by PostSharp, so do it. Next you'll notice we inherit from "OnMethodBoundaryAspect". This is a PostSharp class (Laos specifically) that will help make things simpler for us. It'll handle the weaving of the aspect for us. If you don't know what that means, don't worry about it. But this specific one, will allow us to provide "hooks" into our function calls. It's going to give us functions we can override, like "OnEntry", "OnExit", etc. Feel free to check the class out. But the one we're going to focus on is "OnEntry". This does mean there is some room here to get fancy - perhaps you're only interested in auditing when the function exits, or want to make sure your audit doesn't write if an exception occurs. Things like this, and more, are possible. But, our implementation is only going to make use of OnEntry. We're getting a smidge ahead of ourselves though.

What do we need in our constructor? Well, from our previous list, we only need two things given to us - the action, and the function parameter that will have our object we're auditing. Using the information PostSharp will give us, we really only need the parameter name. So, our constructor will require those two, and we'll save them in private fields.

Next is our OnEntry method. We'll override the base one, and I'm going to seal it because I don't want it screwed with. We're going to get the calling instance, the arguments, and the parameter names from the event args, and store them in public properties. Our consuming aspects might need this to get the user name. We'll also need to get the object that's being audited. Then, we can call our auditors. We're going to break both of those into their own functions.

First is GetArgumentValueByName. Pretty obvious what it does. It'll loop through our parameter info, and once it finds a match, it'll return the argument itself. If it can't find a match, it'll return null. Because our attributes can't be generic, it can only return an "object". This function is pretty useful though, so lets make it public. It'll come in handy again soon.

The last piece is actually firing up our audit manager, in a function called "CallAudit". We want to control when this happens, so we'll make it private. The first thing we need to do, is create our argument array. Both of the AuditManager's TrackAction calls take the same 4 parameters in the same order. So, we'll grab the action, call our abstract GetUser function that our subclass will have defined, grab the object we're auditing, and our time stamp.

Next we'll check if our object is a collection, by seeing if it's of type IEnumerable. If it is, we'll use Reflector (part of Nvigorate, for those of you not following along) to call TrackActionEnumerable. If not, we'll just call the regular TrackAction. Reflector.CallMethod() has several overloads, including ones to call generic functions. First we tell it the type of our class we want to call (AuditManager), then the function name, then we pass in our arguments. If it's our collection, we need to get the type of collection, then the type of the elements it holds. Else, we can just get the type.

Last thing there is our abstract "GetUser" function. All we need from it is a string, which would be the username.

2 + 2 = 4

Time to add it all up, and see what we get! We're done now with our base aspect, so how do we implement and consume it? Easy. Let's make a couple of simple ones that are liable to have lots of reuse potential. Well, one obvious one is using this in an asp.net app. All we need to do this is:

   1: [Serializable]
   2: public class AspNetAuditAspect : AuditAspectBase
   3: {
   4:     public AspNetAuditAspect(AuditAction action, string functionArgumentName) : base(action, functionArgumentName)
   5:     {
   6:     }
   7:  
   8:     public override string GetUser()
   9:     {
  10:         return HttpContext.Current.User.Identity.Name;
  11:     }
  12: }

Doesn't get much simpler. To consume it then, all we do is:

   1: [AspNetAuditAspect(AuditAction.Update, "data")]
   2: public void SaveMyData(MyFirstType data)
   3: {
   4:     MyDataLayer.WriteData(data);
   5: }
   6:  
   7: [AspNetAuditAspect(AuditAction.Update, "data")]
   8: public void SaveMyDataCollection(List<MyFirstType> data)
   9: {
  10:     MyDataLayer.WriteDataCollection(data);
  11: }

Our aspect, and audit manager, and auditors themselves All handle the rest! Wether it's a collection or an instance. That's pretty sweet.

Here's another good aspect you might want to make use of (and yes, both of these will be included in Nvigorate when it gets updated):

   1: [Serializable]
   2:  public class FunctionArgumentAuditAspect : AuditAspectBase
   3:  {
   4:      private string UserNameArgument;
   5:  
   6:      public FunctionArgumentAuditAspect(AuditAction action, string functionArgumentName, string userNameArgument)
   7:          : base(action, functionArgumentName)
   8:      {
   9:          UserNameArgument = userNameArgument;
  10:      }
  11:  
  12:      public override string GetUser()
  13:      {
  14:          return GetArgumentValueByName(UserNameArgument).ToString();
  15:      }
  16:  }

This will handle the case where the user name is in the function call itself. Perhaps it's a webservice function or some such. So, that'd obviously need to be passed in on the constructor for our aspect. Then, using our GetArgumentValueByName function we defined in our base aspect, retrieving that value is simple. All we need to do then, to consume it this time, is this:

   1: [FunctionArgumentAuditAspect(AuditAction.Update, "data", "username")]
   2: public void SaveMyDataService(string username, MyFirstType data)
   3: {
   4:     MyDataLayer.WriteData(data);
   5: }

Now, we've managed to get the code noise out of our function, and auditing just "happens" for us. Our code is cleaner, better organized, and more flexible. This will make it easier to implement, easier to maintain, and easier to enhance.

This is the end, my only friend, the end

My favorite part is when I was done writing all this. Hopefully you can now go forth and audit like a man! Let's see that smile!

No more tears!

That'll do horse. That'll do.

Designed by Posicionamiento Web | Bloggerized by GosuBlogger