NCache, as an in-memory distributed cache enhances application performance and scalability by efficiently storing data in the cache. While it’s known for speeding up data access, it’s important to understand the types of data NCache can handle. Just as databases offer flexibility in storing various data types, NCache provides a range of options for storing JSON, custom objects, and complex data structures. This blog explores how NCache accommodates different data forms, ensuring you can optimize your caching strategy to meet your application’s specific needs.
Key-Value Pairs
NCache operates as a key-value store, where each piece of data is associated with a unique key (a string-based identifier), and has the following properties:
- A key is supposed to be unique and no duplication is allowed.
- A key cannot be null.
- A key is case-sensitive.
The value represents the object stored in the cache, and we will explore the supported data types in detail later in the blog. Essentially, the key serves as the identifying attribute for the value stored against it. The figure below provides a visual representation of how data is added to and stored in the cache.
Supported Data Types in NCache
NCache supports the data in the form of objects. These objects can be in any of the following forms:
- Primitive and Non-primitive types
- Custom Class Object
- Data Structures
- JSON Data
Primitive & Non-primitive Data Types
NCache supports all .NET primitive data types. You can add a string as a value against a key. The following are the types supported by NCache:
- byte/sbyte
- short/ushort
- double
- bool
- int/uint
- long/ulong
- char
- float
- decimal
Non-primitive data types include:
- object
- string
- TimeSpan
- DateTime
Let us quickly walk through a brief code example showing the addition of a string against the key in the cache:
1 2 3 4 5 6 7 8 |
// Specify the customer name as string string customerName = "John Wick"; // Generate a unique cache key string key = "Customer"; // Add the string to cache cache.Add(key, customerName); |
Custom Class Object
A custom class object refers to any user-defined .NET or Java class, such as Product, Order, or Customer. To add a custom class object to your cache, it must be serializable. Serialization converts the class into a format suitable for storage and retrieval from the cache. You can achieve this in two ways:
- Adding the .NET Serializable attribute in your custom class.
- Using Dynamic Compact Serialization, a framework provided by NCache that offers cost-effective and dynamic serialization for registered classes.
Non-serialized data cannot be added to the cache. For detailed guidance on cache data serialization, refer to the Serialization section. Here’s how to add a serialized custom class object to the cache. Your class must be marked as serializable before proceeding with cache operations.
1 2 3 4 5 6 7 |
[Serializable] public class Customer { public int CustomerID { get; set; } public string CustomerName { get; set; } public string CustomerAge { get; set; } } |
1 2 3 4 5 6 7 8 9 10 11 12 |
// Get customer from database string customerKey = $"Customer:ALFKI"; Customer customer = FetchCustomerFromDB(customerKey); // Get customer from database if not found in cache if (customer == null) { // Get customer from database customer = FetchCustomerFromDB("ALFKI"); cache.Add(customerKey, customer); } // Item added in cache successfully |
Data Structures
NCache being distributed in nature supports data structures that allow the addition, removal, and retrieval of data without affecting the data consistency. Following are the data structures supported by NCache:
- Distributed List: Native .NET implementation of the IList interface. The code below demonstrates creating a list of Product types in the cache using CreateList with the key ProductList and adding products to the list with the Add method
1 2 3 4 5 |
// Create list of Product type IDistributedList<Product> list = cache.DataTypeManager.CreateList<Product>("ProductList"); // Add product to the List list.Add(new Product)(){ Name = "Tofu", Category = "Food", UnitPrice = 0.15}); |
- Distributed Queue: The first-in-first-out (FIFO) implementation for items is known as Distributed Queue. The code below demonstrates creating a queue of Candidate type in the cache with the key CandidateQueue and adding items to the queue.
1 2 3 4 5 |
// Create Queue of Candidate type IDistributedQueue<Candidate> queue = cache.DataTypeManager.CreateQueue<Candidate>("CandidateQueue"); // Add candidates to queue queue.Enqueue(new Candidate(){ Name = "John Doe", Position = "Software Engineer" }); |
- Distributed Hashset: An unordered datatype implementation where the values of a set are unique and distinctive. The code below creates a HashSet of int type in the cache using the CreateHashSet method for users logging in on Monday and then adding data to this HashSet.
1 2 3 4 5 6 |
// Create HashSets of int type IDistributedHashSet<int> userSetMonday = cache.DataTypeManager.CreateHashSet<int>("MondayUsers"); // Add user IDs for Monday userSetMonday.Add(1223); userSetMonday.Add(34564); |
- Distributed Dictionary: A key-value pair that is a native .NET implementation of the IDictionary interface. The following code sample demonstrates creating a dictionary in the cache with the key ProductDictionary and adding data to it.
1 2 3 4 5 |
// Create dictionary of Product type IDistributedDictionary<string, Product> dictionary = cache.DataTypeManager.CreateDictionary<string, Product>("ProductDictionary"); // Add products dictionary.Add(new Product(){ Name = "Laptop", Category = "Electronics", UnitPrice = 999.99 }); |
- Distributed Counter: A data structure used to increment or decrement the value easily. The following code sample shows how to create a counter in the cache using the CreateCounter against the cache key SubscriptionCounter. It then further increments the value using the Increment method.
1 2 3 4 5 6 7 8 |
// Set initial value of counter long initialValue = 15; // Create counter ICounter counter = cache.DataTypeManager.CreateCounter("SubscriptionCounter", initialValue); // Increment value long newValue = retrievedCounter.Increment(); |
You can use all these data structures as per your business logic. For more information on these data structures, refer to the NCache documentation.
JSON Data in NCache
JSON is a lightweight readable language used by applications for being extremely fast and compatible with all major JavaScript frameworks. NCache lets you add your JSON data easily in the cache-store by providing a few classes derived from a base class JsonValueBase as discussed below:
- JsonObject: This class is for unordered name-value pairs where name is the name of the object and value is the value of the JSON object that can be of any primitive type.
- JsonValue: This class represents the primitive data types in JSON’s conventions such as string, integer, or DateTime.
- JsonArray: This class represents a collection of items and represents JArray in NCache’s domain.
- JsonNull: This class represents a NULL value in JSON
The following example shows how to retrieve a JsonObject as a custom object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// Create a new JsonObject with the following attributes in the string string jsonString = $@"{{ ""CustomerID"": ""1"", ""ContactName"": ""David Johnes"", ""CompanyName"": ""Lonesome Pine Restaurant"", ""Phone"": ""12345 - 6789"", ""Address"": ""Silicon Valley, Santa Clara, California"" }}"; // Retrieve the JsonObject from the string JsonObject customer = new JsonObject(jsonString); // Create a unique key for this object string key = "CustomerID:1001"; // Insert JsonObject in the cache cache.Insert(key, customer); // Get the JsonObject as a custom object object retrievedItem = cache.Get<Customer>(key); if (retrievedItem != null) { // Perform operations according to business logic } else { // No such item exists } |
Location Affinity
An added feature of NCache that enhances performance is Location Affinity. This feature allows you to keep data from different classes on the same node, creating an affinity between items and reducing the cost of fetching them. To achieve this, you can group items with similar affinities using braces {}, ensuring they reside on the same node. For instance, Order_{Customer:1001}, shows affinity between the orders belonging to the customer object with the customer key 1001.
Here’s a code example demonstrating how to use Location Affinity to add Customer and Order on the same node:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Customer customer = FetchCustomerFromDB(1001); string customerKey = "Customer:1001"; var customerCacheItem = new CacheItem(customer); // Add CacheItem to cache cache.Add(customerKey, customerCacheItem); Order order = FetchOrderFromDB(1001); // Unique orderKey for this order using location affinity syntax // This will create an affinity for this orderKey with the respective customerKey string orderKey = "Order_{"+ customerKey +"}"; var orderCacheItem = new CacheItem(order); // Add order with location affinity to the cache cache.Add(orderKey, orderCacheItem); |
Conclusion
To conclude our discussion, NCache provides flexible ways to add data into the cache catering to your application’s requirements. All you need to do is use a simple API to add the data to the cache. The best part about NCache is that with every new version, along with the support for new features and enhancements, data addition becomes easier and more efficient. To explore the exciting features that NCache provides, download NCache today!