Latest Publications

A Misadventure in (someone else’s) GoF Land

So, here I go again. I don’t want my sparsely populated blog to turn into a collection of articles from the Daily WTF, but I do want to use a real world example, with the names changed to protect the innocent and guilty, to illustrate a point.

Design patterns, object oriented design, and the employment thereof can be just as much a cure in search of a disease as they are a cure for a disease. I’ve recently run into this lock, stock, and barrel in a project with my unnamed employer.

There is this project, let’s call it FLEA, which is a relatively straightforward web forms application. Written as a front end to handle some day to day process for a particular customer, it was made out to be some kind of shimmering example of, well what the architect might have said “Stuff I read on the back cover of a book in the lavatory.”

It had “everything” from service locators, coding against interfaces, and perhaps most glaringly, a monstrous construct that was referred to as “The Repository Pattern on steroids.”

It’s one thing to permit reuse of code by decoupling interface from implementation, but it’s quite another to hobble the developer by only making available interfaces that operate on fully populated domain objects.

I saw places where they would set a flag in a table record by loading an object, all of it’s sub-objects, and sub-objects, with carefully constructed IF-THEN logic to subvert graph loops, then set ‘myPerson.Disabled = true; PersonRepository.Save(myPerson);’. Now its fine if you present that interface, then detect that only the Disabled flag changed, and update just that, but what I found was quite another beast: It proceeded to delete half of the sub-objects, re-add them, then ignore the rest of them, and do a multi-table update of the full object (without using a join, anywhere in the logic) creating a new database connection for every other call. Needless to say, this worked brilliantly when the thing only loaded 2 or 3 master objects but when it was scaled to include ten thousand users, disabling a user, and I use this phrase QUITE LITERALLY sent the server out to lunch. You could go to the corner store and back before it finished running a top-down depth loop of connection grabbing calls to update stuff.

It’s still not probably quite clear the degree of repository extremism I’m talking about here:

We had:
PersonRepository (with methods to GetAll, GetByLocation, GetById, Update, and Delete)
PersonLocationRepository (which was called repeatedly for every person loaded, even when displaying them in a dialog box, to avoid having to learn what a join is.)
PersonLocationActivityRepository
ActivityRepository
PlacesRepository
PersonEthnicityRepositiry
PersonJobCodeRepository
PersonHRRecordRepository
PersonMotherInLawRepository (Ok, I’m making that up.)
plus about 20 more (not exaggerating, I swear.)

That variety alone is not a problem. But calling LoadPerson or whatever the actual routine was called invoked a service locator, which loaded a bunch of other things, which called a service layer, which called a business logic layer, which then loaded a repository, which loaded a DAL object for each one of these things, DEEP. So that loading a person ended up instantiating a generic service loader factory, which created a service layer factory, and then proceeded to call a business logic layer factory, to create a DAL factory, and eventually instantiate a DAL object, to read the ethinicity of a person. Then all of those were destroyed, and the PersonHrRecord was loaded with a new chain of the same stuff.

Loading the user administration page literally kicked off 300,000 database connections, and called half a million stored procedures, loaded ALL of the data client side, and filtered out the results with fancy anonymous method calls to IList enumerators. And just because they realized what a hulking mess this was on the database, all of the results were chucked into ViewState, which to save time was chucked into the Session.

I, along with two others, retooled the entire project using Linq To SQL in about a month of half time working on this, but wow it was amazing.

If it would have been a well crafted joke, it would have been too meticulously executed to be even considered funny.

My conclusion is that it’s actually bad because of the way they made the database calls. Not the patterns themselves. There was also a demonstrable lack of understanding of what the implications of iteratively looping and loading are for performance.

The theory, I was told, was to make it reusable. From a design perspective, it looked like they might have been able to do that, but when I looked at what the actual layers were doing, apart from what they looked like in UML diagrams, MAN was it a mess.

Put another way, it’s one thing to design your application for reusablity, but quite another to implement it in the same spirit. At the outset of the project, it probably was originally, long ago, in a galaxy far away, a good idea to design a service layer separate from business logic layer, and a DAL layer, etc. But to just stop there and make the rest ‘work somehow’ without concern for the consequences of the ways the data actually got into the bottom bits.. and the way that it was unfiltered until the top bits, that was just silly. Of course I shouldn’t blame the pattern, but if they hadn’t been aiming for that methodology, as opposed to the operations that they were actually performing underneath it, it might have been a better ending.

Sure it’s common to say ‘That’s just an implementation detail’ when you’re designing, and scoff it at for a while until it comes time to implement it, but holy crap, when it comes time to implement it, all you HAVE are the implementation details. At that point they not only deserve SOME attention, but ALL of it.

This is the difficulty with GoF: Pretty Conceptual Pictures. People think they understand these, and it gives them the confidence to go implement this stuff without learning the other fundamentals. Like, well, SQL. (I’ve actually heard things like “I’m a C# developer, I don’t really DO SQL!” If I had my way, I’d put them in a corner with a command line OSQL.exe tool for the next three months and not let them come back until they’d gone through Books Online from top to bottom three times after that statement.) Pretty pictures like this also make it easy to gloss over an actual lack of planning on the bottom so that management doesn’t know they’ve bought into a dud until the delivered app kills the clients database farm.

The goal of an application is to have it work. Whatever abstractions you put on it to make it ‘easier to develop’ are not billable features to the client. You do not get extra line items on the proposal for Service Locator Patterns, and certainly that’s not a justification for screwing up the actual project.

Customer’s Boss: “This thing is a piece of crap, it kills our servers!”
Developer: “But it was completely decoupled from the implementation!”
Customer’s Boss: “Oh, right, sorry, I guess you did actually deliver what we ordered. I’m glad you know what you’re doing when it comes to object oriented design. So many people focus solely on the final product, without so much as a thought devoted to the design process. Clearly you have attended one of our finest higher educational institutions to know such things. And besides, I didn’t realize you were a SENIOR developer, I should be yelling at a grunt code right now. You shouldn’t be troubled with implementation details.”
Developers Boss: “I smell a RAISE for one of our brightest SENIOR developers!”
Developer: Nancy Kerrigan and I are going to Disney Land!

I’m a convert… Well, lets say I’m Converting

ASP.NET MVC. I know I’m not the first on this train. I don’t know why it took me so long… Ok, yes I do, and I’m going to tell you, in a longer than average anecdotal story about the problem: Ruby-on-Rails. People talk about Ruby like it’s the best thing since sliced Swiss cheese. And Rails, well that’s just grilled cheese sandwiches!

Some people talk that way, anyway. I realize that it may be developer-reputational suicide, but I have to come clean: either I’m not enough of a geek to appreciate it, or no, it’s not [the best thing since the proverbial sliced stuff.] Some of Ruby is great, in that it can do some things quickly with little ‘ceremonial preparation,’ but then again the syntax is so abrupt and boxy that it actually appears HARDER for me to read than C#. This is in complete contradiction to those who assert that Ruby is so nice that you don’t even have to comment it. (Because it’s so obvious what the code is doing just by looking!) Maybe I’m just afraid of change, or talking my book, but I’m not feeling Ruby, and Rails, (which was the quintessential example of the MVC pattern,) well they kinda left me with a less than sweet taste about the MVC concept.

I know what you’re thinking here, and you’re right. It’s not a fair leap to make from “Rails is not My Thing” to the “MVC Pattern Sucks.” I try to be rational, and reasonable, but clearly here I wasn’t. Sorry about that.

Now back to ASP.NET MVC. It’s the coolest thing since sliced ASP.NET.

The concept of MVC made sense to me. Inversion of Control, Separation of Concerns, Dependency Injection, convention over configuration, testability… even back from my Rails tribulations. But I still didn’t feel it. It was someone else’s sense making not mine.

So then I decided I’d try to learn this jQuery thing… and I started playing around with raw HTML pages, throwing some jQuery into them in script tag after tag, and suddenly it hit me… Handling updates client side was great, simple, and often light. If all I had to do was juggle CSS classes and read a few fields here and there, Post a single variable now and again, I didn’t need the whole ball of wax! But, using web forms, you don’t have much of a choice. It was not fun having to register client scripts every time with a control’s server generated client name to go with controls that used the INamingContainer model. I started using the CssClass attribute quite improperly to get a grip on elements that should just have been simple enough to grab by ID. And then finally, the last straw was when I ran into a string of ViewState validation errors. Now, if you’re using ViewState, fine, there is a good reason to want ASP.NET to validate it. But I wasn’t using it, and ViewState was just an incidental side effect of the fact that web forms hate people who are using JavaScript.

So now that I had developed a new passion for everything jQuery, from the flashy jQuery UI, to simple things, I had to find a way to keep feeding it. Such ‘fancy’ sounding things were kids play now, like automatically italicizing certain text artifacts for fun and profit, or making post backs nothing but a service call or two. Services, as it turned out, were very comparable to Controllers, and in fact, could be serviced just as easily by Controllers as they could by ‘real services.’ Models were already familiar — I know about the repository pattern, I’ve done that quite a lot, even before I’d ever heard of ASP.NET MVC or indeed even Ruby on Rails. So the only things left to ‘get’ were the fact that both the view and the model were to be instantiated by the controller and called and returned (respectively.) The idea wasn’t foreign to me, but it now suddenly clicked. “Wait! This works, and I don’t have to worry about some of the things I spent time worrying about before.” Not to mention that the integration with Visual Studio is great. Another big bonus was the URL structure was nicer.

It took me a bit to realize that I was supposed to write custom html helpers, or still better, encapsulate the helper stuff into the equivalent of a ViewModel class that will do the html helping it its own logic.

So, I realize there is nothing really profound or new I’ve been talking about here, but I just have been so moved by ASP.net MVC that I had to say something about it. I think I’ll try to stick with using it wherever I can. The One Complaint that I do have with it is that SharePoint doesn’t support it, and I support SharePoint. Oh well. I always knew SharePoint wasn’t perfect either.

Theme, again

Sorry, I can’t help it, the other theme was too busy, and I felt guilty about it almost every other day. (I didn’t think of it every day, or I would have every day.) I like this one. It’s much more my style — though nothing is ‘technically’ my style since I haven’t ever made my own theme.

Bad Microsoft!

Eating your own dogfood again, Microsoft?

I’m on a page published by Microsoft, the makers of ASP.NET and the creators of the best practices for its use. Notice how they go to all of the trouble of defaulting the customErrors=”On” in the web.config file when you create a new one. Right, so… what did I see smack in the middle of their public facing ‘unamed purpose site’ today? A stack dump. (Note: it’s a heavily edited screencap so as not to give anything away unduly, but enough to show that it really happened.)

Tsk! Tsk!


bad practices

(All in good humor, of course.)

Quick, just had to say something

This guy, Daniel Pollard, has a really cool blog with lots of neat asp.net/SharePoint/Webparts ideas and examples on it. It’s a good thing not to miss. He also notes that “JQuery is the Jessica Alba of JavaScript,” which I found terribly amusing. Ok. That’s it. Just an unsolicited advertisement for his blog cause I liked it.

Parameterizable SharePoint SqlQueryWebPart

I’ve been so frustrated with the lack of a flexible way to just display the output of a raw SQL Query via SharePoint so I wrote one.

At first I thought I didn’t need parameters, but I was wrong. Everyone needs parameters, or else it’s not all that useful. Of course, that doesn’t mean you can’t still do parameterization correctly. So I took this lemon-like opportunity to demonstrate how to [make lemonade and...]

  • Write Web Parts for WSS3
  • Integrate a Parameterizable SQL Query (with a few security caveats I’ll readily admit, but injectibility isn’t likely one of them, more on this in a bit) with an in-page GridView and show the output
  • How to hook it up to the Form web part using the standard SharePoint IWebPartRow interface.

First thing is first, to run a web part that hits a SQL server directly, you need Medium Trust, or to have modified your policy file to allow Sql Query permissions in your SharePoint environment.

Not far after first thing is the second thing, which I’ll discuss now (second.) I’m using the non-sharepoint style web part, otherwise known as ASP.NET web part (System.Web.UI.WebControls.WebParts.WebPart) as the base class for my part.

C#


    [Guid("7fd6fa72-9214-4cf0-b30b-ef7d931261cb")]
    public class SqlQueryWebPart : System.Web.UI.WebControls.WebParts.WebPart {

Why have I done this? Becaue it’s the new recommended best practice from the kids at Microsoft who built it. If you need SharePoint, you can get it with a wink, a nod, and the SharePoint Object Model.

I have the Query string, the connection stuff, the authentication type, and probably a few other doodads I forgot to mention as web part parameters. (If you look at my code, the Sorting stuff is all commented out cause I kept screwing it up, and I felt that I didn’t really want to mess with it anymore for the time being. The web part works without it.) The reason I’m bothering to show you on the page here is to point out a few things, the Attributes with which we adorn the properties are slightly different than those of the SharePoint web parts variety of them you might know from WSS V2.

Personalizable means that it will be serialized and stored as a parameter and re-populated after the part is instantiated.

WebBrowsable means that it will genearate an editor field in the Default Editor Part (These were formerly known as ToolParts in the old model,) when you go in to configure the web part.

WebDisplayName just means “This is what we’ll label it in the Default Editor Part”.

Category is short for “The name of the collapsable section under which it appears in the default Editor Part.”

C#


        [Personalizable(),
         WebBrowsable(true),
         WebDisplayName("Grid Lines"),
         Category("Query Details")]
        public GridLines GridLineConfig {
            get {
                return m_GridLines;
            }
            set {
                m_GridLines = value;
            }
        } private GridLines m_GridLines = GridLines.Both;
 
        
        [Personalizable(),
         WebBrowsable(true),
         WebDisplayName("Server Name"),
         Category("Query Details")]
        public string ServerName {
            get {
                return m_hostName;
            }
            set {
                m_hostName = value;
            }
        } private string m_hostName = string.Empty;
 
        
        [Personalizable(),
         WebBrowsable(true),
         WebDisplayName("Database Name"),
         Category("Query Details")]
        public string DatabaseName {
            get {
                return m_dbName;
            }
            set {
                m_dbName = value;
            }
        } private string m_dbName = string.Empty;
        
        
        [DefaultValue(AuthType.SQL),
         Personalizable(),
         WebBrowsable(true),
         WebDisplayName("Authentication"),
         Category("Query Details")]
        public AuthType AuthentictionMethod {
 
            get { return m_AuthType; }
            set { m_AuthType = value; }
 
        } private AuthType m_AuthType = AuthType.Windows;
 
        
        [Personalizable(),
         WebBrowsable(true),
         WebDisplayName("User Id"),
         Category("Query Details")]
        public string UserName {
            get {
                return m_loginUser;
            }
            set {
                m_loginUser = value;
            }
        } private string m_loginUser = string.Empty;
 
        
 
        [Personalizable(),
         WebBrowsable(true),
         WebDisplayName("Password"),
         Category("Query Details")]
        public string Password {
 
            get { return m_MaskedPassword; }
 
            set { m_MaskedPassword = value; }
 
        } private string m_MaskedPassword = string.Empty;
 
        
        [Personalizable(),
         WebBrowsable(false)]
        public string InnerPassword {
 
            get { return m_PrivatePassword; }
            set { m_PrivatePassword = value; }
        } private string m_PrivatePassword = string.Empty;
 
        
        [Personalizable(),
         WebBrowsable(true),
         WebDisplayName("Select Query"),
         Category("Query Details")]
        public string SQLQuery {
            get {
                return m_QueryText;
            }
            set {
                m_QueryText = value;
            }
        } private string m_QueryText = string.Empty;
 
        
 
        [Personalizable(),
         WebBrowsable(true),
         WebDisplayName("Page Size"),
         Category("Query Details")]
        public int PageSize {
            get {
                return m_PageSize;
            }
 
            set {
                m_PageSize = value;
            }
        } private int m_PageSize = 10;

Now, for the security caveats: I’ll tell you that it’s a little dangerous to put the Sql username and password in your web part instances configuration properties, cause if someone decides to export it, then you can see the username and password; but this does not apply if you use the Windows authentication mode. So, you figure out whether or not you can work around the issues.

As for the Parameterization:

I extract the expected parameters, as specified in the web part property, using a string-through, ie, I jump through the string one character at a time, looking for the parameters. This means I get them all in one pass, instead of a bunch of regexes or finds and splits, etc. They then go into a list, which is the list of expected ‘named paramters’ coming from the connected web parts. If I don’t get a match between the ones that are in your query and the ones connected to me, then I display a message.

C#


private void ExtractExpectedParameters(string p) {
 
            m_expectedParams = new List<string>();
 
            bool collecting = false;
            StringBuilder sb = new StringBuilder();
            for (int x = 0; x < p.Length; x++) {
 
                char c = p[x];
                
                if (collecting) {
                    bool dropOut = false;
 
                    if (x == (p.Length - 1)) {
                        sb.Append(c);
                        dropOut = true;
                    }
 
                    if (!char.IsLetterOrDigit(c) || dropOut) {
                        collecting = false;
 
                        // found a parameter name
                        m_expectedParams.Add(sb.ToString());
                        sb = new StringBuilder();
                    }
                    else {
                        sb.Append(c);
                    }
                }
                
                if (!collecting &&
                    c == '@') {
                    collecting = true;
 
                    m_HasParameters = true;
                }
            }
        }

So, after I set the fact that I have some parameters, I have to know when to read the values, and after much tribulation, and reference perusal, I have discovered that the time to suck the data from your producing source is OnPreRender:

C#


protected override void OnPreRender(EventArgs e) {
 
            // parse the SQL and rip out what we're looking for. Also set m_HasParameters
            ExtractExpectedParameters(this.SQLQuery);
 
            bool canQuery = false;
 
            if (m_HasParameters) {
                if (m_provider != null) {
                    m_provider.GetRowData(new RowCallback(GetRowData));
                }
 
                canQuery = false;
            }
            else
                canQuery = true;
 
            EnsureChildControls();
 
            
 
            m_CmdParameters = new Dictionary<string, object>();
            
            // we don't want to run any query unless we have all the right params and stuff.
            
 
            if ( m_HasParameters &&
                 m_provider != null) {
                PropertyDescriptorCollection props = m_provider.Schema;
 
                if (props != null &&
                    props.Count > 0 &&
                    props.Count == m_expectedParams.Count &&
                    m_tableData != null &&
                    m_tableData.Row != null &&
                    m_tableData.Row.ItemArray != null &&
                    m_tableData.Row.ItemArray.Length == m_expectedParams.Count) {
                    foreach (PropertyDescriptor prop in props) {
                        this.m_CmdParameters.Add(prop.Name, m_tableData.Row[prop.Name]);
                    }
 
                    canQuery = true;
 
                }
                else {
 
                    
                        registerError(string.Format("Supply required parameters before results can be displayed. Expecting: {0}.", string.Join(",", m_expectedParams.ToArray())));
                    
 
                }
 
            }
            else {
 
                if (m_HasParameters) {
                    registerError(string.Format("Based on the specified query, one or more parameter(s) are required. </br> Please connect this web part to a Form Web Part to obtain the required input parater(s): {0}.", string.Join(",", m_expectedParams.ToArray())));
                }
            }
 
            if (canQuery) {
                RetreiveData();
                RebindGrid();
            }
 
            
            base.OnPreRender(e);
        }

Note about catching exceptions: In this situation, I’ve decided to catch this error and display the output on the screen, so I don’t let it get through. This is with the intention of allowing one to not only know that something broke, but to help fix it without bringing down the entire process. If you are really concerned, you can do what I have done in the query results bit and only display the error text to admins, but I display the query parameter errors to everyone for the sake of helping the poor user who is hooking this up on their MySite (or wherever else some newbie to SharePoint, but not to SQL, might try it) trying to make sense of the idea of connected web parts. If you use my part, you can change that before deploying it. I’m fully aware that under most circumstances one does not want to catch and hold an exception. But in this case, I feel that it is warranted.




Finally, the coup de gras is to actually perform the parameterized query. No biggity now.

C#

private void RetreiveData() {
 
            SqlConnection oConn = null;
            if (m_MaskedPassword != passowrdMask) {
                InnerPassword = m_MaskedPassword;
                m_MaskedPassword = passowrdMask;
            }
 
            if (m_AuthType == AuthType.SQL) {
                m_connectionString = string.Format("Data Source={0};Initial Catalog={1};User Id={2};Password={3};Persist Security Info=false", m_hostName, m_dbName, m_loginUser, m_PrivatePassword);
            }
            else {
                m_connectionString = string.Format("Data Source={0};Initial Catalog={1};Integrated Security=SSPI", m_hostName, m_dbName);
            }
            
            try {
                oConn = new SqlConnection(m_connectionString);
 
                oConn.Open();
 
                using (SqlCommand cmd = oConn.CreateCommand()) {
 
                    cmd.CommandText = this.m_QueryText;
                    cmd.CommandType = CommandType.Text;
 
                    if (m_HasParameters) {
                        //do parameters!
                        foreach (String s in m_CmdParameters.Keys) {
                            if (!string.IsNullOrEmpty(m_CmdParameters[s].ToString())) {
                                cmd.Parameters.AddWithValue("@" + s, m_CmdParameters[s]);
                            }
                        }
                    }
 
                    if (!m_HasParameters ||
                          cmd.Parameters.Count == m_CmdParameters.Count) {
                        
                        SqlDataReader dr = cmd.ExecuteReader();
                        dt = new DataTable();
 
                        dt.Load(dr);
 
                    }
                    else {
                      
                        if (m_HasParameters) {
                            registerError(string.Format("Supply required parameters before results can be displayed. Expecting: {0}.", string.Join(",", m_expectedParams.ToArray())));
                        }
 
                    }
                }
            }
            catch (Exception ex) {
 
                // this is a Smart Error Handler, in that it shows you a generic message
                // if you're a Schmoe and a Detailed description if you're a site collection admin.
 
                SPUser usr = SPContext.GetContext(this.Context).Web.CurrentUser;
 
                if (usr.IsSiteAdmin) {
                    registerError(string.Format("{0}: {1} <p style=\"font-weight:bold;\">{2}</p><p>Connection String = {3}", ex.GetType().Name, ex.Message, ex.StackTrace, m_connectionString));
                }
                else {
                    registerError("Error loading SqlQuery Web Part. Please contact your administrator.");
                }
 
            }
            finally {
                if (oConn != null)
                    oConn.Dispose();
            }
 
        }

This is a rough model of the web part, and in need of a bit of refactoring to remove some duplication, but you get the idea. I hope.

Attached is the code files/project for VS 2008. Click to Download it. I’ve decided, after considering it carefully that I will let you have the wsp already built. No warranty. If you don’t understand how it works, you can ask, and I’ll try to answer. And NO, I won’t customize it for your needs, nor will I change it to do something else — with one exception: if you figure out a way to fix the paging junk then I’ll put your fix in and give you credit. (Yes I could do it eventually, but I don’t want to mess with it right now!) So, here have at it!

Late Breaking edit: The code provided here has been fixed now that I got my real version from the source control. I’ve corrected the problem with the ‘default values’.

(PS: Use WSSOnVista from BambooSolutions.com to install SharePoint on your development Vista host and run/test/debug without remote desktop etc.)

Last Name Generator

If you’re trying to work with a ton of fake data, sometimes writing the code that generates all the fake stuff, yet still makes some bit of sense is an annoying and time consuming process. I think so too. Here, have a pronounceable (I think in most cases) LastNameGenerator class to help you on your quest.


public class LastNameGenerator
    {
        public string GetLastName()
        {
            StringBuilder sb = new StringBuilder();
            StringGetter[] Getters = Generator[rnd.Next(Generator.Count - 1)];
            
            bool first = true;
            foreach (StringGetter g in Getters)
            {
                string s = g();
 
                if (first)
                {
                    s = string.Format("{0}{1}", Char.ToUpper(s[0]), s.Substring(1));
                    first = false;
                }
 
                sb.Append(s);
                
            }
 
            return sb.ToString();
        }
 
        // certainly I've missed a few
        static List<string> NonEndingConsonantChunks = new List<string>(new string []
        {
            "b", "br", "bl", "c", "ch", "cr", "cl", "d",
            "dr", "f", "fl", "fr", "g", "gr", "gl", "gh", "h", "j", "k",
            "kr", "kl", "l", "m", "n", "p", "pl", "pr", "q", "r", "s",
            "st", "str", "sh", "sl", "sp", "sk", "sc", "sm", "sn",
            "t", "tr", "v", "w", "x", "y", "z"
        });
 
        static List<string> EndingConsonantChunks = new List<string>( new string []
        {
            "b",
            "c", "c", "c", "c", "c", "c", "c", "c", "c",
            "ld",
            "d",
            "f",
            "g",
            "gh",
            "h",
            "k",
            "l",
            "lm",
            "ln",
            "m",
            "n", "n","n", "n", "n", "n", "n", "n", "n", "n", "n", "n",
            "nd",
            "p",
            "r",
            "rd",
            "rn",
            "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s",
            "t", "t", "t", "t", "t",
            "v",
            "w",
            "x",
            "y", "y", "y", "y", "y",
            "z"
        });
 
        static List<string> Vowelies = new List<string>(new string[] {
            "a",
            "e",
            "i",
            "o",
            "u",
            "ou",
            "ea",
            "ie",
            "ei"
        });
 
        // seed the random
        Random rnd = null;
 
        public  string NEC()
        {
            return NonEndingConsonantChunks[rnd.Next(NonEndingConsonantChunks.Count - 1)];
        }
 
        public  string V()
        {
            return Vowelies[rnd.Next(Vowelies.Count - 1)];
        }
 
        public  string EC()
        {
            return EndingConsonantChunks[rnd.Next(EndingConsonantChunks.Count - 1)];
        }
 
        public delegate string StringGetter();
 
        public List<StringGetter[]> Generator = null;
 
        StringGetter[] Seq(params StringGetter [] arr )
        {
            return arr;
        }
 
        public LastNameGenerator()
        {
            Generator = new List<StringGetter[]>();
 
            rnd = new Random(Convert.ToInt32(DateTime.Now.Ticks % Int32.MaxValue));
 
            Generator.AddRange(new StringGetter [][] {
                Seq(NEC, V, NEC, V, EC),
                Seq(NEC, V, NEC, V, NEC, V, EC),
                Seq(NEC, V),
                Seq(NEC, V, EC)}
            );
        }
 
    }

On a sample run I get such classic names as:

Skukrapond, Nubas,
Cloudeas, Ge, Wiekleagheaw,
Houtabley, (I think this is a word)
Peaten, Nobon, Smislokien,
Clestrospus, Hoveac, Ghidriepien,
Wu (ethnically neutral),
Preacreashut (thats a good one),
Vieglakrot, Flaqorug, Ki, Joyogab, Spukrout
Leslalm, Pou , Droughaskarn ,Klocow,
Ka, Kre, Treakoun , and Koward

You can adjust the arrays, and the frequency of characters to suit your needs. Have fun.

Syntax is simple to use:


            // new it up
            LastNameGenerator gen = new LastNameGenerator();
 
            for (int x = 0; x < 100; x++)
            {
                // call it forever
                Console.WriteLine(gen.GetLastName());
            }
 

Edit Note: Cleaned up the vast number of newlines to make it obvious why I’ve repeated things.

Custom SharePoint Workflow MetaData

Have you ever wondered what VooDoo is going on in the “MetaData” element in the sharepoint workflow template definition (Workflow.xml)? No? Well I have.

It’s not documented, but you can read it using the SharePoint Workflow API. (The method you can call is documented, that is, listed, in the MSDN documentation. It’s not actually explained, or as I would say it, ‘documented.’)

If you see our friend, the class SPWorkflowTemplate on MSDN, you’ll notice that the Item property is there, but it’s not telling you that you basically just use that to pull the metadata.

So, from within a workflow template code block, how would you access this magical SPWorkflowTemplate? There are a bunch of ways, but the simplest is to use the SPWorkflowProperties object that’s bound to your OnWorkflowActivated activity.


 
     string GetMetaDataString(string valueName) {
 
            // get the template.
            SPWorkflowTemplate template = workflowProperties.Workflow.ParentAssociation.BaseTemplate;
 
            // WARNING: check the value for null before .ToString()
 
            // in C# there is only an indexer that aliases the .Item
            return template[valueName].ToString();
 
        }
 

Well what’s that good for? I’m not sure! That’s really up to you. I did see a couple of cool guys from the Ted Pattison Group using it to make a framework for doing effective workflow in WSS 3. (Here’s a link, it’s called WSS3Workflow, and it’s great.)

I always end up putting things in the web.config, but some of the more static things I might never change, but technically want to be able to without recompiling the thing, might go in there. I don’t know. I just thought it was cool.

Ok, I promised a better LINQ example..

So, I made one, except I posted it on CodeProject instead of this blog. I figured with this thing I wanted to get a lot of people in the audience. Not to mention that makes free advertising for me and my blog anyway.

Court Injunction Levied against Neighborhood Watch

IMAGINARY, NY — Associate Pressed

Three adolescent members of the Imaginary Neighborhood Watch were slapped with a court injunction today barring them from presenting their findings about the locked status of the Mrs. Fanny Frankenwilder’s back door. The three had planned to present a litany of possible exploits for such a door lock state at the next INW meeting. The three state they were just trying to let her know, and that she should indeed lock it, but the judge ordered their illustrative PowerPaint presentation to be confiscated.

“I just thought that it was a good idea for us to let her know it’s something she should consider taking care of,” said Herb Smith, of Nonsense Ave.

“Honestly, I think they’re only inviting people to walk in. It’s better for everyone not to KNOW that I leave my door unlocked in the first place. After all, who has time to go around locking doors these days? What with my bridge club and the Red Hat meeting, I don’t have time to even consider, let alone implement their proposed solutions. They need to stop this malicious prattle before someone just up and walks in using one of their contrived exploits of my door state’s vulnerabilities, ” Frankenwilder comments.

The Frankenwilder case is not the only such incident where dissident teenagers reportedly spilled the beans on possibly glaring security holes in the neighborhood. It is, however, probably among the most interesting and daring exposures of commonplace security laxity to come to light in recent years.