<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>GigaMegaBlog &#187; .Net</title>
	<atom:link href="http://gigamegatech.com/category/programming/net/feed/" rel="self" type="application/rss+xml" />
	<link>http://gigamegatech.com</link>
	<description>Powered by GigaMegaWatts</description>
	<lastBuildDate>Mon, 07 Jun 2010 23:23:22 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<cloud domain='gigamegatech.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
		<item>
		<title>kitsrus Introduction to LCDs &#8211; Part 2: Hello Internet</title>
		<link>http://gigamegatech.com/2009/05/28/kitsrus-introduction-to-lcds-part-2-hello-internet/</link>
		<comments>http://gigamegatech.com/2009/05/28/kitsrus-introduction-to-lcds-part-2-hello-internet/#comments</comments>
		<pubDate>Thu, 28 May 2009 17:02:47 +0000</pubDate>
		<dc:creator>Dan</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[Electronics]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://gigamegatech.com/?p=202</guid>
		<description><![CDATA[A little while ago I wrote about an electronics kit from kitusrus that interfaces an LCD to a PC through its parallel port.  Like all kitsrus stuff it was fun to build, and the instructions included with the kit do a great job of explaining the design of the circuit.  
However, I was [...]]]></description>
			<content:encoded><![CDATA[<p>A little while ago <a href="http://gigamegatech.com/2009/04/18/kit-construction-the-kitsrus-introduction-to-lcds/">I wrote about an electronics kit</a> from kitusrus that interfaces an LCD to a PC through its parallel port.  Like all <a href="http://kitsrus.com/">kitsrus</a> stuff it was fun to build, and the instructions included with the kit do a great job of explaining the design of the circuit.  </p>
<p>However, I was a little surprised that they didn&#8217;t include the source code for the Windows program that communicates with the board.  Sending a &#8220;hello world&#8221; message to the LCD is fine for verifying that the kit works, but an LCD connecting to a PC could be used for so much more.  </p>
<p>When Googling for some .Net code that I could use as a starting point for writing my own interface, I was delighted to find that somebody had already done most of the work for me.  An <a href="http://www.codeproject.com/KB/cs/cspplcds.aspx">article on The Code Project site</a> describes the construction of a homebrew parallel port LCD connection, and includes the C# .Net code that the creator used to send data to the LCD.  Out of curiosity I downloaded the code and ran the .exe to see what happened, and I was astounded to see it successfully writing The Code Project&#8217;s RSS feed to the kitsrus LCD, under Vista no less. &#8220;Hello World&#8221;, indeed.</p>
<p>The Code Project article&#8217;s creator wasn&#8217;t using the kitrsrus kit, but because his LCD is based on a similar controller chip, and by a lucky coincidence in the selection of parallel port pins, the code mostly worked. The only problem was that the data wasn&#8217;t scrolling across the LCD as intended, but was stuck in the last column of the display, writing only to that one position.  The data was getting across, it just wasn&#8217;t being positioned correctly.  This is a pretty common and easily solved problem when writing data to an LCD, as I&#8217;ll explain below.</p>
<p>(In order to preserve your sanity and mine, I&#8217;ll refrain some referring to &#8220;the Code Project project&#8221; and the &#8220;kitsrus kit&#8221;, and call them the &#8220;CP project&#8221; and &#8220;the kit&#8221;.  Apologies to their respective trademark owners.)</p>
<p>Although there is no &#8220;standard interface&#8221; for connecting an LCD to a parallel port, there is a fairly close correspondence between the parallel port&#8217;s pins and the pins used by all LCDs that use an HD44780-compatible controller chip.  (Most LCDs intended for use by hobbyists are compatible with this standard, dating back to the late 90s.  The <a href="http://en.wikipedia.org/wiki/HD44780_Character_LCD">Wikipedia entry for HD44780</a> links to an Everyday Practical Electronics article from 1997.)  </p>
<p>The CP project used an LCD based on an old Samsung KS0066 chip, and the kit contains an LCD based on the KS0070 and manufactured in 1999, and both of those are HD44780-compatible.</p>
<p>The Code Project author wrote 2 articles that contain all the technical information you need to know in order to understand his .Net source code: <a href="http://www.codeproject.com/KB/cs/csppleds.aspx">this article covers the parallel port&#8217;s pins</a>, and <a href="http://www.codeproject.com/KB/cs/cspplcds.aspx">this one covers the LCD&#8217;s</a>.  The illustration below is linked to his second article.</p>
<p>Both the parallel port and the LCD use 8 data pins, D0 through D7. Not surprisingly, both the CP project and the kit wire the 2 sets of pins in a one-to-one correspondence, D0 to D0, D1 to D1, etc.  The data sent by the .Net code arrives just fine at the LCD, then.</p>
<p>LCDs based on the HD44780 interface also have 3 control pins: RS (register select), R/W (read/write) and E (enable).   Since LCDs don&#8217;t have much to say, both the CP project and the kit do the same thing with the R/W pin &#8211; wire it to ground, putting it in a permanent write state.  </p>
<p>By a happy coincidence, both the CP project and the kit happened to wire parallel port pin C0 to the E pin of the LCD &#8211; without pin C0 turning things off and on, nothing would have made it to the LCD. There is a non-intuitive pattern of setting the enable pin low then high then low again that is necessary to write data to the LCDs &#8212; this seems to be the thing that trips up most novice LCD programmers, but the CP project&#8217;s source code does a nice job of commenting this code so that you can understand what&#8217;s going on.</p>
<p>The only area where the 2 approaches differ is in the selection of the RS pin: the CodeProject board uses pin C2  while the kitsrus board uses C1. (See the CP project&#8217;s pinout diagram below). Since this pin is used to tell the LCD whether it is receiving data or instructions, and the pin needs to be high for data, this difference should have caused everything being sent to the LCD to the treated as instructions, resulting in a jumpy cursor but no characters on the screen.  However, by another lucky coincidence, the parallel port pin used by the kit, C1, is reversed &#8211; it is normally high and is set to low by sending it a &#8220;1&#8243;.  This results in everything sent to the LCD being treated as data &#8212; characters with no cursor control, exactly what we got. </p>
<div class="img aligncenter" style="width:600px;">
	<a href="http://www.codeproject.com/KB/cs/cspplcds.aspx"><img src="http://www.codeproject.com/KB/cs/cspplcds/circuit_with_potentiometer.gif" alt="Parallel port connections to the LCD, by Levent Saltuklaroglu, courtesy of The Code Project" width="600" height="500" /></a>
	<div>Parallel port connections to the LCD, by Levent Saltuklaroglu, courtesy of The Code Project</div>
</div>
<p>To adapt the CP project&#8217;s code to the kit, the only change required is to redirect all signals intended for C2 to C1, and flip the bit from allow for the fact that kit&#8217;s pin is reversed.</p>
<p>I decided to implement this change by adding a go-between method that would convert the instructions sent by the CodeProject code to the ones required by the kitsrus board:</p>
<pre class="brush: csharp">
        private void writeToControl(int intValue)
        {
            int intModifiedValue = intValue;
            if ((intValue &amp; 4) &gt; 0)
				// if C2 is being set  high, set C1 high by sending it a 0
                intModifiedValue = intModifiedValue &amp; 253;
            else
				// else, if C2 is being set low, set C1 low by sending it a 1
                intModifiedValue = intModifiedValue | 2;

            PortAccess.Output(intControl, intModifiedValue);
        }
</pre>
<p>And that&#8217;s it &#8212; the kit can now be fully controlled by the CP project&#8217;s code.  </p>
<p>This is quite cool, since it breathes new life into a 10-year old kit.  When originally introduced the kit had only a command line interface that would only run in DOS &#8211; real DOS, not the command line in Windows XP.  However, the .Net code works just fine under Vista and Windows 7.  By tinkering with the code you can now use the LCD as a remote display for whatever you like: RSS feeds, e-mail, twitter.  A poor geek&#8217;s <a href="http://www.chumby.com/">Chumby</a>!</p>
]]></content:encoded>
			<wfw:commentRss>http://gigamegatech.com/2009/05/28/kitsrus-introduction-to-lcds-part-2-hello-internet/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>UniVerse and .Net: Give or take a period</title>
		<link>http://gigamegatech.com/2009/04/13/universe-and-net-give-or-take-a-period/</link>
		<comments>http://gigamegatech.com/2009/04/13/universe-and-net-give-or-take-a-period/#comments</comments>
		<pubDate>Tue, 14 Apr 2009 00:25:46 +0000</pubDate>
		<dc:creator>Dan</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[IBM Universe (U2)]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://gigamegatech.com/?p=125</guid>
		<description><![CDATA[q]]></description>
			<content:encoded><![CDATA[<p>We&#8217;ve recently come across a couple of tricky bugs in IBM&#8217;s ADO.NET driver for their <a href="http://www-01.ibm.com/software/data/u2/">U2 UniVerse database</a>, showing that there are still a few sharp edges on that shiny new finish.</p>
<p>The first and more serious bug is that the driver always returns numbers with a decimal point as a zero when using French &#8220;regional settings&#8221; in Windows.</p>
<p>For example, when using these Regional Settings&#8230;</p>
<div class="img size-full wp-image-128" style="width:404px;">
	<img src="/wp-content/uploads/2009/04/regionalsettings.png" alt="Zut alors, c'est un comma!" width="404" height="485" />
	<div>Zut alors, c'est un comma!</div>
</div>
<p>&#8230;the sample .Net application that IBM provides in its <a href="http://www.ibm.com/developerworks/edu/dm-dw-dm-0805else-i.html">QuickStart tutorial</a> displays this:</p>
<div class="img size-full wp-image-127" style="width:500px;">
	<img src="/wp-content/uploads/2009/04/ibmsampleprogram.png" alt="French toast" width="500" height="386" />
	<div>French toast</div>
</div>
<p>We ran into this bug because many of our product&#8217;s users have the &#8220;French (Canada)&#8221; setting on their PCs.  I assume that the root cause of the bug is that a comma, rather than a period, is used as  the decimal point, as marked in yellow above.  IBM support has confirmed that this is a bug are internally testing a fix in the Runtime Client, so the fix will likely be available in Fix Pack 3c or the one after that.</p>
<p>I&#8217;m surprised that nobody (including us) ran into this bug earlier, but that&#8217;s probably because most developers (including us) have got into the habit of reading data from UniVerse as strings, then converting the data into its proper format in their code.  As I&#8217;ve mentioned in past articles, UniVerse has a &#8220;multivalue&#8221; heritage where all data is stored internally as strings.  Developers should go against this grain at their own peril.</p>
<p>For example, rather than defining a currency field in the Universe dictionary like this&#8230;</p>
<pre class="brush: sql">
COMMISSION
001 A
002 34
005 S
007 MD3
009 R
010 7
</pre>
<p>&#8230; define it as&#8230;.</p>
<pre class="brush: sql">
COMMISSION.STR
001 A
002 34
005 S
006 CHAR,7
009 R
010 7
</pre>
<p>&#8230; and read the value into a String.  Then, you can convert it to a Decimal variable using code like&#8230;</p>
<pre class="brush: csharp">
Decimal.TryParse(strValue, System.Globalization.NumberStyles.Number,
System.Globalization.CultureInfo.InvariantCulture, out decValue);
</pre>
<p>The &#8220;InvariantCulture&#8221; thing is needed because, when defined as a CHAR, UniVerse will return a decimal value using &#8212; surprise, surprise &#8212; a period as the decimal point.</p>
<p>The second bug that we tripped over was a little more obscure.    The bug was found by one of our developers, who was double-checking the configuration of our clients&#8217; databases to make sure that all of our latest patches had been applied.  He found that one particular SQL statement used in our application was failing with the following error:</p>
<pre class="brush: sql">
Error  occurred: ERROR [Command_ExecuteDirect]  [IBM  U2][UCINET][UNIVERSE]:UniVerse/SQL: syntax error.  Unexpected verb.  Token was  &quot;LIST&quot;. Scanned command was FROM VENDORS SELECT LIST  :  IBM.Data.DB2: -2147467259
</pre>
<p>This type of error usually indicates a mismatch between our dictionary entries and our application, which was the type of configuration issue that he was checking for.  It wasn&#8217;t a big surprise, since the problem was found in a database that is only used for in-house training.  Surprisingly, though, after reapplying the latest dictionary entries the bug remained.</p>
<p>Like most database systems that I&#8217;ve worked with, UniVerse&#8217;s SQL parser tends to return some pretty vague error messages.  The particular SQL statement that was failing was quite long,  containing about 20 columns, none of which were named &#8220;LIST&#8221;.  By trial-and-error, I found that the culprit was a field named DEMO.LIST.  The DICT definition of this field was fine &#8211; it exactly matched the dictionary entry used in the other databases at this client site, none of which were experiencing this error.  While scratching my head over this, our developer told me that he found the same error in the same SELECT statement in a database at a different client.  This database was also one used for in-house training.</p>
<p>This made me suspect that the error was caused by bad data.  The databases used for in-house training are more likely to contain weird characters like embedded tabs or carriage-returns.</p>
<p>The dictionary definition of DEMO.LIST was a 100-character string:</p>
<pre class="brush: sql">
DEMO.LIST
001 A
002 5
005 S
006 CHAR,100
009 L
010 100
</pre>
<p>As mentioned earlier, strings are usually a safe choice for data in UniVerse.  I&#8217;m not aware of any type of data that can cause a SELECT of a a string to fail in UniVerse&#8217;s ADO.NET driver, but there&#8217;s a first time for everything.  However, a bit of trial-and-error testing made it clear that this SELECT statement was failing on the plainest of records: vanilla text that couldn&#8217;t possibly cause a problem.  It was a real whodunnit.</p>
<p>Back to the not-very-helpful error message,  It was complaining about a &#8220;token&#8221; named LIST.  If I simplified the variable name to something like FIELD1, did that affect the error message?  Well, yeah: it made the error go away completely. The SELECT statement worked fine if the column name was changed to FIELD1, or DEMO or even DEMOLIST.  Weird.  We have periods in lots of our column names, and a lot of them are named LIST.  DEMO, not so much.</p>
<p>At this point, things finally fell into place.  The name that we give our databases that are used for in-house training is DEMO.  And, of course, in standard SQL syntax, a period in a column name is used to identify the table, such as &#8220;SELECT TABLE1.FIELD1&#8243;.  Sure enough, I ran some tests in our development database, named DEV, and found that naming a column &#8220;DEV.LIST&#8221; or &#8220;DEV.FIELD&#8221; or &#8220;DEV.WHATEVER&#8221; caused SELECT statements that use that field to fail.</p>
<p>The moral of the story, I suppose, is don&#8217;t use periods in your column names.  That was IBM support&#8217;s suggestion, though I&#8217;m hoping that they will fix this bug as well.</p>
<p>I suppose that both of these bugs are indicative of what happens when you drag a 40-something year-old non-relational database kicking and screaming into the .Net world.  Oops, better make that &#8220;dotNet&#8221; to be safe!</p>
]]></content:encoded>
			<wfw:commentRss>http://gigamegatech.com/2009/04/13/universe-and-net-give-or-take-a-period/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>IBM UniVerse and .Net, BFF</title>
		<link>http://gigamegatech.com/2008/12/18/ibm-universe-and-net-bff/</link>
		<comments>http://gigamegatech.com/2008/12/18/ibm-universe-and-net-bff/#comments</comments>
		<pubDate>Thu, 18 Dec 2008 22:37:51 +0000</pubDate>
		<dc:creator>Dan</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[IBM Universe (U2)]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://gigamegatech.com/2008/12/18/ibm-universe-and-net-bff/</guid>
		<description><![CDATA[
body { border: 0px; font-family:verdana; font-size :10pt; direction :ltr; background-color :#ffffff; line-height :1.2; margin:0% 10% 0% 10%;}
table { font-size: 10pt;} p { margin-top:0px; margin-bottom:0px; }


This article is the third and final part of a series that covers some lessons learned when trying to plug Visual Studio into our IBM U2 UniVerse database using IBM&#8217;s ADO.NET [...]]]></description>
			<content:encoded><![CDATA[<p><html><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"></meta><meta content="text/html;charset=utf-8" http-equiv="Content-Type" id="metaid"></meta><meta content="pagewise" id="fntype"></meta><meta content="1-1" id="fnstyle"></meta><meta content="212" name="zid"><br />
<style id="styletagforeditor">body { border: 0px; font-family:verdana; font-size :10pt; direction :ltr; background-color :#ffffff; line-height :1.2; margin:0% 10% 0% 10%;}</style>
<style id="styletagtwoforeditor">table { font-size: 10pt;} p { margin-top:0px; margin-bottom:0px; }</style>
<link href="/styles/print.css" id="stylesheetforpreview" media="print" rel="stylesheet"></link>
<link href="/styles/print_preview.css" id="stylesheetforprintpreview" media="screen" rel="stylesheet"></link>
<link href="/styles/editor.css" id="stylesheetforeditor" rel="stylesheet"></link></meta></head><body>This article is the third and final part of a series that covers some lessons learned when trying to plug Visual Studio into our <a href="http://www-01.ibm.com/software/data/u2/" zid="195">IBM U2 UniVerse database</a> using IBM&#8217;s ADO.NET driver.&nbsp; <a href="http://gigamegatech.com/2008/11/25/visual-studio-and-ibm-universe-playing-the-matchmaker/" zid="196">Part 1</a> was an overview of how well the ADO.NET driver has worked for us, and the prerequisite software that is required to use it.&nbsp; <a href="http://gigamegatech.com/2008/12/03/the-meaning-of-everything-universe-dictionaries-for-the-net-programmer/" zid="197">Part 2</a> gave some tips on preparing your UniVerse dictionaries for use with Visual Studio.&nbsp; In this part of the series, we finally get to fire up Visual Studio and connect it to the database.<br zid="6"/><br zid="8"/>One of the great things about the ADO.NET driver, when compared to other UniVerse APIs like UniOLEDB and UniVerse Objects.NET, is that it allows Visual Studio to finally recognize UniVerse as a database.&nbsp; That means you don&#8217;t have to write a bunch of code in order to see the data &#8212; you can use use Visual Studio to explore the database and test your SQL statements.&nbsp; (On the other hand, if you prefer to just write code and avoid all the point-and-click stuff, you can <a href="#StartCoding" zid="203">skip ahead to this point in the article</a>.)<br zid="9"/><br zid="10"/><img align="right" alt="UniVerse Data Connections" border="0" hspace="3" src="http://writer.zoho.com:80/ImageDisplay.im?name=305886000000072001/1229560596507_12-17-2008%207-34-19%20PM.png&#038;accId=305886000000002007" vspace="8" zid="205"/>The first step is to configure your UniVerse database in Visual Studio&#8217;s Server Explorer. &nbsp;This process is explained well in IBM&#8217;s developerWorks article <a href="http://www.ibm.com/developerworks/edu/dm-dw-dm-0802kumar-i.html" zid="204">&quot;Access IBM U2 data server from your .NET applications, Part 2&quot;</a>.&nbsp; Since I would strongly advise you to read IBM&#8217;s three-part tutorial series anyway, I won&#8217;t repeat the information here.<br zid="11"/><br zid="12"/>You should now have a new node in the Data Connections section of the Server Explorer, as shown in the screenshot on the right, with a name like &quot;&lt;user-ID&gt;@&lt;account&gt;[UNIVERSE 10.02.0000]&quot;.&nbsp; Expand that node, and you&#8217;ll see 4 sections: Tables, Views (which will be forever empty), Procedures and Functions (also empty).&nbsp; Expand the Tables node and, hopefully, you&#8217;ll see a list of the tables in your account.&nbsp; Expand one of the tables, and you&#8217;ll hopefully see the columns in that table.&nbsp; Right-click on the table and select &quot;Show Data&quot;, and you&#8217;ll see a grid containing all the rows of the table.&nbsp; Groovy!&nbsp; UniVerse and Visual Studio sitting in a tree-ee&#8230;<br zid="15"/><br zid="16"/><img align="left" alt border="0" hspace="8" src="http://writer.zoho.com:80/ImageDisplay.im?name=305886000000072001/1229560839859_12-17-2008%207-38-03%20PM.png&#038;accId=305886000000002007" vspace="0" zid="206"/>Alas, you might, like I,&nbsp;find that Visual Studio&#8217;s love for UniVerse is not so easily requited.&nbsp; When I expand the Tables node in our development account, I see the warning on the left.&nbsp; After clicking on the Yes button, I am then left staring at the hourglass for a few minutes before the table list appears.&nbsp; If I expand one of the tables to view its columns, I once again find myself contemplating the Windows hourglass for a minute or so, even if the table only contains a few columns.&nbsp; If I want to use Visual Studio&#8217;s query builder with my UniVerse database: you guessed it, again with the hourglass.&nbsp; Trust me, that gets real old real fast!<br zid="17"/><br zid="18"/>It would seem that Visual Studio&#8217;s Explorers like to, well, explore.&nbsp; If you have a large UniVerse master dictionary, all that exploring takes some time.&nbsp; The key to speeding up Visual Studio is to limit how much of your master dictionary it sees.&nbsp; If you read Chapter 4 of the <a href="http://www-01.ibm.com/software/data/u2/pubs/library/102univ/" zid="207">UniVerse ODBC Guide</a>, you&#8217;ll find that happily there is a mechanism for doing just that: create a file named HS_FILE_ACCESS and add an entry in it for each file that you want to use with Visual Studio.&nbsp; Unhappily, while this limits the number of tables that you&#8217;ll see in Visual Studio, it only makes things slightly faster. <br zid="19"/><br zid="20"/>I suspect that the solution to this problem is to create a new account whose master dictionary contains only the data files you want Visual Studio to work with.&nbsp; I&#8217;ll be trying this for myself at some point in the future, and I&#8217;ll blog about it then.&nbsp; <br zid="21"/><br zid="22"/>Fortunately, even if you find that slow performance makes some of Visual Studio&#8217;s point-and-click features unusable, you are still left with enough tools to get the job done.&nbsp; <br zid="23"/><br zid="24"/>    Let&#8217;s start with some SELECT queries.&nbsp; Right-click the database connection in the Server Explorer and, instead of New Query, choose New Script.&nbsp;&nbsp; This opens the Script Builder (shown in the screenshot below), an IBM tool that is basically a stripped-down version of Visual Studio&#8217;s Query Builder: no Diagram pane, no Criteria pane, just a box for your SQL and a&nbsp; grid with your results.&nbsp; (And a tiny little 1-icon toolbar &#8211; cute!).&nbsp; More typing, but a lot less waiting, since this dialog do a scan of your UniVerse master dictionary.<br zid="27"/><img align="middle" alt="UniVerse Script Builder" border="0" hspace="0" src="http://writer.zoho.com:80/ImageDisplay.im?name=305886000000072001/1229562120646_12-17-2008%207-59-52%20PM.png&#038;accId=305886000000002007" vspace="8" zid="208"/><br zid="28"/>The SELECT commands that you enter here follow most of the same rules as for a SQL database, but there are a few tricky things:<br zid="31"/><br zid="32"/>1) Case matters &#8211; if your columns are defined in your DICT in upper-case, you must use upper-case here, something that is sure to trip up any SQL Server or Oracle developer.<br zid="46"/><br zid="33"/>2) Columns and tables that contain hyphens must be surrounded with double quotes &#8212; the square brackets that you may be used to using with other databases won&#8217;t cut it here.&nbsp; For example,</p>
<pre class="brush: sql">
select USER.ID from &quot;CSUSER-FILE&quot;
</pre>
<p>is valid, but not</p>
<pre class="brush: sql">
select USER.ID from [CSUSER-FILE]
</pre>
<p>or</p>
<pre class="brush: sql">
select user.id from &quot;csuser-file&quot;
</pre>
<p>Oddly, names that contain a period do not have to be quoted &#8211; unlike its SQL database brethren, UniVerse is smart enough to figure out when the period delimits the table ID from the column name.<br zid="44"/><br zid="45"/>3) A &quot;SELECT *&quot; query will return the key fields along with whatever fields are defined in a special dictionary entry named &quot;@&quot;.&nbsp; For example, if you want SELECT * FROM USERS to return the FIRST-NAME and LAST-NAME fields, you&#8217;ll need to edit the dictionary as follows:</p>
<pre class="brush: sql">
ED DICT USERS
Record name = @
2 lines long.

----: 2
0002: 1 2
Bottom at line 2.
----: R FIRST-NAME LAST-NAME
0002: FIRST-NAME LAST-NAME
Bottom at line 2.
----: FI
&quot;@&quot; filed in file &quot;DICT USER&quot;.
</pre>
<p>If you don&#8217;t have a record named &quot;@&quot; in your file&#8217;s dictionary, then &quot;SELECT *&quot; will return the column named &quot;@ID&quot; if you have one (which you probably do, since the primary key is  automatically added to all dictionaries under this name when you first create them).&nbsp; If you happen to have deleted the &quot;@ID&quot; column, then SELECT * will fail with a &quot;No column rows&quot; error.<br zid="61"/><br zid="62"/>What if you have a 200 column table and you want SELECT * to return all 200 columns?&nbsp; Sorry, but you&#8217;ll have to enter all 200 column names in that &quot;@&quot; record.&nbsp; And if you add, delete or rename a column, you&#8217;ll have to remember to edit that &quot;@&quot; record. I feel your pain!&nbsp; This cumbersome approach dates back to UniVerse&#8217;s ODBC driver, and I suppose they can&#8217;t change it now without breaking a lot of applications.&nbsp; You can find the details described in inscrutable IBM-ese in the UniVerse ODBC Guide.<br zid="13"/><br zid="14"/>4) Errors encountered when executing your SQL statement are reported in a pretty obscure manner.&nbsp; You&#8217;ll see a message box with the &quot;Error in SQL.&nbsp; Check the SQL and try again.&nbsp; For error detail information refer to the IBM Output Message Pane&quot;.&nbsp; It took me awhile to figure out where the &quot;IBM Output Message Pane&quot; was hidden.&nbsp; It&#8217;s in the Output window (Ctrl-W O), but you&#8217;ll need to change the &quot;Show output from&quot; combo to &quot;IBM Output Message Pane&quot; as shown below.&nbsp; <br zid="209"/><img align="middle" alt="IBM Output Message Pane" border="0" hspace="0" src="http://writer.zoho.com:80/ImageDisplay.im?name=305886000000072001/1229562562845_12-17-2008%208-08-07%20PM.png&#038;accId=305886000000002007" vspace="8" zid="210"/><br zid="2"/><br zid="65"/>5) If you have subvalued fields, only the first subvalue in the field will appear in the Results grid.&nbsp; (Subvalues are ASCII 252 delimiters which are used to fit multiple fields into the same column).&nbsp; This is presumably a side effect of a bug in IBM&#8217;s ADO.NET driver which replaces subvalue delimiters with CR/LF.<br zid="148"/><br zid="149"/>6) There are a couple of bugs in the ADO.NET driver for which, as far as I know, there is no good workaround.&nbsp; The first is that the SQL processor can&#8217;t handle hard-coded values which contain the &quot;at sign&quot;, such as:</p>
<pre class="brush: sql">
SELECT * FROM MY.TABLE WHERE THE.KEY = &quot;DAN@WORK&quot;
</pre>
<p>Depending on the structure of your data you may be able to get at the data by using a wildcard instead</p>
<pre class="brush: sql">
SELECT * FROM MY.TABLE WHERE THE.KEY LIKE &quot;DAN%WORK&quot;
</pre>
<p>The other bug is that zero-length numeric fields such as the one in attribute 5 below will result in an <span style="width: 500px;" zid="158"><font size="-1" zid="159">&ldquo;Input string was not in a correct format&rdquo; error (that funny little accented character is ASCII 253, the multivalue delimiter):</p>
<pre class="brush: sql">
0001: SMITH
0002: 1414 W. 8TH
0003: DENVER
0004: CO
0005: V2001Ã½Ã½C8181
0006: 200Ã½3599Ã½1050
</pre>
<p>This latter bug was fixed by IBM in the recently released Fix Pack 3, but if you have users on earlier releases there is a workaround you can implement in your code, as explained later in the article.<br zid="66"/></font></span><br zid="67"/>7) Last, but not least, multivalues.&nbsp; As described in <a href="http://gigamegatech.com/2008/12/03/the-meaning-of-everything-universe-dictionaries-for-the-net-programmer/" zid="211">my previous article</a> there are a few different ways to configure your dictionary to return multivalues, but the method that IBM recommends is to list the multivalued fields in a special &quot;association&quot; dictionary entry that looks like this:</p>
<pre class="brush: sql">
    DETAILS
001 PH ASSOCIATION OF ORDER DETAIL FIELDS
002 ORDER.IDS ORDER.AMOUNTS
</pre>
<p>The advantage of this approach is that you can return the items within the multivalued field as if they were separate columns, which makes the data a lot easier to work with.&nbsp; The disadvantage is that you&#8217;ll have to puzzle out the syntax for a join with a &quot;virtual table&quot;. &nbsp; If the above field DETAILS was defined in the dictionary of a table named CUSTOMERS, then a SQL statement to return a list of order IDs and amounts would be:</p>
<pre class="brush: sql">
SELECT a.KEY.FULL, ORDER.IDS, ORDER.AMOUNTS FROM CUSTOMERS a JOIN CUSTOMERS_DETAILS b ON a.KEY.FULL = b.KEY.FULL
</pre>
<p>Since the dictionary field that is automatically added for the primary key, &quot;@ID&quot;, can&#8217;t be used in SQL statements, you&#8217;ll have to add one yourself in order to use it in the JOIN &#8212; in the above example I added a field named KEY.FULL to CUSTOMERS.<br zid="101"/><br zid="102"/>If by this point you&#8217;re fully freaked out by the UniVerse way of implementing SQL, then I&#8217;ve got some good news for you: the code itself is pretty much plain vanilla and won&#8217;t require much explanation.&nbsp; <br zid="200"/><img align="right" alt border="0" hspace="8" src="http://writer.zoho.com:80/ImageDisplay.im?name=305886000000072001/1229562797642_12-15-2008%207-48-19%20PM.png&#038;accId=305886000000002007" vspace="3" zid="212"/><br zid="201"/><a class="ItemAnchor" name="StartCoding" title="StartCoding" zid="202"></a>Before you start coding, you&#8217;ll need to add a reference to the IBM.Data.DB2 DLL to your project References list, as shown at the right.&nbsp; You&#8217;ll also need to add &quot;IBM.Data.DB2&quot; to your list of namespaces:</p>
<pre class="brush: csharp">
using IBM.Data.DB2;
</pre>
<p>The process of building a connection string is quite similar to other databases like SQL Server.&nbsp; You&#8217;ll need to set the Server, User, Password and Database (which is the UniVerse account name). &nbsp; In addition, you need to set two less familiar properties &#8212; ServerType and Pooling:</p>
<pre class="brush: csharp">
private string providerName = &quot;IBM.Data.DB2&quot;;
private DbConnectionStringBuilder builder;
private DbConnection conUniVerse;

builder = new DbConnectionStringBuilder();
builder[&quot;Server&quot;] = txtServer.Text.Trim();
builder[&quot;User ID&quot;] = txtUserID.Text.Trim();
builder[&quot;Password&quot;] = txtPassword.Text.Trim();
builder[&quot;ServerType&quot;] = &quot;universe&quot;;
builder[&quot;Pooling&quot;] = &quot;false&quot;;
builder[&quot;Database&quot;] = txtAccount.Text.Trim();
string strConnectionString = builder.ConnectionString;

DbProviderFactory m_provider = DbProviderFactories.GetFactory(providerName);
conUniVerse= m_provider.CreateConnection();

conUniVerse.ConnectionString = strConnectionString;
conUniVerse.Open();
</pre>
<p>You&#8217;ll then need to create an ADO Data Adapter and feed it the SQL statement.</p>
<pre class="brush: csharp">
DbProviderFactory factory = DbProviderFactories.GetFactory(providerName);
DbDataAdapter adapter = factory.CreateDataAdapter();
adapter.FillError += new FillErrorEventHandler(adapter_FillError);
adapter.SelectCommand = DB_GetCommand(strSQL);
</pre>
<p>Lastly, you create an ADO Dataset to hold the results of the query, then bind that Dataset to a grid on your form to display the data:</p>
<pre class="brush: csharp">
DataSet dsData = new DataSet();
adapter.Fill(dsData , &quot;MyData&quot;);

bindingSource1.DataSource = dsData ; // bindingSource1 is a BindingSource object that I dragged from the Toolbox onto my form
bindingSource1.DataMember = &quot;MyData&quot;;
gridData.DataSource = bindingSource1; // gridData is a DataGridView object on my form
conUniVerse.Close();
</pre>
<p>You may have noticed that I added a FillError event handler to my adapter.&nbsp; This is a workaround for the zero-length multivalue problem that I mentioned earlier in the article.&nbsp; In the error handler, you can examine the Exception&#8217;s properties and, if it appears to be the zero-length multivalue error, set the e parm&#8217;s Continue property to true.</p>
<pre class="brush: csharp">
void adapter_FillError(object sender, FillErrorEventArgs e)
{
    if (e.Errors.Message.Contains(&quot;Input string was not in a correct format&quot;))
        e.Continue = true;
}
</pre>
<p>This will result in the dataset being filled, but without the multivalued &quot; virtual record&quot; that contained the zero-length value. If the zero-length value is just bad data then that&#8217;s probably what you want &#8212; otherwise, you&#8217;ll need Fix Pack 3.<br zid="193"/><br zid="194"/>Whew!&nbsp;&nbsp; That might seem like a lot of work to read some data into a grid, but it&#8217;s stable, it&#8217;s pretty fast, and it&#8217;s still a lot less coding than the only other stable and fast UniVerse API out there, UniVerse Objects.&nbsp; Once you&#8217;ve learned the ropes, UniVerse is now a viable database option for .Net development.&nbsp; <br zid="5"/><br zid="1"/></body></html></p>
]]></content:encoded>
			<wfw:commentRss>http://gigamegatech.com/2008/12/18/ibm-universe-and-net-bff/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Meaning of Everything: UniVerse Dictionaries for the .Net Programmer</title>
		<link>http://gigamegatech.com/2008/12/03/the-meaning-of-everything-universe-dictionaries-for-the-net-programmer/</link>
		<comments>http://gigamegatech.com/2008/12/03/the-meaning-of-everything-universe-dictionaries-for-the-net-programmer/#comments</comments>
		<pubDate>Thu, 04 Dec 2008 00:49:09 +0000</pubDate>
		<dc:creator>Dan</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[IBM Universe (U2)]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://gigamegatech.com/2008/12/03/the-meaning-of-everything-universe-dictionaries-for-the-net-programmer/</guid>
		<description><![CDATA[
body { border: 0px; font-family:verdana; font-size :10pt; direction :ltr; background-color :#ffffff; line-height :1.2; margin:0% 10% 0% 10%;}
table { font-size: 10pt;} p { margin-top:0px; margin-bottom:0px; }


This article is a continuation of my previous post about using IBM U2 UniVerse&#8217;s ADO.NET driver with Visual Studio.&#160; Today I&#8217;m going to deal with the part of the process that [...]]]></description>
			<content:encoded><![CDATA[<p><html><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"></meta><meta content="text/html;charset=utf-8" http-equiv="Content-Type" id="metaid"></meta><meta content="pagewise" id="fntype"></meta><meta content="1-1" id="fnstyle"></meta><meta content="200" name="zid"><br />
<style id="styletagforeditor">body { border: 0px; font-family:verdana; font-size :10pt; direction :ltr; background-color :#ffffff; line-height :1.2; margin:0% 10% 0% 10%;}</style>
<style id="styletagtwoforeditor">table { font-size: 10pt;} p { margin-top:0px; margin-bottom:0px; }</style>
<link href="/styles/print.css" id="stylesheetforpreview" media="print" rel="stylesheet"></link>
<link href="/styles/print_preview.css" id="stylesheetforprintpreview" media="screen" rel="stylesheet"></link>
<link href="/styles/editor.css" id="stylesheetforeditor" rel="stylesheet"></link></meta></head><body>This article is a continuation of <a href="http://gigamegatech.com/2008/11/25/visual-studio-and-ibm-universe-playing-the-matchmaker/" zid="112">my previous post</a> about using <a href="http://www-01.ibm.com/software/data/u2/" zid="113">IBM U2 UniVerse</a>&#8217;s ADO.NET driver with Visual Studio.&nbsp; Today I&#8217;m going to deal with the part of the process that makes .Net programmers cringe but is the key to crafting effective SELECT statements: the UniVerse dictionary.<br zid="102"/><br zid="103"/>You probably already have dictionary entries that you (or others) use with UniVerse Basic code, but there are a few cases where you may need to modify them in order to access the fields through ADO.Net.&nbsp; UniVerse Basic is pretty forgiving about incorrect dictionary entries &#8212; as long as the entry tells the code which attribute to look in, UniVerse Basic is happy.&nbsp; Unfortunately, ADO.Net (and other SQL-oriented APIs) are picky about data types and conversion codes &#8212; they frown on UniVerse&#8217;s attempt to portray every piece of data as a string.<br zid="104"/><br zid="105"/>IBM has provided a utility named HS.SCRUB which analyzes your dictionaries and flags any fields which might cause problems for the database driver.&nbsp; If you are brave, you can even let HS.SCRUB &quot;autofix&quot; your dictionaries. This utility was actually introduced with the UniVerse ODBC driver, and you&#8217;ll find the documentation for it in the UniVerse ODBC Guide and the Using UniOLEDB manual.&nbsp; (You can download PDF copies of any of the UniVerse manuals from <a href="http://www-01.ibm.com/software/data/u2/pubs/library/102univ/" zid="114">here</a>.) Frankly, the utility found so many problems in our dictionaries, including a lot of false positives, that I ended up ignoring it and building new dictionary entries from scratch.&nbsp; Your mileage may vary.<br zid="106"/><br zid="107"/>However, even after letting HS.SCRUB have its way with your dictionaries, there are some cases that will require special attention:<br zid="28"/><br zid="136"/><span style="font-weight: bold;" zid="108">1) Non-string fields that may be null.</span><br zid="137"/><br zid="29"/><span style="font-weight: bold;" zid="138"></span>If you don&#8217;t specify the data type of a field, the database driver will helpfully try to guess the data type on-the-fly by looking at the data.&nbsp; HS.SCRUB uses a similar approach to determine the correct data type.&nbsp; Unfortunately, both of them are tripped up by non-string fields which contain some zero-length values &#8212; if you &nbsp; define these as any data type except strings, the zero-length values will result in &quot;invalid data type&quot; .Net Exceptions at run time.<br zid="109"/><br zid="110"/>In cases like this, I&#8217;d suggest you force the field to be read as a string, by specifying &quot;CHAR&quot; in the data type attribute of the dictionary entry.&nbsp; The data type attribute is 6 for A and S-type dictionary entries, and attribute 8 for D- and I-type correlatives.&nbsp; If you have no idea what that means, then you&#8217;d better have a look at Chapter 5 of the UniVerse System Description manual.&nbsp; (As mentioned earlier, PDF copies of all of the UniVerse manuals can be found <a href="http://www-01.ibm.com/software/data/u2/pubs/library/102univ/" zid="115">here</a>.)<br zid="30"/><br zid="31"/><span style="font-weight: bold;" zid="147">2) Time fields with contain milliseconds.&nbsp; </span><br zid="148"/><br zid="149"/>In Universe files, time fields are written in a &quot;Julian&quot; format which converts the time to the number of seconds past midnight: for example, 1:30 am is 5400.&nbsp; Optionally, these time fields can contain the number of milliseconds, which is written as a decimal value: for example, 5400.5 is half a second after 1:30 am.&nbsp; Unfortunately, the UniVerse ADO.Net driver can&#8217;t convert a string with that format into a TimeSpan.&nbsp; One workaround is to define the field as a string, by specifying &quot;CHAR&quot; as the data type (see point (a), above).&nbsp; You will then have to write a bit of code to convert the string to a .Net TimeSpan value:</p>
<pre class="brush: csharp">
public static TimeSpan TimeSpan_FromUniverseString(string strUniverseTime)
{
    int intUniverseTime = 0;
    // if the string contains milliseconds, discard them
    int intIndex = strUniverseTime.IndexOf(&#039;.&#039;);
    if (intIndex &gt;= 0)
    {
        strUniverseTime = strUniverseTime.Substring(0, intIndex);
    }

    if (!Int32.TryParse(strUniverseTime, out intUniverseTime))
    {
        // if not a numeric, then return 00:00:00
        return new TimeSpan(0);
    }
    int intDays = intUniverseTime / 86400;
    int intRemainder = intUniverseTime % 86400;
    int intHours = intRemainder / 3600;
    intRemainder = intRemainder % 3600;
    int intMinutes = intRemainder / 60;
    int intSeconds = intRemainder % 60;
    return new TimeSpan(intDays, intHours, intMinutes, intSeconds);
}
</pre>
<p>However, an easier workaround (which I stumbled across after using the above approach for months) is to take advantage of the &quot;correlative&quot; code.&nbsp; <br zid="120"/><br zid="121"/>This code only exists in A- and S-type dictionary entries, in attribute 8.&nbsp; Ordinarily, time fields are defined in UniVerse dictionaries by specifying a &quot;conversion code&quot;, such as &quot;MTS&quot; in attribute 7.</p>
<pre class="brush: sql">
    ACCESS.TIME
001 A
002 3
005 S
007 MTS
009 R
010 5
</pre>
<p>This tells ADO.Net (or UniVerse Basic) that the value in this field can be regarded as a time.&nbsp; However, by specifying the same conversion code in attribute 8, the conversion to a time value is handled internally by the UniVerse database&nbsp;before ADO.Net sees it. </p>
<pre class="brush: sql">
    ACCESS.TIME.SQL
001 A
002 3
005 S
008 MTS
009 R
010 5
</pre>
<p>Therefore, a value such as &quot;3600.5&quot; will be seen by the ADO.Net driver as 1:30 am, without that nasty millisecond value that it so dislikes.<br zid="57"/><br zid="150"/><span style="font-weight: bold;" zid="151">3) Multivalues</span><br zid="58"/><br zid="152"/>Ah yes, multivalues.&nbsp; This is a method for storing multiple values into the same field, separated by a special delimiter.&nbsp; For example, attribute 1 might contain a list of order numbers, and attribute 2 the amount (in cents) of each of order:</p>
<pre class="brush: sql">
001 C2000Ã½M3000Ã½S3000
002 600782Ã½700422Ã½101456
</pre>
<p>Multivalued strings are the primary feature that distingishes UniVerse and its brethern databases from all the others, but .Net doesn&#8217;t really understand the concept (nor do many .Net programmers, actually).&nbsp; UniVerse provides some tools which allow you to dress up the multivalued data to resemble columns in a normalized SQL database &#8212; it is up to you whether you want to play dress-up, or just process the multivalued fields in their underlying form: delimited strings.<br zid="158"/><br zid="159"/>There are 3 different approaches to definining multivalued strings in the dictionary so that they are safe for consumption by .Net:<br zid="59"/><br zid="60"/>i) The documented approach is to use a D-type dictionary entry which contains an &quot;M&quot; in attribute 6 and an &quot;association&quot; name in attribute 7.&nbsp; For example, the definition of the Order ID and Order Amount fields might look like:</p>
<pre class="brush: sql">
    ORDER.IDS
001 D Order IDs
002 1
004 Order IDs
005 10L
006 M
007 DETAILS

    ORDER.AMOUNTS
001 D Order Amounts
002 2
003 MD0,$
004 Order Amount
005 7R
006 M
007 DETAILS
</pre>
<p>And the dictionary entry which defines the virtual &quot;DETAILS&quot; table would be:</p>
<pre class="brush: sql">
    DETAILS
001 PH ASSOCIATION OF ORDER DETAIL FIELDS
002 ORDER.IDS ORDER.AMOUNTS
</pre>
<p>If the main table which contained the multivalued fields was named SALES, then there would now be a virtual table named SALES_DETAILS containing three fields: ORDER.IDS, ORDER.AMOUNTS, and&nbsp;a foreign key field that points back to the primary key of the SALES table.&nbsp;<br zid="160"/><br zid="161"/>For details, see chapter 5 of the UniVerse System Description manual, and for a thorough example see the sample code which is included with <a href="http://www.ibm.com/developerworks/edu/dm-dw-dm-0802kumar-i.html" zid="189">Part 2 of IBM&#8217;s tutorial on the ADO.Net driver</a>.&nbsp; In the third part of this series, I will show an example of how you would use a JOIN clause in a SELECT statement to return the fields in the virtual table in the same record as the fields in the main table.<br zid="187"/><br zid="188"/>The advantage of this approach is that the resulting SQL statements will closely resemble those that would be used for a relational database, and this should make it easier to port your application from UniVerse to a relational database should you wish to.&nbsp; The downside is that it can become quite cumbersome to code when your records contain multivalued attributes that aren&#8217;t related on one another. For example, if a customer record contains a list of order numbers in attribute 1, and a list of customer contacts in attribute 2, you&#8217;ll have to specify a different association name for these 2 entries,&nbsp;and retrieve them using either a very complicated 3-way JOIN, or using 2 different SELECT statements. Also, since UniVerse does not offer any constraints or other mechanisms for enforcing the integrity of your data, you are likely to find that bad data fouls up your application, resulting in missing rows or runtime Exceptions.&nbsp; In our application, we use this approach only for small and simple tables.<br zid="61"/><br zid="70"/>ii) You can return the multivalued strings in their &quot;native&quot; form &#8212; strings with delimiters separating the values.&nbsp; You do this by using an I-type dictionary entry that converts the multivalue delimiter (ASCII 253) to whatever character you want.&nbsp; For example, to return attribute 1 with the multivalued fields delimited by commas:</p>
<pre class="brush: sql">
   FIELD.LIST
001 I
002 CONVERT(@VM,&#039;,&#039;,@RECORD&amp;lt;1&gt;)
004 FIELD.LIST
005 50L
006 S
008 VARCHAR,32767
</pre>
<p>You might be wondering why you can&#8217;t just return the strings using the original delimiter, ASCII 253.&nbsp; For reasons known only to IBM, this results in a runtime exception. &nbsp;Incidentally, there is is a second type of delimiter which is also common in UniVerse, the subvalue delimiter (ASCII 252). &nbsp;UniVerse subvalues&nbsp; occur when the parts of a multivalued fields are, in turn, delimited into smaller fields.&nbsp; For reasons also known only to IBM, these are automatically converted by the ADO.NET driver to a carriage return and line feed (i.e. &quot;\r\n&quot;).&nbsp;&nbsp; IBM support has indicated that this delimiter conversion is actually a bug and will be changed back to ASCII 252 to a future Fix Pack.<br zid="190"/><br zid="195"/>Once you have the delimited string, you&#8217;ll need to use .Net&#8217;s string parsing capabilities to separate it into individual fields and convert the fields to their correct data type.&nbsp; You&#8217;ll find that .Net isn&#8217;t ideally suited for this type of string processing.&nbsp; UniVerse, on the other hand, is very much suited to it, and IBM provides a library named UniVerse Objects.Net that contains a variety of methods for extracting data from delimited strings.&nbsp; Frustratingly, your ability to use UniVerse Objects to parse your data is hampered by the fact that ADO.NET has forced you to replace all the multivalue and subvalue delimiters with other delimiters that UniVerse Objects doesn&#8217;t support.<br zid="192"/><br zid="193"/>If you decide to read UniVerse multivalued data in its native form, you might want to consider bypassing ADO.NET altogether and reading the data using UniVerse Objects.&nbsp; This is what our application does when dealing with tables that contain data that is mostly multivalued.&nbsp; It is much faster, and will likely require less code.<br zid="72"/><br zid="191"/>iii)&nbsp; While &quot;official&quot; UniVerse multivalued fields are delimited using ASCII 253 and 252, many UniVerse Basic programmers grow so fond of multivalues that they use them all over the place, with whatever delimiter character tickles their fancy.&nbsp; I&#8217;ve seen multivalued strings used as the primary key, and I&#8217;ve seen multivalued strings that contain embedded dates and time fields.&nbsp; While that kind of data structure would make SQL programmer cringe and a SQL database crawl, UniVerse is tuned to handle these strings efficiently and provides functions that you can insert in your dictionary to extract these fields relatively easily. <br zid="2"/><br zid="3"/>The trick is to place a correlative in attribute 8.&nbsp; Appendix C of the UniVerse Basic manual documents these correlatives, but be forewarned: they are cryptic enough to make a Perl programmer weep with frustration.&nbsp; Here are a couple of examples to get you started.<br zid="4"/><br zid="5"/>Say you had a key that was delimited with &quot;@&quot; signs.&nbsp; Just to make things interesting, say that one of the fields was a UniVerse Julian date, and another was a UniVerse Julian time.&nbsp; A typical key would be: FIRSTPART@14927@49055.75.<br zid="7"/><br zid="8"/>If you wanted to return the 1st part of the key (&quot;FIRSTPART&quot;) as a separate field in the SELECT statement, you could define it as follows in the UniVerse dictionary:</p>
<pre class="brush: sql">
    THE.STRING
001 S
002 0
005 S
008 F;0;(G0@1)
009 L
010 20
</pre>
<p>The correlative in attribute 8 means &quot;take attribute 0 (the key), then split it into sections with &#8216;@&#8217; as the delimiter and return part 0&quot;.&nbsp; If you want all the gory details of what the &quot;F code&quot; can do, see Appendix C of the UniVerse Basic manual.&nbsp; <br zid="22"/><br zid="23"/>To return the date string as a date, you would use a similar &quot;F code&quot; string in attribute 8, along with a conversion code in attribute 7:</p>
<pre class="brush: sql">
    THE.DATE
001 S
002 0
005 S
007 D2
008 F;0;(G1@1)
009 R
010 9
</pre>
<p>The time field is trickier &#8211; as mentioned above, the ADO.NET driver doesn&#8217;t like time fields with milliseconds.&nbsp; The best way to handle these fields is by putting the conversion code in attribute 8, but that&#8217;s where we need to put the correlative, and (as far as I know) you can&#8217;t put both a conversion code and a correlative in the same dictionary attribute.&nbsp; You can, however, combine 2 extract commands in a correlative:</p>
<pre class="brush: sql">
	THE.TIME
001 S
002 0
005 S
007 MTS
008 F;0;(G2@1);(G0.1)
009 R
010 12
</pre>
<p>This correlative tells the database to extract the time field from the key and then, having done that, extract the part before the decimal point.&nbsp; The conversion code in attribute 7 then converts that value to a .Net TimeSpan. &nbsp;(The Universe Basic manual refers to this as &quot;<span style="font-style: italic;" zid="198">reverse Polish format (Lukasiewicz)</span>&quot; &#8212; how geeky is that?)<br zid="196"/><br zid="197"/>So, there you have it: an overview of the tricks that I&#8217;ve used to whip our UniVerse data into a form suitable for consumption by .Net.&nbsp; <br zid="199"/><br zid="200"/>If you are new to UniVerse, you are likely thinking at this point: &quot;um, so how do I edit a dictionary&quot;.&nbsp; As a crash course on UniVerse for Windows programmers, I would strongly recommend the &quot;<a href="http://212.241.202.162/cms/cmsview.wsp?id=learner_pack" zid="117">Learner Pack</a>&quot; put together by the U2 User Group.&nbsp; The UniVerse manuals are a well written, exhaustive reference when you are ready to dive deeper, but as a crash couse you can&#8217;t do better than the U2UG Learner Pack.<br zid="118"/><br zid="119"/>In third and final part of this series, I&#8217;ll show you the (relatively simple) coding required to load UniVerse data into a DataGridView using ADO.Net.<br zid="6"/><br zid="1"/></body></html></p>
]]></content:encoded>
			<wfw:commentRss>http://gigamegatech.com/2008/12/03/the-meaning-of-everything-universe-dictionaries-for-the-net-programmer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Visual Studio and IBM UniVerse &#8211; Playing the Matchmaker</title>
		<link>http://gigamegatech.com/2008/11/25/visual-studio-and-ibm-universe-playing-the-matchmaker/</link>
		<comments>http://gigamegatech.com/2008/11/25/visual-studio-and-ibm-universe-playing-the-matchmaker/#comments</comments>
		<pubDate>Tue, 25 Nov 2008 23:05:39 +0000</pubDate>
		<dc:creator>Dan</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[IBM Universe (U2)]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://gigamegatech.com/2008/11/25/visual-studio-and-ibm-universe-playing-the-matchmaker/</guid>
		<description><![CDATA[
body { border: 0px; font-family:verdana; font-size :10pt; direction :ltr; background-color :#ffffff; line-height :1.2; margin:0% 10% 0% 10%;}
table { font-size: 10pt;} p { margin-top:0px; margin-bottom:0px; }


It has been a couple of months since we switched database APIs for accessing our IBM U2 UniVerse database from .Net.&#160; We had a lot of reasons to leave UniOLEDB behind, [...]]]></description>
			<content:encoded><![CDATA[<p><html><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"></meta><meta content="text/html;charset=utf-8" http-equiv="Content-Type" id="metaid"></meta><meta content="pagewise" id="fntype"></meta><meta content="1-1" id="fnstyle"></meta><meta content="340" name="zid"><br />
<style id="styletagforeditor">body { border: 0px; font-family:verdana; font-size :10pt; direction :ltr; background-color :#ffffff; line-height :1.2; margin:0% 10% 0% 10%;}</style>
<style id="styletagtwoforeditor">table { font-size: 10pt;} p { margin-top:0px; margin-bottom:0px; }</style>
<link href="/styles/print.css" id="stylesheetforpreview" media="print" rel="stylesheet"></link>
<link href="/styles/print_preview.css" id="stylesheetforprintpreview" media="screen" rel="stylesheet"></link>
<link href="/styles/editor.css" id="stylesheetforeditor" rel="stylesheet"></link></meta></head><body>It has been a couple of months since we switched database APIs for accessing our <a href="http://www-01.ibm.com/software/data/u2/">IBM U2 UniVerse</a> database from .Net.&nbsp; We had a lot of reasons to leave UniOLEDB behind, and pretty high expectations from IBM&#8217;s new &quot;DB2 Client&quot; ADO.NET driver, but the 3 most important reasons behind the switch were:
<ol>
<li><span style="font-weight: bold;">Stability</span>.&nbsp; UniOLEDB&#8217;s intermittent &quot;memory is corrupt&quot; errors were driving me nuts.&nbsp; Upgrading the database server from UniVerse 10.1 to 10.2 improved UniOLEDB&#8217;s stability significantly, but switching to the ADO.NET driver has made things better still.&nbsp; Stability is no longer a concern &#8212; we&#8217;ve had no problems with dropped connections or failed queries.</li>
<p><br zid="189"/>
<li><span style="font-weight: bold;">Deployment</span>.&nbsp; UniOLEDB&#8217;s archaic install program was driving our clients&#8217; IT guys nuts.&nbsp; The fact that UniOLEDB had to be installed by manually on each desktop was a dealbreaker for the larger sites.&nbsp; The DB2 Client&#8217;s installation program has its own quirks,  <a href="http://gigamegatech.com/2008/10/01/silent-but-exasperating-automated-install-of-the-ibm-db2-client/">as I wrote about here</a>, but once we figured out the trick our clients have had no problems rolling it out with automated tools like Microsoft Systems Management Server.</li>
<p><br zid="190"/>
<li><span style="font-weight: bold;">Productivity</span>.&nbsp; The ability to have Visual Studio treat UniVerse like a full-fledged database would make it a lot easier to developer new forms.&nbsp; With UniOLEDB we had to laboriously handcraft the DataAdapters for each table: write the SELECT, INSERT, UPDATE and DELETE statements, set their parameters, use them to construct the DataAdapter, then write the code to populate and manage the DataSet tables.&nbsp; That was a lot of code, and we have a lot of tables!&nbsp; Wouldn&#8217;t it be nice to replace all of that with a simple drag-and-drop?</li>
</ol>
<p>Well, 2 out of 3 ain&#8217;t bad.<br zid="192"/><br zid="193"/>We haven&#8217;t quite reached that third goal yet.&nbsp;&nbsp;We&#8217;ve got a relatively messy UniVerse database: our master dictionary and most of our data dictionaries are littered with references to things that don&#8217;t exist or aren&#8217;t correctly defined.&nbsp; UniVerse itself ignores these obsolete entries, but it seems that the DB2 Client dutifully tries to make sense of them, each and every time that we connect.<br zid="192"/><br zid="193"/>As a result, any attempt to access a table through the Visual Studio IDE, be it from the Server Explorer or the Data Sources window, is painfully slooooooow. &nbsp; I have an idea for doing an end-run around this problem, and when I have time to pursue it I&#8217;ll write a blog post describing what I did and how it turned out.<br zid="194"/><br zid="195"/>But, for now, drag and drop isn&#8217;t an option.<br zid="196"/><br zid="197"/> Our internal developers are still accessing UniVerse data through a labour-intensive code-based approach, without using the Server Explorer or Data Sources IDE at all.&nbsp; That&#8217;s fine for us, since we&#8217;ve built a framework to support it, but when I was recently asked to help a client&#8217;s .Net developers get to our data, I knew I had to give them something easier.<br zid="198"/><br zid="199"/>My boss assured them that once they had the UniVerse 10.2 and its new IBM ADO.NET driver, UniVerse would be as easy to use as SQL Server.&nbsp; Heck, we&#8217;d even create Views for the data they want.<br zid="200"/><br zid="201"/>Well, not so fast.&nbsp; Even with an ADO.NET driver, UniVerse doesn&#8217;t support Views. There&#8217;s a node called &quot;Views&quot; sitting under the UniVerse data connection in Visual Studio&#8217;s Server Explorer, but it will always remain forlornly barren. &nbsp;There is simply no equivalent entity in the world of UniVerse.<br zid="202"/><br zid="203"/>When I asked IBM about this, they recommended using UniVerse subroutines instead.&nbsp; These are regarded by the ADO.NET driver as stored procedures, meaning that they can be viewed and invoked from the Server Explorer under the Procedures node. &nbsp; You can use them with Visual Studio&#8217;s Data Source Configuration Wizard to create DataSets and DataAdapters without having to write any code.<br zid="204"/><br zid="205"/>Pretty nifty, and that&#8217;s something you couldn&#8217;t do with UniOLEDB.&nbsp; (Predictably, it would throw up a &quot;memory is corrupted&quot; error if you tried).&nbsp; But the subroutines are written in dowdy old UniVerse Basic, and our clients&#8217; developers didn&#8217;t want to go there.&nbsp; They opted for SQL.<br zid="206"/><br zid="207"/>Running SQL SELECT statements against a UniVerse database is a lot easier with the new ADO.NET driver, but it&#8217;s still tricky, with quite a few pitfalls that aren&#8217;t mentioned in IBM&#8217;s tutorials or (as far as I know) anywhere else.<br zid="208"/><br zid="209"/>So, having just played matchmaker between UniVerse and some .Net developers, I thought it might be useful to describe the quickest path to getting your UniVerse data into a .Net grid.<br zid="210"/><br zid="211"/>My next post will describe what needs to be done to prepare the UniVerse dictionaries for .Net, and the following post will show how to create UniVerse-friendly SELECT statements and feed the results into a DataGridView.</body></html></p>
]]></content:encoded>
			<wfw:commentRss>http://gigamegatech.com/2008/11/25/visual-studio-and-ibm-universe-playing-the-matchmaker/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>The Infragistics UltraToolbar: Didn&#8217;t those used to be underlined?</title>
		<link>http://gigamegatech.com/2008/11/19/the-infragistics-ultratoolbar-didnt-those-used-to-be-underlined/</link>
		<comments>http://gigamegatech.com/2008/11/19/the-infragistics-ultratoolbar-didnt-those-used-to-be-underlined/#comments</comments>
		<pubDate>Thu, 20 Nov 2008 00:40:08 +0000</pubDate>
		<dc:creator>Dan</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://gigamegatech.com/2008/11/19/the-infragistics-ultratoolbar-didnt-those-used-to-be-underlined/</guid>
		<description><![CDATA[A week or so after writing the previous post about our almost bug-free upgrade of Infragistics NetAdvantage, I belatedly noticed another bug worth mentioning.
In NetAdvantage, there aren&#8217;t separate &#34;menu bar&#34; and &#34;toolbar&#34; controls &#8212; everything is a toolbar.&#160; A menu bar is just a toolbar with lots of text and not many icons.&#160; Fair enough.&#160; [...]]]></description>
			<content:encoded><![CDATA[<p><body>A week or so after writing the <a href="http://gigamegatech.com/2008/10/28/infragistics-ultragrid-the-case-of-the-out-of-key-band/">previous post</a> about our almost bug-free upgrade of <a href="http://www.infragistics.com/dotnet/netadvantage.aspx">Infragistics NetAdvantage</a>, I belatedly noticed another bug worth mentioning.</p>
<p>In NetAdvantage, there aren&#8217;t separate &quot;menu bar&quot; and &quot;toolbar&quot; controls &#8212; everything is a toolbar.&nbsp; A menu bar is just a toolbar with lots of text and not many icons.&nbsp; Fair enough.&nbsp; But not all NetAdvantage toolbars are equal &#8211; you can set the <a href="http://help.infragistics.com/Help/NetAdvantage/NET/2008.2/CLR2.0/html/Infragistics2.Win.UltraWinToolbars.v8.2%7EInfragistics.Win.UltraWinToolbars.UltraToolbar%7EIsMainMenuBar.html">IsMainMenuBar</a> property on one (and only one) toolbar.&nbsp; In the version of NetAdvantage that we were previously using, 2007 Volume 1, this property seemed to have only one effect: it forced that toolbar to be on a separate row from all of the others.&nbsp; This seemed like an odd feature, so we never used it.</p>
<p>At some point between 2007 Volume 1 and 2008 Volume 2, the IsMainMenuBar property became a lot more important: if none of your toolbars have it, then you lose your accelerator keys.&nbsp; (By &quot;accelerator keys&quot;, I mean the underlined letters in menus that can be used with the Alt key as a shortcut.&nbsp; Some of the young folk today call them &quot;access keys&quot;, go figure.)</p>
<p>I didn&#8217;t notice until one of our internal testers diplomatically asked &quot;um, didn&#8217;t those used to be underlined&quot;?&nbsp; It took me awhile to figure out that the IsMainMenuBar property was at the root of the problem &#8211; this behaviour isn&#8217;t documented and, oddly, I couldn&#8217;t find any reference to the problem on the Infragistics forums.&nbsp;&nbsp; I submitted the problem to Infragistics, and so far they are handling it as a bug, not a feature.&nbsp; </p>
<p>The workaround is easy enough: use the IsMainMenuBar property.&nbsp;&nbsp;Since this forces other toolbars onto separate rows of the screen, we also merged everything into one toolbar: I figure the loss of screen real estate would be more annoying to users than losing the ability to position the toolbars on the screen.<br /></body></p>
]]></content:encoded>
			<wfw:commentRss>http://gigamegatech.com/2008/11/19/the-infragistics-ultratoolbar-didnt-those-used-to-be-underlined/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Infragistics UltraGrid: The Case of the Out-of-Key Band</title>
		<link>http://gigamegatech.com/2008/10/28/infragistics-ultragrid-the-case-of-the-out-of-key-band/</link>
		<comments>http://gigamegatech.com/2008/10/28/infragistics-ultragrid-the-case-of-the-out-of-key-band/#comments</comments>
		<pubDate>Wed, 29 Oct 2008 00:24:16 +0000</pubDate>
		<dc:creator>Dan</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://gigamegatech.com/2008/10/28/infragistics-ultragrid-the-case-of-the-out-of-key-band/</guid>
		<description><![CDATA[As I&#8217;ve written in my past articles about Infragistics NetAdvantage for .Net, we&#8217;ve been stuck on an older version for some time.&#160;  I was worried that an upgrade would introduce some new bugs and other code-breaking changes.&#160; NetAdvantage has a rich feature set that Infragistics aggressively adds to four times a year, but as [...]]]></description>
			<content:encoded><![CDATA[<p><body>As I&rsquo;ve written in <a href="http://gigamegatech.com/2008/06/27/infragistics-netadvantage-tips-part-1-the-ultragrid/" zid="99">my</a> <a href="http://gigamegatech.com/2008/07/30/infragistics-netadvantage-tips-part-2-the-ultratoolbarsmanager/" zid="100">past</a> <a href="http://gigamegatech.com/2008/08/24/infragistics-netadvantage-tips-part-3-editing-with-the-ultragrid/" zid="101">articles</a> about <a href="http://www.infragistics.com/dotnet/netadvantage.aspx" zid="102">Infragistics NetAdvantage for .Net</a>, we&rsquo;ve been stuck on an older version for some time.&nbsp;  I was worried that an upgrade would introduce some new bugs and other code-breaking changes.<span style zid="11">&nbsp; </span>NetAdvantage has a rich feature set that Infragistics aggressively adds to four times a year, but as a result the product has a relatively high number of bugs for a development tool.  Over the months we&rsquo;ve developed workarounds for the bugs and other eccentricities in the older version of NetAdvantage, and with the team focused on adding new functionality to our product I hated to throw a new variable into the mix.
<p zid="15">We finally took the plunge a few weeks ago, upgrading from 2007 Volume 1 to the latest and greatest, 2008 Volume 2.  I was relieved to find that it was an almost pain-free transition. </p>
<p zid="19">The only bump that we hit wasn&#8217;t even a bug in the current version: it might have been a bug in 2007 Vol 1 that they fixed, or just an undocumented syntax change. The problem occurred in code which identified an UltraGrid band by key rather than index number:</p>
<pre class="brush: csharp">
// by key:
UltraGridColumn colCode = grid.DisplayLayout.Bands[&quot;clsOurObject&quot;].Columns[â€œcodeâ€];

// by index:
UltraGridColumn colName = grid.DisplayLayout.Bands[0].Columns[â€œnameâ€];
</pre>
<p zid="42">Our coding standard was to identify bands by key rather than index number in multi-band grids, in case the grid was reorganized and a band was added or removed.</p>
<p zid="48">The UltraGrid band key is read-only, being automatically set by the Infragistics code.&nbsp; When we first started using NetAdvantage in 2006, I noticed that the key for the first-level band was set to the name of the underlying class, but the keys for the child bands were set to the name of the list variable which contained the band&rsquo;s rows.&nbsp; Strange, but not something that ruined my day.&nbsp; Life went on.</p>
<p zid="62">However, after the upgrade to 2008 Volume 2, this eccentricity became a problem.&nbsp; The key names were now all based on the list variable name.&nbsp; After fruitlessly poking around the UltraGrid&rsquo;s properties and methods for an option that would change things back to the way they were, I gave up and went with the low tech approach of changing all the band key references in our code:</p>
<pre class="brush: csharp">
//UltraGridColumn colCode = grid.DisplayLayout.Bands[&quot;clsOurObject&quot;].Columns[â€œcodeâ€];
// new key name:
UltraGridColumn colCode = grid.DisplayLayout.Bands[&quot;lstOurStuff&quot;].Columns[â€œcodeâ€];
</pre>
<p zid="85">We posted the problem to Infragistics&rsquo; support forum, and their support team was surprised that our original code had ever worked when using the class name as the key.<span style zid="86">&nbsp; </span>So, the problem remains unexplained: strange, but still not something that ruined my day.</p>
<p zid="89">I haven&rsquo;t taken advantage of the new functionality yet, but one clear and unexpected advantage from the upgrade is a marked performance improvement in large grids.  We have a window which loads <a href="http://gigamegatech.com/2008/05/26/unioledb-performance-optimization-hits-and-misses/" zid="110">massive amounts of data</a> into a two-level grid: sometimes over a million rows.<span style zid="91">&nbsp; </span>A grid of 200,000 rows which used to take about 30 seconds to load on my development PC (measured from the point that the underlying list was populated until the grid was up and responsive to user input) now takes just 6 seconds.<span style zid="92">&nbsp; </span>Navigating through this type of large grid and applying sorts and filters is also noticeably faster &ndash; sorting the 200,000 row grid takes less than a second, filtering takes about 3.<span style zid="93">&nbsp; </span>The Release Notes mention some  tweaking that Infragistics did to the grid back in Version 2007 Volume 3, but I didn&#8217;t expect an improvement on this scale.</p>
<p zid="106">A major update with just 1 bug and a huge performance boost: that&#8217;s what I call a upgrade!</p>
<p></body></p>
]]></content:encoded>
			<wfw:commentRss>http://gigamegatech.com/2008/10/28/infragistics-ultragrid-the-case-of-the-out-of-key-band/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Silent But Exasperating: Automated Install of the IBM DB2 Client</title>
		<link>http://gigamegatech.com/2008/10/01/silent-but-exasperating-automated-install-of-the-ibm-db2-client/</link>
		<comments>http://gigamegatech.com/2008/10/01/silent-but-exasperating-automated-install-of-the-ibm-db2-client/#comments</comments>
		<pubDate>Wed, 01 Oct 2008 23:24:17 +0000</pubDate>
		<dc:creator>Dan</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[IBM Universe (U2)]]></category>

		<guid isPermaLink="false">http://gigamegatech.com/2008/10/01/silent-but-exasperating-automated-install-of-the-ibm-db2-client/</guid>
		<description><![CDATA[



body { border: 0px; font-family:verdana; font-size :10pt; direction :ltr; background-color :#ffffff; line-height :1.2; margin:0% 10% 0% 10%;}
table { font-size: 10pt;} p { margin-top:0px; margin-bottom:0px; }
Having made the leap over to IBM&#8217;s new &#34;DB2 Client&#34; ADO.Net driver for the U2 UniVerse database, we recently spent some time figuring out how to get it onto users&#8217; PCs.&#160; [...]]]></description>
			<content:encoded><![CDATA[<p><html><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"></meta><meta content="text/html;charset=utf-8" http-equiv="Content-Type" id="metaid">
<link href="/styles/editor.css" id="stylesheetforeditor" rel="stylesheet"></link>
<link href="/styles/print.css" id="stylesheetforpreview" media="print" rel="stylesheet"></link>
<link href="/styles/print_preview.css" id="stylesheetforprintpreview" media="screen" rel="stylesheet">
<style id="styletagforeditor">body { border: 0px; font-family:verdana; font-size :10pt; direction :ltr; background-color :#ffffff; line-height :1.2; margin:0% 10% 0% 10%;}</style>
<style id="styletagtwoforeditor">table { font-size: 10pt;} p { margin-top:0px; margin-bottom:0px; }</style>
<p><meta content="pagewise" id="fntype"></meta><meta content="1-1" id="fnstyle"></meta><meta content="86" name="zid"></meta></link></meta></head><body>Having <a href="http://gigamegatech.com/2008/09/16/u2-and-adonet-relatively-relational/" zid="35">made the leap</a> over to IBM&#8217;s new <a href="http://www-01.ibm.com/software/data/db2/support/db2_9/download.html" zid="36">&quot;DB2 Client&quot; ADO.Net driver</a> for the <a href="http://www-01.ibm.com/software/data/u2/" zid="37">U2 UniVerse database</a>, we recently spent some time figuring out how to get it onto users&#8217; PCs.&nbsp; This turned out to be harder than I expected, and harder than it should be.<br zid="4"/><br zid="5"/>The new Data Client&#8217;s installer was actually a big selling point for us.&nbsp; The previous database driver, UniOLEDB, used a proprietary install program that didn&#8217;t support automated, silent installs.&nbsp; This means that someone from the user&#8217;s IT department had to visit each user&#8217;s desktop and 1) launch the install program, 2) click Next a bunch of times, 3) phone us and complain about how annoying this is.&nbsp; Every install program that we&#8217;ve created or incorporated into our product for the last 10 years has supported silent installs, except this one.&nbsp;&nbsp;<br zid="6"/><br zid="7"/>I was so giddy at the prospect of moving on to a proper installer that I assured our clients that we would now fully support silent installs without (ahem) actually trying to do one.&nbsp; My optimism was based on the fact that 1) the new database client uses the same installer as IBM&#8217;s flagship database, DB2, 2) this installer was created using one of the industry standard tools, InstallShield, which has supported silent installs for ages, and 3) the <a href="http://publib.boulder.ibm.com/infocenter/db2luw/v9r5/index.jsp" zid="85">DB2 Information Center</a>&#8217;s extensive documentation includes a section entitled &quot;<a href="http://publib.boulder.ibm.com/infocenter/db2luw/v9r5/topic/com.ibm.swg.im.iis.prod.install.core.doc/topics/iiypisco-silent.html" zid="86">Silent Installation</a>&quot;.&nbsp; It sure looks easy when you read about it.<br zid="8"/><br zid="9"/>Unfortunately, it doesn&#8217;t quite work the way it&#8217;s described, and finding the specifics required quite a lot of Googling as well as some trial and error.&nbsp; <br zid="10"/><br zid="11"/>Here&#8217;s what doesn&#8217;t work:<br zid="38"/><br zid="12"/>1) Installing without a response file.&nbsp; This will launch the installer, but it will quickly abort, and the error message written to the log is enigmatic:<br zid="40"/><br zid="74"/><span style="font-family: courier new,courier,monospace;" zid="79">Action ended 20:06:46: DB2SetDB2INSTANCECA.2BC48F01_561E_4906_8321_946A9F5A90AA. Return value 3.</span><br style="font-family: courier new,courier,monospace;" zid="80"/><span style="font-family: courier new,courier,monospace;" zid="81">Action ended 20:06:46: INSTALL. Return value 3.</span><br style="font-family: courier new,courier,monospace;" zid="82"/><span style="font-family: courier new,courier,monospace;" zid="83">MSI (s) (E4:68) [20:06:46:607]: Product: IBM Data Server Runtime Client &#8211; DB2COPY1 &#8212; Configuration failed.</span><br /><br zid="84"/>Presumably, the response file is mandatory.<br zid="39"/><br zid="17"/>2) The standard InstallShield command line switch for generating a response file in the Windows folder (-r).&nbsp; I never thought that the Windows folder was a great place to put it &#8212; apparently IBM agrees.<br zid="56"/><br zid="13"/>3) The <a href="http://publib.boulder.ibm.com/infocenter/db2luw/v9r5/topic/com.ibm.db2.luw.qb.server.doc/doc/c0007508.html" zid="58">Response File Generator utility</a> (db2rspgn).&nbsp; Although this utility does, indeed, generate a response file, we had absolutely no luck when trying to use this response file to do an install.&nbsp; Googling the problem suggested that many others had the same result, but I still have no idea what the solution is, if any. <br zid="57"/><br zid="14"/>4) Manually editing one of the sample response files, <a href="http://publib.boulder.ibm.com/infocenter/db2luw/v9r5/topic/com.ibm.swg.im.iis.prod.install.core.doc/topics/iiypisco-silent.html" zid="15">as suggested by the Information Center</a>, might have worked if we actually had a sample response file.&nbsp; The sample response files are apparently located on the <span class="filepath" zid="59"></span><span zid="60">&quot;Information Integration Product DVD&quot;, but I&#8217;ve never seen such a beast.</span><br zid="18"/><br zid="19"/>Here&#8217;s what does work:<br zid="61"/><br zid="20"/>1) Run the installer normally (with no command line switches), then look in your %TEMP% folder.&nbsp; Sure, enough, there&#8217;s a .rsp file left behind in there.&nbsp; <br zid="62"/><br zid="21"/>2) This response file works just fine with the standard InstallShield parameters for a silent install.&nbsp; For the current version of the DB2 runtime client, this is :<br zid="63"/><br zid="64"/>v9.5fp2_nt32_rtcl_en.exe /v&quot;/qn RSP_FILE_PATH=&lt;path of the .rsp file&gt;.<br zid="22"/><br zid="24"/>The contents of the elusive response file are somewhat of a letdown.&nbsp; Unless you need to install 2 different versions of the database drivers, you&#8217;re unlikely to ever want to change a thing. &nbsp; Should you not have an Information Integration Product DVD at hand, feel free to use our response file as a sample:</p>
<pre class="brush: csharp">
PROD=RUNTIME_CLIENT
INSTALL_TYPE=TYPICAL
DB2_COPY_NAME=DB2COPY1
DEFAULT_COPY=YES
DEFAULT_CLIENT_INTERFACE_COPY=YES
DEFAULT_INSTANCE=DB2
INSTANCE=DB2
DB2.NAME=DB2
LIC_AGREEMENT=ACCEPT
</pre>
<p></body></html></p>
]]></content:encoded>
			<wfw:commentRss>http://gigamegatech.com/2008/10/01/silent-but-exasperating-automated-install-of-the-ibm-db2-client/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>U2 and ADO.Net: Relatively Relational</title>
		<link>http://gigamegatech.com/2008/09/16/u2-and-adonet-relatively-relational/</link>
		<comments>http://gigamegatech.com/2008/09/16/u2-and-adonet-relatively-relational/#comments</comments>
		<pubDate>Tue, 16 Sep 2008 19:36:42 +0000</pubDate>
		<dc:creator>Dan</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[IBM Universe (U2)]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://gigamegatech.com/2008/09/16/u2-and-adonet-relatively-relational/</guid>
		<description><![CDATA[
body { border: 0px; font-family:verdana; font-size :10pt; direction :ltr; background-color :#ffffff; line-height :1.2; margin:0% 10% 0% 10%;}
table { font-size: 10pt;} p { margin-top:0px; margin-bottom:0px; }


 I come to bury UniOLEDB; not to praise it.Which won&#8217;t be a big surprise if you&#8217;ve seen by previous whining and kvetching on the subject.At the time we adopted UniOLEDB, [...]]]></description>
			<content:encoded><![CDATA[<p><html><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"></meta><meta content="text/html;charset=utf-8" http-equiv="Content-Type" id="metaid"></meta><meta content="pagewise" id="fntype"></meta><meta content="1-1" id="fnstyle"></meta><meta content="340" name="zid"><br />
<style id="styletagforeditor">body { border: 0px; font-family:verdana; font-size :10pt; direction :ltr; background-color :#ffffff; line-height :1.2; margin:0% 10% 0% 10%;}</style>
<style id="styletagtwoforeditor">table { font-size: 10pt;} p { margin-top:0px; margin-bottom:0px; }</style>
<link href="/styles/print.css" id="stylesheetforpreview" media="print" rel="stylesheet"></link>
<link href="/styles/print_preview.css" id="stylesheetforprintpreview" media="screen" rel="stylesheet"></link>
<link href="/styles/editor.css" id="stylesheetforeditor" rel="stylesheet"></link></meta></head><body> I come to bury UniOLEDB; not to praise it.<br zid="208"/><br zid="209"/>Which won&#8217;t be a big surprise if you&#8217;ve seen by previous <a href="http://gigamegatech.com/2007/10/31/on-u2-and-net/" zid="210">whining</a> and <a href="http://gigamegatech.com/2008/05/26/unioledb-performance-optimization-hits-and-misses/" zid="211">kvetching</a> on the subject.<br zid="202"/><br zid="203"/>At the time we adopted UniOLEDB, it was only ADO.Net-compatible driver for our <a href="http://www-01.ibm.com/software/data/u2/">IBM U2</a> UniVerse database, other than some third party products with expensive runtime licenses.&nbsp; UniOLEDB was never widely used in the U2 developer community, with preference given instead to the venerable but decidedly non-relational UniObjects.Net API.&nbsp; <br zid="212"/><br zid="213"/>UniOLEDB does work &#8212; our app has been running live on it for 9 months with decent reliability and performance. &nbsp;However, in my experience UniOLEDB had a few significant drawbacks, in descending order of priority:<br zid="4"/><br zid="5"/>1) It is not reliable when updating the database: INSERT and UPDATE commands would often complete without any error message but leave some fields untouched.&nbsp; We never could find a pattern to this problem (it was more common with dates and numeric fields, but would also occur with plain vanilla strings), and IBM support wasn&#8217;t able to recreate it.&nbsp; I reluctantly decided to use UniObjects for all database updates, thus giving up some of the advantages of ADO.Net.<br zid="6"/><br zid="7"/>2) It has an alarming tendency to raise a &quot;protected memory&quot; error &#8212; alarming both because of the panic-inducing but (as far as I can tell) totally misleading wording of the error message, and because it is quite frequent.&nbsp; Most users of our system will encounter this error a few times each day (though, needless to say, we replaced the error message text with something less worrisome).&nbsp; I never found a workaround for this.<br zid="214"/><br zid="215"/><img align="baseline" alt border="0" hspace="0" src="http://writer.zoho.com:80/ImageDisplay.im?name=305886000000065001/1221519351978_MemoryIsCorruptError.png&#038;accId=305886000000002007" vspace="0" zid="216"/><br zid="8"/><br zid="9"/>3) It doesn&#8217;t play nicely with Visual Studio&#8217;s design-time database tools.&nbsp; If you use the Add Data Connection wizard to add a UniOLEDB database to Visual Studio you&#8217;ll promptly be greeted with the aforementioned &quot;protected memory&quot; error, and be unable to use the Data Connections tool to graphically view the database&#8217;s tables and fields.&nbsp; (You can, however, type in SQL statements and view their results).<br zid="10"/><br zid="11"/>4) For us, at least, it doesn&#8217;t work well with the latest version of UniVerse, 10.2.&nbsp; Our app would frequently encounter &quot;Native error: 930065&quot; exceptions &#8212; the error code indicates that the maximum number of database licenses has been reached, but it would occur randomly and with no apparent connection to license usage.&nbsp; IBM&#8217;s tech support was not able to recreate this problem, either, and though they tried adding patches in various point upgrades to 10.2, none of the patches worked for us.<br zid="22"/><br zid="23"/>5) In order to make the non-relational UniVerse database accessible to a relational database driver, a large amount of effort must be spent tinkering with the UniVerse data dictionary trying to map out the data.&nbsp; Even then, you&#8217;ll almost certainly end up with some parts of the database that UniOLEDB can&#8217;t read without resorting to the native UniVerse delimited string format.&nbsp; I list this as the least of the problems only because it&#8217;s one that I never really expected UniOLEDB to solve.&nbsp; As Barack Obama might say, &quot;you can put lipstick on a multivalue database, but it&#8217;s still a multivalue database&quot;.&nbsp; (No, not you, Sarah &#8212; I&#8217;m talking about UniVerse).<br zid="206"/><br zid="207"/>Late in 2007 IBM finally released a native ADO.Net driver for U2 which, for lack of a better name, I&#8217;ll refer to as &quot;the ADO.Net driver&quot;. (Not very ingenious, I admit, but inarguably a better name than IBM&#8217;s.&nbsp; Depending on where you look, IBM refers to it as the &quot;IBM Data Server Provider for .NET&quot;, or the &quot;IBM Data Server Client&quot; or the &quot;DB2 9.5 Client&quot;. <br zid="18"/><br zid="19"/>This driver, when combined with a separate piece of software called the &quot;IBM Database Add-Ins for Visual Studio&quot;, was primarily targeted at addressing the third of the above problems. However, given that U2 is a very small fish in IBM&#8217;s large database pond, I found it particularly reassuring to see that this is part of a unified driver which also covers their flagship DB2 product along with the database business they bought from Informix.&nbsp; Since this driver was, on the client side, a complete rewrite when compared to UniOLEDB,&nbsp;I was pretty confident that it would also fix the fourth problem, and somewhat optimistic about finally solving the first two.<br zid="14"/><br zid="15"/>About a month ago we finally found time in the project timetable to begin porting our UniOLEDB code to the ADO.Net driver.&nbsp; Since the new driver is fully supported only in UniVerse 10.2, and all of our user sites are still on 10.1, we have to support both UniOLEDB and ADO.Net for the time being.&nbsp; So, I was hoping that the conversion would require relatively few changes to our code.&nbsp; <br zid="16"/><br zid="17"/>I&#8217;m pleased to report that it was a pretty simple conversion.&nbsp; On the server side, ADO.Net requires the same dictionary entries as UniOLEDB (and ODBC before it), so there was virtually nothing to be done within the database itself.&nbsp; On the client side, a new namespace is required, IBM.Data.DB2.&nbsp; In this namespace are &quot;DB2&quot;-specific replacements on a one-for-one basis for the OLEDB objects, such as connections, data adapters and parameters. &nbsp;After making these 1-for-1 substitutions, almost all of our code &quot;just worked&quot;. We retested every SQL statement in the application and found only two compatibility problems between UniOLEDB and the ADO.Net driver, as described below.<br zid="24"/><br zid="25"/>Here&#8217;s a blow-by-blow description of what we needed to do to port UniOLEDB to ADO.Net:<br zid="26"/><br zid="27"/>1.&nbsp; Download and install the ADO.Net client software. This step is actually incredibly difficult, thanks to IBM&#8217;s byzantine web site.&nbsp; Currently the right page to use is <a href="http://www-01.ibm.com/software/data/db2/support/db2_9/download.html" zid="217">http://www-01.ibm.com/software/data/db2/support/db2_9/download.html</a>, &nbsp;and the file you are looking for is the &quot;IBM Data Server Driver for<br zid="218"/>ODBC, CLI, and .NET&quot; (as opposed to the &quot;IBM Data Server Client&quot; or the &quot;IBM Data Server Runtime Client&quot;).&nbsp; The file is named v9.5fp2_nt32_dsdriver_EN.exe (where fp2 refers to Fix Pack 2).&nbsp; The Visual Studio Add-In is somewhat easier to spot, and is named v9.5fp2_nt32_vsai.exe.&nbsp; Once you have the files, I&#8217;d strongly recommend that you use IBM&#8217;s excellent series of tutorials (Parts <a href="http://www-128.ibm.com/developerworks/db2/library/techarticle/dm-0711kumar/?S_TACT=105AGX11&#038;S_CMP=TUT" zid="219">one</a>, <a href="http://www.ibm.com/developerworks/edu/dm-dw-dm-0802kumar-i.html" zid="220">two</a> and <a href="http://www.ibm.com/developerworks/edu/dm-dw-dm-0805else-i.html" zid="221">three</a>) to walk you through installation and configuration, and to familiarize yourself with what the new driver can do.<br zid="43"/><span style="font-size: 12pt; font-family: &quot;Times New Roman&quot;;" zid="35"><br zid="44"/></span>2. OK, back to your code.&nbsp; As the tutorial explains, you&#8217;ll need to add a reference to your project to IBM.Data.DB2.dll in order to get the IBM.Data.DB2 namespace.&nbsp; Naturally, you&#8217;ll need to declare this namespace (e.g. &quot;using IBM.Data.DB2&quot;) at the top of your source code, along with System.Data.Common.&nbsp; You should be able to remove the &quot;System.Data.OleDB&#8217; declaration.<br zid="45"/><br zid="46"/>3. Replace the OleDBConnection object with a DbConnection object.<br zid="47"/><br zid="48"/>4. Replace the OLEDB Connection string parameters with those used by the DbConnection object.&nbsp; You&#8217;ll need to add a new &quot;ServerType&quot; parameter and set to to &quot;Universe&quot; (since IBM&#8217;s unified ADO.Net driver wouldn&#8217;t otherwise have any idea that you want to use a UniVerse database).&nbsp; Since connection pooling is turned on by default, unless you have a UniVerse license that supports pooling you&#8217;ll also have to add a parameter to turn it off: set &quot;Pooling&quot; to &quot;false&quot;.&nbsp; A couple of other connection string parameters, for the server and database, now have new names.&nbsp; When all is said and done, you should have a connection string that looks like this:<br zid="49"/><br zid="50"/></p>
<pre class="brush: csharp">
string strConn = @&quot;User ID=JoeUser;Password=HisPassword;Database=c:\account\path;Server=ourserver;ServerType=universe;pooling=false&quot;;
</pre>
<p>Incidentally, you can now do away with the cryptic &quot;uci.config&quot; initialization file used by UniOLEDB.&nbsp; The ADO.Net driver does not require any configuration beyond what is in the code.&nbsp; Welcome to the 21st century!<br zid="60"/><br zid="37"/>5. Certain OleDB database objects need to be replaced with their ADO equivalents.&nbsp; Note that the syntax for DataReaders, Commands and Parameters is quite similar, with the main difference being that the ADO objects don&#8217;t have their own constructors.&nbsp; The DataAdapter, on the other hand, requires considerably more code to create using the ADO.Net driver:
<ul zid="89">
<li zid="65">OleDbCommand is now DbCommand</li>
</ul>
<pre class="brush: csharp">
// UniOLEDB
public static OleDbCommand DB_GetCommand(string strSQL)
{
    OleDbCommand cmd = new OleDbCommand(strSQL, conn);
    return cmd;
}

// ADO.Net
public static DbCommand DB_GetCommand(string strSQL)
{
    DbCommand cmd = conn.CreateCommand();
    cmd.CommandText = strSQL;
    cmd.CommandType = CommandType.Text;
    return cmd;
}
</pre>
<ul zid="62">
<li zid="63">OleDbDataReader is now DbDataReader </li>
</ul>
<pre class="brush: csharp">
// UniOLEDB:
public static DbDataReader DB_GetReader(string strSQL)
{
    OleDbCommand cmd = new OleDbCommand(strSQL, conn);
    DbDataReader rdr = cmd.ExecuteReader();
    return rdr;
}

// ADO.Net:
public static DbDataReader DB_GetReader(string strSQL)
{
    DbCommand cmdSQL = conn.CreateCommand();
    cmdSQL.CommandText = strSQL;
    cmdSQL.CommandType = CommandType.Text;
    DbDataReader rdr = cmdSQL.ExecuteReader();
    return rdr;
}
</pre>
<ul zid="121">
<li zid="68">OleDbDataAdapter is now DbDataAdapter</li>
</ul>
<pre class="brush: csharp">
// UniOLEDB:
public static OleDbDataAdapter DB_GetAdapter()
{
    return new OleDbDataAdapter();
}

// ADO.Net:
public static DbDataAdapter DB_GetAdapter()
{
    DbProviderFactory factory = DbProviderFactories.GetFactory(&quot;IBM.Data.DB2&quot;);
    DbDataAdapter adapter = factory.CreateDataAdapter();
    return adapter;
}
</pre>
<ul zid="146">
<li zid="147">OleDbType is now Db2Type (since we needed to support both UniOLEDB and ADO.Net, I changed our code to use the generic DbType enum and added conversion methods for the types that we use):</li>
</ul>
<pre class="brush: csharp">
// UniOLEDB:
public static OleDbType DB_GetOleDbType(DbType type)
{
    switch (type)
    {
        case DbType.Date:
            return OleDbType.DBDate;
        case DbType.Decimal:
            return OleDbType.Decimal;
        case DbType.String:
            return OleDbType.VarChar;
        case DbType.Int32:
            return OleDbType.Integer;
    }
}       

// ADO.Net:
private static DB2Type DB_GetDB2Type(DbType type)
{
    switch (type)
    {
        case DbType.Date:
            return DB2Type.Date;
        case DbType.Decimal:
            return DB2Type.Decimal;
        case DbType.String:
            return DB2Type.VarChar;
        case DbType.Int32:
            return DB2Type.Integer;
    }
}
</pre>
<ul zid="135">
<li zid="71">OleDbParameter is now DbParameter</li>
</ul>
<pre class="brush: csharp">
// UniOLEDB:
public static void DB_AddParameter(OleDbCommand cmd, string strName, DbType type, int intSize, string strSourceColumn)
{
    OleDbType oleDbType = DB_GetOleDbType(type);
    cmd.Parameters.Add(strName, oleDbType, intSize, strSourceColumn);
}       

// ADO.Net:
public static void DB_AddParameter(DbCommand cmd, string strName, DbType type, int intSize, string strSourceColumn)
{
    DB2Type db2Type = DB_GetDB2Type(type);
    DbParameter param = new DB2Parameter(strName, db2Type, intSize, strSourceColumn);
    cmd.Parameters.Add(param);
}
</pre>
<p>6. Once all of the tedious but straight-forward &quot;a Quarter Pounder with Cheese is now a Royale with Cheese&quot; stuff is out of the way, you should find that the two drivers speak pretty much the same language and not many other code changes are required. When we fired up the app and tried out our SQL statements, we only found two cases where the same statement didn&#8217;t return the same data:<br zid="192"/><br zid="193"/>a. Subvalue markers (ASCII 252) are returned by the ADO.Net driver as carriage-return/line-feed (&quot;/r/n&quot;). &nbsp;Subvalues are a method used by UniVerse to stuff 3 levels of data into a single field: if a multivalued field can be thought of as representing a parent-child table relationship, then subvalues can be thought of as parent-child-grandchild.&nbsp; We don&#8217;t use them much, and it seems that most other developers don&#8217;t either, since IBM support wasn&#8217;t aware of this when I reported it to them.&nbsp; However, IBM intends to resolve this problem in Fix Pack 3.<br zid="194"/><br zid="195"/>b. The ADO.Net driver doesn&#8217;t support use of the virtual &quot;@ASSOC_ROW&quot; field in SQL statements.&nbsp; The UniOLEDB driver stored a row number in this field when converting a multivalued field into multiple virtual rows.&nbsp; SQL statements that rely on row numbers are almost certainly not a good idea in any database &#8212; you should be able to rewrite the SQL to avoid using them.<br zid="196"/><br zid="225"/><br zid="197"/>And there you have it &#8212; with a relatively small and straightforward set of code changes, you&#8217;re now accessing your UniVerse data with a true-blue ADO.Net driver.&nbsp; <br zid="198"/><br zid="199"/>As for the set of problems listed at the top of this article, I can report that the new driver solves problems #2 and #4.&nbsp; I haven&#8217;t tried doing database updates with ADO.Net yet, so I can&#8217;t say if problem #1 is solved.&nbsp; I noticed that IBM&#8217;s series of tutorials for the new driver recommends that database subroutines (written in UniVerse Basic) be used to handle updates, so I plan to stick to using UniObjects for the time being.&nbsp; <br zid="200"/><br zid="201"/><img align="right" alt border="0" hspace="3" src="http://writer.zoho.com:80/ImageDisplay.im?name=305886000000065001/1221524307573_ItMayTakeAWhile.png&#038;accId=305886000000002007" vspace="8" zid="226"/>We haven&#8217;t completely cracked problem #3 yet, partly because of the unavoidable &quot;multivalued database with lipstick&quot; issue.&nbsp; The Visual Studio add-in works great with the sample HS.SALES account, and every crusty old UniVerse Basic programmer that I&#8217;ve showed it too is quite surprised to see a graphical database design tool that works with UniVerse.&nbsp; Unfortunately, when we tried accessing our real world database we ran into a serious performance problem. &nbsp;HS.SALES has 749 entries in its master dictionary &#8212; our database has over 5 thousand.&nbsp; As a result, it took Visual Studio a few minutes to enumerate all of our schema objects.&nbsp; A few minutes might not seem so bad, but this enumeration is done not only when the database&#8217;s object tree is first displayed, but each and every time a new query is created from the Data Connection.&nbsp; It&#8217;s really cramping our style!<br zid="223"/><br zid="224"/>However, there is hope that Visual Studio and UniVerse can now get along.&nbsp; IBM provides a few different ways to filter the number of schema objects that are available to Visual Studio, and we&#8217;ll be trying out those methods in the future.&nbsp; I&#8217;ll write about that in a future article.<br zid="77"/></body></html></p>
]]></content:encoded>
			<wfw:commentRss>http://gigamegatech.com/2008/09/16/u2-and-adonet-relatively-relational/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Infragistics NetAdvantage Tips &#8211; Part 3: Editing with the UltraGrid</title>
		<link>http://gigamegatech.com/2008/08/24/infragistics-netadvantage-tips-part-3-editing-with-the-ultragrid/</link>
		<comments>http://gigamegatech.com/2008/08/24/infragistics-netadvantage-tips-part-3-editing-with-the-ultragrid/#comments</comments>
		<pubDate>Sun, 24 Aug 2008 14:11:16 +0000</pubDate>
		<dc:creator>Dan</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://gigamegatech.com/2008/08/24/infragistics-netadvantage-tips-part-3-editing-with-the-ultragrid/</guid>
		<description><![CDATA[
body { border: 0px; font-family:verdana; font-size :10pt; direction :ltr; background-color :#ffffff; line-height :1.2; margin:0% 10% 0% 10%;}
table { font-size: 10pt;} p { margin-top:0px; margin-bottom:0px; }


In this installment of the Infragistics NetAdvantage series, I&#8217;m going to revisit the UltraGrid, which I wrote about in Part 1.&#160; The UltraGrid (nee WinGrid) is one of those programming tools [...]]]></description>
			<content:encoded><![CDATA[<p><html><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"></meta><meta content="text/html;charset=utf-8" http-equiv="Content-Type" id="metaid"></meta><meta content="pagewise" id="fntype"></meta><meta content="1-1" id="fnstyle"></meta><meta content="340" name="zid"><br />
<style id="styletagforeditor">body { border: 0px; font-family:verdana; font-size :10pt; direction :ltr; background-color :#ffffff; line-height :1.2; margin:0% 10% 0% 10%;}</style>
<style id="styletagtwoforeditor">table { font-size: 10pt;} p { margin-top:0px; margin-bottom:0px; }</style>
<link href="/styles/print.css" id="stylesheetforpreview" media="print" rel="stylesheet"></link>
<link href="/styles/print_preview.css" id="stylesheetforprintpreview" media="screen" rel="stylesheet"></link>
<link href="/styles/editor.css" id="stylesheetforeditor" rel="stylesheet"></link></meta></head><body>In this installment of the Infragistics <a href="http://www.infragistics.com/dotnet/netadvantage.aspx" zid="313">NetAdvantage</a> series, I&#8217;m going to revisit the UltraGrid, which I wrote about in <a href="http://gigamegatech.com/2008/06/27/infragistics-netadvantage-tips-part-1-the-ultragrid/" zid="314">Part 1</a>.&nbsp; <br zid="6"/><br zid="7"/>The <a href="http://help.infragistics.com/Help/NetAdvantage/NET/2008.2/CLR2.0/html/WinGrid.html" zid="318">UltraGrid</a> (nee WinGrid) is one of those programming tools that &quot;just works&quot;, as long as you&#8217;re happy with the way it works.&nbsp; If you just want to give users a way to view and edit data in Lists or DataTables the UltraGrid works quite well out of the box.&nbsp; It also provides a lot of useful features that the standard DataGridView doesn&#8217;t include and which require virtually no coding to implement: filtering, grouping (similar to a pivot table), and Excel export, to name just three.<br zid="8"/><br zid="9"/>However, if you&#8217;re one of those finicky developers who modifies their code to match the design, and not vice versa, then the UltraGrid&#8217;s rich feature set, backed by a vast set of properties and methods, becomes a steep learning curve.&nbsp; How do you paste the clipboard into a selected range of cells, like Excel does?&nbsp; How do you allow the user to navigate through the grid using the Enter key, also like Excel does?&nbsp; How do you make certain rows or certain columns read-only at runtime?<br zid="10"/><br zid="11"/>Well, RTFM is a good start.&nbsp; The NetAdvantage <a href="http://help.infragistics.com/NetAdvantage/NET/2008.2/CLR2.0/" zid="317">documentation</a> is excellent, far better than most development tools.&nbsp; In addition to the standard listing of classes, properties, methods and events, the help file includes &quot;How Do I?&quot; and &quot;Walkthroughs&quot; sections for many of their controls, covering some of the more commonly used non-obvious features.&nbsp; Their online <a href="http://devcenter.infragistics.com/Support/KnowledgeBase.aspx" zid="315">KnowledgeBase</a> is a good source of sample code, though it can be hard to sift out the obsolete material.&nbsp; Their online <a href="http://forums.infragistics.com/forums/default.aspx" zid="316">forums</a> are also a great resource.&nbsp; Infragistics employees closely monitor the forums and provide answers to almost all of the questions &#8212; every time that I&#8217;ve used the forum I&#8217;ve come away with the answer I needed.<br zid="12"/><br zid="13"/>But, of course, the first thing that most developers do to find an answer is &quot;Google it&quot;.&nbsp; And that is probably how you came to be reading my humble blog.&nbsp; So, without further ado, here is my list of non-obvious answers to common problems when editing with the Infragistics UltraGrid.<br zid="14"/><br zid="15"/><span style="font-weight: bold;" zid="319">Changing the editable state of a cell</span><br zid="20"/><br zid="21"/>One of the first things that tripped me up when working with the UltraGrid is that it isn&#8217;t possible to specify that &quot;column 2 in the rows 3, 4 and 7 should be read-only&quot;.&nbsp; The &quot;column 2&quot; part is not a problem, but you can set the editable state of cells in the active row only.&nbsp; So, your best bet is to handle the ActiveRowActivate event, and add code like the following:</p>
<pre class="brush: csharp">
if (grid.ActiveRow.IsDataRow)
{
	grid.ActiveRow.Band.Columns[&quot;columnkeyname&quot;].TabStop = false;
	grid.ActiveRow.Band.Columns[&quot;columnkeyname&quot;].CellActivation = Activation.NoEdit
}
</pre>
<p>There are a number of things in this short piece of code that require some explanation for those not accustomed to the ways of the UltraGrid:<br zid="33"/>
<ul zid="320">
<li zid="321"><img align="right" alt border="0" hspace="8" src="http://writer.zoho.com:80/ImageDisplay.im?name=305886000000064003/1219530095821_New%20Picture%20%282%29.bmp&#038;accId=305886000000002007" vspace="3" zid="324"/>The IsDataRow property is necessary to make sure that you&#8217;re not trying to set the edit state of a row that isn&#8217;t displaying your data, such as the header rows that the UltraGrid automatically inserts when using its Group By feature.</li>
<li zid="322">The Band property is used to identify which drill-down level the row belongs to.&nbsp; An example is the screenshot at the right, from Infragistics&#8217; website, showing a grid with 3 bands.&nbsp; And if you&#8217;re working with a plain vanilla grid with just 1 set of rows?&nbsp; Tough, you&#8217;ve still got a band, like it or not, so get used to coding a Band reference for rows, columns and cells.</li>
<li zid="323">The only .CellActivation setting that allows users to Tab past a non-editable cell is &quot;disabled&quot;, which also prevents the user from selecting the cell for copying into the clipboard.&nbsp; So, you&#8217;ll generally want to use Activation.NoEdit and set the TabStop separately.</li>
</ul>
<p><br zid="36"/><span style="font-weight: bold;" zid="325">Changing the editable state of a row</span><br zid="37"/><br zid="38"/>You might spend hours looking in vain for the setting to make a row non-editable.&nbsp; You can set the edit state for columns and cells, but not rows.&nbsp; Here&#8217;s some code to do it the hard way:</p>
<pre class="brush: csharp">
public static void Infragistics_SetAllColumnsEditState(UltraGrid grid, bool blnEditable,
	bool blnAllowTab)
{
	UltraGridBand band = grid.ActiveRow.Band;

	for (int i = 0; i &lt; band.Columns.Count; i++)  // columns belong to a band, not a row
	{
		if (!band.Columns[i].Hidden) // don&#039;t mess with hidden columns!
		{
			if (blnEditable)
			{
				column.TabStop = true;
				column.CellActivation = Activation.AllowEdit;
			}
			else
			{
				column.TabStop = blnAllowTab;
				column.CellActivation = Activation.NoEdit;
			}
		}
	}
}
</pre>
<p>Notice that Columns belong to a Band, not a Row.&nbsp; Cells belong to a Row, but the CellActivation property doesn&#8217;t exist for Cells, just Columns.&nbsp; Go figure.<br zid="67"/><br zid="68"/><span style="font-weight: bold;" zid="326">Translating Grid Text</span><br zid="69"/><br zid="70"/>The UltraGrid is unusually verbose for a screen component: there&#8217;s text in the filter settings (as shown in the screenshot), text in the column chooser, text in the Group By interface, and so on.&nbsp; All those prompts and instructions are better than Ikea-like pictograms, I suppose, but it&#8217;s all in English.&nbsp; <br zid="328"/><br zid="329"/><img align="baseline" alt border="0" hspace="0" src="http://writer.zoho.com:80/ImageDisplay.im?name=305886000000064003/1219531296991_Image-0028.jpg&#038;accId=305886000000002007" vspace="3" zid="331"/><br zid="330"/>If you need to support other languages, you&#8217;ll need to provide your own translations. &nbsp; To do so, you&#8217;ll need a reference to the Infragistics ResourceCustomizer class, and a <a href="http://help.infragistics.com/Help/NetAdvantage/NET/2008.2/CLR2.0/html%5CWinGrid_Resource_Strings.html" zid="327">list of the keys used to identify each piece of text</a>.</p>
<pre class="brush: csharp">
            Infragistics.Shared.ResourceCustomizer rc = Infragistics.Win.UltraWinGrid.Resources.Customizer;
            rc.SetCustomizedString(&quot;GroupByBoxDefaultPrompt&quot;, NLS.TxDragaColumnHeadr);
            rc.SetCustomizedString(&quot;ColumnChooserButtonToolTip&quot;, NLS.TxFieldChooser);
            rc.SetCustomizedString(&quot;ColumnChooserDialogCaption&quot;, NLS.TxFieldChooser);
            rc.SetCustomizedString(&quot;FilterClearButtonToolTip_RowSelector&quot;, NLS.TxClearAllFilterCriteria);
            // and so on...
</pre>
<p>The &quot;NLS&quot; in the above code is a reference to the project&#8217;s resource class where we store all our text translations.<br zid="83"/><br zid="84"/><span style="font-weight: bold;" zid="332">Selecting All Rows in the Grid</span><br zid="85"/><br zid="86"/>This is another common task that seems more convoluted than necessary.&nbsp; Rather than just tell the UltraGrid &quot;Select All&quot;, you need to tell it &quot;Add to the collection of selected rows all the data rows that are currently visible in the grid&quot;:</p>
<pre class="brush: csharp">
grid.Selected.Rows.AddRange(grid.Rows.GetFilteredInNonGroupByRows());
</pre>
<p><span style="font-weight: bold;" zid="333">Make Enter Work Like Tab</span><br zid="90"/><br zid="91"/>A grid looks like a spreadsheet, so UltraGrid should work like Excel, right?&nbsp; Your users will probably think so, but UltraGrid&#8217;s designers don&#8217;t entirely agree.&nbsp; By default, pressing Enter in a grid cell won&#8217;t do anything.&nbsp; The solution is extremely non-obvious, but fortunately it&#8217;s covered in the Infragistics <a href="http://devcenter.infragistics.com/Support/KnowledgeBaseArticle.aspx?ArticleID=2028" zid="334">KnowledgeBase article KB2028</a>:</p>
<pre class="brush: csharp">
Infragistics.Win.UltraWinGrid.GridKeyActionMapping newKam;

foreach (Infragistics.Win.UltraWinGrid.GridKeyActionMapping ugKam in Grid.KeyActionMappings)
{
	if (ugKam.KeyCode == Keys.Tab)
	{
		newKam = new Infragistics.Win.UltraWinGrid.GridKeyActionMapping(Keys.Enter, ugKam.ActionCode, ugKam.StateDisallowed, ugKam.StateRequired, ugKam.SpecialKeysDisallowed, ugKam.SpecialKeysRequired);
		Grid.KeyActionMappings.Add(newKam);
	}
}
</pre>
<p><span style="font-weight: bold;" zid="335">Select A Range of Cells</span><br zid="256"/><br zid="257"/>By default, when the user drags the mouse down a column of cells the grid will select cells row-by-row, similar to a Word document.&nbsp; Most users will expect cell selection to work like Excel, not Word, and allow a rectangular region of cells to be selected. &nbsp; Fortunately, turning on this support is a one-liner, but the trick is that you&#8217;ll need to put that 1 line in the InitializeLayout event:</p>
<pre class="brush: csharp">
private void grid_InitializeLayout(object sender, InitializeLayoutEventArgs e)
{

		// allow drag select like in Excel
		e.Layout.Override.SelectTypeCell = SelectType.Extended;

}
</pre>
<p><span style="font-weight: bold;" zid="336">Paste Into A Range of Cells</span><br zid="105"/><br zid="106"/>This is another case where users will expect your grid to emulate Excel.&nbsp; If you a copy a cell into the clipboard then select a rectangular set of cells and click Paste, they&#8217;ll want the contents of the clipboard to be pasted into the full range of selected cells, repeating the value(s) as often as necessary.&nbsp; Unfortunately, to UltraGrid paste means &quot;paste once&quot;, so you&#8217;ll have to do the work yourself:</p>
<pre class="brush: csharp">
int intNumRows;
int intNumCols;
UltraGridCell startingCell = GetSelectedCells(grid, out intNumRows, out intNumCols);
if (startingCell != null)
{
	string[,] strClipboardCells = Array_FromClipboard(strHeaderValues);
	int intClipboardRows = strClipboardCells.GetUpperBound(0) + 1;
	int intClipboardCols = strClipboardCells.GetUpperBound(1) + 1;
	// if only 1 cell is selected, assume that the user wants to paste the entire clipboard (Excel&#039;s behaviour)
	if ((intNumRows == 1) &amp; (intNumCols == 1))
	{
		intNumRows = intClipboardRows;
		intNumCols = intClipboardCols;
	}
	if ((intClipboardRows &gt; 0) &amp;
		(intClipboardCols &gt; 0))
	{
		int intClipboardRow = 0;
		int intClipboardCol = 0;
		int intCurrentRow = 0; // row index within the selection
		int intCurrentCol = 0; // col index within the selection
		int intStartingColOfClipboardBlock = 0; // col index within selection of start of current paste block
		int intStartingRowOfClipboardBlock = 0; // row index within selection of start of current paste block
		UltraGridCell currentCell = startingCell;
		UltraGridRow currentRow = currentCell.Row;
		UltraGridCell startingCellOfClipboardBlock = startingCell;
		UltraGridColumn col; // current column to be pasted in the grid
		UltraGridRow row; //current row to be pasted in the grid

		while (intCurrentRow &lt; intNumRows)
		{
			// paste the value
			currentCell.Value = strClipboardCells[intClipboardRow, intClipboardCol];

			// advance to next cell in clipboard
			intClipboardCol += 1;
			// advance to next cell in selection block
			intCurrentCol += 1;
			// advance to the next cell in the grid
			col = currentCell.Column.GetRelatedVisibleColumn(VisibleRelation.Next);
			// if we&#039;ve reached the last cell of the clipboard, or last cell of the selection block, or last cell of
			//   the grid, need to wrap around
			if ((intClipboardCol &gt;= intClipboardCols) | (intCurrentCol &gt;= intNumCols) | (col == null))
			{
				intClipboardRow += 1;
				intClipboardCol = 0;
				row = currentCell.Row.GetSibling(SiblingRow.Next);
				intCurrentRow += 1;
				intCurrentCol = intStartingColOfClipboardBlock;

				// if we&#039;ve reached the last row of the clipboard, or last row of the selection block, or last row of
				//   the grid, need to advance to next set of columns to be pasted
				if ((intClipboardRow &gt;= intClipboardRows) | (intCurrentRow &gt;= intNumRows) | (row == null))
				{
					// start pasting a new clipboard block, unless we&#039;ve reached the end of the selection
					intCurrentRow = intStartingRowOfClipboardBlock;
					intClipboardCol = 0;
					intClipboardRow = 0;
					intCurrentCol = intStartingColOfClipboardBlock + intClipboardCols;
					intStartingColOfClipboardBlock = intCurrentCol;
					col = currentCell.Column.GetRelatedVisibleColumn(VisibleRelation.Next);
					if ((intCurrentCol &gt;= intNumCols) | (col == null))
					{
						// reached last column of paste selection, advance to next set of rows to be pasted into
						intCurrentRow += intClipboardRows;
						intCurrentCol = 0;
						intStartingColOfClipboardBlock = 0;
						intStartingRowOfClipboardBlock = intCurrentRow;
						if (row != null)
						{
							currentCell = row.Cells[startingCell.Column.Key];
							startingCellOfClipboardBlock = currentCell;
						}
						// else, we&#039;ve reached the end of the grid, so presumably intRow is &gt; intNumRows and the
						//   while loop will end
					}
					else
					{
						// start pasting a new block in the same row as the previous block
						currentCell = startingCellOfClipboardBlock.Row.Cells[col.Key];
						startingCellOfClipboardBlock = currentCell;
					}
				}
				else
				{
					// wrap around to next cell
					currentCell = row.Cells[startingCellOfClipboardBlock.Column.Key];

				}
			}
			else
			{
				currentCell = currentCell.Row.Cells[col.Key];

			}
		}
	}
}
</pre>
<p>Much of this code is pretty mundane stuff that I&#8217;m including to complete the solution, but a couple of odd methods stand out. The call to currentCell.Column.GetRelatedVisibleColumn(VisibleRelation.Next) is a complicated solution to a seemingly easy requirement: get the next column to the right.&nbsp; Columns in UltraGrid are indexed according to their position in the data source, not on the screen. Similarly, to move to the next row you can&#8217;t just increment the row index: that won&#8217;t skip past filtered-out rows and hidden row.&nbsp; If you&#8217;re using drill-down rows then row navigation becomes even more complicated, so the approach I use is currentCell.Row.GetSibling(SiblingRow.Next). &nbsp; Surprisingly, there is no collection in the UltraGrid that presents the grid cells in the same order that the user sees them &#8212; your code will have to figure that out on the fly in order to navigate through the grid cell by cell.<br zid="254"/><br zid="255"/><img align="right" alt border="0" hspace="0" src="http://writer.zoho.com:80/ImageDisplay.im?name=305886000000064003/1219532311988_waldocorner%5B1%5D.gif&#038;accId=305886000000002007" vspace="0" zid="340"/>Notice that the instruction that does the actual pasting is a one-liner that is overshadowed by all the grid navigation code &#8212; picking that line out of the pile is sort of like playing &quot;<a href="http://whereswaldo.com/" zid="339">Where&#8217;s Waldo</a>&quot;.<br zid="252"/><br zid="253"/>The Array_FromClipboard method called by this code is pretty self-explanatory, but the GetSelectedCells method qualifies as non-obvious because of the same navigation problems described above.&nbsp; It&#8217;s easy to get the collection of selected cells &#8212; grid.Selected.Cells &#8212; but that collection of cells isn&#8217;t nicely organized into rows and columns for you.&nbsp; To figure out how many rows and columns are selected, and which of the cells is the upper left corner of the selection, you&#8217;ll need to do a bit of work:</p>
<pre class="brush: csharp">
// initialize
int intFirstSelectedRow = Int32.MaxValue;
int intFirstSelectedCol = Int32.MaxValue;

int intLastSelectedRow = -1;
int intLastSelectedCol = -1;
UltraGridCell upperLeftCell = null;

foreach (UltraGridCell cell in grid.Selected.Cells)
{
	bool blnFirstCol = false;
	bool blnFirstRow = false;
	if (cell.Row.VisibleIndex &lt; intFirstSelectedRow)
	{
		// use visible row -- otherwise, hidden network babies cause problems
		intFirstSelectedRow = cell.Row.VisibleIndex;
		blnFirstRow = true;
	}
	if (cell.Row.VisibleIndex &gt; intLastSelectedRow)

		intLastSelectedRow = cell.Row.VisibleIndex;
	if (cell.Column.Header.VisiblePosition &lt; intFirstSelectedCol)
	{
		intFirstSelectedCol = cell.Column.Header.VisiblePosition;
		blnFirstCol = true;
	}
	if (cell.Column.Header.VisiblePosition &gt; intLastSelectedCol)
		intLastSelectedCol = cell.Column.Header.VisiblePosition;
	if (blnFirstCol &amp; blnFirstRow)
		upperLeftCell = cell;
}

intNumSelectedCols = intLastSelectedCol - intFirstSelectedCol + 1;
intNumSelectedRows = intLastSelectedRow - intFirstSelectedRow + 1;
</pre>
<p>Notice that the VisibleIndex and VisiblePosition properties are used instead of Index and Position.&nbsp; The rows&#8217; Index property includes filtered-out rows, while the columns&#8217; Position property includes hidden columns and does not account for the fact that UltraGrid allows users to drag columns to a new position.<br zid="270"/><br zid="271"/><span style="font-weight: bold;" zid="337">Tabbing Into the Grid</span><br zid="272"/><br zid="273"/>OK, for my last trick of the day, I&#8217;d like to cover yet another scenario that should &quot;just work&quot;, but doesn&#8217;t.&nbsp; If a user tabs into a grid, they&#8217;ll expect focus to be on the first editable cell so that they can just start typing.&nbsp; By default, when you tab into UltraGrid it gives the focus to&#8230; nothing. As far as I can tell, it isn&#8217;t possible for users to start editing a grid without using the mouse.&nbsp; After much trial and error, I ended up adding the following code to the grid&#8217;s Enter event:</p>
<pre class="brush: csharp">
if (grid.ActiveCell == null)
{
	if (grid.ActiveRow == null)
	{
		if (grid.Rows.Count == 0)
			return; // empty grid, so no way to give a cell focus
		grid.ActiveRow = grid.Rows[0];
	}
	// make the leftmost cell the active cell -- will find the first editable cell below
	grid.ActiveCell = grid.ActiveRow.Cells[&quot;theKey&quot;];
}
UltraGridCell cell = grid.ActiveCell;
if (!cell.CanEnterEditMode)
	// find the next editable cell
	grdRatings.PerformAction(UltraGridAction.NextCell);
if (grdRatings.ActiveCell != null)
{
	while (!grid.ActiveCell.CanEnterEditMode)
	{
		// have we reached the end?
		grid.PerformAction(UltraGridAction.NextCell);
		if (grid.ActiveCell == null)
			return; // give up
		if (grid.ActiveCell == cell)
			return; // we aren&#039;t going anywhere, give up
		if ((grid.ActiveCell.Row.Index == 0) &amp; (grid.ActiveCell.Column.Key == &quot;theKey&quot;))
			return; // looped around, so give up
		cell = grid.ActiveCell;

	}
	if (cell.CanEnterEditMode)
		grid.PerformAction(UltraGridAction.EnterEditMode);

	grid.ActiveRowScrollRegion.ScrollCellIntoView(grid.ActiveCell, grid.DisplayLayout.ColScrollRegions[0]);
}
</pre>
<p>Ye gods, that&#8217;s a lot of work for such a simple and commonplace task!&nbsp; I guess that&#8217;s why they pay us coders the big bucks.&nbsp; <br zid="311"/><br zid="312"/>Notice that the list line of the above code sample contains a reference to something called a column scroll region.&nbsp; Oooh, what&#8217;s that?&nbsp; It&#8217;s a method of divided up your grid rows into sections that scroll independently, similar to Excel&#8217;s Split feature.&nbsp; It&#8217;s also filled with more pitfalls than the <a href="http://www.imdb.com/title/tt0087469/" zid="338">Temple of Doom</a>.&nbsp; Sounds like a good topic for a future article!<br zid="249"/><br zid="5"/><br />
<em>Update on November 26, 2008: </em><br zid="1"/><br zid="2"/>In response to the question in the comments section about overriding the default UltraGrid error messages&#8230;&nbsp; <br zid="3"/><br zid="4"/>In general the best way to prevent the Infragistics grid from displaying its error messages to the user is to handle the grid&#8217;s Error event.&nbsp; Just set the e.Cancel parm to true.&nbsp; <a href="http://help.infragistics.com/Help/NetAdvantage/NET/2008.3/CLR2.0/html/Infragistics2.Win.UltraWinGrid.v8.3%7EInfragistics.Win.UltraWinGrid.UltraGrid%7EError_EV.html" zid="10">This Infragistics help page</a> includes a good example of the things you can do in the Error event.<br zid="5"/><br zid="6"/>In the case of null values, you can set the column&#8217;s <a href="http://help.infragistics.com/Help/NetAdvantage/NET/2008.3/CLR2.0/html/Infragistics2.Win.UltraWinGrid.v8.3%7EInfragistics.Win.UltraWinGrid.UltraGridColumn%7ENullable.html" zid="11">Nullable property</a>&nbsp; to tell the grid that null values are allowed. If your grid&#8217;s data source is a database table then UltraGrid will automatically set this property to an appropriate value for the column &#8212; otherwise you&#8217;ll need to set this property yourself. The property is set to whatever value you want to display in the column when it is empty, which is usually a zero length string: e.g. col.Nullable = Infragistics.Win.UltraWinGrid.Nullable.EmptyString;&nbsp;<br />
</body></html></p>
]]></content:encoded>
			<wfw:commentRss>http://gigamegatech.com/2008/08/24/infragistics-netadvantage-tips-part-3-editing-with-the-ultragrid/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>
