How to get started with Couchbase with ASP.NET Core 3 using Linq2Couchbase AND Dependency Injection

Manager — SDE
Search for a command to run...

Manager — SDE
No comments yet. Be the first to comment.
← Previous: What’s New in the SDK and Tooling for .NET 9 .NET Aspire is a set of tools, templates, and packages designed to help developers build observable, production-ready apps that embrace cloud-native and distributed computing best practices. .N...

← Previous: What’s New in .NET Libraries for .NET 9 The .NET 9 SDK introduces several new capabilities and improvements to streamline development workflows, enhance build and test experiences, improve NuGet security and performance, and provide great...

← Previous: What’s New in .NET MAUI for .NET 9 .NET 9 adds a wide range of enhancements and new APIs across the base class libraries. These improvements touch everything from low-level data structures and cryptography to JSON serialization, text pr...

← Previous: What’s New in ML.NET .NET Multi-platform App UI (.NET MAUI) in .NET 9 focuses on improving overall product quality, test coverage, performance, and stability. While previous releases introduced major new capabilities and controls, the emp...

← Previous: What’s new in ASP.NET Core 9.0 ML.NET continues to evolve as a full-featured, open-source machine learning framework for .NET developers, enabling you to create, train, and deploy custom ML models directly in your .NET applications. ML....

This tutorial will create a sample repo for using Couchbase with ASP.NET Core 3 with Linq2Couchbase AND Dependency Injection.
Assuming you already have an up-and-running Couchbase server running. If not, check out official Couchbase docs and come back.
Create a new bucket named contacts add a few documents like this
{
"name": "Satish K. Yadav",
"number": "9876543210",
"type": "Contact"
}
Since this
bucketmay contain any type of document, an optional propertytypehas been added to filtercontactobjects easily.
Create a new Project with ASP.NET Core - Web Application (MVC).
Install the following Nuget packages.
CouchbaseNetClient - Couchbase .NET SDK
Couchbase.Extensions.DependencyInjection - Dependency Injection extensions
Linq2Couchbase - Linq-to-Couchbase provider for accessing databases like other ORMs e.g. EntityFramework.
Add Couchbase Database Server configuration in appsettings.json e.g.
{
"Couchbase": {
"Servers": [
"http://localhost"
],
"Username": "USERNAME",
"Password": "PASSWORD",
"UseSsl": false
}
}
For Production, you may want to set
UseSslastruedepending on your server configuration.
Add configuration to ConfigureServices method
public void ConfigureServices(IServiceCollection services)
{
//Other configurations
//CouchBase configuration using Dependency Injection
services.AddCouchbase(Configuration.GetSection("Couchbase"));
}
Instead of hardcoding bucket names all over the project, use buckets with named providers.
Create new folder Data in the root, and add an interface named IContactsBucketProvider. It should implement INamedBucketProvider. Leave it blank. It should look like this.
public interface IContactsBucketProvider: INamedBucketProvider
{
}
Register named bucket provider with AddCouchbaseBucket by chaining it to AddCouchbase call, so it looks like this:
public void ConfigureServices(IServiceCollection services)
{
//Other configurations
//CouchBase configuration using Dependency Injection
services
.AddCouchbase(Configuration.GetSection("Couchbase"))
.AddCouchbaseBucket<IContactsBucketProvider>("contacts");
}
Add a IHostApplicationLifetime parameter to Configure method and code to clean up Couchbase once the application stops, so it looks like this:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime hostApplicationLifetime)
{
//Other configurations
hostApplicationLifetime.ApplicationStopped.Register(() =>{
//Cleaning up using using Dependency Injection
app.ApplicationServices.GetRequiredService<ICouchbaseLifetimeService>().Close();
});
}
Linq2CouchbaseRegister a BucketContext, that will be used for accessing documents from the database. In ConfigureServices method add the following:
public void ConfigureServices(IServiceCollection services)
{
//Other configurations
//CouchBase configuration using Dependency Injection
services
.AddCouchbase(Configuration.GetSection("Couchbase"))
.AddCouchbaseBucket<IContactsBucketProvider>("contacts");
services.AddTransient(x =>
{
var contactsBucket = x.GetRequiredService<IContactsBucketProvider>();
return new BucketContext(contactsBucket.GetBucket());
});
}
Add a Contact class in Data folder for accessing Couchbase documents.
public class Contact
{
public string Name { get; set; }
public string Number { get; set; }
public string Type => typeof(Contact).Name;
}
Now create a Model class under Models folder for controllers.
public class ContactDto
{
[Key]
public string Id { get; set; }
public string Name { get; set; }
public string Number { get; set; }
}
Add a new ContactsController and add all CRUD methods, that looks like this:
public class ContactsController : Controller
{
private BucketContext _bucketContext;
public ContactsController(BucketContext bucketContext)
{
_bucketContext = bucketContext;
}
// GET: Contacts
public ActionResult Index()
{
var allContacts = _bucketContext
.Query<Contact>()
.Where(x => x.Type == "Contact")
.Select(x => new ContactDto
{
Id = N1QlFunctions.Meta(x).Id,
Name = x.Name,
Number = x.Number
})
.ToList();
return View(allContacts);
}
// GET: Contacts/Details/5
public ActionResult Details(string id)
{
var contact = _bucketContext
.Query<Contact>()
.Select(x => new ContactDto
{
Id = N1QlFunctions.Meta(x).Id,
Name = x.Name,
Number = x.Number
})
.FirstOrDefault(x => x.Id == id);
return View(contact);
}
// GET: Contacts/Create
public ActionResult Create()
{
return View();
}
// POST: Contacts/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ContactDto contactDto)
{
try
{
// TODO: Add insert logic here
//contactsModel.Id = Guid.NewGuid().ToString();
var contact = new Contact { Name = contactDto.Name, Number = contactDto.Number };
_bucketContext.Save(contact);
return RedirectToAction(nameof(Index));
}
catch (Exception e)
{
ModelState.TryAddModelException("exception", e);
return View();
}
}
// GET: Contacts/Edit/5
public ActionResult Edit(string id)
{
var contact = _bucketContext
.Query<Contact>()
.Select(x => new ContactDto
{
Id = N1QlFunctions.Meta(x).Id,
Name = x.Name,
Number = x.Number
})
.FirstOrDefault(x => x.Id == id);
return View(contact);
}
// POST: Contacts/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, ContactDto contact)
{
try
{
// TODO: Add update logic here
_bucketContext.Save(new Contact { Name = contact.Name, Number = contact.Number });
return RedirectToAction(nameof(Index));
}
catch (Exception e)
{
ModelState.TryAddModelException("exception", e);
return View();
}
}
// GET: Contacts/Delete/5
[HttpGet(Name = "Delete")]
public ActionResult ConfirmDelete(string id)
{
var contact = _bucketContext
.Query<Contact>()
.Select(x => new ContactDto
{
Id = N1QlFunctions.Meta(x).Id,
Name = x.Name,
Number = x.Number
})
.FirstOrDefault(x => x.Id == id);
return View(contact);
}
// POST: Contacts/Delete/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(string id)
{
try
{
// TODO: Add delete logic here
var contact = _bucketContext.Query<Contact>().FirstOrDefault(x => N1QlFunctions.Meta(x).Id == id);
if (contact == null)
return NotFound();
_bucketContext.Remove(contact);
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
Add Index, Create, Edit, Details, and Delete views, under Views-> Contacts folder. Alternatively, generate these views by scaffolding. Views should look something like these:
@model YOUR_NAMESPACE_HERE.Models.ContactDto
@{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>ContactsModel</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Number" class="control-label"></label>
<input asp-for="Number" class="form-control" />
<span asp-validation-for="Number" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
@model YOUR_NAMESPACE_HERE.Models.ContactDto
@{
ViewData["Title"] = "Delete";
}
<h1>Delete Contact</h1>
<h3>Are you sure you want to delete this?</h3>
<div>
<hr />
<dl class="row">
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Name)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Name)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Number)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Number)
</dd>
</dl>
<form asp-action="Delete">
<input type="submit" value="Delete" class="btn btn-danger" /> |
<a asp-action="Index">Back to List</a>
</form>
</div>
@model YOUR_NAMESPACE_HERE.Models.ContactDto
@{
ViewData["Title"] = "Details";
}
<h1>Contact Details</h1>
<div>
<hr />
<dl class="row">
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Name)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Name)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Number)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Number)
</dd>
</dl>
</div>
<div>
@Html.ActionLink("Edit", "Edit", new { id = Model.Id }) |
<a asp-action="Index">Back to List</a>
</div>
@model YOUR_NAMESPACE_HERE.Models.ContactDto
@{
ViewData["Title"] = "Edit";
}
<h1>Edit Contact</h1>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="All" class="text-danger"></div>
<input type="hidden" readonly asp-for="Id" class="form-control" />
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Number" class="control-label"></label>
<input asp-for="Number" class="form-control" />
<span asp-validation-for="Number" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
@model IEnumerable<YOUR_NAMESPACE_HERE.Models.ContactDto>
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Name)
</th>
<th>
@Html.DisplayNameFor(model => model.Number)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Number)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
@Html.ActionLink("Details", "Details", new { id = item.Id }) |
@Html.ActionLink("Delete", "Delete", new { id = item.Id })
</td>
</tr>
}
</tbody>
</table>
jQuery validation library_ValidationScriptsPartial.cshtml looks like this
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
That's it! Run and your application is ready to be served.
If you've run into an error, which looks something like this:
CouchbaseQueryException: No index available on keyspace contacts that matches your query. Use CREATE INDEX or CREATE PRIMARY INDEX to create an index, or check that your expected index is online.
You need to create an index before querying. For simplicity create a Primary Index on type field with the following script.
CREATE PRIMARY INDEX ON contacts