Hazelcast is an open-source in-memory computing product. It provides caching, distributed processing, and distributed messaging with excellent speed, scalability, and security. In-memory data grids (IMDGs) allow applications to quickly perform big data operations compared to conventional databases and data store technologies.
This article demonstrates how to install and set up a local Hazelcast cluster that works as a server. We highlight how to create an ASP.NET Core application to connect to and access Hazelcast in a client-server fashion.
We’ll create a sample shopping application to demonstrate how this setup works. You should know C# to follow along.
Installing and Starting a Local Hazelcast Cluster
We need to begin by creating a local cluster on our development machine.
First, access the Get Started page and download either a TAR or a ZIP file. You can install and run a Hazelcast cluster on Linux, macOS, or Windows machines. You can also download a Docker image to run Hazelcast.
Following the Get Started page, run the appropriate command (bin/start.bat in Windows or bin/start.sh in Linux) to create and start a new cluster.

If we run the start command three times, Hazelcast starts three members that automatically discover each other and form a cluster — even if they’re running locally.

Using the ASP.NET Core Demo Web App
To use the ASP.NET Web app, follow these steps:
- Install Visual Studio 2019 Community Edition or superior.
- Download the working code from this GitHub repository to follow this article.
- Open the downloaded solution in Visual Studio, where you see two ASP.NET Core projects with .NET 5. These two projects are similar, representing two versions of the same application that simulates e-commerce.

Using a Wrapper Class for the Hazelcast .NET Client
The ECommerce
and ECommerce-Hazelcast
projects have two different wrapper classes encapsulating the data access functionalities. They are ECommerceData
and ECommerceDataHazelcast
.
The ECommerceData
class uses the standard Dictionary
and Queue
classes:
public interface IECommerceData : IBaseECommerceData
{
void Initialize();
List GetCartItems();
void AddCartItem(CartItem cartItem);
void Checkout();
List OrdersAwaitingPayment();
List OrdersForDelivery();
List OrdersRejected();
void ApprovePayment();
void RejectPayment();
}
The ECommerceDataHazelcast
wrapper class replaces these standard classes with the IHMap
and the IHQueue
structures from Hazelcast that mirror the IEcommerceData
interface. The difference is that the latter uses tasks
and async
in methods because Hazelcast exposes asynchronous operations.
public interface IECommerceData : IBaseECommerceData
{
void Initialize();
List GetCartItems();
void AddCartItem(CartItem cartItem);
void Checkout();
List OrdersAwaitingPayment();
List OrdersForDelivery();
List OrdersRejected();
void ApprovePayment();
void RejectPayment();
}
Hazelcast .NET Client Startup and Shutdown
To properly use Hazelcast in ASP.NET Core, we need to deal with the Hazelcast .NET client’s lifecycle events. Our ECommerceDataHazelCast
wrapper class exposes a method that initializes the Hazelcast map. Note how the Program
class in the ECommerce-Hazelcast
project calls the InitializeAsync
method.
public static async Task Main(string[] args)
{
var webHost = CreateHostBuilder(args).Build();
await SetupHazelCast(webHost);
webHost.Run();
}
private static async Task SetupHazelCast(IHost webHost)
{
await webHost.Services.GetRequiredService().InitializeAsync();
}
The Configure
method of Startup.cs registers the ASP.NET Core application lifecycle event triggered when our web app stops. We obtain an instance of IECommerceDataHazelCast
to call the ShutdownAsync
method.
hostApplicationLifetime.ApplicationStopping.Register(() =>
{
using (var scope = app.ApplicationServices.GetRequiredService().CreateScope())
{
var eCommerceDataHazelCast = scope.ServiceProvider.GetService();
eCommerceDataHazelCast.ShutdownAsync().Wait();
}
});
Dependency Injection for the Wrapper Class
Although we can use Hazelcast as a distributed cache, we want a more straightforward approach: using ASP.NET Core to use Hazelcast directly.
We don’t want to create a new connection to Hazelcast every time we call it. That is why we’re registering the Hazelcast client wrapper class in the ASP.NET Core dependency injection (DI) as a singleton. We want to ensure we can connect it to a Hazelcast cluster once and reuse the connection.
The code below demonstrates how we call AddSingleton
in the ConfigureServices
method of the Startup
class to add a singleton service of the IECommerceDataHazelCast
type:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSingleton();
}
Hazelcast
client with the automatic options, we call the StartNewClientAsync
method of HazelcastClientFactory
. Here’s how we implement it in the StartAsync
method of the ECommerceDataHazelCast
class:
private void StartAsync()
{
var options = HazelcastOptions.Build();
// create an Hazelcast client and connect to a server running on localhost
this.hazelcastClient = HazelcastClientFactory.StartNewClientAsync(options).Result;
}
IHMap
and the IHQueue
instances we created:
public async Task ShutdownAsync()
{
// destroy the map
await hazelcastClient.DestroyAsync(cartItemsMap);
// destroy the queue
await hazelcastClient.DestroyAsync(ordersAwaitingPaymentQueue);
Debug.WriteLine("ShutdownAsync finished successfully.");
}
Using a Hazelcast Map in .NET
When we open the ECommerce project in this solution, built using only standard .NET structures, we notice the shopping cart items are Dictionary<int, CartItem>
:
private Dictionary cartItems;
The Dictionary<TKey, TValue>
(where the TKey
represents the product ID) ensures that the shopping cart doesn’t contain duplicate items of the same product.
In Hazelcast.NET, IHMap
represents a distributed map, which is the equivalent to the standard Dictionary<TKey, TValue>
.NET collection:
private IHMap cartItemsMap;
Now let’s see how we can seamlessly write to and read from IHMap
using .NET code.
Set the ECommerce-Hazelcast project as the solution’s default project and run the app. The Index.cshtml Razor page displays a set of predefined items already in the customer’s shopping cart:

The code obtains these shopping cart items when the user requests the Razor page:
public async Task OnGetAsync()
{
await InitializePageAsync();
}
private async Task InitializePageAsync()
{
this.CartItems = await eCommerceData.GetCartItemsAsync();
}
How does the page obtain the eCommerceData
instance? Note that we already registered the IECommerceDataHazelCast
interface as a singleton in Startup.cs, so we just provide it as a parameter for the Razor page constructor:
public IndexModel(ILogger logger, IECommerceDataHazelCast eCommerceData)
{
this.logger = logger;
this.eCommerceData = eCommerceData;
}
InitializePageAsync
method above requests the GetCartItemsAsync
method in the wrapper class. This method obtains all cart items from cartItemsMap
by converting the IHMap
into a list.
private IHMap cartItemsMap;
public async Task InitializeAsync()
{
// Get the Distributed Map from Cluster.
cartItemsMap = await hazelcastClient.GetMapAsync("distributed-cartitem-map");
}
public async Task> GetCartItemsAsync()
{
return (await cartItemsMap.GetValuesAsync()).ToList();
}
GetMapAsync
method of the Hazelcast .NET Client API first initialized the cartItemsMap
variable before its use.
Now, look at the buttons at the bottom of the page: 
The Check out and the Add to Cart buttons execute form actions that redirect the user to the AddToCart
Razor page or the checkout operation, respectively.
Here’s how ECommerceDataHazelCast.cs implements the CheckoutAsync
method:
public async Task CheckoutAsync()
{
int orderId = ++MaxOrderId;
var cartItems = await cartItemsMap.GetValuesAsync();
var order = new Order(orderId, DateTime.Now, cartItems.Count, cartItems.Sum(i => i.Quantity * i.UnitPrice));
await ordersAwaitingPaymentQueue.PutAsync(order);
await cartItemsMap.ClearAsync();
}
The Add to Cart button redirects users to the AddToCart.cshtml Razor page:

The AddToCart.cshtml page has two elements that trigger AJAX requests: quantity input and product selection. Changing either of these elements causes AJAX to invoke the OnGetCartItem
server-side endpoint in AddToCart.cshtml.cs with the recalculated total:
public JsonResult OnGetCartItem(CartItem cartItem)
{
var product = eCommerceData.GetProductList().Where(p => p.Id == cartItem.ProductId).Single();
var newItem = new CartItem(cartItem.Id, product.Id, product.Icon, product.Description, product.UnitPrice, cartItem.Quantity);
return new JsonResult(newItem);
}

Once the user clicks Confirm, the item
, quantity
, and product
appear on the Razor page, invoking AddCartItemAsync
on the ECommerceDataHazelcast
client wrapper class. This action calls PutAsync
on the IHMap
, representing the shopping cart items map. The PutAsync
method asynchronously puts the cart item key and the item value in the cartItemsMap
map.
private IHMap cartItemsMap;
public async Task AddCartItemAsync(CartItem cartItem)
{
var product = GetProductList().Where(p => p.Id == cartItem.ProductId).Single();
var newItem = new CartItem(cartItem.Id, product.Id, product.Icon, product.Description, product.UnitPrice, cartItem.Quantity);
await cartItemsMap.PutAsync(newItem.ProductId, newItem);
}
PutAsync
using newItem.ProductId
as the item key. So, if we add another item to the shopping cart using an existing key, PutAsync
replaces the current item’s value instead of adding a duplicate item.
Using Hazelcast IHQueue in .NET
If we open the ECommerce project in this solution, built using only standard .NET structures, the shopping cart items areQueue<Order>
.
private Queue ordersAwaitingPayment;
Queue<T>
(where the TKey
represents the Order
object) ensures that the code processes orders awaiting payment in a “first-in-first-out” sequence.
In Hazelcast.NET, the IHQueue
structure represents a distributed, non-partitioned, and visual queue, which is the equivalent to the standard Queue<T>
.NET collection:
private IHQueue ordersAwaitingPaymentQueue;
Now let’s see how we can quickly fill and consume an IHQueue
using .NET code.
When the shopping cart is complete, a user clicks Check out to post a new order:

The AddToCartModel
class then invokes the ECommerceDataHazelcast
wrapper class to call the PutAsync
method on the ordersAwaitingPaymentQueue
queue.
public async Task CheckoutAsync()
{
int orderId = ++MaxOrderId;
var cartItems = await cartItemsMap.GetValuesAsync();
var order = new Order(orderId, DateTime.Now, cartItems.Count, cartItems.Sum(i => i.Quantity * i.UnitPrice));
await ordersAwaitingPaymentQueue.PutAsync(order);
await cartItemsMap.ClearAsync();
}
This code also calls the ClearAsync
method on the cartItemsMap
object to clear the shopping cart:

Now click Payment Approval to see how the shopping cart turns into a new order:

Hazelcast
wrapper class to request a list of orders from the ordersAwaitingPaymentQueue
queue:
public async Task OnGetAsync()
{
await InitializePageAsync();
}
private async Task InitializePageAsync()
{
this.OrdersAwaitingPayment = await eCommerceData.OrdersAwaitingPaymentAsync();
}
The list of orders is in ECommerceDataHazelcast.cs, as below:
public async Task> OrdersAwaitingPaymentAsync()
{
var list = await ordersAwaitingPaymentQueue.GetAllAsync();
return list.OrderByDescending(o => o.Id).ToList();
}

The Payment.cshtml Razor page allows us to approve or reject orders as submitted. Approved orders go to ordersForDeliveryQueue
. Rejected orders go to ordersRejectedQueue IHQueues
.
public async Task ApprovePaymentAsync()
{
var order = await ordersAwaitingPaymentQueue.TakeAsync();
await ordersForDeliveryQueue.PutAsync(order);
}
public async Task RejectPaymentAsync()
{
var order = await ordersAwaitingPaymentQueue.TakeAsync();
await ordersRejectedQueue.PutAsync(order);
}
Note that the PutAsync
and the TakeAsync
methods work by adding and removing items to and from a queue, similarly to the Enqueue
and Dequeue
methods in a .NET Queue
collection.
Now click Order Tracking to see the orders prepared for delivery and the orders with rejected payment:


When a user opens the Tracking.cshtml Razor page, it fills OrdersForDelivery
and the OrdersRejected
lists with values from the Hazelcast
wrapper class:
public async Task OnGetAsync()
{
await InitializePageAsync();
}
private async Task InitializePageAsync()
{
this.OrdersForDelivery = await eCommerceData.OrdersForDeliveryAsync();
this.OrdersRejected = await eCommerceData.OrdersRejectedAsync();
}
The ECommerceDataHazelcast
wrapper class provides delivery and rejected orders from the ordersForDeliveryQueue
and the ordersRejectedQueue IHQueues
:
public async Task> OrdersForDeliveryAsync()
{
var list = await ordersForDeliveryQueue.GetAllAsync();
return list.OrderByDescending(o => o.Id).ToList();
}
public async Task> OrdersRejectedAsync()
{
var list = await ordersRejectedQueue.GetAllAsync();
return list.OrderByDescending(o => o.Id).ToList();
}
Other Hazelcast Data Structures
BesidesIHMap
and IHQueue
, the Hazelcast
.NET client can access other Hazelcast
distributed objects managed by the Hazelcast
cluster. You can start with the demo web app in this article to explore various possibilities for distributed stores:
- HMultiMap is a distributed key/value store like a .NET
Dictionary
but can store multiple values in a single key. For example, you can store different values in the same SKU to represent various product colors, versions, or flavors that an e-commerce Web app offers. - HReplicatedMap is a distributed key/value store that replicates data for all members in a cluster.
- HSet is a distributed set store similar to the .NET
HashSet
, which does not allow duplicates and where you can add values without a key. - HTopic is a distributed message-publishing store (pub/sub) that you can use in a notification system alerting e-commerce users as they navigate the website.
Next Steps
In-memory data grids likeHazelcast
have cluster-based architectures offering data access with high-speed data processing and low latency. As .NET developers, it’s exciting to learn how to bring the benefits of fast in-memory data grids to our projects.
You just saw Hazelcast
in action within a realistic ASP.NET Core application. Hazelcast.NET is a helpful member of the .NET community, bringing the power of in-memory data grids to the Microsoft .NET ecosystem. It provides data structures such as distributed maps and queues to integrate into new or existing .NET solutions easily.
To learn more about the available Open Source, Cloud, and Enterprise distributions, visit the Get Started and Download page and explore what Hazelcast can do for your organization.
Happy Hazelcasting!
If you’re interested in developing expert technical content that performs, let’s have a conversation today.