LinqPad Related Posts by ThinqLinq

Using LINQPad with MongoDb and NoRM

I’ve recently been working on a project that might be a good fit for the rising wave of Document Databases espoused by the rising NoSQL movement. Unlike the traditional Relational Databases like SQL Server, Document Databases can store object hierarchies, or unshaped data for quick and efficient saving and retrieval. There are quite a number of these databases appearing, including MongoDb, CouchDb, RavenDb. Wikipedia also has a listing of Document Databases with links to other options.

Typically, these data stores expose their data through XML or JSON interfaces. Luckily,  many of these databases also have LINQ providers to make interfacing with the systems easier. For example, if you are using MongoDb, you can use a provider like the NoRM (No-ORM) provider. Today I had the pleasure of sitting in on a webcast that Michael Kennedy of DevelopMentor did showing MongoDb with NoRM. During the presentation the question came up regarding using LINQPad in the environment. I promised to tell them how to do this, so here you go:

Install and Startup Mongo

The first step you need to do is download and startup MongoDb. To download it, go to the MongoDb site and download the version appropriate to your OS. Once you’ve downloaded it, unzip the files to your favorite directory. From the command line, start Mongo by entering the following from the bin directory that you unzipped:

mongob -dbpath=c:\projects\data\mongo

Of course, this assumes you want to put the database in the c:\projects\data\mongo path. The path does need to be present before running this command. You will be prompted to open a firewall hole for Mongo. Once you’re done, Mongo should be up and running.

Preparing NoRM

Now that the database is running, we need to prepare the .Net provider. Download the NoRM provider through Github, or directly from their download site. Unzip these files and open the appropriate sln file in Visual Studio. NoRM works in VS 2008 and NoRM – VS2010 works in VS 2010. Once you open it, compile it. Now you’re ready to roll. If you’re not sure how to get started with the NoRM provider, take a look at the many unit tests that they’ve provided to get up and running quickly.

Using LINQPad

Assuming you’ve downloaded and installed LINQPad, you should now be ready to start consuming Mongo through NoRM in LINQPad. Start by opening LINQPad and creating a new query. In order to work with our database, we need to create some class structures. As a result, you need to select the C# Program or VB Program option under the Language drop-down. (I’m choosing to do this in C# this time because the NoRM provider doesn’t fully support VB expression trees at this point.)

image 

By selecting the Program option, we can now create not only LINQ queries, but entire classes and methods. Scroll to the bottom of the code window and add a class describing the shape that we want to save and retrieve.


class Post
{
	public ObjectId Id { get; set; }
	public string Title { get; set; }
	public string Content { get; set; }
	public DateTime PubDate { get; set; }
	public string[] Categories { get; set; }
}

Keeping with the typical samples I have on this site, I’m modeling a Blog post. This will create a hierarchical shape containing posts with a collection of categories. Now that the shape is defined, we need to figure out how to insert and retrieve it using LINQPad.

To start, press F4 (or select Query and Query Properties from the menu). This will bring up a dialog to add references. In this dialog, click the Add button and locate the Norm.dll that you generated when you compiled it above when Preparing NoRM.

image

Before you close this window, select the “Additional Namespace Imports” tab at the top. This will allow us to add the Norm imports/using clauses. Add an import for Norm and Norm.Linq. (Notice here, you don’t include the “using” or “imports” keywords, just the namespaces that you want to import.) Once you’ve added the reference, close the dialog so that we can continue consuming Mongo.

image

In the code window, enter the following inside the body of the void Main() method that was generated when we selected the Program option in LINQPad:


using (var cn = MongoQueryProvider.Create("mongodb://127.0.0.1/ThinqLinq?strict=false"))
{
	var posts = cn.DB.GetCollection<Post>();
	posts.Insert(new Post
	{
		Title="Testing Linqpad",
		Content="This is a test using LinqPad",
		PubDate=DateTime.Now,
		Categories = new string[] {"NoSql", "MongoDb", "LinqPad"}
	});

	var foundPosts = from p in new MongoQuery<Post>(cn)
			where p.Title == "Testing Linqpad"
			select p;
		
	foundPosts.Dump();
}

Let’s step through this code a bit. The first line sets up our connection to the Mongo Database. We pass in the URI of the server (mongodb://127.0.0.1/) along with the name of the “database” that we want to use, or create if it hasn’t already been created (“ThinqLinq”).

This connection behaves similarly to the LINQ to SQL DataContext or Entity Framework’s ObjectContext. As with those contexts, we next need to access the “Table” for the type that we are trying to create. We do that by accessing the GetCollection method of the DB object referencing the type that we want to get (Post).

With the reference to the posts collection, we can insert a new Post object into our database by calling the Insert method. Notice, unlike a traditional RMDBS, we have not actually created anything in the database yet. This is the great thing about a Document Database. We can save our objects directly in the database without having to create the structures ahead of time.

With the object inserted into the database, we can now fetch it back using a standard LINQ query. The only difference we see here to the pattern in other LINQ providers is that we access the data source by calling MongoQuery passing in the Type that we want to fetch and the connection object that we declared at the top of the method. We view the results by calling the LINQPad Dump extension method on the results. Once completed, here’s a screen shot including the final code and results. Notice that the results do include the three items we added in the Categories array along with the single post record.

image

Interestingly, the built-in performance timer and Lambda expression options in LINQPad still work here even though we aren’t accessing a traditional database. The SQL tab remains blank because we aren’t issuing a SQL statement to Mongo.

Document Databases and the whole NoSQL movement are quite intriguing. There are plenty of times where relational data stores are a better fit, particularly when you are needing to report on related objects rather than working with object hierarchies natively. However, tools like MongoDB and NoRM make working with non-traditional hierarchical document stores quite easy as well and point to some interesting options for data storage and retrieval for other needs.

Posted on - Comment
Categories: C# - LinqPad -

LINQ in Action XML samples added to LINQPad

The beginning of this month, we released the samples from "LINQ in Action" for chapters 1-8 which covers LINQ to Objects, LINQ to SQL, and the new Language features. We're happy to announce that the next three chapters covering LINQ to XML are now available. That's over 70 new samples in VB and C# each. Follow the directions on the original announcement to download and use these samples.

Posted on - Comment
Categories: Linq to XML - LinqPad -

Add Extension Methods in LinqPad

As we already announced, the samples for chapters 1-8 of our LINQ in Action book are available through LINQPad. This includes the LINQ to Objects and LINQ to SQL. I've been working on the LINQ to XML chapters (9-11) and hope that we will add them to the download soon. In the process, I've needed to learn a bit about how LINQPad works under the covers in order to add specialized classes.

By default, LINQPad offers three options: Expressions, Statements and Programs. With the Expressions, you can include a single statement and have the result output. Typically here you would include a LINQ query as follows:

From cust in Customers _
Where cust.Country = "USA" _
Order By cust.CompanyName _
Select cust.CompanyName, cust.ContactName

Notice here that we don't include the Context as we typically would inside Visual Studio. That's the first clue as to what's happening under the covers. Keep this in mind as we'll come back to this in a bit.

If you need more than just a single statement, for example when demonstrating deferred execution, you can use the Statements option to include multiple statements that would otherwise appear inside a single method:

Dim books = dataContext.GetTable(Of Book)()
Dim query = From book In books _
            Skip 2 _
            Take 2 _
            Select book.Title, book.Pricequery.Dump()

If you need to refer to external methods or add other classes, choose the Program option. This will add a Sub Main method and allow you to add additional methods. Here's the sample we used for the compiled query option:


Sub Main
  ExpensiveBooks(Me, 30).Dump()
End Sub

''' 
''' Precompiled version of the Expensive Books query
''' 
Public Shared ExpensiveBooks As Func(Of TypedDataContext, Decimal, _
                                     IQueryable(Of Book)) = _
  CompiledQuery.Compile(Function(context As TypedDataContext, minimumPrice As Decimal) _
  From book In context.Books() _
  Where (book.Price >= minimumPrice) _
  Select book)

Notice here, when we pass the context in the Sub Main, that we are referring to "Me" (in C#, "this"). So what is this "Me" class that we are referring to and how were we able to refer to the Customers in the first query without including the Context? In a nutshell, LINQPad wraps your code inside of a class that is generated when you run the snippet. This class inherits from DataContext and includes the typical generated code for the objects in the database similar to the definitions generated by SqlMetal. (There are subtle differences which can cause some unexpected results, particularly when looking at the concurrency SQL on generated update statements.) Thus when using the Program option, your code is inserted into a class using the following Pseudo-code:


Public Class TypedDataContext
  Inherits DataContext
  'Generated constructors, tables, functions, views, etc
  'LINQPad user entered code
  Sub Main
    'Your functionality goes here
  End Sub
  'Other LINQPad user entered code
End Class

In the area of the other LINQPad user entered code, you are not limited to methods, fields, etc., but can also include full class/module/type definitions. Since we can include full classes, we should be able to add extension method definitions. We can't add extension methods to the generated TypedDataContext class because it doesn't fit the required signature for extension method classes (Module in VB or Shared Class in C#). Thus we need a separate class.

To create an extension method that uppercases each word, it would be nice if we could do the following:

Sub Main
  Console.WriteLine("this is a test".ToTitleCase())
End Sub

' Define other methods and classes here
Public Module Extensions
  <System.Runtime.CompilerServices.Extension()> _
  Public Function ToTitleCase(ByVal input As String) As String
    Return New System.Globalization.CultureInfo("en-US") _
                 .TextInfo.ToTitleCase(input)
  End Function
End Module

At first glance, this would seem to work. However remember that this extension module is actually nested inside of the TypedDataContext class. Here's a snapshot of the class relationships:


Public Class TypedDataContext
  Inherits DataContext
  ' SQL Metal generated classes
  ' LINQPad user entered code
  Sub Main
  End Sub
  Public Module Extensions
  End Module
End Class

If we try to run this, we get the message indicating that the extension method can't be found. By definition, nested classes can't contain extension methods. They have to be root level classes. The trick here is to trick our code to close off the generated TypedDataContext and then inject the start of a new dummy class definition at the end which will be closed off when we insert our code into the code generated by LINQPad as follows:


Sub Main
  Console.WriteLine("this is a test".ToTitleCase())
End Sub
' Close off the TypedDataContext Class
End Class

Public Module Extensions
  <System.Runtime.CompilerServices.Extension()> _
  Public Function ToTitleCase(ByVal input As String) As String
    Return New System.Globalization.CultureInfo("en-US") _
                 .TextInfo.ToTitleCase(input)
  End Function
End Module

' Create a new dummy class which will be closed by LINQPad
Class Foo

Notice here, we don't explicitly close the Foo class, but rather let LINQPad add that End Class for us. (For those of you familiar with SQL Injection, this is a technique that is typically used there as well).

Realizing the relationship between your code and the TypedDataContext that LINQPad generates allows you to use LINQPad in a host of interesting ways. Try playing with it, and while you're at it, check out the LINQ in Action samples.

Posted on - Comment
Categories: LINQ - VB - LinqPad -