LINQ test for custom IEnumerator

written by Jason Short on Friday, June 27 2008

 
Mike and I had an interesting discussion this afternoon about LINQ and how it interprets enumerable rows. Needless to say, some test code had to be written to prove or disprove some theories.
 
Concrete vs Interface
 
Our thought experiment was what if you had an interface to an object and wanted to expose it to LINQ for query? Did you need to implement IEnumerable on the concrete class, or the interface level? Would it work on both? What about strongly typing and intellisense in the IDE?
 
Interface wins
 
It turns out that you can implement it in either place. But if you implement it only in the Concrete class then LINQ can’t see the IEnumerable to give you intellisense. 
 
Concrete example (pun intended)
 
OK, how did we test this? We took a Row as our base object. We want to store a collection of Rows and iterate over them using a Table (something we do quite a bit). In VistaDB all Table objects actually implement the ITable interface to give them a common set of controls (things like RowCount).
 
Parts
 
Row – Stores Data
ITable – Interface for the Table
Table – concrete class for the Table
 
Questions you can ask with LINQ
 
Once you have this done you can ask some interesting questions with LINQ.
 
Give me all the rows that have data > 0 starting with item #2
 
var foundrows = firsttable.Where(c => c.StringData.Length > 0).Skip(1);
 
Easier in SQL?
 
This would be MUCH harder to do without LINQ. This is a powerful query with intellisense. Don’t think of this in terms of SQL, you could do this with SQL as a single execute as well. Think DDA. In DDA you would have to set .First(), then .MoveNext() and then move through each row checking for the length > 0.
 
DDA with LINQ?
 
No, this is NOT supported today. We were just using it as a thought experiment. Could you expose something like that through the IEnumerable interface and actually have it work?
 
The answer is yes. It actually looks like you could do it. Now, how much work would it be in VistaDB? No idea yet. But it is an interesting thought.
 
Sample Code
  
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LinqTestEnumerator
{
    class Program
    {
        static void Main(string[] args)
        {
            Table firsttable = new Table();

            var foundrows = firsttable.Where(c => c.StringData.Length > 0).Skip(1);
            Console.WriteLine("The following rows where found after the filter was applied");
            foreach (var c in foundrows)
            {
                Console.WriteLine(c.StringData);
            }

            var singlerow = firsttable.First(c => c.StringData.Length > 0);
            Console.WriteLine("Single row: " + singlerow.StringData);


            ITable secondtable = new Table();
            var foundrows2 = secondtable.Where(c => c.StringData.Length > 0).Skip(1);
            Console.WriteLine("The following were found using the ITable class rather than the Table concrete class");
            foreach (var c in foundrows)
            {
                Console.WriteLine(c.StringData);
            }

        }
    }

    /// <summary>
    /// This is our BASE for everything here.  We are trying to store a list of rows for this test
    /// </summary>
    public class Row
    {
        public string StringData;
    }

    /// <summary>
    /// An interface for Table objects - common required data
    /// Note that IEnumerable MUST be implemented at this level, not at the Table level or LINQ can't see it
    /// </summary>
    public interface ITable : IEnumerable<Row>
    {
        // How many rows are in this table
        int RowCount();
    }

    public class Table : ITable
    {
        List<Row> rows;

        public Table()
        {
            rows = new List<Row> { new Row { StringData = "row data 1" }, new Row { StringData = "row data 2" }, new Row { StringData = "row data 3" } };
        }

        public IEnumerator<Row> GetEnumerator()
        {
            return (rows.GetEnumerator());
        }

        // Note that the namespace collision between the Generic and normal Collections of IEnumerator
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return (rows.GetEnumerator());
        }
        
        public int RowCount()
        {
            return (rows.Count);
        }
    }
}

Similar Posts

  1. Exposing strongly typed interfaces that are not CLS Compliant
  2. Using SqlBulkCopy to move data from VistaDB to SQL Server
  3. SQL Server 2008 (Katmai) Information

Comments

  • Jason Short on on 6.27.2008 at 6:10 PM

    Jason Short avatar

    The code didn't post so well, I will put it in the LINQ forum as well.

  • Mark on on 6.28.2008 at 10:53 AM

    Mark avatar

    Jason,

    I am confused.

    var foundrows = firsttable.Where(c => c.StringData.Length > 0).Skip(1);

    This will the all items in firsttable, evaluate the condition selecting only those that have a length > 0 and then skip the first item of that filtered set.

    "In DDA you would have to set .First(), then .MoveNext() and then move through each row checking for the length > 0."

    What you describe here I believe is this -

    var foundrows = firsttable.Skip(1).Where(c => c.StringData.Length > 0);

    These two queries will return different results. Using LinqPAD

    var data = new string[] {"", "x", "", "xx" };

    var qOne = data.Where(s => s.Length > 0).Skip(1);

    var qTwo = data.Skip(1).Where(s => s.Length > 0);

    qOne.Dump();

    IEnumerable<String>

    xx

    qTwo.Dump();

    IEnumerable<String>

    x

    xx

    Am I missing something?

  • Jason Short on on 6.28.2008 at 9:54 PM

    Jason Short avatar

    I was not being LITERAL in the comment to the code. :)

    The comment was about complexity of your query in a single line compared to what it would take in DDA today.

    You can do a lot in that single line, LIKE reducing the queryset to only those with more than xx number of characters and then skipping to only the odd numbers. That is easy in LINQ. Maybe it is a contrived scenario (it is), but I am sure there are places for it.

  • Christer Jensen on on 6.29.2008 at 8:32 AM

    Christer Jensen avatar

    Do you expect to implement LINQ, in your base product. As far as i can see from the samples on the site, you query the data and then manipulate them using linq2objects. Am i missing something here ?. One of the powers in linq is to reduce the dataset required from the db. On a sidenote, how big can a vistadb table be, is there a limit ? regarding size / os.

  • Jason Short on on 7.01.2008 at 11:15 AM

    Jason Short avatar

    Currently there is nothing internal to do with LINQ. That is what we are discussing. The Entity Framework is part of the solution, but we are discussing adding it internal to the engine for DDA.

    You are pretty much limited to your OS/RAM, size of tables, etc.

Comments are closed

Options:

Size

Colors