Managing Data Streams With C# Iterators

Published 01 July 08 11:33 PM

Architecturally, I lean pretty heavily towards 2 philosophies - Domain Driven Design and the Rich Domain Model pattern.  And while neither requires the other for success (contrary to much of what you read out there), they seem to work well together for me, and hence, I have internalized them into a single approach for building solutions.

As a result of my approach, I typically start with (and end up with) an assembly for my domain model (another assembly for my domain model's automated unit tests), an assembly for my presentation logic, and an assembly for my data access layer - or where to put it into DDD terms, the layer where my repository objects live.  There is an ongoing debate about how the assembly references should look - I'm not going to get into the nuances of the arguments, but these seem to be the 2 basic philosophies:

  1. Have the data access assembly reference the domain model assembly with the UI layer referencing both (and calling the repository objects directly to get domain instances)
  2. Have the UI layer reference only the domain model assembly reference and have the domain model objects call their respective repository objects in the data access layer

There are a couple other more specific approaches, but I think that those give you an idea of the underlying philosophies.  I tend to gravitate towards philosophy #1, though I'm much more comfortable with #2 when done in conjunction with a DI container like Windsor.

So there you have my general layering philosophy - UI talks to repository to get and work on domain objects.  Why is this important?  Because one of the cases that has always made me a little uncomfortable is the classic case where I need to generate a UI element like a grid and I don't need to bring back the entire domain object (or in DDD terms, the entire aggregate).  The 3 major approaches I have seen and used are:

  1. Return a IDbDataReader instance from the appropriate repository object in the data access layer
  2. Fetch all of the relevant results into an in-memory data structure in the data access layer and hand that back to the caller
  3. Use a SqlDataSource object at the UI layer and configure it with all of the proper query information

I don't really like any of these solutions.  The problem with solution #1 is that once you pass the stream reader out of the scope where it was initiated, you are relying on the consumer code to close the reader.  The problem with solution #2 is that you're potentially buffering a lot of data that is going to be almost immediately discarded.  The problem with solution #3 (which is the one I have ended up using most frequently) is that it puts you in a situation where you are managing data access in multiple places.

So I was thinking the other night that the ideal solution would be one where I could, in the same scope, manage the lifetime of my stream reader object, but still hand back some kind of safe reference to that stream to calling code - and then I remembered that really cool C# iterators feature that I had thought was nifty but very rarely used.  I added the following method to my repository class (DB is standard AdventureWorks - ignore the horrible practice of not parameterizing my query).

public class ProductsRepository
{
    private const string COMMAND_TEXT =
        "select top {0} ProductID, [Name] from Production.vProductAndDescription where CultureID = 'en'";

    public static IEnumerable<object> GetProducts(int numberRecords) {
        /*
         * in this example, numberRecords is a very simple illustration 
         * of how you could expose more complex criteria parameters based
         * on the specific domain
         */
        using (var conn = new SqlConnection(Settings.Default.AdWksCN))
        using (var cmd = conn.CreateCommand()) {
            cmd.CommandText = string.Format(COMMAND_TEXT, numberRecords);
            conn.Open();
            using (var rdr = cmd.ExecuteReader()) {
                if (rdr != null) {
                    while (rdr.Read()) {
                        var productID = rdr.GetInt32(0);
                        var name = rdr.GetString(1);
                        yield return new {productID, name};
                    }
                }
            }
        }
    }
}

Note the yield return statement - when the compiler sees this, it generates a whole other class that implements IEnumerable<object> and contains the code in your method along with a whole bunch of other code to manage the enumeration activity.  For example, here's the code for the MoveNext method of my generated enumerable class (ala Reflector).

private bool MoveNext()
{
    bool CS$1$0000;
    try
    {
        int CS$4$0001 = this.<>1__state;
        if (CS$4$0001 != 0)
        {
            if (CS$4$0001 != 4)
            {
                goto Label_0127;
            }
            goto Label_00F7;
        }
        this.<>1__state = -1;
        this.<conn>5__1 = new SqlConnection(Settings.Default.AdWksCN);
        this.<>1__state = 1;
        this.<cmd>5__2 = this.<conn>5__1.CreateCommand();
        this.<>1__state = 2;
        this.<cmd>5__2.CommandText = string.Format(
            "select top {0} ProductID, [Name] from Production.vProductAndDescription " + 
            "where CultureID = 'en'", this.numberRecords);
        this.<conn>5__1.Open();
        this.<rdr>5__3 = this.<cmd>5__2.ExecuteReader();
        this.<>1__state = 3;
        if (this.<rdr>5__3 != null)
        {
            while (this.<rdr>5__3.Read())
            {
                this.<productID>5__4 = this.<rdr>5__3.GetInt32(0);
                this.<name>5__5 = this.<rdr>5__3.GetString(1);
                this.<>2__current = new { productID = this.<productID>5__4, name = this.<name>5__5 };
                this.<>1__state = 4;
                return true;
            Label_00F7:
                this.<>1__state = 3;
            }
        }
        this.<>m__Finally8();
        this.<>m__Finally7();
        this.<>m__Finally6();
    Label_0127:
        CS$1$0000 = false;
    }
    fault
    {
        this.System.IDisposable.Dispose();
    }
    return CS$1$0000;
}

If you don't go cross-eyed from reading the compiler's choice of variable names, what you can see here is that the generated enumerator runs the query the first time MoveNext is called, then for subsequent calls, it simply calls the Read method on the data reader and returns the data elements extracted from the row.  When it is done enumerating, it cleans itself up, which includes calling the rest of the cleanup code you defined in the body of your method.

So what's nice about this approach?  Well, first and foremost, I can keep all of my data access code, including projection style queries with potentially complex criteria, in the same place as my object persistence data access code.  At the same time, since I'm returning IEnumerable, I still retain all the benefits of a simple usage pattern (including data binding).  For me, this is a nice hybrid solution for many of those cases where you just need to get a little off the rails!

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# kfarmer said on July 2, 2008 11:47 PM:

Or you could use LINQ to SQL to make the database call, avoid the sin of not parameterizing your strings, and get strongly-typed IEnumerables right out.

:)

# hdierking said on July 3, 2008 12:23 AM:

yup - though not quite the point of this post, and I would probably be more inclined to use NHib - but that's just because I'm old and set in my ways <g>

# Dan said on August 1, 2008 5:44 PM:

Hi Howard

First of all, thanks for the post this is a great idea.

Following up on your comments, I do agree that the point of the post is not about DLINQ and that's fair enough but on the other hand I reckon Keith here has a point about the strongly typed enumerable. You build your post around the DDD approach and one of the core principles of DDD is the layering and loose coupling and as you don't return a strong type you lose this advantage.

What do you think of the same implementation but strongly typed, such as:

public static IEnumerable<Product> GetProducts(int numberRecords)

       {

           /*

            * in this example, numberRecords is a very simple illustration

            * of how you could expose more complex criteria parameters based

            * on the specific domain

            */

           using (var conn = new SqlConnection(Settings.Default.AdWksCN))

           using (var cmd = conn.CreateCommand())

           {

               cmd.CommandText = string.Format(COMMAND_TEXT, numberRecords);

               conn.Open();

               using (var rdr = cmd.ExecuteReader())

               {

                   if (rdr != null)

                   {

                       while (rdr.Read())

                       {

                           Product p = new Product();

                           p.productID = rdr.GetInt32(0);

                           p.name = rdr.GetString(1);

                           yield return p;

                       }

                   }

               }

           }

       }

# hdierking said on August 1, 2008 7:08 PM:

Hi Dan - sure, you could close the type with something more specific than object.  I chose not to here because the example is built around the case where you are basically grabbing a projection of data (and not a full domain object) for the sole purpose of rendering it to a UI (in my case via a simple data binding).  You can absolutely create types for each of these projection queries - i just found it to be a bit overkill in this example.

# serializable said on August 2, 2008 7:43 AM:

That makes sense.

Cheers,

Dan

# Aaron said on August 7, 2008 5:14 PM:

Good stuff.  Just a build on a briefly mentioned topic in this post since I'm a self admitted DI junkie.  

With respect to using a DI container like Windsor, I think you are spot on in your 'comfortable' version of your materialization interaction model.  So spot on in fact that in my own practice, this observation has driven me to start including the interface for repository functions in my actual domain classes instead of separating repository classes that correlate to domain classes out into another assembly.  

For me, repository interface implementation generally lives in classes within 'provider' assemblies so that I can support different repository implementations.  I never found that I enjoyed much benefit from separating out repository definition (interface) into its own assembly as distinct from the related domain classes.

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required

About hdierking

I am currently the Editor-in-Chief for MSDN Magazine. I joined Microsoft in 2006 as a product planner with the certification team at Microsoft Learning. Prior to that, I spent my career as a developer and later as an architect. My main technology passions include pretty much anything on language theory, agile development, and service-oriented architecture.
Page view tracker