Skip to main content

.NET NoSQL Integration

In today's data-driven applications, traditional relational databases aren't always the best fit for all types of data and access patterns. NoSQL (Not Only SQL) databases provide alternative data storage solutions that excel in specific scenarios like storing unstructured data, handling high volumes of data, or requiring flexible schema designs.

This guide introduces you to integrating NoSQL databases with .NET applications, providing practical examples and step-by-step instructions for beginners.

What is NoSQL?

NoSQL databases are designed to handle data structures different from the table-based structure of relational databases. They provide various data storage models including:

  • Document databases: Store data in document-like structures (typically JSON)
  • Key-value stores: Simple key-value pairs for quick access
  • Column-family stores: Store data in columns rather than rows
  • Graph databases: Designed for data whose relationships are well represented as a graph

Why Use NoSQL with .NET?

  • Schema flexibility: Add fields without affecting existing records
  • Horizontal scaling: Distribute data across multiple servers
  • High performance: Optimized for specific data access patterns
  • Native JSON support: Easier handling of JSON data common in web applications

Let's explore how to integrate three popular NoSQL databases with .NET:

  1. MongoDB
  2. Redis
  3. Azure Cosmos DB

MongoDB Integration

MongoDB is a document-oriented database that stores data in flexible, JSON-like documents.

Setting Up MongoDB with .NET

First, install the MongoDB.Driver NuGet package:

bash
dotnet add package MongoDB.Driver

Basic CRUD Operations

Let's create a simple example with a Product class:

csharp
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;

// Product model
public class Product
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }

public string Name { get; set; }

public decimal Price { get; set; }

public string Category { get; set; }
}

Connecting to MongoDB

csharp
// Connection string
string connectionString = "mongodb://localhost:27017";

// Create client
var client = new MongoClient(connectionString);

// Get database
var database = client.GetDatabase("storeDb");

// Get collection
var collection = database.GetCollection<Product>("products");

Creating Documents

csharp
// Create a new product
var product = new Product
{
Name = "Laptop",
Price = 999.99m,
Category = "Electronics"
};

// Insert into collection
await collection.InsertOneAsync(product);
Console.WriteLine($"Inserted product with ID: {product.Id}");

// Output:
// Inserted product with ID: 60a7c8d245b7f23d4c9a4b1e

Reading Documents

csharp
// Find all products
var allProducts = await collection.Find(_ => true).ToListAsync();
foreach (var p in allProducts)
{
Console.WriteLine($"{p.Name}: ${p.Price}");
}

// Find products by category
var electronics = await collection.Find(p => p.Category == "Electronics").ToListAsync();
Console.WriteLine($"Found {electronics.Count} electronic products");

// Output:
// Laptop: $999.99
// Found 1 electronic products

Updating Documents

csharp
// Update price
var updateFilter = Builders<Product>.Filter.Eq(p => p.Name, "Laptop");
var updateDefinition = Builders<Product>.Update.Set(p => p.Price, 899.99m);
var updateResult = await collection.UpdateOneAsync(updateFilter, updateDefinition);

Console.WriteLine($"Modified {updateResult.ModifiedCount} document(s)");

// Output:
// Modified 1 document(s)

Deleting Documents

csharp
var deleteFilter = Builders<Product>.Filter.Eq(p => p.Name, "Laptop");
var deleteResult = await collection.DeleteOneAsync(deleteFilter);

Console.WriteLine($"Deleted {deleteResult.DeletedCount} document(s)");

// Output:
// Deleted 1 document(s)

Redis Integration

Redis is a popular in-memory key-value store often used for caching, message brokering, and more.

Setting Up Redis with .NET

Install the StackExchange.Redis package:

bash
dotnet add package StackExchange.Redis

Basic Redis Operations

csharp
using StackExchange.Redis;

// Connect to Redis
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379");
IDatabase db = redis.GetDatabase();

String Operations

csharp
// Set a string value
bool setResult = db.StringSet("user:1:name", "John Doe");

// Get a string value
string name = db.StringGet("user:1:name");
Console.WriteLine($"Name: {name}");

// Output:
// Name: John Doe

Hash Operations

csharp
// Store user information in a hash
db.HashSet("user:1", new HashEntry[]
{
new HashEntry("name", "John Doe"),
new HashEntry("email", "[email protected]"),
new HashEntry("age", 30)
});

// Get specific field
string email = db.HashGet("user:1", "email");
Console.WriteLine($"Email: {email}");

// Get all fields
HashEntry[] userFields = db.HashGetAll("user:1");
foreach (var field in userFields)
{
Console.WriteLine($"{field.Name}: {field.Value}");
}

// Output:
// Email: [email protected]
// name: John Doe
// email: [email protected]
// age: 30

Caching Example

csharp
public class RedisCache
{
private readonly ConnectionMultiplexer _redis;
private readonly IDatabase _database;

public RedisCache(string connectionString)
{
_redis = ConnectionMultiplexer.Connect(connectionString);
_database = _redis.GetDatabase();
}

public async Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> dataFactory, TimeSpan expiry)
{
// Try to get from cache
RedisValue value = await _database.StringGetAsync(key);
if (!value.IsNull)
{
return JsonSerializer.Deserialize<T>(value);
}

// Get from data source
T data = await dataFactory();

// Store in cache
await _database.StringSetAsync(
key,
JsonSerializer.Serialize(data),
expiry
);

return data;
}
}

// Usage example
var cache = new RedisCache("localhost:6379");
var product = await cache.GetOrSetAsync<Product>(
"product:123",
async () => await GetProductFromDatabaseAsync(123),
TimeSpan.FromMinutes(10)
);

Azure Cosmos DB Integration

Azure Cosmos DB is Microsoft's globally distributed, multi-model database service that supports multiple APIs including SQL, MongoDB, and Cassandra.

Setting Up Cosmos DB with .NET

Install the Microsoft.Azure.Cosmos package:

bash
dotnet add package Microsoft.Azure.Cosmos

Working with Cosmos DB SQL API

csharp
using Microsoft.Azure.Cosmos;

// Model class
public class Customer
{
public string id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string City { get; set; }
}

Connecting to Cosmos DB

csharp
// Connection details
string endpoint = "https://your-account.documents.azure.com:443/";
string key = "your-primary-key";
string databaseId = "CustomersDb";
string containerId = "Customers";

// Create CosmosClient
CosmosClient cosmosClient = new CosmosClient(endpoint, key);

// Get container reference
Database database = await cosmosClient.CreateDatabaseIfNotExistsAsync(databaseId);
Container container = await database.CreateContainerIfNotExistsAsync(
containerId,
"/City", // partition key path
400 // RU/s
);

Creating Items

csharp
// Create a customer
var customer = new Customer
{
id = Guid.NewGuid().ToString(),
Name = "Sarah Johnson",
Email = "[email protected]",
City = "Seattle"
};

// Add to Cosmos DB
ItemResponse<Customer> response = await container.CreateItemAsync(
customer,
new PartitionKey(customer.City)
);

Console.WriteLine($"Created customer. Cost: {response.RequestCharge} RUs");

// Output:
// Created customer. Cost: 7.43 RUs

Reading Items

csharp
// Read a specific item
ItemResponse<Customer> readResponse = await container.ReadItemAsync<Customer>(
customer.id,
new PartitionKey(customer.City)
);

Customer readCustomer = readResponse.Resource;
Console.WriteLine($"Retrieved: {readCustomer.Name} from {readCustomer.City}");

// Query multiple items
QueryDefinition query = new QueryDefinition(
"SELECT * FROM c WHERE c.City = @city")
.WithParameter("@city", "Seattle");

using FeedIterator<Customer> resultSet = container.GetItemQueryIterator<Customer>(
query,
requestOptions: new QueryRequestOptions { MaxItemCount = 10 }
);

List<Customer> customers = new List<Customer>();

while (resultSet.HasMoreResults)
{
FeedResponse<Customer> response = await resultSet.ReadNextAsync();
foreach (Customer item in response)
{
customers.Add(item);
Console.WriteLine($"Found {item.Name}");
}
}

// Output:
// Retrieved: Sarah Johnson from Seattle
// Found Sarah Johnson

Updating Items

csharp
// Update a customer
readCustomer.Email = "[email protected]";
ItemResponse<Customer> updateResponse = await container.ReplaceItemAsync(
readCustomer,
readCustomer.id,
new PartitionKey(readCustomer.City)
);

Console.WriteLine($"Updated item. New email: {updateResponse.Resource.Email}");

// Output:
// Updated item. New email: [email protected]

Deleting Items

csharp
// Delete a customer
ItemResponse<Customer> deleteResponse = await container.DeleteItemAsync<Customer>(
customer.id,
new PartitionKey(customer.City)
);

Console.WriteLine($"Deleted customer. Status code: {deleteResponse.StatusCode}");

// Output:
// Deleted customer. Status code: NoContent

Real-World Example: Product Catalog Service

This example demonstrates a simple product catalog service using MongoDB:

csharp
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class Program
{
public static async Task Main()
{
var productService = new ProductService("mongodb://localhost:27017", "catalogDb");

// Add some products
await productService.AddProductAsync(new Product
{
Name = "Smartphone",
Price = 499.99m,
Description = "Latest model smartphone with 128GB storage",
Category = "Electronics",
Tags = new[] { "phone", "mobile", "tech" }
});

await productService.AddProductAsync(new Product
{
Name = "Coffee Maker",
Price = 89.95m,
Description = "Automatic drip coffee maker with timer",
Category = "Home Appliances",
Tags = new[] { "coffee", "kitchen", "appliance" }
});

// Find products
var electronics = await productService.FindByCategory("Electronics");
Console.WriteLine("Electronics products:");
foreach (var product in electronics)
{
Console.WriteLine($"- {product.Name}: ${product.Price}");
}

// Get products with tag
var coffeeProducts = await productService.FindByTag("coffee");
Console.WriteLine("\nCoffee-related products:");
foreach (var product in coffeeProducts)
{
Console.WriteLine($"- {product.Name}: {product.Description}");
}

// Find products in price range
var affordableProducts = await productService.FindByPriceRangeAsync(0, 100);
Console.WriteLine("\nProducts under $100:");
foreach (var product in affordableProducts)
{
Console.WriteLine($"- {product.Name}: ${product.Price}");
}
}
}

// Product model
public class Product
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }

public string Name { get; set; }

public decimal Price { get; set; }

public string Description { get; set; }

public string Category { get; set; }

public string[] Tags { get; set; }

[BsonDateTimeOptions(Kind = DateTimeKind.Utc)]
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

// Product service for database operations
public class ProductService
{
private readonly IMongoCollection<Product> _products;

public ProductService(string connectionString, string databaseName)
{
var client = new MongoClient(connectionString);
var database = client.GetDatabase(databaseName);
_products = database.GetCollection<Product>("products");

// Create indexes
var indexKeysDefinition = Builders<Product>.IndexKeys.Ascending(p => p.Category);
_products.Indexes.CreateOne(new CreateIndexModel<Product>(indexKeysDefinition));

var tagsIndexDefinition = Builders<Product>.IndexKeys.Ascending("Tags");
_products.Indexes.CreateOne(new CreateIndexModel<Product>(tagsIndexDefinition));
}

public async Task<string> AddProductAsync(Product product)
{
await _products.InsertOneAsync(product);
return product.Id;
}

public async Task<List<Product>> FindAllAsync()
{
return await _products.Find(_ => true).ToListAsync();
}

public async Task<List<Product>> FindByCategory(string category)
{
return await _products.Find(p => p.Category == category).ToListAsync();
}

public async Task<List<Product>> FindByTag(string tag)
{
return await _products.Find(p => p.Tags.Contains(tag)).ToListAsync();
}

public async Task<List<Product>> FindByPriceRangeAsync(decimal minPrice, decimal maxPrice)
{
var filter = Builders<Product>.Filter.And(
Builders<Product>.Filter.Gte(p => p.Price, minPrice),
Builders<Product>.Filter.Lte(p => p.Price, maxPrice)
);

return await _products.Find(filter).ToListAsync();
}

public async Task UpdateProductAsync(Product product)
{
await _products.ReplaceOneAsync(p => p.Id == product.Id, product);
}

public async Task DeleteProductAsync(string id)
{
await _products.DeleteOneAsync(p => p.Id == id);
}
}

/* Output:
Electronics products:
- Smartphone: $499.99

Coffee-related products:
- Coffee Maker: Automatic drip coffee maker with timer

Products under $100:
- Coffee Maker: $89.95
*/

Choosing the Right NoSQL Database

When selecting a NoSQL database for your .NET application, consider the following factors:

Database TypeBest ForExamples
DocumentComplex, nested data structuresMongoDB, Cosmos DB SQL API
Key-ValueSimple but fast data lookupsRedis, DynamoDB
Column-FamilyTime-series data, loggingCassandra, HBase
GraphHighly connected dataNeo4j, CosmosDB Gremlin API

Consider these factors when choosing:

  • Data structure and complexity
  • Query patterns
  • Scale requirements
  • Budget constraints
  • Team familiarity

Summary

In this guide, we've explored how to integrate popular NoSQL databases with .NET applications:

  1. MongoDB: A flexible document database perfect for storing complex, variable data structures
  2. Redis: A high-performance key-value store excellent for caching and real-time data
  3. Azure Cosmos DB: Microsoft's globally distributed multi-model database service

NoSQL databases offer distinct advantages for specific use cases compared to traditional relational databases. By understanding these different database models and their .NET integration options, you can make informed decisions on which database technology best suits your application's requirements.

Additional Resources

Exercises

  1. Create a simple note-taking API using .NET and MongoDB that allows creating, reading, updating, and deleting notes with tags.

  2. Implement a shopping cart using Redis that can add items, update quantities, and expire after 24 hours of inactivity.

  3. Build a user profile service with Azure Cosmos DB that stores user preferences and activity history.

  4. Create a product inventory system using MongoDB that includes search functionality by product name, category, and price range.

  5. Implement a caching layer using Redis for an existing .NET application to improve performance.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)