Sunday, October 24, 2010

Adding MongoDB samus / mongodb-csharp to the Linq Comparison

Last month I compared several Linq providers. Lately I’ve been playing with MongoDB. MongoDB is a Document (read NoSQL) database that runs on windows and has a .NET provider.

To do this, I am going to set up an environment with the same data as I used in the previous examples (the infamous Northwind database from SQL Server).

Create the Data

To recreate the data from Northwind, I wrote the following query to create insert statements.

SELECT 'db.Product.insert({' 
    + 'ProductID: ' + CAST(p.ProductID AS VARCHAR(10)) + ', '
    + 'ProductName: "' + p.ProductName + '", '
    + 'SupplierID: ' + CAST(p.SupplierID AS VARCHAR(10)) + ', '
    + 'CategoryID: ' + CAST(p.CategoryID AS VARCHAR(10)) + ', '
    + 'QuantityPerUnit: "' + p.QuantityPerUnit + '", '
    + 'UnitPrice: ' + CAST(p.UnitPrice AS VARCHAR(10)) + ', '
    + 'UnitsInStock: ' + CAST(p.UnitsInStock AS VARCHAR(10)) + ', '
    + 'UnitsOnOrder: ' + CAST(p.UnitsOnOrder AS VARCHAR(10)) + ', '
    + 'ReorderLevel: ' + CAST(p.ReorderLevel AS VARCHAR(10)) + ', '
    + 'Discontinued: ' + CASE WHEN p.Discontinued = 1 THEN '"true"' ELSE '"false"' END + ', '
    + 'CategoryName: "' + c.CategoryName + '"'
    + '})'
FROM Northwind.dbo.Products p
    JOIN Northwind.dbo.Categories c ON p.CategoryID = c.CategoryID
ORDER BY p.ProductID

I run the above query against a Northwind Database that I have lying around on my home computer and I pasted the result set into a Mongo Shell window.

Notice that I added CategoryName to Product. From a relational point of view, this is criminal. However, Mongo is a DOCUMENT database and repeating data isn’t quite sin; I am also leaving this in the CategoryName because repeating data is in the spirit of document databases as I understand them. (Besides the version of MongoDB.DLL I’m using doesn’t appear to support joins very well.)

The POCO Class

To run straightforward Linq queries using MongoDB.DLL, I need a POCO class to hydrate the data into. Since MongoDB is a document database, the data can be unstructured, but to work in Linq, structured data makes things easier.

public class Product
{
    public int ProductID { get; set; }
    public string ProductName { get; set; }
    public int SupplierID { get; set; }
    public int CategoryID { get; set; }
    public string QuantityPerUnit { get; set; }
    public decimal UnitPrice { get; set; }
    public int UnitsInStock { get; set; }
    public int UnitsOnOrder { get; set; }
    public int ReorderLevel { get; set; }
    public bool Discontinued { get; set; }
    public string CategoryName { get; set; }
}

The Code

Now that I have copied the data that I need to “simulate” the Northwind Product and Category data into a MongoDB collection, I can use this code to do the same thing as I did in the last post:

public static IMongoDatabase GetContext()
{
    var mongo = new Mongo();
    mongo.Connect();
    return mongo.GetDatabase("Northwind");
}

public static void DoDemo()
{
    var mdb = GetContext();
    var list = from p in mdb.GetCollection().Linq()
               where p.CategoryName == "Seafood"
               orderby p.ProductName
               select new
               {
                   p.ProductID,
                   p.ProductName,
                   p.UnitPrice,
                   p.CategoryName
               };

    Console.WriteLine("MongoDB Demo");
    foreach (var item in list)
    {
        Console.WriteLine(
            string.Format("{0}\t{1:c}\t{2}\t{3}",
                item.ProductID,
                item.UnitPrice,
                item.ProductName,
                item.CategoryName));
    }
}

GetContext() is straight forward: You new-up a Mongo, connect and get a database.

The most interesting statement is mdb.GetCollection().Linq(). The GetCollection method links the content with the name of T to an IMongoCollection of Ts. LinqExtensions.Linq() is an extension method that converts the IMongoCollection to IQueryable.

Last month I compared several Linq providers. Lately I’ve been playing with MongoDB. MongoDB is a Document (read NoSQL) database that runs on windows and has a .NET provider.

To do this, I am going to set up an environment with the same data as I used in the previous examples (the infamous Northwind database from SQL Server).

Create the Data

To recreate the data from Northwind, I wrote the following query to create insert statements.

SELECT 'db.Product.insert({' 
    + 'ProductID: ' + CAST(p.ProductID AS VARCHAR(10)) + ', '
    + 'ProductName: "' + p.ProductName + '", '
    + 'SupplierID: ' + CAST(p.SupplierID AS VARCHAR(10)) + ', '
    + 'CategoryID: ' + CAST(p.CategoryID AS VARCHAR(10)) + ', '
    + 'QuantityPerUnit: "' + p.QuantityPerUnit + '", '
    + 'UnitPrice: ' + CAST(p.UnitPrice AS VARCHAR(10)) + ', '
    + 'UnitsInStock: ' + CAST(p.UnitsInStock AS VARCHAR(10)) + ', '
    + 'UnitsOnOrder: ' + CAST(p.UnitsOnOrder AS VARCHAR(10)) + ', '
    + 'ReorderLevel: ' + CAST(p.ReorderLevel AS VARCHAR(10)) + ', '
    + 'Discontinued: ' + CASE WHEN p.Discontinued = 1 THEN '"true"' ELSE '"false"' END + ', '
    + 'CategoryName: "' + c.CategoryName + '"'
    + '})'
FROM Northwind.dbo.Products p
    JOIN Northwind.dbo.Categories c ON p.CategoryID = c.CategoryID
ORDER BY p.ProductID

I run the above query against a Northwind Database that I have lying around on my home computer and I pasted the result set into a Mongo Shell window.

Notice that I added CategoryName to Product. From a relational point of view, this is criminal. However, Mongo is a DOCUMENT database and repeating data isn’t quite sin; I am also leaving this in the CategoryName because repeating data is in the spirit of document databases as I understand them. (Besides the version of MongoDB.DLL I’m using doesn’t appear to support joins very well.)

The POCO Class

To run straightforward Linq queries using MongoDB.DLL, I need a POCO class to hydrate the data into. Since MongoDB is a document database, the data can be unstructured, but to work in Linq, structured data makes things easier.

public class Product
{
    public int ProductID { get; set; }
    public string ProductName { get; set; }
    public int SupplierID { get; set; }
    public int CategoryID { get; set; }
    public string QuantityPerUnit { get; set; }
    public decimal UnitPrice { get; set; }
    public int UnitsInStock { get; set; }
    public int UnitsOnOrder { get; set; }
    public int ReorderLevel { get; set; }
    public bool Discontinued { get; set; }
    public string CategoryName { get; set; }
}

The Code

Now that I have copied the data that I need to “simulate” the Northwind Product and Category data into a MongoDB collection, I can use this code to do the same thing as I did in the last post:

public static IMongoDatabase GetContext()
{
    var mongo = new Mongo();
    mongo.Connect();
    return mongo.GetDatabase("Northwind");
}

public static void DoDemo()
{
    var mdb = GetContext();
    var list = from p in mdb.GetCollection().Linq()
               where p.CategoryName == "Seafood"
               orderby p.ProductName
               select new
               {
                   p.ProductID,
                   p.ProductName,
                   p.UnitPrice,
                   p.CategoryName
               };

    Console.WriteLine("MongoDB Demo");
    foreach (var item in list)
    {
        Console.WriteLine(
            string.Format("{0}\t{1:c}\t{2}\t{3}",
                item.ProductID,
                item.UnitPrice,
                item.ProductName,
                item.CategoryName));
    }
}

GetContext() is straight forward: You new-up a Mongo, connect and get a database.

The most interesting statement is mdb.GetCollection().Linq(). The GetCollection method links the content with the name of T to an IMongoCollection of Ts. LinqExtensions.Linq() is an extension method that converts the IMongoCollection to IQueryable.

Thursday, September 30, 2010

Comparing different Linq Providers

I’ve been playing with Linq and different "back ends" (not using "providers" since Linq to DataSets probably doesn’t qualify).

In all three examples below, I create the automatic default model with Northwind’s Products and Categories tables. The Linq queries require a join and a filter.

Linq to DataSet

Over the past year or so, I’ve been working fairly heavily what I call Linq To DataSets, you know, Linq on top of classic ADO.NET. I have written several blog entries and made a couple of Code Camp presentations on this subject.

What makes Linq to DataSets is that it uses a disconnected data model (data is eagerly loaded).

public static NorthwindDS CreateContext()
{
    NorthwindDS ds = new NorthwindDS();
    using (var catda = new CategoriesTableAdapter())
    {
        catda.Fill(ds.Categories);
    }
    using (var proda = new ProductsTableAdapter())
    {
        proda.Fill(ds.Products);
    }
    return ds;
}

public static void DoDemo()
{
    var ds = CreateContext();
    var list = from p in ds.Products
             join c in ds.Categories on p.CategoryID equals c.CategoryID
        where c.CategoryName == "Seafood"
        orderby p.ProductName
        select new
        {
            p.ProductID,
            p.ProductName,
            p.UnitPrice,
            c.CategoryName
        };

        Console.WriteLine("DataSet Demo");
        foreach (var item in list)
        {
            Console.WriteLine(
                string.Format("{0}\t{1:c}\t{2}\t{3}", 
                    item.ProductID, 
                    item.UnitPrice,
                    item.ProductName,
                    item.CategoryName));
        }
    }
}

This one requires real code to load. And everything is loaded. If I need to, I can filter at the DataAdapter level, but that requires fancy Designer work.

Linq to SQL

On my last project, we used Linq to SQL. It is easy to develop and capable, but limited. You will use SQL Server, You will not use Stored Procedures to read and write data (it does have some stored procedure support, but not even to the level of Classic ADO.NET).

public static NorthwindL2SDataContext CreateContext()
{
    return new NorthwindL2SDataContext();
}

public static void DoDemo()
{
    var context = CreateContext();
    var list = from p in context.Products
            join c in context.Categories on p.CategoryID equals c.CategoryID
        where c.CategoryName == "Seafood"
        orderby p.ProductName
        select new
        {
            p.ProductID,
            p.ProductName,
            p.UnitPrice,
            c.CategoryName
        };

    Console.WriteLine("Linq2Sql Demo");
    foreach (var item in list)
    {
        Console.WriteLine(
            string.Format("{0}\t{1:c}\t{2}\t{3}",
                item.ProductID,
                item.UnitPrice,
                item.ProductName,
                item.CategoryName));
    }
}

Creating the context in Linq to Sql is trivial, just new it up. The Linq is really similar to the Linq to DataSets that I’ve been working with for the last year.

The cool thing is that the data isn’t gotten until I ask for it in the Linq query and only the records I ask for.

Linq to Sql’s model is really close to the table structure of the underlining database. It is less capable than the Entity Framework (below), but it is also less complicated.

Entity Framework

I have also played with the Entity Framework. It is more powerful than the others but more difficult to use than the others.

public static NorthwindEntities CreateContext()
{
    return new NorthwindEntities();
}

public static void DoDemo()
{
    var ds = CreateContext();
    var list = from p in ds.Products
        where p.Categories.CategoryName == "Seafood"
        orderby p.ProductName
        select new
        {
            p.ProductID,
            p.ProductName,
            p.UnitPrice,
            p.Categories.CategoryName
        };

    Console.WriteLine("EntityFramework Demo");
    foreach (var item in list)
    {
        Console.WriteLine(
            string.Format("{0}\t{1:c}\t{2}\t{3}",
                item.ProductID,
                item.UnitPrice,
                item.ProductName,
                item.CategoryName));
    }
}

Again it is simple to make a new context.

The Entity Framework supports lazy loading and using stored procedures for Insert, Update and Delete operations.

Notice that there is no join in this query, the Category is imbedded in to the Products. The Entity Framework can work with data models that differ from the underlining data store.

The same treatment is applied to Mongo DB

Friday, August 27, 2010

Quickly change Themes in Visual Studio 2008

I like dark backgrounds when I code; my co-workers don’t. I want to be able to change the screen back and forth from Darkness and Light. So I created a couple of toolbar buttons to switch themes (don’t use the tool until I say you can do so).

  1. I save my current theme (Tools => Import and Export Settings… => Export … => Next => Next => "C:\VS_Themes\Darkness.vssettings" => Finish).
  2. Create a standard theme (Tools => Import and Export Settings… => Reset all Settings).
  3. Save the standard theme (same steps as 1 but call it "C:\VS_Themes\Default.vssettings")
  4. Open Macros IDE (((Alt+F11) || (Tools => Macros => Macros IDE …))
  5. Create a module (Right Click on MyMacros => Add => Add Module => Name it "LoadThemes")
  6. Type the following VB.NET code into the module:
  7. Public Module LoadThemes 
        Public Sub SetThemeToDarkness()
            LoadThemeByFilename("C:\VS_Themes\Darkness.vssettings")
        End Sub<
        Public Sub SetThemeToDefault()
            LoadThemeByFilename("C:\VS_Themes\Default.vssettings")
        End Sub<
        Private Sub LoadThemeByFilename(ByVal fileName As String)
            DTE.ExecuteCommand("Tools.ImportandExportSettings", "/import:""" &
                fileName & """")
        End Sub
    End Module
    
  8. And I hooked them to buttons on my tookbar (Tools => Customize => New… "Theme" => Commands => Select Macros => Drag your macros onto the new Toolbar).
  9. Resave the Darkness Theme (The toolbar is part of the theme, otherwise when you use the tool bar to switch themes, you will lose the toolbar)
  10. Do the same thing for the Default Theme.

Now you are set.

Sunday, July 18, 2010

Avoid Duplicate Detail Forms in MDI programs

In a MDI Windows Forms/CRUD application, I don't want to display the same Detail form more than once at the same time. What if the user saves the address on one form and the email on another? Even if you can trust the user to do the right thing, it just feels icky to have more than 1 form for "John Smith" up at the same time.

It is less dangerous to have duplicate list forms, but I still don't like to gunk up the application with extra forms hanging around.

This code is a modernization of some code I used a couple of years ago. I am using Generics and Extension methods to reduce the amount of code I used in the old .NET 2.0 code!

using System.Windows.Forms;
namespace AvoidDupeForms
{
    /// <summary>
    /// A Detail Form is a form that can display a single record.
    /// In this example, there can me more than 1 of these loaded
    /// at once, as long as a record is only loaded once.
    /// </summary>
    public interface IDetailForm
    {
        /// <summary>
        /// Get the Id loaded in this form
        /// </summary>
        int Id { get; }
        /// <summary>
        /// Set the id for the form to load
        /// </summary>
        /// <param name="id">Id # to be loaded</param>
        void SetId(int id);
    }
    public static class FormsUtil
    {
        /// <summary>
        /// Reveal a form, load a new one if one doesn't yet exist 
        /// in the context of this MDI 
        /// </summary>
        /// <typeparam name="T">The type of the form to be revealed</typeparam>
        /// <param name="thisForm">
        /// The calling form (used to get MdiParent to be searched)
        /// </param>
        /// <example>
        /// <![CDATA[
        /// // As an extention method
        /// this.RevealForm<ListForm>();
        /// // The oldfashioned way
        /// FormsUtil.RevealForm<ListForm>(this);
        /// ]]>
        /// </example>
        public static void RevealForm<T>(this Form thisForm) 
            where T : Form, new()
        {
            Form MdiParent = thisForm.GetMdiParent();
            if (MdiParent != null)
            {
                foreach (Form frm in MdiParent.MdiChildren)
                {
                    if (frm.GetType() == typeof(T))
                    {
                        frm.BringToFront();
                        return;
                    }
                }
                T flf = new T();
                flf.MdiParent = MdiParent;
                flf.Show();
            }
        }
        /// <summary>
        /// Reveal a form with a given detailId, load a new one if 
        /// one doesn't yet exist in the context of this MDI 
        /// NOTE: > 1 instance of T can exist, but only
        /// 1 fore each IDetailForm.Id
        /// </summary>
        /// <typeparam name="T">The type of the form to be revealed</typeparam>
        /// <param name="thisForm">
        /// The calling form (used to get MdiParent to be searched)
        /// </param>
        /// <param name="detailId">The ID of the Detail record to be displayed</param>
        /// <example>
        /// <![CDATA[
        /// // As an extention method
        /// this.RevealDetailForm<DetailForm>(42);
        /// // The oldfashioned way
        /// FormsUtil.RevealDetailForm<DetailForm>(this, 42);
        /// ]]>
        /// </example>
        public static void RevealDetailForm<T>(this Form thisForm, int detailId) 
            where T : Form, IDetailForm, new()
        {
            Form MdiParent = thisForm.GetMdiParent();
            if (MdiParent != null)
            {
                foreach (Form frm in MdiParent.MdiChildren)
                {
                    if (frm.GetType() == typeof(T))
                    {
                        T saFrm = (T)frm;
                        if (saFrm.Id == detailId)
                        {
                            saFrm.BringToFront();
                            return;
                        }
                    }
                }
                T modify = new T();
                modify.MdiParent = MdiParent;
                modify.SetId(detailId);
                modify.Show();
            }
        }
        /// <summary>
        /// Gets the MdiParent for a given form
        /// (it could be self)
        /// </summary>
        /// <param name="thisForm">
        /// The calling form (used to get MdiParent to be searched)
        /// </param>
        /// <returns>
        /// The MdiParent of thisForm, or null if thisForm has no parent
        /// </returns>
        /// <example>
        /// <![CDATA[
        /// // As an extention method
        /// Form MdiParent = thisForm.getMdiContainer();
        /// // The oldfashioned way
        /// Form MdiParent = FormsUtil.getMdiContainer(thisForm);
        /// ]]>
        /// </example>
        public static Form GetMdiParent(this Form thisForm)
        {
            if (thisForm.IsMdiContainer)
                return thisForm;
            if (thisForm.IsMdiChild)
                return thisForm.MdiParent;
            return null;
        }
    }
}

Saturday, June 26, 2010

Back in Time

I took this picture this year on Indiana Street in Spokane.


 
Posted by Picasa

Saturday, June 05, 2010

Unscientific polling in the internet

I see the following tweet:

@ChrisLove: Do You Prefer a Stick Shift or Automatic? 
[Stick Shift: http://twp.li/6lbj || Automatic: http://twp.li/p8y4 ]
#twittpoll

(I really like the way he used “||” instead of “or”.) Being a stick shift enthusiast (ok weirdo), I clicked on http://twp.li/6lbj without thinking about it; when the page loaded I saw that the stick shift was beating automatic 80 to 20. This doesn’t match what I see in the world.

The problem is that people who prefer stick care deeply, but people who prefer automatic don’t feel strongly about their preference (unless they are forced to drive a stick).

This example is probably harmless. However, both the Republicans and Democrats in the US are using this kind of polling to push their point of view.

Monday, May 17, 2010

Portland Code Camp and Me

I will be presenting Introduction to Linq and Linq2DataSets an the Portland Code Camp from 9:00 to 10:15 AM on Saturday, May 22, 2010 (Session 0x01) in Room 13.