- Table of Contents
- .NET Book Recommendations
- What Is .NET?
- The Microsoft .NET Framework
- The Common Language Runtime (CLR), the Common Type System (CTS), and the Common Language Specification (CLS)
- .NET Framework Class Library
- Visual Studio .NET
- .NET Enterprise Servers and .NET My Services
- .NET Compliant Languages
- C#
- Visual Basic .NET (VB .NET)
- ASP.NET
- XML Web Services
- ADO.NET
- XML.NET
- Windows Forms
- Why .NET?
- Displaying Errors with the Error Provider
- COM Interoperability
- Comparing Java and .NET
- Calling Unmanaged Code
- .NET Application Security
- Code Access Security
- .NET Standards Support
- Numeric Types in the .NET Framework
- Working with Strings
- Formatting Strings
- Trimming Character Strings
- Comparing Strings in .NET 2.0
- Arrays and Collections
- Arrays as Class Members
- Sorting a Multi-Dimensional Array
- Sorting a Multi-Dimensional Array with LINQ
- File I/O (System.IO)
- Working with File Names
- Using the File System
- Working with Files and Directories
- Monitoring the File System
- Working with Streams
- Working with Text Encodings
- Working with Date and Time
- Extending the DateTime Class
- Fun with Dates
- Exceptions
- Delegates
- Events
- Asynchronous Programming
- Asynchronous File I/O
- Timers
- Random Numbers
- Cryptographically Secure Random Numbers
- Serialization
- MultiThreading (System.Threading)
- Multi-Threading Overview
- The Managed Thread Pool
- Managed Threading
- Thread Synchronization
- Synchronizing Data Access
- Trace Debugging
- Tracing in .NET 2.0
- ASP.NET Trace
- Validating User Input in ASP.NET Web Pages
- Event Logging
- Monitoring Application Performance
- Accessing the Registry
- Accessing Environment Information
- Environment Variables in .NET 2.0
- Managing Windows Forms Applications
- Working with Email
- Working with Graphics
- Animating a Background
- Working with Images
- Drawing Cycloid Curves
- Simulating the Spirograph
- Building International Web Applications
- .NET Compact Framework
- Mobile Web Development with ASP.NET
- Speech Technologies
- Microsoft MapPoint Web Service
- Working with Typed DataSets
- Using Relationships in DataSets
- DataColumn Expressions
- Playing Simple Sounds
- Playing Sounds with .NET 2.0
- Returning an Image in a Web Page
- RSS
- Best Practices Project Structure
- Best Practices Application Blocks
- The Data Access Application Block
- The Exception Management Application Block
- Best Practices — Performance
- Best Practices — Performance and Scalability
- Best Practices - Testing
- Reading the Tea Leaves, 2005
- Predictions: A Look Back at 2005, and a Look Ahead to 2006
- .NET Downloads
- Application Deployment Overview
- Application Deployment — Versioning
- Application Deployment — Version Policy
- Application Deployment — Packaging and Distribution
- .NET Remoting Overview
- A Remoting Demonstration
- Remoting Configuration
- Remoting: Lifetimes and Leases
- Remoting: Other Issues
- Attributes
- Writing Custom Attributes
- Accessing Attributes in Code
- Reflection
- Class Design: Inheritance, Interface, or Composition?
- The TriTryst Game
- Console Applications in .NET 2.0
- New File I/O Methods in .NET 2.0
- Building Projects with MSBuild
- Unmanaged Callbacks in .NET 2.0
- Timer Troubles
- Non-Rectangular Windows Forms
- Windows Forms Transparency
- 10 Things I Hate About Visual Basic
- 10 Things I Hate About C#
- Background Processing with Idle Time
- Scaling Windows Forms
- Reading and Writing Binary Data
- New Memory Management Functions in .NET 2.0
- Compatibility Between .NET 1.1 and .NET 2.0
- Managed Debugging Assistants in .NET 2.0
- XDir: A Program for Viewing Directory Sizes
- The Microsoft.VisualBasic Namespace
- Operator Overloading
- Working with GPS Data
- Hidden Visual Studio Tools
- .NET 3.0
- The .NET 2.0 Stopwatch Class
- Nullable Types
- Drawing Rotated Text
- Unsafe Code
- Other .NET Languages
- Compiler Directives
- Safe Handles
- Predictions, 2007 Edition
- New Features in C# 3.0
- Generics
- Network Client Programming
- On the Misuse of Exceptions
- Maximum Object Size in .NET
- More on Maximum Object Sizes
- Keyed Collection Memory Limitations
- Matching String Endings
- Allocating Small Data Structures
- Grumbling About Limitations
- Some Thoughts on the Nature of What We Do
- Working with Predicates in Collections
- Working with DataReaders
- Outputting XML with XmlWriter
- Writing XML Data
- Working with Compression
- Another Look at Compressed Streams
- Compressing a Very Large File
- Canonical URIs
- Constructing URIs
- Using OneWayAttribute for Remote Calls
- Selecting a Garbage Collector
- Linked List
- Linked List Application - The MRU List
- Auto-implemented Properties in C#
- The HashSet Collection
- Looking Ahead: 2018
- An Experiment in Optimization
- A Larger Integer
- Extension Methods
- Language Integrated Query (LINQ)
- Variable Length Parameter Lists
- The ReaderWriterLockSlim Synchronization Primitive
- Sorting a Text File
- Sorting a Large Text File
- Using ListView with Large Data Sets
- LINQ One-Liners
- Regular Expression Optimization
- Random File I/O
- Computing the Size of a Structure
- More on Computing Structure Sizes
- UnmanagedMemoryStream
- Dynamically Loading Code
- Building a String Table
- Delegates Versus Function Pointers
- Visual Studio Editor Features
- A Simple Profile Timer
- New Features in C# 4.0
- IEnumerator or IList?
- New Features in .NET 4.0
- Set Operations with IEnumerable and HashSet
- Using File Locks
- Extending Object Functionality
- Clearing a HashSet
- When Hash Codes Matter
- Parsing Command Line Options
- Creating a Single-Instance Program
- Asynchronous Windows Forms Events
- The BackgroundWorker Component
- Fixing a Dumb Mistake
- Thinking About Multi-Threaded Programs
- JavaScript Object Notation
- Useful .NET-related Sites
- Markov Models
- Building an Order 0 Markov Model
- Higher Order Markov Models
- Webmaster's Guide to robots.txt
- An Overview of the Parallel Extensions to .NET
- Parallel Extensions Synchronization Objects
- Thread Safe Collections
- A Bug and a Conundrum
- Another Bug and an Answer
- Task Parallel Library
- Good and Bad Ideas in C#
- Parallel LINQ
- Copying Large Files
- Replacing File.Copy
- Learning from Our Mistakes
- Symbolic Links
- There Is No Easy Fix
- Tracking Hurricanes
- Examining Hurricane Data
- Searching for Multiple Strings
- Simple JSON Processing
- Aho-Corasick String Searching
- Writing a Web Crawler
- Web Crawler Politeness
- Source Control Management
- Subversion
- Communicating with Datagrams
- Fun with Actions and Funcs New
- The Future of Media
- The Importance of Metadata
- Of Comparison and IComparer
- IComparer, Comparer, IComparable, Oh My!
- Comparing Generic Types New
- A Simple HTTP Server New
- Informit Reference Library
DataColumn Expressions
Last updated Sep 23, 2004.
DataColumn Expressions
Most of the columns in your data tables will be simple columns that map directly to columns in a database table. There are circumstances that, just as with a database, you will want to include a computed column in a table. For example, you might want a column to contain a count, average, or other aggregate expression, or you might want a column to contain data from a column in a related table. This is possible by setting the Expression property of a DataColumn.
The DataColumn.Expression property uses its own syntax, which is "similar to" SQL, but different in many ways. You can find a detailed explanation of the syntax in the DataColumn.Expression online help topic. The remainder of this topic will expand on that discussion somewhat.
Creating Computed Columns
The simplest type of expression is probably the calculated column. For example, in the sample program provided with the discussion of Relationships, the code that outputs order items calculates the extended price (quantity times price) in C# like this:
int qty = (int)orderItem["Quantity"]; System.Decimal price = (Decimal)orderItem["Price"]; Decimal extendedPrice = qty * price;
There's nothing "wrong" with writing such code, but it has one major drawback: every time you want to know the extended price, you have to compute it. It'd be much nicer if there were a column in the table that contained the computed value. That way, the extended price would be recomputed every time the price or quantity changed. Adding such a column is extremely easy, as shown here:
// Create computed columns
DataTable tblOrderItems = ds.Tables["OrderItems"];
tblOrderItems.Columns.Add("ExtendedPrice", typeof(Decimal), "Price * Quantity");
If you add that line of code to the sample program after the Products primary key is created, you can then change the output code in the innermost loop to the single statement below, eliminating the three lines that were used to compute the value in the previous version of the program:
Console.WriteLine("\t\t{0} {1} ({2}) {3} {4}",
orderItem["Quantity"], productRow["ProductNumber"],
productRow["Description"], orderItem["Price"], orderItem["ExtendedPrice"]);
You can also concatenate strings in DataColumn expressions. For example, if you want to display a customer's name as LastName comma FirstName, you could do it by constructing the string, or you could create a computed column called "FullName", like this:
tblCustomers.Columns.Add("FullName", typeof(string),
"LastName + ', ' + FirstName");
The "+" operator is used for string concatenation and literal strings in expressions are enclosed in apostrophes (single quotes).
Aggregates and Related Columns
Computed columns are handy, but that's not all that you can do with DataColumn expressions. As with SQL, you can create columns that contain aggregates: counts of items, sums, averages, and such. The documentation contains a full list of supported aggregates. I've found that aggregates are most useful when used with related columns. For example, a count of orders for a customer. If you were writing code to display each customer and the number of orders that the customer had placed, you would have to loop through the Orders table to count the orders, or use a DataView or DataTable.Select statement that gets the items. It's possible, though, to create an aggregate within the DataTable itself, giving you the benefit of not having to compute the value manually. For example, the code below adds the count of orders to the Customers table:
// Add OrderCount column to Customers table
tblCustomers.Columns.Add("OrderCount", typeof(int),
"Count(Child(CustomerOrders).OrderId)");
The syntax here can get a bit confusing, and unfortunately the compiler can't check it for you. The only way you'll know if you got the expression wrong is if you try to run the program. The error messages that result from a bad expression are sometimes rather cryptic, so it's a good idea to double check your work before you compile and run. There is one good thing, though: case is not significant in expressions, except in quoted strings. So "Count" and "cOUnt" are the same as "count".
There are two ways to reference a relationship within an expression. If there is only one related table, you could write Child.Column. If there are multiple relationships, the syntax is Child(RelationshipName).Column. Since you never know when you might add a relationship, it's a good idea to always use the fully qualified syntax like I did in the example above.
Functions
DataColumn expressions recognize a handful of functions that make some things easier. Most of the functions are pretty self explanatory, especially if you're familiar with SQL syntax. But two of them, ISNULL and IIF can be somewhat confusing to the uninitiated.
You might expect ISNULL to return a Boolean value: true if the expression is NULL and false if it is non-NULL. (NULL in this case is the SQL meaning rather than the C# value null or Visual Basic Nothing.) But that's not how ISNULL works. ISNULL takes two parameters: an expression and a replacement value. If the expression resolves to a non-NULL value, then the value of the expression is returned. If the expression resolves to NULL, the replacement value is returned. For example, suppose you want to display the Company column from the Customers table, but if the Company column is NULL, you want to display "None". The resulting DataColumn expression would be:
"IsNull(Company, 'None')"
Another use of ISNULL (and expressions in general) is in DataView.RowFilter expressions. If you wanted to create a DataView that references all of the Customer rows that have no company name, you would include this filter expression:
"IsNull(Company, 'None') = 'None'"
The IIF function returns one of two values depending on the result of a logical expression. For example, suppose your order entry system required management review of all order items greater than $10,000. You could write code to check the order items individually, or you could include a column in the table so that items could easily be flagged whenever the extended price changes.
tblOrderItems.Columns.Add("MgmtReview", typeof(Boolean),
"IIF(ExtendedPrice>10000.00, true, false)");
The returned values don't have to be Boolean values. The above column could just as easily have been a string or a character flag or a numeric value. One possible use would be to compute a discount off the total price based on the amount of the order. In that case, you'd create a Discount column that's computed from the ExtendedPrice, and then create another column called FinalPrice (for example) that applies the Discount to the ExtendedPrice. Chained computed columns.
Conclusion
DataColumn expressions can turn your boring DataSets into dynamic views of data, with columns that update automatically as other data columns change, removing much of the tedium associated with writing code to report such changes. Be aware, though, that this doesn't come for free. There is some CPU overhead associated with computed columns, especially with aggregates, and in rare cases you may find yourself having to remove columns during intensive update procedures and adding them afterwards.



Account Sign In
View your cart