mo.notono.us

Thursday, July 31, 2008

Dell: "One year" = One year minus 24 hrs

Apparently my home computer shipped on 07/31/07 - a year ago, today.  Since I had a one year warranty, all logic dictates that that warranty should expire on 07/30/08, yesterday.

Fine, Dell agrees...  Except Dell claims the warranty expired at midnight, the morning/beginning of 07/30/08, not midnight, the evening/end of 07/30/08, as should be correct.

I had fun trying to explain that my warranty should still be in effect to the Indian helpdesk staff at Dell last night, considering a) their computers told me my warranty had expired, and b) they were already a day ahead (07/31/08) by the time I called them from the US. 

In the end, after talking to three people my warranty was indeed honored, but some developer should go back and check his/her date and time calculations...  I wonder if the mistake was using 365 days as a year (this year being a leap year) or if they just couldn't figure out the correct logic...

Labels: , , , ,

iPhone Business Card Scanner Software

Ok, this is partly an experiment to see if putting a sexy title on the blog will result in additional hits, and partly stating yet another pre-stolen idea (not necessarily restricted to an iPhone):  Use a client-server model mobile app that lets you take a picture of a business card, email/MMS it to the server and have the server respond with a contact file.

Turns out scanR already does exactly this, but I think their monetization scheme is all wrong - they have a free trial that allows 1 - ONE - upload, and then they jump straight to 2.50 quid ($5) per month or 15 quid per year.  Seems this could be monetized quite easily through targeted ads accompanying the business card.

Mistake number two is that they haven't taken advantage of the hype and made an iPhone app that does the picture taking, server log in, image and contact file transport (and ad display)...

Labels: , , , ,

Tuesday, July 29, 2008

SQL: Once again, for the record...

Performance is not a compelling reason to choose stored procedures over dynamic SQL.

At work today I was surprised to overhear a suggestion to dynamically create stored procedures in SQL, just "to take advantage of the added performance of stored procedures" over dynamic/ad-hoc/inline SQL.

So here we go again, for the record:

Performance of Stored Proc vs. Dynamic/Ad-hoc SQL

Actual, simplistic test by BlackWasp:

image
http://www.blackwasp.co.uk/SpeedTestSqlSproc.aspx

SQL Books On Line:

SQL Server 2000 and SQL Server version 7.0 incorporate a number of changes to statement processing that extend many of the performance benefits of stored procedures to all SQL statements. SQL Server 2000 and SQL Server 7.0 do not save a partially compiled plan for stored procedures when they are created. A stored procedure is compiled at execution time, like any other Transact-SQL statement. SQL Server 2000 and SQL Server 7.0 retain execution plans for all SQL statements in the procedure cache, not just stored procedure execution plans. The database engine uses an efficient algorithm for comparing new Transact-SQL statements with the Transact-SQL statements of existing execution plans. If the database engine determines that a new Transact-SQL statement matches the Transact-SQL statement of an existing execution plan, it reuses the plan. This reduces the relative performance benefit of precompiling stored procedures by extending execution plan reuse to all SQL statements.
http://msdn2.microsoft.com/en-us/library/aa174792(SQL.80).aspx

DeKlarit dev take:

People still thinks Stored Procedures are faster, even if there is much evidence that shows otherwise. Fortunately, when they go and ask the LinQ for SQL/Entities team they get the same answer than they get from me. They are not.
http://weblogs.asp.net/aaguiar/archive/2006/06/22/Stored-Procs-vs-Dynamic-SQL.aspx

There are lots of good reasons to use Stored Procedures, and lots of good reasons to use Dynamic SQL.  Performance is rarely a good reason to chooswe one way or the other, and if performance is  the decision factor often dynamic sql comes out on top, e.g. if you can use dynamic sql to eliminate an OR for example (as when using optional query parameters in an advanced search form).

Labels: , , ,

Monday, July 28, 2008

Another Way to Google Yourself

...err - search for yourself, I mean:  Cuil.com has launched and has some interesting results.

Not sure how the Cuil index works, but the top listings for "oskar austegard" are a bunch of public pages for services I no longer use.  Contrast that with Google, whose top results are my blog, my LinkedIn profile, my Facebook profile, my Yahoo profile, etc.

Less narcissistic searches render similarly strange results - a search for Microsoft on Google gets you the main page, the download center, help and support and the wikipedia page, etc.  The same search on Cuil gets you Microsoft Office, Help and Support, more Office, MS UK, and Quick Basic - Quick Basic???

I don't think Google has to worry quite yet.

Labels: , , ,

Thursday, July 24, 2008

SQL (reminder): Executing a Stored Procedure Dynamically with SP_EXECUTESQL

I've been doing SharePoint too long if I need a reminder on how to do this...

If you, for some reason that I will not go into here, have a table cell with the name of a stored procedure, along with the parameters for said procedure, and you wish to execute that procedure, below is the general format:

Stored Procedure Name and parameter values:

MySP 1, 'a', 'foo', 'New Jersey'

Valid SQL to execute:

EXEC SP_EXECUTESQL 'EXEC MySP 1, ''a'', ''foo'', ''New Jersey'''

SP_EXECUTESQL does not allow inline string manipulation (such as prepending the EXEC statement or escaping the single quotes), so that manipulation has to be done before executing the string.

Thanks to Andre for the reminder.

Labels:

On Multitasking

My friend and former colleague, Andre Vazhnov, has a tendency of randomly throwing out some thought-provoking statements.  Last night he asked me what I thought about multitasking.  While being an effective multitasker seems to be a requirement for every other job, and by Andre's measure, nearly every candidate he interviews claims to be good at multitasking, he claims he's never seen it working well in practice.  Further, he states, the term is severely generalized if not misplaced entirely.

I have to agree.  While I happen to be typing this while at the same time (sort of) IM'ing Andre, and having a conversation with a current colleague about BI, and another about his current project, as well as checking my email, I doubt I am improving the quality of any of these actions by virtue of doing the others at the same (or nearly the same) time.

Let me be clear, I believe the ability to multitask with minimal loss of efficiency is a very important skill.  I also believe that multitasking can help improve attention in a monotonous work environment.  But I don't believe it ever improves overall efficiency.

Several studies seem to agree with this, as do some notable bloggers.  Heck, even Dale Carnegie®, who offers a course on multi-tasking seems to acknowledge that it's a beast that needs to be managed, rather than fed.

Labels:

Wednesday, July 23, 2008

Google should buy Aptana

I guess I'm slow, cause today was the first time I heard of Aptana, its IDE, and the deliciously tempting Jaxer AJAX server, which lets you "write entire applications or presentation layers in Ajax", with "full DOM and JavaScript on the server", "seamless communication between browser and server", "database, file and socket access from JavaScript", as well as "share[d] validation code on the brower and server", all using "open-source, standards[and ...] the APIs you already know". "If you know JavaScript and HTML, you can already build Jaxer applications."

This set me off on quite the tangent, downloading Jaxer (as well as Studio), and then looking at the code examples, and how Dion Almaer (who is both on the Google Gears team and on the Aptana Advisory Board) have integrated Jaxer with Google Gears, and also how there is an MVC drive going on.  (Here's another take on (client-side) JavaScript MVC.)

Aptana is also working on a cloud solution for PHP/Jaxer/(and soon) Ruby on Rails - it certainly sounds interesting, but I'm not going to attempt to pretend I grok the underlying architecture, compared to something like the Google App Engine.

Finally, the Aptana Advisory Board reads like a Who's Who on JavaScript and Ajax frameworks.

Google should buy these guys.  It would fill both an IDE and a language-hole, and it'd be some serious competition for Microsoft's Live Mesh, Silverlight and DLR strategy.

Labels: , , , ,

Tuesday, July 22, 2008

C#: String.Inject() - Format strings by key tokens

I generally prefer to use String.Format() instead of doing a bunch of string additions, but constantly find myself splitting the code onto multiple lines with an appended comment to keep track of the indices:

string myString = string.Format("{0} is {1} and {2} is {3}",
  o.foo, //0
  o.bar, //1
  o.yadi, //2
  o.yada //3
);

Wouldn't it be nice you could instead write something like this?:

string myString = "{foo} is {bar} and {yadi} is {yada}".Inject(o);

Well, now you can - see the string extension method Inject below; it accepts an object, IDictionary or HashTable and replaces the property name/key tokens with the instance values.  Since it uses string.Format internally, it also supports string.Format-like custom formatting:

   1:  using System;
   2:  using System.Text.RegularExpressions;
   3:  using System.Collections;
   4:  using System.Globalization;
   5:  using System.ComponentModel;
   6:   
   7:  [assembly: CLSCompliant(true)]
   8:  namespace StringInject
   9:  {
  10:    public static class StringInjectExtension
  11:    {
  12:      /// <summary>
  13:      /// Extension method that replaces keys in a string with the values of matching object properties.
  14:      /// <remarks>Uses <see cref="String.Format()"/> internally; custom formats should match those used for that method.</remarks>
  15:      /// </summary>
  16:      /// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
  17:      /// <param name="injectionObject">The object whose properties should be injected in the string</param>
  18:      /// <returns>A version of the formatString string with keys replaced by (formatted) key values.</returns>
  19:      public static string Inject(this string formatString, object injectionObject)
  20:      {
  21:        return formatString.Inject(GetPropertyHash(injectionObject));
  22:      }
  23:   
  24:      /// <summary>
  25:      /// Extension method that replaces keys in a string with the values of matching dictionary entries.
  26:      /// <remarks>Uses <see cref="String.Format()"/> internally; custom formats should match those used for that method.</remarks>
  27:      /// </summary>
  28:      /// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
  29:      /// <param name="dictionary">An <see cref="IDictionary"/> with keys and values to inject into the string</param>
  30:      /// <returns>A version of the formatString string with dictionary keys replaced by (formatted) key values.</returns>
  31:      public static string Inject(this string formatString, IDictionary dictionary)
  32:      {
  33:        return formatString.Inject(new Hashtable(dictionary));
  34:      }
  35:   
  36:      /// <summary>
  37:      /// Extension method that replaces keys in a string with the values of matching hashtable entries.
  38:      /// <remarks>Uses <see cref="String.Format()"/> internally; custom formats should match those used for that method.</remarks>
  39:      /// </summary>
  40:      /// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
  41:      /// <param name="attributes">A <see cref="Hashtable"/> with keys and values to inject into the string</param>
  42:      /// <returns>A version of the formatString string with hastable keys replaced by (formatted) key values.</returns>
  43:      public static string Inject(this string formatString, Hashtable attributes)
  44:      {
  45:        string result = formatString;
  46:        if (attributes == null || formatString == null)
  47:          return result;
  48:   
  49:        foreach (string attributeKey in attributes.Keys)
  50:        {
  51:          result = result.InjectSingleValue(attributeKey, attributes[attributeKey]);
  52:        }
  53:        return result;
  54:      }
  55:   
  56:      /// <summary>
  57:      /// Replaces all instances of a 'key' (e.g. {foo} or {foo:SomeFormat}) in a string with an optionally formatted value, and returns the result.
  58:      /// </summary>
  59:      /// <param name="formatString">The string containing the key; unformatted ({foo}), or formatted ({foo:SomeFormat})</param>
  60:      /// <param name="key">The key name (foo)</param>
  61:      /// <param name="replacementValue">The replacement value; if null is replaced with an empty string</param>
  62:      /// <returns>The input string with any instances of the key replaced with the replacement value</returns>
  63:      public static string InjectSingleValue(this string formatString, string key, object replacementValue)
  64:      {
  65:        string result = formatString;
  66:        //regex replacement of key with value, where the generic key format is:
  67:        //Regex foo = new Regex("{(foo)(?:}|(?::(.[^}]*)}))");
  68:        Regex attributeRegex = new Regex("{(" + key + ")(?:}|(?::(.[^}]*)}))");  //for key = foo, matches {foo} and {foo:SomeFormat}
  69:        
  70:        //loop through matches, since each key may be used more than once (and with a different format string)
  71:        foreach (Match m in attributeRegex.Matches(formatString))
  72:        {
  73:          string replacement = m.ToString();
  74:          if (m.Groups[2].Length > 0) //matched {foo:SomeFormat}
  75:          {
  76:            //do a double string.Format - first to build the proper format string, and then to format the replacement value
  77:            string attributeFormatString = string.Format(CultureInfo.InvariantCulture, "{{0:{0}}}", m.Groups[2]);
  78:            replacement = string.Format(CultureInfo.CurrentCulture, attributeFormatString, replacementValue);
  79:          }
  80:          else //matched {foo}
  81:          {
  82:            replacement = (replacementValue ?? string.Empty).ToString();
  83:          }
  84:          //perform replacements, one match at a time
  85:          result = result.Replace(m.ToString(), replacement);  //attributeRegex.Replace(result, replacement, 1);
  86:        }
  87:        return result;
  88:   
  89:      }
  90:   
  91:   
  92:      /// <summary>
  93:      /// Creates a HashTable based on current object state.
  94:      /// <remarks>Copied from the MVCToolkit HtmlExtensionUtility class</remarks>
  95:      /// </summary>
  96:      /// <param name="properties">The object from which to get the properties</param>
  97:      /// <returns>A <see cref="Hashtable"/> containing the object instance's property names and their values</returns>
  98:      private static Hashtable GetPropertyHash(object properties)
  99:      {
 100:        Hashtable values = null;
 101:        if (properties != null)
 102:        {
 103:          values = new Hashtable();
 104:          PropertyDescriptorCollection props = TypeDescriptor.GetProperties(properties);
 105:          foreach (PropertyDescriptor prop in props)
 106:          {
 107:            values.Add(prop.Name, prop.GetValue(properties));
 108:          }
 109:        }
 110:        return values;
 111:      }
 112:   
 113:    }
 114:  }

File downloads:

Labels: , , , , , , , , ,

Monday, July 21, 2008

Silly SharePoint Bug: Folder name blocked as if it was a file

I was creating a folder for one of our clients in one of our Document Libraries, and was going to use the name of the client, but SharePoint wouldn't let me:

"The file you are attempting to save or retrieve has been blocked from this Web site by the server administrators."

Huh?

Turns out the name of the client ends with ".com" and com is one of the blocked file extensions on our SharePoint portal.  The same happens if you try to create a folder ending with ".bat", ".exe", etc...

Labels: , , , , ,

Thursday, July 17, 2008

MOSS: "an Adventure"

I'm tired of ranting about this myself, so below follows choice excerpts of fallenRogue's goodread about other developers' SharePoint complaints (i.e. what the courts call hearsay).

From SharePoint Development is an Adventure!

There is an amazing amount of pain that developers in the .net space are experiencing with SharePoint development.

He turns to me and screams “You’re doing WHAT?! SharePoint sucks! It sucks so hard! Why the fuck would you even do that!?” [...] I get these types of reactions often.

There is a lot of pissing and moaning on the internet these days about SharePoint as a bad development platform. Really? Well, it’s not developer centric, I’ll give you that.

...when the developer, perhaps an expert in asp.net, tries SharePoint and sees the poor tooling, documentation or worse finds one of the many bugs in the Object Model they throw their hands in the air and walk away from the table.

...the difference [between SharePoint and asp.net] is that SharePoint is a platform that is usually endorsed by the enterprise. Meaning, the developer is thrust into the environment and expected to perform at the same level that they have in the past with asp.net; after all, the Microsoft rep assured the management team that the learning curve would be low considering the platform is “just asp.net”.

The above is an altogether unfair set of negative excerpts, so I'll include some positive ones as well:

it is a rich and rewarding platform that has more to offer than most are giving it credit for. [...] nothing worth having isn’t without some effort. [...] lighten up a bit and enjoy life. ;)

Labels: , ,

Rant: Further proof that the patent system is patently broken

(this blog is turning into just a set of rants, ain't it?)

Via TechCrunch: This "patent" is the source of a new lawsuit against a number of companies:

Does the US Patent Office just not understand computer science, do they not have the ability to search for prior art, or do they simply not care to?

Labels: ,

Monday, July 14, 2008

Dunnowhatthisis: The New Yorker

Regarding today's controversy - Should we really be surprised that the New Yorker has no discernable sense of humor?

Labels: , , ,

Rant: Pyramid Schemes

Ok, so maybe they're not illegal pyramid schemes, but they sure are pyramid schemes in that the incentive is more about recruiting sub-associates than they are about providing any kind of value-add, as in the traditional distributor/middle-man business model.

10-12 years ago, my wife (to be) and I were invited by her then boss to attend a session about Excel Communications (anyone know where they went off to?) which promised cheaper phone service and income potential at the same time.  For an initial fee, of course, as well as the discomfort of bugging friends and neighbors for business.

7-8 years ago I went to a "job interview" which turned out to be a pyramid scheme initiation session to sell detergents.  When I discovered what the meeting actually was about I immediately left - the people in charge actually openly laughed AT me for passing up such a wonderful opportunity...

Then the other day I come across Ignite/Stream Energy (whose website I will not dignify with a link).  Same scheme:

Directors are Independent Associates who choose to enroll in the Ignite Services Program. Directors will receive a marketing kit (Power Pack), training, home office support, and downline organization and commission reports. The initial cost of the
Program is $329*, with an annual renewal fee of $99. Any Independent Associate who purchases the Ignite Service Program may cancel in writing within three (3) days of the date the IA Application and Agreement is received at Ignite (i.e.,  Start Date) and receive a full refund.
* Residents of Connecticut, Maryland and North Carolina pay $199, residents of Maine, Oklahoma, South Carolina and South Dakota pay $249, and residents of Louisiana, Utah and Washington pay $299.

And here's what you are supposed to earn:

image

How many fools will be parted from their money this time

Labels: , ,