Using .NET Caching in EF Core thru Extension Methods

Recorded webinar
Ron Hussain and Nick Zulfiqar

Entity Framework (EF) Core is the new cross-platform and light-weight version of the popular Entity Framework from Microsoft. EF Core is an object-relational mapping engine for .NET that eliminates the need for most of the data-access code that developers otherwise write.

EF Core is increasingly being used in high transaction server applications (ASP.NET, WCF, and other .NET server apps). And, these applications need scalability to handle large amount of user requests without slowing down. But, database becomes a bottleneck and distributed caching must be used to eliminate this bottleneck.

Learn how to use distributed cache like NCache in your EF Core applications for removing database scalability bottlenecks.

This webinar will cover (including hands-on source examples:

  • Introduction to EF Core and its new features (Model, Query, Saving Data)
  • Database scalability bottlenecks in EF Core and how distributed cache resolves them
  • Various options of using NCache in EF Core (Direct APIs and EF Core Extension methods)
  • Details of using EF Core Extension Methods for Caching
  • How to handle caching of Collections and Relationships for reference and transactional data?
  • Some important features of a good distributed cache like NCache

EF Core is an object relational mapping engine for .NET and .NET Core applications. This allows developers to generate data access code automatically, normally would have to write that code themselves. Similar to .NET Core, EF Core is also cross-platform and I mean that you could run it on Windows, Linux, Mac and it's getting very popular with the developer community.

In this webinar we will talk about performance and scalability needs in EF Core apps and will demonstrate different strategies to cache reference and transactional data using EF Core extension methods. What are the performance and scalability challenges that you would face in a typical EF Core Application and then we'll talk about various options of using caching within Entity Framework Core Applications.

I will primarily focus on Extension Methods, EF Extension Methods that we have implemented for Entity Framework Core Applications. So, I'll go through all of the Entity Framework Core Extension Methods of NCache and then we'll talk about different strategies to handle reference data and transactional data as Nick suggested. We’ll also see how to keep data in sync when data exists in two different places when you start using a distributed cache. So, that's what we have in agenda for today's webinar. I'll quickly get started. We have a lot to cover. There are lots of hands-on examples that I want to showcase. So, let's quickly get started.

What is Entity Framework / EF Core?

So, first slide is more of an introduction towards Entity Framework and EF Core.

what-is-entity-framework-core

EF and EF Core, this is an object relational mapping engine for .NET and .NET core applications. The latest version of Entity Framework is named as EF Core. You know, we've recently seen EF Core 2.1 released along with .NET Core 2.1. You know, it simplifies your database programming. You know, it allows you to, automatically generates your data access model. So, you don't have to write that yourself. It cuts down on the development effort needed for that. You don't have to write persistence code which you would typically write yourself on an ADO.NET level and then you have to have mapping between your data model and the object model. So, it’s simply optimizes that for you.

So, it's very flexible. It's getting very popular with, within developer community and its cross platform and it's very popular in .NET and .NET Core Applications, specifically high transactional applications which are dealing with a lot of request load.

Architectural diagram of Entity Framework

This is an architectural diagram of Entity Framework.

architecture-diagram

You know, it also applies on EF Core. There are few differences between EF and EF Core. EF Core is made lightweight. It has modular approach. There are not a lot of, you don't have to go through in complete installation. You can have a modular approach. They have decomposed it into smaller packages, so, you can work with the package that you're interested in.

So, you don't have to go through in complete installation for that matter and they've also gotten rid of .edmx and those mapping files. So, with you know, the NuGet packages and there are some commands that you run and it generates the model for you and then you simply work with LINQ to Entities APIs on the application layer and overall approach remains the same.

What is Scalability?

Typically, in an EF Core Application you know, we'll talk about the requirements of performance and scalability. Scalability is an ability within an application where you can increase the amount of requests that, that application has to handle. For example, your user load grows or those users which are contributing towards requests load, that request load grows within an application and if your application slows down, that application is not scalable.

So, high performance under peak loads that's what we categorize as scalability. If your application is performing pretty fine, in a sub millisecond, latency under five users, it should have the same kind of performance, same kind of latency, low latency under, let's say, five thousand users or five hundred thousand users and a lot of request load.

So, high performance under high stress load, under peak loads, that's what we categorize as scalability. EF Core.

What are the applications which need scalability?

what-applications-need-scalability

It could be an ASP.NET web application. ASP.NET or ASP.NET Core which may be dealing with a lot of requests. It could be .NET and .NET Core web services, again on the website. Then we have IoT Server Applications. Again it could be .NET or .NET Core or any other general .NET and .NET Core Applications which may be dealing with a lot of requests or users in their own domains.

So, those are the candidates for scalability.

Where exactly is the scalability problem?

Typically, EF and EF Core, they’re deployed on a web farm. If it's a web application, they scale linearly. You don't have any scalability issues on the application tier, but they always have to talk to a back-end relational data source. It could be a SQL Server. It could be Oracle Server or it could be any other data storage that is supported by EF or EF Core. In EF Core they have a lot of data providers. There's a list of providers that you can choose from. So, you end up talking to a database.

Although your application tier scales out nicely but this data storage, the database, relational database becomes a source of contention. It is slow to start off because it's not in-memory. Although EF Core has come up with an in-memory data provider but that's an InProc, store and that's for local testing. That eases your testing but once your application is deployed in production you have to deal with a relational database which is slow and it does not scale out. It's very good for storage but it does not scale out when you actually need that scalability within your application.

So, it's a single source it can choke down under extreme loads.

The Solution

Solution is very simple that you start using a disturbed caching system like NCache, for your EF core Application then you use it in combination to a relational database.

Deployment Architecture

Here is the deployment architecture of NCache.

deployment-architecture

First of all, it's in-memory. So, it's super-fast in comparison. Then you have multiple servers which are storing, the data that let you usually fetch from back end relational data source. You can bring it into the cache and it could be your ASP.NET, or ASP.NET Core Apps, .NET or .NET, Web Services or it could be .NET or .NET Core Server Applications or any sort of .NET or .NET Core Applications or even Java Applications can take advantage of this distributed caching system. It saves your expensive trips to the relational data sources. It's in-memory. You can add more servers and you can grow the capacity of this cache cluster in a linear fashion.

So, it's not a single point of failure. It's not a performance, bottleneck. It's not a scalability bottleneck. So, that's the recommended approach to go with it and that's what we will cover in great details today, how to use .NET caching and that's going to be NCache as an example product in an EF Core Application through Extension Methods.

3 Commonly Used Distributed Caching Use Cases

distributed-cache-usecases

Some common use cases of distributed cache. You can use it for data caching. You can use direct API calls. Or use Entity Framework or Extension Methods even for EF Core Applications and you save your trips to the database as much as possible.

Then on website, the website of the things, you have ASP.NET, ASP.NET Core Caching. It could be a web application or a web service. If it's a web application, then if you might want to use sessions. You might want to use ASP.NET and ASP.NET Core SignalR Backplane, Response Caching for a ASP.NET Core. Similarly, IDistributedCache interface is available, view state and output caching. These are all no code options for your applications.

And, then you have powerful Pub/Sub Messaging. You can have a Topic that you subscribe to and then you share messages between different applications. Publishers and subscribers can send messages to one another.

So, that's a general idea. We'll focus more on data caching today within an EF Core Application and I'll walk you through simple, steps to get started and then we'll actually expand more, sharing more and more details on different scenarios.

So, let's quickly get started with this.

APP Data Caching: EF Core Entities

First thing that I would like to cover today is Application Data Caching.

what-ef-core-entities-to-cache

You have a EF Core Entities. So, those Entities need to be cached. So, you may see single entity. For example, you have a count, sum. That's a value or you could, you're doing some sorting and you get the first customer out of it or the criteria is such that it's returning a single customer, single product, single order. So, that's a single entity, how to cache it? Similarly, we have query results it could be entity collection. So, it's not a single entity. It’s a collection of entities. There are two options. You can cache entire collection as one item in the cache or you can cache each collection item, each entity within that collection separately in the distributed cache.

EF Core Entity Caching Options

So, let's go through the approaches around this.

efcore-entity-caching-options

As discussed earlier, you can use direct NCache APIs. NCache API is a key value store, you add data to NCache yourself and then you receive from it yourself or you can use EF Core Extension Methods and take charge of this, single entity and entity collection.

So, let me first show you the direct NCache API approach which is common for all kinds of applications. You can use it in EF Core. You can use it in regular EF or any other .NET applications and then I'll talk about EF Core Extension Methods which are specifically designed for your EF core applications and this makes your life a lot easier where it gets a lot of things done automatically for you and you just need to call this extension method along with your queries. So, I'll show you the, both of these approaches one by one and then we'll focus more on the extension methods going forward.

Caching EF Core Single Entity: NCache Direct APIs

So, our first example is caching EF Core single entity.

Customers GetCustomer (string CustomerId)
{
	string key = "Customer:CustomerId:" + CustomerId;
	Customers customer = (Customers)_cache.Get(key);
	
	if (customer != null)
	{
		return customer;
	}
	else
	{
			customer = (from cust in database.Customers
					where cust.CustomerId == CustomerId
					select cust).FirstOrDefault();
                    _cache.Insert(key, customer);
	
		return customer;
}
}

We have a GetCustomer, method right here. I'll show you this with the help of, sample application that I have formulated for this. So, this is our first method. I hope you can see my screen. So, we have Get Customer. It's taking our customer ID and idea here is that this is returning a single entity, right. So, typically with direct API, the challenge is that you have to, take into account or something that you have to implement yourself.

First of all, you need to have a key and that key is something that you identify an entity inside NCache and you need to maintain all the keys in the same format. You can come up with a sample format, as shown on the screen. There is a customer key word and then the customer ID and a runtime parameter of customer ID which makes the cache key.

In NCache everything is stored in a key value pair. Key is a string key and value is .NET permitted object and it could be any EF Core entity as well, but you formulate the key and then you use the entity as an object to be stored in NCache. Now you first check whether that key already exists in the cache or not by calling cache.Get and if you do get that customer directly from the cache and this is a direct NCache API cache.Get, you pass on the key and if you get that customer you return the code from here, if it's not null. But, if it is null, for example, you're executing that query for the very first time, right, nothing is in the cache as yet. So, in that case you would execute against, Entity Framework Core, the LINQ API against the database and then you fetch that customer from the database and also call cache.Insert, store it with the same key that you formulated and then customer is the entity that you want to add and then you also return it.

So, that's how you would actually deal with a single entity using the right API.

Caching EF Core Entity Collection: NCache Direct APIs

Then the collection. Collection could be either a collection as a single object or again as a single entity.

List<Customers> GetCustomersByCity (string CustomerCity)
{
	string key = "Customers:City = " + CustomerCity;
	List<Customers> custList;
    custList = (List<Customers>)_cache.Get(key);

	if (custList != null)
	{
		return custList;
	}
	else
	{
		custList = (from cust in database.Customers
					where cust.City == CustomerCity
					select cust).ToList();

		_cache.Insert(key, custList);
		return custList;
	}
} 

Each collection entry could be a single entity added separately in the cache. So, for that we have get customers by city, so, that's the next method. It's returning a list of customers. Again, it's passing the customer city as a criterion. Again, you formulate a key. Again, this is you know, this is representing a collection of customers based on the city and then this city could be a runtime parameter here. New York, Paris, London and then you fetch first by calling cache.Get from the cache yourself and then you check if it exists in the cache, if it does you return from here, if it doesn't you simply store it as a single collection.

So, this list of customers is stored as one object. Other alternative could be that you iterate through the list of individual, customers in customer entities and call cache.Insert individually as shown right here. Right? So, single entities and entity collections using direct NCache API as are going to be cached using these approaches. Now we've covered direct APIs. This is something that you can use in any application.

Caching EF Core Single Entity: EF Core Extension Methods

Let's focus on Entity Framework Core Extension Methods. So, same to, a single entity and entity collection using the NCache extension methods. So, I'm going to introduce FromCache.

Customers GetCustomer (string CustomerId)
{
	CachingOptions options = new CachingOptions
	{
		StoreAs = StoreAs.SeperateEntities
	};
	
	Customers customer  = (from cust in database.Customers
							where cust.CustomerId == CustomerId
							select cust).FromCache(out string cacheKey,
                            options).FirstOrDefault();
	return customer;
}

That's our first extension method and by the way for this extension method to be used, first of all you need to introduce NCache NuGet package Entity Framework Core NuGet package inside your application. So, that's a must. If you go to the installed, this is a Alachisoft.NCache.EFCore NuGet package. That's what you need to introduce inside your application, that's one and then after that all you have to do is, let me, just open the context. So, inside the context you simply call NCacheConfiguration.Configure and it's taking a cacheID which I'm reading from app settings and then it's also taking the type of, if you're using dependencies, that's an advanced feature within Entity Framework Core extension methods. You need to provide the type of the data source.

But, once you've called NCache Configure method, there are various things that this method request. You can also set up a logger but that's it. That allows you to start calling NCache, EF Core Extension Methods inside your application.

Caching EF Core Entity Collection: EF Core Extension Methods

So, same you know, method but this time get customer through extension methods.

List<Customers> GetCustomersByCity (string CustomerCity)
{
	List<Customers> custList; 
	CachingOptions options = new CachingOptions
	{	
		StoreAs = StoreAs.Collection
	};
	custList = (from cust in database.Customers
				where cust.City == CustomerCity
				select cust).FromCache(out string cacheKey,
                options).ToList();
	return custList;	
}

It's going to make everything a lot simpler. You don't have to check data in the cache yourself and you don't have to call insert explicitly yourself and most importantly you don't have to call, you don't have to construct the cache key yourself. This is completely automated as part of the implementation of this ‘FromCache’.

Now, from a behavioral standpoint this is exactly what we've shown earlier. FromCache, first of all takes a caching option, the options and ‘cacheKey’ is an out reference. So, it constructs the cache key automatically for you behind the scenes. You just need to call from cache alongside your query as an extension method and you simply pass on the options of storing that if, it always returns a collection, right. So, in that case you can either choose it to be separate entity or a separate collection, right. So, it could be a collection of entities or it could be separate entities out of that collection.

So, FromCache works in such a way that if an entity or if a query that you executed and those resulting entities are already in the cache, it would get it from NCache automatically. But, if they're not in the cache in that case it would automatic execute that query against database and the result set is returned and at the same time it's cached as well and we also attached the database context for subsequent calls.

For example, if you call FromCache for first time, it caches it. Next time same query executes, obviously it will execute against the cache but not the database. This time your context, if that was disposed previously and for this request you were using the using construct for that matter, if you make any changes in the entities that also attach those changes to the context. And when you call Save Changes, all the changes that were done on those entities which were fetched from the cache are actually applied on the database. So, that's slightly, again a slightly advanced concept. I'll spend some more time, at a later stage, but just to let you know, the from cache method works in such a way that if something is not in the cache, it would fetch it from the database and cache it. But, if something is available in the cache already, it would just return it from there. So, this automates the whole, direct API approach for you but it also constructs the keys. It also, organize the data in a separate entity or a separate collection automatically and you just have to call from cache and pass on the out key and options.

Same case for collections. For a collection, get customer by city. It would store as a collection and then again you can call from cache. So, that's how simple it is to get started with Entity Framework Core extension methods.

What Data to Cache in EF Core?

Our next topic is around, our next topic is again around the usage of, the type of data in the cache.

what-data-to-cache-in-efcore

You could either have reference data or transactional data. Is there a question?

Yeah, Ron I have a question here. Extension Methods of NCache work with EF Core, you recommend .NET Core 2.0 Framework or a .NET Framework?

Alright. So, since EF Core itself can run on .NET as well as in .NET Core, so, it's very flexible. NCache extension methods would work absolutely fine. As a matter of fact I'm running this sample application on .NET framework 4.6.1 and I have another sample application which is working on I believe .NET core 2.0. So, this is .NET Core 2.0. So, it works with both .NET frameworks and basically it is preferred that you use .NET Core, but it's flexible. It will work for both. I hope that answers your question.

Moving on, next segment within the presentation is when you start planning on caching right and obviously entity framework core extension methods make your, life a lot easier in terms of caching. So, it actually gets a lot of things done automatically. So, next question is what data to cache, right?

So, I've categorized data into two categories. We have reference data. That's a lookup data and then we have our transactional data, which, is dynamically created and changes very frequently. So, reference data, a quick example would be products, employees. It's not a 100% static data but it's something which does not change that frequently. But, it does change, frequency of change is not that great. And for this you should plan on caching entire reference data. All of the reference data should be cached as a must and then transactional data is a dynamically created data. Such as orders, accounts, some workflow processing on some specific collection of, data and the scope of that collection is only for that workflow. Once that workflow completes execution, that data is not needed anymore and this data can change very frequently as well. So much so, it can change within five minutes. Whereas reference data is usually a lookup data that may change between hours. 1 to 2 hours, 5 to 10 hours or even in days.

We have a question here. How would you identify this cached object when retrieving using EF Extensions just as keys created automatically? Actually, not retrieving but deleting a particular key?

Alright. So, we do have an extension method for retrieval., The extension method executes along with EF Core. So, the return object would be identified on the same lines as your LINQ query would identify it. The question I think is more focused on what happens when you plan on updating the records and for that we have a set of APIs. We give you method which allows you to generate a key against an entity. There is a key generation method which we made public. So, before making a change in the cache directly and I'm assuming that this question is more focused on deleting something in the cache or updating something in the cache or removing or adding something in the cache explicitly. We've exposed the cache class which I'll show you in a bit and then there is a key generation. Again, you pass on a string and, use the out reference and you pass on the entity as well. So, against that entity we use the same approach and we will give you a key and using that key you can identify the object that entity, if it exists in the cache you should be able to update it and remove it.

I think towards the end I'll show you an example of how to achieve that but just to let you know, there are methods which we were exposed to, get the key out of NCache as well. I hope that answer your question.

Caching Reference Data in EF Core

Moving on. So, we've talked about lookup data and then dynamically created data.

caching-reference-data-in-ef-core

So, I'll go through one by one, different approaches to handle this. So, first of all we'll talk about reference data in EF Core. As I mentioned, this is usually a lookup data. So, there are two things that you should do as a must. You should first of all load entire data, entire reference data into the cache as a must and motivation here is that this data is something which is not changing that frequently. It's a product catalog in an EF, in an e-commerce application. There are some discounts on certain products while that discount is, enabled, let's say for three days. Those products may not change, so, it's better to cache all those products into NCache and save your expensive database trips to do that extent as much as possible. 100%, data should be loaded in the cache. So, that you don't have to go back to the data source and then always cache them as separate entities. Why? Because products, the entire data set could be thousands and thousands of products.

Let's say, Northwind database. Products could range something from 50,000 to 100,000 products. But, you may only need, let's say, 10 out of those or 100 out of those for a given request. So, if you store them as separate entities, you're able to run LINQ queries. NCache also supports LINQ on NCache directly. So, you're able to run LINQ on getting the subset of the data querying the subset of the entities out of NCache as well.

So, these are two rules that you should follow as a must and I have an example and here I'm introducing our second, extension method that's called Load Into the Cache.

Caching Reference Data in EF Core: Pre-Load Cache

Now, this works in such a way and this is targeted for reference data only. This is a pre-loader into the cache.

void LoadAllProducts (NorthwindContext database)
{
	CachingOptions options = new CachingOptions
	{
		StoreAs = StoreAs.SeperateEntities,
	};	
	
	// Loads all products into cache as individual entities
	var res = (from products in database.Products select
    products).LoadIntoCache(options).ToList();
}

So, the motivation here is that you should call this method, let's say Load All Products and you should make sure that you're storing it as a separate entities and you should run a query which says from products in the database products and select products. So, you're not specifying any criteria that means this would load all 60,000 products into the cache and then you're using LoadIntoCache, right. When you say separate entities you just need to pass on this option and we'll just create a separate entity for each product in the cache and this is again a pre-loader, right. You should preload this..

There are two options. You can run a separate application for this which can take responsibility of pre-loading the cache. This could be application startup or a separate application or it could be a cache loader implementation of NCache as well. There is a cache loader and in future releases were also working on cache refresher.

The responsibility of cache loader is that it actually runs at your cache startup and then it makes sure that all the data that you want to load into the cache is loaded as part of that. It's your implementation but NCache simply calls it. But, like I mentioned, you can come up with the method like this and make sure that you call it once at your application startup or have a separate application do this and share this data across all your application instances.

Now once this is done, how to query data out of the cache and at that point. I'm going to introduce my third extension method which is From Cache Only.

Caching Reference Data in EF Core: Search Reference Data from Cache Only
List<Products> FindDiscontinuedProducts (NorthwindContext database)
{
	//Fetch discontinued products only out of all products 
	List<Products> discontinuedProducts;
	
	discontinuedProducts = (from product in database.Products 
   	 where product.Discontinued == true
   	 select product).FromCacheOnly().ToList();
	
	return discontinuedProducts;

}

So, first extension method was FromCache which was, if something is in the cache,get it from there. If it's not, automatically go to the database and fetch it. LoadIntoCache always execute against database. It's never executed against cache although it's returning, but this is returning from the database as a must so that data set is refreshed. A new data is loaded and then it's also caching it, and then FromCacheOnly is only executing against NCache because this is designed only for the reference data. So, for reference data you would use a combination of LoadIntoCache. So that data all the data set exists in the cache as separate entities and then you would call FromCacheOnly so that only data set that you're interested in that's, discontinued products in this case has brought only from the cache. So, you don't have to go to the database at all. I'll talk about how to keep this data fresh when data changes in the database. So, I hope this makes sense. Let me show you this example right here.

So, I'm calling this method right here and then I'm returning a list of products, discontinued products by using a simple EF, the LINQ API directly on NCache entities.

So, this would not execute against database at all. This would execute only against cache because I'm using FromCacheOnly, extension method. I hope this helps. We've covered three extension methods, FromCache which is more of transactional data use case and then we covered LoadIntoCache and FromCacheOnly. For reference data you should focus on using entire data using LoadIntoCache and then separate entities and then for queries you should use FromCacheOnly.

Caching Transactional Data in EF Core

Next, we'll talk about transactional data and then we'll talk about how to keep this data fresh for both reference as well as transactional.

caching-transactional-data-in-efcore

Now, transactional data is usually a working set. I've already covered that it's a dynamically created data. It's only needed for a current user, current request or a workflow is executing and once that is executed you may not need that data anymore. So, chances are that only the instance of the application which actually retrieve that data that those entities are needed only by that instance of application. So, it doesn't make sense to store all your transactional data in the cache. It's not a requirement. You should only cache the working set. So, that's step number one. So, you should cache query results. You can fetch most readily access entities and second step is there are two options 2a, 2b.

Fetch and Cache as Collection

You can cache your transactional data as an entire collection. That's a preferred approach.

List<Orders> GetCustomerOrders (string CustomerID)
{
	CachingOptions options = new CachingOptions	
	{
		StoreAs = StoreAs = StoreAs.Collection,
	};

	//Fetch from cache. If not found then fetch from DB.
	orderList = (from customerOrder in database.Orders 
				where customerOrder.Customer.CustomerId==CustomerID 
				select customerOrder)
				.FromCache(out string cacheKey, options).ToList();
	
	return orderList;
}

Usually you need entire collection always, because you're working on it and then you can also store, separate entities as well. It's entirely up to you and this is for scenario where although you cached a collection but you still store it as separate entities because you might need only two or three entities out of that collection. So, it's better to have a granular approach when storing the collections and we have GetCustomerOrders. Orders are pretty transactional Data. There must be a processing unit which is dealing on certain orders and chances are that each workflow which is doing that has its own set of orders.

So, I'll show you this example real quick. So, it's working in such a way that it's putting a lot of caching options. Let's not actually go into details here. Store as collection. That is how you choose to store it as a collection and then I'm using FromCacheOnly. The reason for transactional data, I'm using FromCache because data can exist in the cache and if it does the entire collection I just need to use it. I don't have to go to the Database. But, if it doesn't exist in the cache and this data changes very frequently as well, we've also set up an expiration on it, let's say, 30 seconds, so you might want to go back to the database. If you use FromCacheOnly you can return a null value if it doesn't exist in the cache, if it has already expired.

So, for transactional data use extension method From Cache, for reference data use LoadIntoCache to preload and then use FromCacheOnly. So, that's the approach to go about it.

Fetch and Cache as Separate Entities

For separate entities, all you have to do is store, call this.

List<Orders> GetCustomerOrders (string CustomerID)
{
	CachingOptions options = new CachingOptions	
	{
		StoreAs = StoreAs.SeperateEntities
	};

	//Fetch from cache. If not found then fetch from DB.
	orderList = (from customerOrder in database.Orders 
				where customerOrder.Customer.CustomerId==CustomerID 
				select customerOrder)
				.FromCache(out string cacheKey, options).ToList();
	return orderList;
}

Let me just use this and instead of using a separate entity just say, (I have to comment should have commented) Right, store as Separate Entities. That’s all what you need to do.

So, Ron while you're going over this, there is a question. In the previous example, querying reference data, is there any indexing? Because, only that's what you would on the database to make it….

Absolutely, that's a very good question and for reference data, since it’s executing against cache-only, this LINQ query require that your product and the attribute are along which you're running the LINQ query is indexed and as a matter of fact I am using index data and it's very simple to index, you just need to provide the type, the model and then provide the attributes that you need to index. So, these are index attributes and we are indeed running queries against index attributes. I hope that answers your question.

Keeping Cache Fresh

I'm going to move on to our hands-on portion real quick, show you the actual product in action and then the most important segment of this webinar how to keep cache data fresh in comparison to database and also in regards to reference data and transactional data.

Alright, so I'm going to open a new project. Alright, so, the hands-on demo portion I will quickly show you the actual product in action so that you know, how a distributed caching looks like and then we'll run the application against it and I'll show you how to keep it fresh, the data and the cache as fresh.

I've already installed NCache on two of my boxes 107 and 108. I'm going to use these as my cache servers and my personal machine right here, my laptop is going to be acting as a client.

So, first of all I will launch the NCache manager tool which comes installed with NCache and I will create a cache, let's say, let's name it EF Core cache. I'll use two servers. Keep partition replicas as our caching topology and then asynchronous replication option because it's faster and here I specified the servers which are going to host my cache. So, I have two servers, demo 1 and demo 2 and TCP/IP port for communication. It's everything driven by TCP/IP as far as communication between servers and clients are concerned.

Size of the cache, based on the data that you plan on caching. If it's reference data you should take into account that you're loading all the products, all the customers. Similarly, if it's transactional data, you need to account for working set and some more room for that as well. Keep everything simple, choose finish and that's it. That's how simple it is to get started with NCache and configure a cache.

Since, I'm running applications on my machine so, I just need to add my box as a client machine. I can start and test this cache cluster and after that I'm good to go run the sample application to connect to it. So that's how simple it is. We have a regular NCache architecture and scaling .NET apps webinar in which we talk about these configurations in great details. So, if there are any questions you might want to look into those webinars as well.

I'll show you some monitoring aspects as well. Statistics window and then I'll open NCache monitoring tool so that I can actually run the stress testing tool application which comes installed with NCache and see things in action. So, this is a stress testing tool. I'll run it against this because my box is a client so, I can simply provide the name and it should connect to this and I should be able to simulate some dummy load on my cache cluster, there you go. The cache size is growing, items are being added and requests per second load is also showing activity on both servers and then this monitoring tool, I think it is taking time, it's creating dashboard.

stress-test-tool

So, let it roll and in the meantime let me run another instance of this tool from the server itself. My bad. Alright, so, you can see the increased amount of requests per second growing. For some reason is taking some time to load the dashboard, the monitoring tool. I think I will leave it right here as soon as it comes back I'll show you the monitoring details of this tool.

Keep Cache Fresh: Reference Data

Alright, our next segment within this webinar is keeping cache data fresh within an Entity Framework Core application and for this I'll again use reference data and transactional data example. So, we'll start off with reference data.

keep-cache-fresh-reference-data

We've already established that in order to cache reference data you should cache entire data set in the cache. For example, you have products, you should load all products in the cache and then load them as separate entities. Then what is the issue with this?

Since, reference data, the entire reference data in the cache and then you only use cache using FromCacheOnly extension method. Now, you don't want to afford a partial data set out of the cache. If it's discontinued products or you're running some criteria, you want all those products in the cache. But, there are chances that those products actually change in the database. Now data exist in two different places. You have database which is your master copy and although you loaded entire data in the cache, there are still chances where database gets updated outside the scope of your applications.

So, in order to keep cache data fresh, you need to ensure that you have some kind of mechanism which updates the data and the cache automatically and for that we have two options. Option number one is that you use expiration.

void LoadAllProducts (NorthwindContext database)
{
	CachingOptions options = new CachingOptions
	{
		StoreAs = StoreAs.SeperateEntities,
	};
	
	options.SetAbsoluteExpiration(DateTime.Now.AddHours(10)); 	
    options.SetResyncProviderName("MyEFCoreResyncProvider");
	
	// Load all products into cache with Expiration and Auto-Reload
	var res = (from products in database.Products select
    products).LoadIntoCache(options).ToList();

}

This is a time-based expiration and then you mix it up, you use it in addition with auto-reload feature. NCache expiration feature allows you to expire data, remove data after the specified period of time, let's say, 5 hours, 10 hours and with reference data since it's a long, running data it's a lookup data, master data, so frequency of change is not that great, so you can come up with a comfortable number of you know, hours or you know, it could be 5 hours, 10 hours, 24 hours. So, based on that you can set up an expiration time. But, as mentioned earlier on, you don't really want to lose or remove that data out of the cache.

What you really need is that the data which gets changed, let's say, 10 products are expired based on the expiry of that you specified, it should automatically reload that data into the cache with auto-reload mechanism. And NCache provides you that with the help of a read-through handler. So, all you have to do is again load entire data and the cache using load into the cache, use separate entities but alongside that use these to, characteristics. Such as, Set Absolute Expiration.

That's the expiration value that you would set to all the products which are, going to be loaded in the cache and then you Set Resync Provider, which essentially calls your read-through provider within NCache. Read-through is combined with expiry. So, instead of removing data out of the cache it actually reloads, since it knows all the keys and all the, database related information we just need you to pass the context and based on that context we actually call your provider automatic and resync. We get the updated value from the data source.

Let me show you a working example of it. Here is a method. I'm storing this as separate entities. Typically, I would set an expiration of 5 hours but since we don't have that much time, so, what I would really do is I will set about 30 second expiry and I will put a breakpoint right here and I will load all the products into the cache. I would not set up a resync provider at this point just to show you how expirations works and as soon as the expiration comes into play it will remove the items from the cache and then I'll show you how to, take care of this scenario in such a way that instead of removing, just reload using the read-through handler.

So, I'll use the local cache for this sample bit. I'll just clear the contents just to be on the safe side and then I'll run this piece of code and hit the breakpoint. Right. So, it has 30 seconds of expiry and I will just hit and I got the discontinued products, right. So, we have all the products here and we have about thousand entries here. After 30 seconds these thousand items are going to be expired based on this absolute expiration, flag that we've set up and I'll run this query again and you would see that it would not return anything because nothing exists in the cache. It's using From Cache Only. So, we have items expired. I'll run this one more time and you would notice that I got 0 value.

So, this is not what is recommended or what is, preferred, considering this is your reference data, entire data is already loaded in the cache and you're calling From Cache Only extension method. So, you need all the data available as a must, not the partial response or missing data and at the same time this should be reloaded after your expiry. Instead of removing it should reload and that's exactly what we will do with the help of this Set Resync Provider name.

I have a sample resync provider, which is right here, IReadThruProvider. What it really does is, we have given you a default implementation of this.

namespace Alachisoft.NCache.EFSampleResyncProvider
{
    public abstract class EFDefaultResyncProvider : IReadThruProvider
    {
        public virtual void Init(IDictionary parameters, string cacheId)
        {
            db = InitializedDbContext();
        }
        public virtual void LoadFromSource(string key, out 					
        ProviderCacheItem cacheItem)
        {
            cacheItem = new ProviderCacheItem(FetchItemFromDb(key));
            cacheItem.AbsoluteExpiration = DateTime.Now.AddHours(10);
            cacheItem.ResyncItemOnExpiration = true;
        }
        public virtual void Dispose()
        {
            db.Dispose();
        }
    }
}

This is our IReadThruProvider. All you have to do is, let's show this with the help of the cache that we've configured. You have to set up the read-through handler. For example, let me just create a new cache, let me just create a local cache. Right. So, it's just a local cache. I’ll just set up a backing source. Enable read through, Add and let's say EF default and this needs to be set up and I can use this as a project and the implementation is already done.

It works in such a way that I've implemented the IReadThru handler. This is the default resync provider, right. It has some methods. It has initialization where you initialize the DB context. LoadFromSource which is called on expiry and inside this I'm getting the item from the database. If I go inside the definition, it actually takes care of all the database related calls and this is something that we're providing as part of our implementation. Our NuGet package covers it as part of it. So, we're getting the key and based on that key you pass on the context and the context is used to obtain the actual object directly from the database.

All you have to do, is you know, inherit, implement this EF Default Resync Provider and give your own implementation and give us the database context.

So, this is an abstract class that we have given you. You just need to give your Northwind Database context or any database context that you have and based on that once this method is overridden / implemented you simply register this with your cache. For example, this is a resync provider. If I open this, it contains two of those. One is default and other one is your implementation and you can pass on your connection string and other information if needed and that's it. You simply deploy this. Let me just take it to this bit and it would just give you a success on this. So, that has to be done. Like I mentioned that this is already done as part of NCache, implementation. It has, the NuGet package comes with a resync provider which you can simply implement by implementing this particular method here and giving us the context and rest of the job is done by this sample default resync provider, which actually works on the same lines as extension methods work.

We construct your key, so we know which key represents which entity and based on that entity and that key information we actually query the database using the Entity Framework, APIs. Now this has already been done for my cache. What I've done here is I have, it has successfully deployed. So, for my cache I've already done this. I have this EF default and I've deployed it. So, I'll run the same use case but this time I instead of simply expiring it, I would actually reload it. So, that's what I'm going to do. All I have to do is set up their resync provider name and then since I'm planning on updating some values as well, so, I'm going to use 60 seconds. Typically, for reference data, you would have a lot more time specified as your absolute expiration such as 5 hours or even more. So, I'll just run it with 60 seconds and this time I see my resync provider is set up.

So, I'll run this use case one more time and show you how this gets updated instead of expiry, instead of removing. So, this has run and hopefully it will come back to this point. Alright. So, we have products loaded. I will run this one more time and, in this time, since it has a 60 second of expiry I'll update this particular product, product ID 17 and what we'll do is we'll just say update it, alright.

Although it's updated in the database, it would not be updated in the application context as yet, because it's only using cache data and it's not expired as yet. So, we'll keep getting the older values and if you notice on my cache we have thousand items and there's a Readthru/sec, this would show some activity after, 60 seconds then it would automatically call the read-through handler that we have just set up.

So, let me just run it one more time and I'm expecting order values again, if not reloaded already but there would be a point where it would just reload and after that it would simply instead of expiring it would just reload the 999 of the products. There's one item which is extra, that’s usually a reference. There you go. So, instead of expiry, only one item got expired because that's a list of keys, otherwise, all the reference data based on my query, let me show you the query, it says where product ID is less than 1000. So, that actually represents 1000 products.

So, I'll run this query one more time and this time since the data has been already updated on expiry, instead of removal. So, I'm expecting the updated product to be added in the cache. So, there you go. So, my product has been updated as part of this. So, this is what we recommend as part of our option number one said you use expiration but with Auto-Reload feature. So, instead of, expiry you simply reload data and this would take care of keeping cache data fresh. So, you come up with a comfortable time that allows you to keep data in the cache you keep getting older data and as soon as that older data, is about to be expired that could be a day worth of time, let's say, 24 hours, it could be 2 days, a week, whichever is comfortable time within your application. Could be some hours as well and after that it would just reload that data in the cache.

So, that's our option number one. I hope it was pretty straightforward.

Option number two is that you don't use an expiration. So, that would just take care of this problem right here where data would always exist in the cache. But data can be stale in the cache and it could be stale forever. For that, you should manually reload and that's very simple as well.

void LoadAllProducts (NorthwindContext database)
{
	CachingOptions options = new CachingOptions
	{
		StoreAs = StoreAs.SeperateEntities,
	};
		
	var res = (from products in database.Products select
    products).LoadIntoCache(options).ToList();

}

All you have to do is, you simply need to call this Load All Products after a periodic interval. For example, if we come back to this bit right here. So, this method needs to be called after some intervals and again as mentioned earlier this is usually done at your application startup and it could be one of the applications which is taking charge of it, which is taking care of all this. So, it could be either that or it could be one of the applications, a separate application which is doing that. So, it could be your application, one of your application instances or it could be a separate application altogether and that application can periodically call this method after, let's say, 5 hours, 24 hours, again which is your comfortable time after which you want this to be executed against database and you want your products to be reloaded.

So, LoadIntoCache would take care of that particular scenario where would execute against database and load afresh copies into the cache. So, that is how you take charge of reference data. I hope I was pretty straightforward.

Keep Cache Fresh: Transactional Data

Let's move on to the next segment where we use transactional data. Now transactional data is something which is short-lived, right. It's a working set, as explained earlier. So, you may use 5 to 10 minutes of expiration. So, what you really need to do here, since you're using FromCache which execute against cache, if it's exists in the cache and if it doesn't exist in the cache it would always execute it against the database automatically. So, this is built into this extension method, unlike FromCacheOnly which you use with the reference data scenario, right, which only execute against the cache. So, it's okay to have some data available, whether you stored it as a collection or as a separate entity.

Short Expiration, No Auto-Reload
 private List<Orders> GetCustomerOrders (string CustomerID)
{
	CachingOptions options = new CachingOptions	
	{
		StoreAs = StoreAs = StoreAs.Collection
	};
	
	options.SetAbsoluteExpiration(DateTime.Now.AddSeconds(60));

    	List<Orders> orderList = (from customerOrder in database.Orders 					
        where customerOrder.Customer.CustomerId==CustomerID 
        select customerOrder).FromCache(out string cacheKey,
        options).ToList();

	return orderList;
 }

So, you simply use short expiration, let's say 60 seconds although I used that example for reference data but that was for the sake of this particular demo, but for reference data you usually have 5 to 10 hours of expiration and for transactional data you have somewhere between 60 seconds or related, expiry attached to it and since you're using From Cache, let's actually have a look at the Get Customer Orders, since you're using FromCache that should allow you to use the, let's actually take this as example.

Right, so, you set up some expiration here and you call this FromCache extension method and that should take charge of getting it from the database if it doesn't exist in the cache and after some time whether you're working set is active in the cache you would get it from the cache, if it's not active in the cache chances are that it's not needed anymore, right. So, you may end up going back to the database and that's the right thing to do with transactional data. So, I hope this covers our reference data as well as transactional data scenarios and how to keep cache data fresh in regards to these two.

Handling Relationships in Cache

I'll move on to our next segment, which is Handling Relationships in Cache.

Ron, I have a couple of questions here. Does the NCache or does the cached items resides in the NCache server or they are shadowed in each application server?

They reside in NCache. Right, so the items actually exist in NCache, right. So, the actual repository is maintained within NCache and we actually construct the keys and objects alongside these entity methods. So, that's the idea here.

Another question is, can we have multiple cache loaders configured or all of them need to be configured into one, when we were adding reference data to the cache?

You can because loading to cache is a generic method, right. So, it actually loads whatever, the query that you're planning to run right. So, it's bound to the query. You can have a multiple of load into cache and there could be one separate application or it could be different places within the application. So, it's very flexible on that end as well.

Handling Relationships in Cache: One-Many

List<Region> GetRegionWithTerritories(NorthwindContext database)
{
	List<Region> regionDetails;
	CachingOptions options = new CachingOptions
	{
		StoreAs = StoreAs.SeperateEntities
	};

	regionDetails = (from region in database.Region select region)
					.Include(region => region.Territories)
					.FromCache(options).ToList();

	return regionDetails;
}

There are few important concepts that I want to highlight. How to handle relationships? One-to-many and you know, many-to-many example.

Let's focus on one-to-many. If you have let's say regions and then there are territories which are part of, regions and there is a one-to-many relationship going on between regions and territories. Let's go to the sync approach and inside, actually let's get the regions from here.

Alright, so, get regions with territories. The basic idea here is that you have Include keyword and then you use FromCache and you always store it as a separate entity. The idea here is that the parent item the region is going to be stored as a separate item in the cache. Since, region also has a one-to-many relationship with the territories and there is a collection of territories inside each region entity so, NCache would obey that. As soon as you call FromCache, it would simply get the region and all the regions are stored as separate entities and based on the scope that you specify and all the territories with the help of FromCache are going to be stored as separate collection inside each region.

So, it's one entity having a many side, the collection side as a related object. So, that's how you tackle one-to-many use case.

Caching Aggregate Operations

Then you have Caching Aggregate Operations. You know, you can result into an entity or result into a value.

caching-aggregate-operations

For example, getting a First or Default shipper, right. So, that is something, let's say, if I just do a First or Default here, this would fetch me the first shipper and then you know based on this, this is going to be stored in NCache.

And similarly, it could be count sum. It could be any aggregate operation so, it could be a value. Again this can be stored in NCache as a separate entity.

Shippers GetFirstShipperInstance (NorthwindContext database)
{
	CachingOptions options = new CachingOptions
	{ 
		StoreAs = StoreAs.Collection
	};

	Shippers shipper = database.Shippers.DeferredFirstOrDefault()
						.FromCache(out string cacheKey, options);

	return shipper;

}

And similarly, it could be count sum. It could be any aggregate operation so, it could be a value. Again, this can be stored in NCache as a separate entity.

int GetTotalShippersCount (NorthwindContext database)
{
	CachingOptions options = new CachingOptions
	{
		StoreAs = StoreAs.Collection 
	};

	int count = database.Shippers.DeferredCount()
				.FromCache(out 	string cacheKey, options);
	
	return count;

}

Distributed Caching Architecture

So, this covers our webinar as far as technical portion is concerned. Towards the end I will show you some distributed caching architecture.

High Availability

NCache as a product is very, scalable, fast. It’s something which is based on peer-to-peer architecture in protocol. There is no single point of failure. You can add or remove servers on the fly from a running cache. You don't have to stop the cache or any of the clients which are connected to it. So dynamically you can make changes to a running cache cluster.

high availability

Caching Topologies

Then our most popular topology is Partition Replica Cache. This allows you to simply distribute data on all servers in the form of partitions. So, they're pooling the memory resources, computational resources, together and that presents you a logical capacity and each serve has a backup as well. Server1 is backup on 2. Serve2 is backup on 1 and Serve3 is backup on Serve1. So, in a round robin fashion each server has a backup on another server.

caching-topologies

Client Cache (Near Cache)

Similarly, this is very fast, very scalable, most popular topology. You can also have a client cache which can run on your own application box. It's a local cache but it’s in sync with the server cache. Subset of the data would automatically be brought into the cache and without any code changes. This improves your performance if your data is mostly of reference nature. So, once you load into cache and then you start calling this subset of the data by calling FromCacheOnly client cache would really help in that case.

client-cache

WAN Replication of Cache

WAN Replication is another feature, the bridge replication. You can have active-passive or active-active data sites. All the data, the EF Core data, ASP.NET sessions or regular data can be transferred in to the target site across the WAN in an active-passive or active-active manner. So, this is what NCache covers as part of it.

wan-replication

Conclusion

Just to reiterate I'll take one more minute before I hand it over to Nick. So, we talked about single entity and storing entity collections using direct APIs. That was slightly, different in terms of extension methods and then we talked about extension methods which were very simpler in nature and you know, more flexible in nature as well. FromCache was first method which fetches something from the cache if it exists, if it doesn't, it automatically fetches it from the data source, unlike direct APIs where you have to implement it yourself. It also constructs the keys and related options that you set up here.

Then we talked about reference and transactional data. For handling reference data, you need to load entire data using LoadIntoCache and the you should call FromCacheOnly. LoadIntoCache always executes against database and then you know, FromCacheOnly, only executes against the cache and then we talked about transactional data that you can handle with the help of FromCache and you set up some expirations. Reference data, in order to keep it fresh you need to use expirations and then you use auto reload or don't use expirations and then manually reload using the LoadIntoCache. Then we talked about transactional data, that you need to come up with, some kind of expiration and use FromCache so that if it exists in the cache you get it. If it doesn't exist in the cache you always get it from the database.

So, that concludes our presentation. Please let me know if there any question.

You can always reach us at support@alachisoft.com. If you have any technical queries you can also reach us via sales@alachisoft.com. If you wanted to take a look at it, download the product, you can go to our website alachisoft.com and you can download NCache and it comes with 60 days trial version that you can use and if there are any questions, let us know, otherwise, thank you very much for attending this show today, the webinar, and we'll see you next time. Thank you very much Ron. Thank you, guys.

What to Do Next?

 

Signup for monthly email newsletter to get latest updates.

Contact Us

PHONE

+1 (214) 764-6933 (US)

+44 20 7993 8327 (UK)

© Copyright Alachisoft 2002 - . All rights reserved.