DEV Community

Cover image for CouchDB and C#. EFCore-like queries!
Matteo Bortolazzo
Matteo Bortolazzo

Posted on

CouchDB and C#. EFCore-like queries!

In this post, we'll see how to query CouchDB from C# using LINQ!

I am going to use my library that after few years, today it reached v3.0.0: CouchDB.NET.

What's CouchDB

Apache CouchDB is a distributed document database and its replication protocol is available from servers to mobile phones and web browsers.

It speaks JSON natively, and that's great when using Javascript, but when using .NET it might be painful because of JSON's dynamic nature.

How CouchDB.NET can help

If you worked with Entity Framework before, this library might look familiar.

The setup

Create your CouchContext with the list of databases you have and configure authentication.

Rebel is of type CouchDocument as it contains things like Id, Rev, Attachments, and others.

public class MyDeathStarContext : CouchContext
{
    public CouchDatabase<Rebel> Rebels { get; set; }

    protected override void OnConfiguring(CouchOptionsBuilder optionsBuilder)
    {
      optionsBuilder
        .UseEndpoint("http://localhost:5984/")
        .EnsureDatabaseExists()
        .UseBasicAuthentication(username: "anakin", password: "empirerule");
    }
}
Enter fullscreen mode Exit fullscreen mode

Use LINQ

The library supports native .NET LINQ, ven the SQL-like syntax.

await using var context = new MyDeathStarContext();
var skywalkers = await context.Rebels
    .Where(r => 
        r.Surname == "Skywalker" && 
        (
            r.Battles.All(b => b.Planet == "Naboo") ||
            r.Battles.Any(b => b.Planet == "Death Star")
        )
    )
    .OrderByDescending(r => r.Name)
    .ThenByDescending(r => r.Age)
    .Take(2)
    .Select(
        r => r.Name,
        r => r.Age
    })
    .ToListAsync();
Enter fullscreen mode Exit fullscreen mode

What is sent to the database

{
  "selector": {
    "$and": [
      {
        "surname": "Skywalker"
      },
      {
        "$or": [
          {
            "battles": {
              "$allMatch": {
                "planet": "Naboo"
              }
            }
          },
          {
            "battles": {
              "$elemMatch": {
                "planet": "Death Star"
              }
            }
          }
        ]
      }
    ]
  },
  "sort": [
    { "name": "desc" },
    { "age": "desc" }
  ],
  "limit": 2,
  "fields": [
    "name",
    "age"
  ]
}
Enter fullscreen mode Exit fullscreen mode

It will automatically optimize the query.

Just to name a few:

  • Merge multiple Where calls;
  • Remove useless condition from the filter (like Where(r => true || r.Age == 19));
  • Convert SingleOrDefault(d => condition) to Where(d => condition).Take(2).SingleOrDefault();
  • Convert Max(d => d.Property) to OrderByDescending(d => d.Property).Take(1).Select(d => d.Property).Max()

Other features

In the year, more and more features have been added.

For example:

  • Adding/Updating/Deleting documents;
  • Adding/Updating/Deleting indexes;
  • Database spitting;
  • Views;
  • Local documents;
  • Users management;

Dependency Injection

If you work with Asp.NET Core a DI package is available.

It just as easy as register the dependency in the Startup:

services.AddCouchContext<MyDeathStarContext>(builder => builder
    .UseEndpoint("http://localhost:5984")
    .UseBasicAuthentication(username: "admin", password: "admin"));
Enter fullscreen mode Exit fullscreen mode

Then just inject the context:

public class RebelsController : Controller
{
    private readonly MyDeathStarContext _context;

    public RebelsController(MyDeathStarContext context)
    {
        _context = context;
    }
}
Enter fullscreen mode Exit fullscreen mode

Finally

If you work with CouchDB and C# I highly suggest checking the library (even if it might result in more bugs to fix for me).

You can see all that it offers in the README and let me know what you think.

Top comments (0)