MongoDB from Visual Studio

You can find plenty of drivers for MongoDB directly on the MongoDB official site, including several versions for the C# language, which currently stands for version 2.2.3. This driver provides support for the version of MongoDB I'm using in this book (v. 3.2).

Actually, this version was created and tested in Visual Studio 2015, so that's another reason to use it here. You can find a whole page with explanations, links to other resources, videos, articles, community supported tools, presentations, and so on at the https://docs.mongodb.com/ecosystem/drivers/csharp/ address. This driver is the officially supported driver for MongoDB.

First demo: a simple query from Visual Studio

There are several approaches for driver's installation, but you can install it using NuGet from Visual Studio, so we'll start by building a new Console project (ConsoleMongo1), and after that, select the NuGet Window interactive. Once there, typing MongoDB will show a bunch of libraries, including the official driver in the first position.

As you can see in the following screenshot, three libraries are installed: two versions of MongoDB driver (core and standard) and Mongo.BSon, which contains a serialization infrastructure that you can use to build high-performance serializers:

First demo: a simple query from Visual Studio

To work with MongoDB from C#, the driver offers a set of convenient objects, which in great part, represent those we have been using in the Mongo Command Window to perform previous operations.

Before any operation, it's important to remember that NoSQL structures are flexible, but in order to work properly from the C# side, it's more useful to have a structure for our data (a data model or contract). To do this, we can copy and paste a single document from our database and use the Paste as JSON option, which will convert the structure to a set of classes containing the keys defined in the document as classes' fields.

For the demos in this part, I've opted for another database source, which is more similar to what we would use in a real application. For this purpose, a possible source is the NorthWind JSON website, which offers JSON versions of the popular NorthWind database used for years in Microsoft Access and SQL Server as the demo database. You can find the database at http://northwind.servicestack.net/customers?format=json. I've downloaded two tables from here: Customers and Orders. Remember that through the import process, a new field named _id will be generated.

When you use the Paste as JSON option, its _id field will be assigned to a string, but internally, it is really an ObjectId type. To avoid problems later, you can change it manually to have a definition like this:

public class Customer
{
  public ObjectId _id { get; set; }
  public string CustomerID { get; set; }
  public string CompanyName { get; set; }
  public string ContactName { get; set; }
  public string ContactTitle { get; set; }
  public string Address { get; set; }
  public string City { get; set; }
  public object Region { get; set; }
  public string PostalCode { get; set; }
  public string Country { get; set; }
  public string Phone { get; set; }
  public string Fax { get; set; }
}

Now let's make a simple query that we can list in the Console window. To achieve this, we need to reference the previously mentioned libraries and follow the basic steps: connect to the NorthWind database, get a reference to a collection, define the query (we can use Linq and/or a generic functionality for this purpose), and present the results.

An initial, simple approach would be as follows:

class Program
{
  static IMongoClient client;
  static IMongoDatabase db;
  static void Main(string[] args)
  {
    BasicQuery();
  }
  private static void BasicQuery()
  {
    client = new MongoClient();
    db = client.GetDatabase("NorthWind");
    var coll = db.GetCollection<Customer>("Customers");

    var americanCustomers = coll.Find(c => c.Country == "USA")
    .ToListAsync().Result;
    string title = "Customers from United States";
    Console.WriteLine(title);
    Console.WriteLine(string.Concat(Enumerable.Repeat("-", title.Length)));
    foreach (var c in americanCustomers)
    {
      Console.WriteLine($"Name: {c.ContactName}, 	 City: {c.City} ");
    }
    Console.Read();
  }
}

If you launch the application, a Console window will show the requested set of customers:

First demo: a simple query from Visual Studio

So, let's quickly review the process here. MongoClient represents a connection to the MongoDB server. It follows a reference to the required database. Once there, we get the Customers collection, but since we already know the customers type and its members, we can use generics to express that, indicating that the result of calling GetCollection<Customer>("Customers") is of that type (note that the collection is a plural name).

When the collection variable is ready, it can be used as any other generic collection, so we can use lambda expressions, LINQ, and all other resources just the same as we did in previous chapters.

Note, though, that we've run a query in a synchronous mode. When the amount of data available (to search for) is high, asynchronous operations are recommended. Therefore, let's make the query a bit more complex and run it this way.

For example, let's assume that we need to know which customers from the United States or the United Kingdom are owners as well (the CustomerTitle field values Owner). So, we need a bit more complex filter. And we also want the process to be asynchronous, to avoid blocking errors or unresponsive user interfaces. Thus, we'll use the async/await operators to build a method in this manner:

async private static void CustomerQueryAsync()
{
  client = new MongoClient();
  db = client.GetDatabase("NorthWind");
  var coll = db.GetCollection<Customer>("Customers");
  var owners = await coll.FindAsync(c =>
    (c.Country == "USA" || c.Country == "UK") && c.ContactTitle == "Owner")
  .Result.ToListAsync();
  string title = "Owners from USA or UK";
  Console.WriteLine(title);
  Console.WriteLine(string.Concat(Enumerable.Repeat("-", title.Length)));
  foreach (var c in owners)
  {
    Console.WriteLine($"Name: {c.ContactName}, 	 City: {c.City} ");
  }
}

So, now we perform the query asynchronously (in a non-blocking fashion), with just a few changes, getting a couple of entries:

First demo: a simple query from Visual Studio

Note that besides using the async/await operators, the end of the query varies a little. We now call the toListAsync() method from the Result object in order to get the final collection. The rest is like what is done in the previous (synchronous) method.

CRUD operations

As you can imagine, CRUD operations are fully supported, especially when using this new version of the driver, which includes various new possibilities.

Most of these operations are presented in two main families depending on whether you want to deal with only one or many documents in the collection. Consequently, we find methods such as DeleteOne/DeleteMany, InsertOne/InsertMany, ReplaceOne/ReplaceMany, and so on. In turn, they present synchronous and asynchronous versions for each one.

Deletion

For instance, in order to delete a single customer, we can use the following:

async private static void DeleteCustomerAsync()
{
  var CustDel = await coll.FindOneAndDeleteAsync(c => c.CustomerID == "CHOPS");
  // List customers from Switzerland to check deletion
  BasicQuery("Switzerland");
}

You can see that we're using a very convenient method, which allows us to find and delete a single document in a sole (atomic) operation (FindOneAndDeleteAsync).

Also, we've changed the BasicQuery method to receive a string with the country to be listed, and we call that method again just after the deletion to check whether everything was okay. Now there's only one customer from that country:

Deletion

As a note, remember that if no document is found, any possible exception thrown by the application, should be handled in the usual manner.

Insertion

Insertion follows a similar pattern. We create a new customer following the contract definition and insert it asynchronously using a simple, straightforward code:

async private static void InsertCustomerAsync()
{
  Customer newCustomer = new Customer()
  {
    CustomerID = "ZZZZZ",
    CompanyName = "The Z Company",
    ContactName = "Zachary Zabek",
    City = "Zeeland",
    Region = "Michigan",
    Country = "USA"
  };
  await coll.InsertOneAsync(newCustomer);
  BasicQuery("USA");
  Console.Read();
}

If everything's okay, we'll be shown an output like what is shown in the following screenshot:

Insertion

Modifications and replacements

Working with document collections, it's common to distinguish between updates and replacements. In the first case, we're managing something similar to the UPDATE clause in standard SQL language.

The second situation deals with a total replacement of a document, with an exception made of the _id field, which is immutable. In this case, since there's no fixed model to follow, the information replaced could be totally different from the previous one.

To replace content, it's handy to use static methods of the Builders class, which provides the C# driver. We can define a generic Builder class for our customers and use the Filter and Update methods to locate and replace a given document.

The following code does exactly that: it locates the previously inserted company and changes the CompanyName field to another string:

async private static void ModifyCustomerAsync()
{
  var filter = Builders<Customer>.Filter.Eq("CompanyName", "The Z Company");
  var update = Builders<Customer>.Update
  .Set("CompanyName", "ZZZZZ Enterprises");
  var result = await coll.UpdateOneAsync(filter, update);
  BasicQueryByCompany("USA");
}

Note I have included another version of the BasicQuery method, called BasicQueryByCompany, in order to allow the returning of the modified field in the output:

Modifications and replacements

In the case of replacements, you can use the ReplaceOneAsync and ReplaceManyAsync methods, just like what we did for the update.

In addition, most typical operations you might be used to in SQL databases are present here as well: grouping, statistical results, security configuration, and so on.

Adoption of NoSQL databases is another story: scalability, availability, previous knowledge of NoSQL, and the learning curve are only a few of the considerations you might ponder at the time of selecting one of these databases in a new project. Whatever the case may be, support from most of the available NoSQL databases from the .NET platform is guaranteed for the majority of implementations, so that shouldn't be an issue.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.37.154