<?xml version="1.0" encoding="utf-8" ?>
<?xml-stylesheet type="text/xsl" href="RSS_xslt_style.asp" version="1.0" ?>
<rss version="2.0" xmlns:WebWizForums="http://syndication.webwiz.co.uk/rss_namespace/">
 <channel>
  <title>DevForce Community Forum : Tech Tip: Working with User-Defined Fields</title>
  <link>http://www.ideablade.com/forum/</link>
  <description>This is an XML content feed of; DevForce Community Forum : DevForce Classic : Tech Tip: Working with User-Defined Fields</description>
  <pubDate>Fri, 12 Jun 2026 10:00:12 -700</pubDate>
  <lastBuildDate>Wed, 06 Jun 2007 15:00:04 -700</lastBuildDate>
  <docs>http://blogs.law.harvard.edu/tech/rss</docs>
  <generator>Web Wiz Forums 9.69</generator>
  <ttl>360</ttl>
  <WebWizForums:feedURL>www.ideablade.com/forum/RSS_post_feed.asp?TID=56</WebWizForums:feedURL>
  <image>
   <title>DevForce Community Forum</title>
   <url>http://www.ideablade.com/forum/forum_images/IdeaBlade_logo_tm.png</url>
   <link>http://www.ideablade.com/forum/</link>
  </image>
  <item>
   <title>Tech Tip: Working with User-Defined Fields : Level 300 DevForce Express Sep...</title>
   <link>http://www.ideablade.com/forum/forum_posts.asp?TID=56&amp;PID=127#127</link>
   <description>
    <![CDATA[<strong>Author:</strong> <a href="http://www.ideablade.com/forum/member_profile.asp?PF=21" rel="nofollow">IdeaBlade</a><br /><strong>Subject:</strong> 56<br /><strong>Posted:</strong> 06-Jun-2007 at 3:00pm<br /><br /><P =Questi&#111;n style="MARGIN: 0in 0in 0pt"><FONT face="Times New Roman" size=3><strong><EM>Level 300 <BR>DevForce Express</EM></strong></FONT></P><P =Questi&#111;n style="MARGIN: 0in 0in 0pt"><strong><EM><FONT face="Times New Roman" size=3>Sep 6, 2006</FONT></EM></strong></P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt"><FONT face="Times New Roman" size=2>Suppose you ship your software application to multiple customers who want to add their own custom columns to the database tables you provide. Naturally, such columns will be of little use if the data they contain can’t be exposed and maintained through the application’s user interface. But how to do that without creating and maintaining multiple versions of your app – something your organization can ill afford? </FONT></P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt"><FONT face="Times New Roman" size=2>You might consider adding a set amount of generic columns to each of the tables (not too many; not too few: good luck!) and writing code to allow your customers to assign particular meanings (and labels) to them. But that’s a big chunk of complex functionality to code and maintain as well. Besides, the end result isn’t quite what your customers really want as they’re locked into the data types you provide, or limited to the particular number of custom columns you provide, or encumbered everywhere they turn (e.g., when writing reports) by columns with abstract meanings, some of which they didn’t need or want to begin with. It’s still a better solution than maintaining 10 (or 100, or 1000) custom variations of your app, but it’s messy, maintenance-intensive, and full of painful compromises.</FONT></P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt"><FONT face="Times New Roman" size=2>What if, on the other hand, you could write a single-version application that could discover customer-added columns on its tables at runtime, and dynamically create bound controls for them? What if the app were also smart enough to permit your customers to add as many such columns as they wish, with their choice of names and data types? How much easier would that make your life? How much happier would that make your customers? </FONT></P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt"><FONT face="Times New Roman" size=2>DevForce provides you with just such a capability. This week’s Tech Tip will describe the first half of the solution – that of discovering the custom columns at runtime. Next week’s tip will complete the picture by showing you how to create properties and bind user interface controls to them.</FONT></P><P =Ms&#111;normal style="MARGIN: 5pt 0in; LINE-HEIGHT: 13pt"><B><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica">Custom Columns in the Object Mapper</SPAN></B><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica"><?:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p></o:p></SPAN></P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt"><FONT face="Times New Roman" size=2>As you may know, when the DevForce Object Mapper generates classes for your business objects, it creates both a developer-level class (e.g., “Employee”) and a “DataRow” class (“EmployeeDataRow”) which the former inherits. Columns in the base table map to simple properties whose definitions are written to the “DataRow” class.</FONT></P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt"><FONT face="Times New Roman" size=2>In the Simple Properties grid of the Object Mapper, if you uncheck the "Include" checkbox for some columns of a table, no property definitions get generated into the “DataRow” class for those columns. From the viewpoint of the generated class – and objects instantiated from it – those properties do not exist.</FONT></P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt"><o:p><FONT face="Times New Roman" size=2>&nbsp;</FONT></o:p></P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt"><o:p><FONT face="Times New Roman" size=2><img src="http://www.ideablade.com/Forum/uploads/20070606_175707_SNAG-0018.jpg" height="447" width="608" border="0" /></FONT></o:p></P><o:p><FONT face="Times New Roman" size=2><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt">This is exactly the same situation that would occur if you generated your business classes against a particular database table, and then someone – a customer, let’s say – subsequently added new columns to that table. The generated business class simply would know nothing about the new columns. </P><P =Ms&#111;normal style="MARGIN: 5pt 0in; LINE-HEIGHT: 13pt"><B><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica">How DevForce Retrieves Data for Your Business Objects</SPAN></B><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica"><o:p></o:p></SPAN></P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt">On the “Class” sub-tab of the “Class Detail” tab in the Object Mapper, you will find a dropdown list labeled “Column Selection Options”. The default setting for this property is “SelectAllColumns”; an alternate setting is “AllowColumnSelection”.</P><DIV></DIV><P><img src="http://www.ideablade.com/Forum/uploads/20070606_175813_SNAG-0019.jpg" height="457" width="618" border="0" /></P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt">Under the default setting, when data is retrieved for one of your business objects, what is retrieved is the entire data row behind that object – including columns for which no properties are defined in the business class. In SQL terms, the retrieval is implemented as a “SELECT *” rather than a “SELECT ColumnName1, ColumnName2, ColumnName3, etc.”. That means, for our customer-added columns scenario, that you get the data for the custom-added columns, even if your business class doesn’t know what to do with it. </P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt">To make use of the additional data, you must somehow discover which columns have been retrieved for which no property is defined in the business class. If you can do that, you have the first half of your custom columns problem solved.</P><P =Ms&#111;normal style="MARGIN: 5pt 0in; LINE-HEIGHT: 13pt"><B><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica">Getting a List of Properties Defined in the Business Class</SPAN></B><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica"><o:p></o:p></SPAN></P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt">To isolate the custom columns, you need two things: </P><OL style="MARGIN-TOP: 0in" =1><LI =Ms&#111;normal style="MARGIN: 8pt 0in 0pt; mso-list: l1 level1 lfo2; tab-stops: list .5in">a list of the properties currently defined for the business object; and</LI><LI =Ms&#111;normal style="MARGIN: 8pt 0in 0pt; mso-list: l1 level1 lfo2; tab-stops: list .5in">a comprehensive list of the columns retrieved. </LI></OL><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt">The custom columns can be isolated by identifying items in the second list that aren’t in the first.</P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt">You can get the list of currently defined properties by calling the Get() method of the IdeaBlade.Util.PropertyDescriptorsList class, letting it know the type about which you want information.</P><P =Ms&#111;normal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: 13pt"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica">&nbsp;<o:p></o:p></SPAN></P><P =Ms&#111;normal style="MARGIN: 5pt 0in; LINE-HEIGHT: 13pt"><B><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica">C#:</SPAN></B><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica"><o:p></o:p></SPAN></P><P =CodeSnippet style="MARGIN: 0in 0in 0pt 0.25in"><strong><FONT color=#808080><FONT style=": #e6e6e6"><FONT face="Courier New">PropertyDescriptorList pdlist = PropertyDescriptorList.Get(typeof(Employee));<SPAN style="FONT-FAMILY: Helvetica"><o:p></o:p></SPAN></FONT></FONT></FONT></strong></P><P =Ms&#111;normal style="MARGIN: 5pt 0in; LINE-HEIGHT: 13pt"><SPAN><BR><strong>VB.NET:</strong></SPAN><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica"><o:p></o:p></SPAN></P><P =CodeSnippet style="MARGIN: 0in 0in 0pt 0.25in"><strong><FONT color=#808080><FONT style=": #e6e6e6"><FONT face="Courier New">Dim pdlist As PropertyDescriptorList = PropertyDescriptorList.Get(GetType(Employee))<SPAN style="FONT-FAMILY: Helvetica"><o:p></o:p></SPAN></FONT></FONT></FONT></strong></P><P =Ms&#111;normal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: 13pt"><SPAN><strong>Getting a List of Columns in the Base Table </strong><o:p></o:p></SPAN></P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt">To get a comprehensive list of the columns defined in the datasource, do the following:</P><P =Ms&#111;normal style="MARGIN: 5pt 0in; LINE-HEIGHT: 13pt"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica">&nbsp;<o:p></o:p></SPAN></P><P =Ms&#111;normal style="MARGIN: 5pt 0in; LINE-HEIGHT: 13pt"><B><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica">C#:</SPAN></B><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica"><o:p></o:p></SPAN></P><P =CodeSnippet style="MARGIN: 0in 0in 0pt 0.25in"><strong><FONT color=#808080><FONT style=": #e6e6e6"><FONT face="Courier New">PersistenceManager.DefaultManager.InitializeSchema(typeof(Employee));<BR>EntityTable employeeTable =<BR>&nbsp;&nbsp;PersistenceManager.DefaultManager.GetTable(typeof(Employee));<BR>foreach(DataColumn col in employeeTable.Columns) {...} <SPAN style="FONT-FAMILY: Helvetica"><o:p></o:p></SPAN></FONT></FONT></FONT></strong></P><P =Ms&#111;normal style="MARGIN: 5pt 0in; LINE-HEIGHT: 13pt"><SPAN><BR><strong>VB.NET:</strong></SPAN><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica"><o:p></o:p></SPAN></P><P =CodeSnippet style="MARGIN: 0in 0in 0pt 0.25in"><strong><FONT color=#808080><FONT style=": #e6e6e6"><FONT face="Courier New">PersistenceManager.DefaultManager.InitializeSchema(GetType(Employee))<BR>Dim employeeTable As EntityTable =<BR>&nbsp;&nbsp;PersistenceManager.DefaultManager.GetTable(GetType(Employee))<BR>For Each col As DataColumn In employeeTable.Columns <SPAN style="FONT-FAMILY: Helvetica"><o:p></o:p></SPAN></FONT></FONT></FONT></strong></P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt">The call to InitializeSchema() tells the PersistenceManager to retrieve the schema for the table on which the business object is based <I>from the datasource</I>. In the normal operation of an app, this schema initialization occurs as a byproduct of other operations, such as a GetEntities() call, or creation of an object of the specified type. In most cases the end result is always the same. However, there is at least one scenario in which the schema created in the PersistenceManager’s cache does not reflect the custom columns. If Create() is called on the business class while the client app is disconnected from the datasource, and before a connected data retrieval has taken place, and before the cache has been restored from a locally stored entity set, the Create() method is forced – having no where else to turn – to look to the business class for its schema information. In doing so, it of course discovers only those properties that were defined in code for the class. Furthermore, the schema, once initialized, remains unchanged until its PersistenceManager is either destroyed or reset with a Clear() call. It never learns about the custom columns.</P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt">You can see the rest of the steps required to discover the full column set from the code sample shown above: you call GetTable() on the PersistenceManager to get an EntityTable and then simply iterate through its Columns collection to discover what’s there.</P><P =Ms&#111;normal style="MARGIN: 5pt 0in; LINE-HEIGHT: 13pt"><B><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica">Isolating the Custom Columns</SPAN></B><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica"><o:p></o:p></SPAN></P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt">Once you have your two lists, isolating the custom columns is easy: just check, for each column discovered in the schema, for a PropertyDescriptor with a PropertyPath the same as the column name. If it isn’t found, you’ve got a custom column.</P><P =Ms&#111;normal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: 13pt"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica">&nbsp;<o:p></o:p></SPAN></P><P =Ms&#111;normal style="MARGIN: 5pt 0in; LINE-HEIGHT: 13pt"><B><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica">C#:</SPAN></B><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: Helvetica"><o:p></o:p></SPAN></P><P =CodeSnippet style="MARGIN: 0in 0in 0pt 0.25in"><FONT style=": #e6e6e6" face="Courier New" color=#808080><strong>foreach(DataColumn col in employeeTable.Columns) {<BR>&nbsp;&nbsp;PropertyDescriptor pd = pdlist.Find(col.ColumnName);<BR>&nbsp;&nbsp;if (pd == null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;// Customer-added column found!<BR>&nbsp;&nbsp;&nbsp;&nbsp;CreateAndConfigureOneUiControl(col);<BR></strong></FONT><SPAN style="FONT-FAMILY: Helvetica"><strong><FONT color=#808080><FONT style=": #e6e6e6">&nbsp;&nbsp;&nbsp;&nbsp;}<BR>} <o:p></o:p></FONT></FONT></strong></SPAN></P><P =Ms&#111;normal style="MARGIN: 5pt 0in; LINE-HEIGHT: 13pt"><SPAN><strong>VB.NET:</strong><o:p></o:p></SPAN></P><P =CodeSnippet style="MARGIN: 0in 0in 0pt 0.25in"><strong><FONT color=#808080><FONT style=": #e6e6e6"><FONT face="Courier New">For Each col As DataColumn In employeeTable.Columns<BR>&nbsp;&nbsp;Dim pd As PropertyDescriptor = pdlist.Find(col.ColumnName)<BR>&nbsp;&nbsp;If pd Is Nothing Then<BR>&nbsp;&nbsp;&nbsp;&nbsp;' Customer-added column found!<BR>&nbsp;&nbsp;&nbsp;&nbsp;CreateAndConfigureOneUiControl(col)<BR>&nbsp;&nbsp;End If<BR>Next col<SPAN style="FONT-FAMILY: Helvetica"><o:p></o:p></SPAN></FONT></FONT></FONT></strong></P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt">Note that the string returned by EntityTable.Columns(n).ColumnName will reflect any renaming done by you in the ObjectMapper. For example, if you rename the property generated from a BirthDate column to “DateOfBirth”, EntityTable.Columns(n).ColumnName will return “DateOfBirth” rather than “BirthDate”. So you needn’t worry that your own renamed properties will be mistaken for custom columns! </P><P =Ms&#111;normal style="MARGIN: 8pt 0in 0pt">That completes your work of discovery to find the columns your customer added to the table after you shipped him your code. In next week’s Tech Tip, you’ll see how to write code that will, at runtime, create temporary properties on your business object for the data from those custom columns. Then, dynamically, you’ll toss some appropriate controls on a form for them and set up data bindings so their values can be displayed and maintained by your customer’s end users. You set all of this up without knowing the first detail about your customer’s post-delivery additions to your tables!</P></FONT></o:p>]]>
   </description>
   <pubDate>Wed, 06 Jun 2007 15:00:04 -700</pubDate>
   <guid isPermaLink="true">http://www.ideablade.com/forum/forum_posts.asp?TID=56&amp;PID=127#127</guid>
  </item> 
 </channel>
</rss>