How to Scale EF Core Apps to Extreme Performance?

Recorded webinar
By Ron Hussain and Zack Khan

EF Core is increasingly being used in high transaction .NET server applications (ASP.NET, Web Services, Microservices, and other server apps). And, these applications face scalability bottlenecks from databases which they can eliminate by using distributed caching inside EF Core.

  • Intro to EF Core new features (Model, Query, Saving Data)
  • How distributed cache solves EF Core database bottlenecks
  • Various ways of using distributed caching in EF Core apps
  • Details about using EF Core Extension Methods for Caching
  • Handling caching of Collections and data relationships

Entity Framework Core or EF Core is the new cross-platform and lightweight object relational mapping engine from Microsoft for .NET that eliminates the need for most of the data access code that developers otherwise write. Similar to .NET Core, EF Core is also cross-platform, meaning you can use it on Windows, Linux, Mac and is getting very popular within the developer community.

How to scale out your application to achieve extreme performance as well as linear growth in request handling Capacity? So, you can scale out your app, Entity Framework Core application using NCache extension methods. So, we have lined up some that you can utilize. Different scenarios that I will paint and then based on those, I will introduce NCache extension methods as well as object caching features that you can use within a typical Entity Framework Core application in your environment.

What is Entity Framework / EF Core?

First, I would talk about Entity Framework and Entity Framework Core in general. I'm pretty sure everybody knows about EF and EF Core but just for the sake of completeness, to build some introductory details.

what-is-entity-framework-core

Entity Framework is an ORM that you can use for .NET and with EF Core you can also use it for .NET as well as for .NET Core. Entity Framework simplifies your database programming. You can work on conceptual object model. So, you don't have to work with data model directly and it simplifies overall data access programming. You don't need to write persistent code yourself. It could generate a lot of interaction between your object layer within your application to the data model that you have and using database access which is generated by Entity Framework Core.

It's very popular for your .NET and .NET Core applications. You can use it in any kind of application which is, it could be a web application, it could be a typical server application, .NET or .NET Core web services, IOT use case or any other server application can utilize this because of its popularity and also because if its ease of use. It's very easy to set up and you can get started with it.

Entity Framework Core is a new variant. That's the new direction Microsoft is taking it's first of all open source, it's cross-platform. So, you can run an application which is using .NET Core or you know .NET. It can run on Windows with .NET and with .NET Core it can run on Windows as well as on Linux environments and similarly Entity Framework Core follows the suit, right? So, it allows you to run on Windows as well as on Linux environments if that's a requirement and then it's very lightweight and modular in nature. Which essentially means that you don't have to go through all components within Entity Framework that you used to have. The EF Core you can work with the component that you need. You can only get NuGet Package for that and based on that you can incrementally build complexity within your application.

So, if you need to, you know, manage complex scenarios within your application for that matter. And, on top of it, it's super-fast because of .NET Core there is a specific focus or performance side as well. So, Entity Framework Core has been designed on the same guidelines where performance is one of the key factors that you would be able to achieve.

Architectural diagram of Entity Framework

So, here is an architectural diagram for Entity Framework Core.

architecture-diagram

I've found an Entity Framework diagram which still applies for, a lot of things still apply on Entity Framework Core architecture as well. This is from MSDN. With EF Core you have ADO.NET data provider. I'll be using SQL Server as a data source example and then you have Entity Client data reader which builds up command trees, data access scenario and then you have object services and on the application side you either work with Entity SQL Query or LINQ to Entities. So, again you work with object model and behind the scenes the Entity Framework Core takes care of all the other details.

There is a command that you run to generate your mapping and model at runtime, so, conceptual model, EDMX, all those mapping files, they've gotten rid of those within Entity Framework Core. So, it's very easy to set up again. Again, it's modular. So, you can introduce NuGet packages and you can build a very simple Entity Framework Core application in a matter of minutes.

What is Scalability?

Next, I will talk about scalability, that's another concept that I wanted to highlight. Why do you need caching in an application which is using Entity Framework Core? First of all, there's a concept of scalability. That is a very good feature within an application, where you are able to achieve high application performance and that too under peak loads, right? So, if you're able to have low latency and lowest latency possible and you maintain that low latency under low user load, let's say five to ten users and if you have an application architecture design in such a way that you can manage the same kind of low latency under huge amount of user requests load then that application would categorize as a very scalable application.

And, linear scalability is a further associated concept where you can add more and more servers and you can distribute your load and by distributing load on multiple servers, more servers would actually increase the request handling capacity and that increase should grow in a linear fashion. So, if you have an application which is growing its capacity as you add more servers and that doing a linear increase banner that would be categorized as a linearly scalable application.

What are the applications which need scalability?

So, typically applications which are high transaction in nature such as the ASP.NET or ASP.NET Core web apps, web services, IOT, big data applications, or any other general .NET or .NET Core applications which are using Entity Framework core, they demand high scalability, right? So, they need high transactional load of handling capacity within their architecture.

what-applications-need-scalability

Where exactly is the scalability problem?

So, where exactly is the problem? Why do you need a caching system to overcome that problem? You can always create a Web Farm and an App Farm where Entity Framework Core application can scale out linearly on itself by creating a Web Farm or an App Farm. You can put a load balancer in front. So, that's not the main point of concern. Your application tier can always scale out. That's how an Entity Framework Core & any typical .NET or .NET Core applications are designed. Where you can route requests to multiple servers and still be able to use them in in combination to one another.

Main problem is your data storage bottleneck. Typically, if it's a relational database, that’s the example we're using. Databases are usually one single source for all your data storage. They’re very good for storage. That's something that they're best at. But when it comes to handling extreme amount of request loads, for example, you have a lot of transactional and reference data load coming in from your applications, database is not designed to handle that increased amount of request load. It would work well under low user loads but when it starts to consume capacity on the CPU side, on the memory side and then there is a DBMS tunnel where all the requests are being routed. Chances are that your data storage becomes a scalability bottleneck. It would give you performance degradation, where it put queue requests which are being generated at runtime. So, there's no way you can add another database server.

First of all, it's expensive to start off and then there is no way you can have two servers being used in combination to one another. So, it would not add up to the performance value it would degrade your performance and that would impact your end user experience as well and that can in turn impact your business as well. Because, if your applications are slow in performance a lot of users would not like that sluggish or slow behavior from your application. So, applications need low latency and high throughput all the times and maintaining high throughput comes with a toll of losing performance and that's what databases tend to see where they simply slow down your applications or they tend to choke down completely in most cases as well.

The Solution

In this section we'll talk about how to tackle this. Solution is very simple. You can use distributed caching system by NCache. It's very fast because it's in-memory. It's very scalable because it's not just single source. You can have as many servers as you need to, added in the cache cluster & those servers in a cache cluster work in combination to one another. You can distribute your data and then you can distribute the request handling load and by adding more servers you achieve more scalability out of the system as well. It's not a replacement of your conventional relational databases. You use a distributed cache within your EF Core application in combination to a database. So, you either cache most readily accessed data, your reference data, or you cache some of your transactional data as well.

And, I'll talk about how to handle different caching scenarios, caching strategies that you can utilize within your application.

Deployment Architecture

Here's the deployment architecture that would further clarify a few things why it's better option in comparison to relational databases.

deployment-architecture

First of all, distributed cache sits in between your application and the database tier. And, by saying in between it's essentially your application would first use cache if it finds data here it should return if it doesn't find data here it should only then go to the backend database, right? So, in a logical placing, it sits in between your application and the database.

As far as deployment is concerned you can have your application deployed on VMs, on physical boxes, on-prem or in Cloud. NCache is supported on Microsoft Azure, as well as, on AWS in the form of a pre-configured image or any other cloud can use our installer which could be downloaded from our website and then you can get started by installation, which is very easy to set up. NCache can be on its own respective tier, separate dedicated servers for NCache or it can run on the same boxes. If you have boxes where your application is deployed, you can deploy NCache on the same tier as well. We recommend having separate boxes and you can add two to three servers to start off and then you can add more and more servers based on your request handling requirements.

If you need to handle let's say 10,000 user and their associated requests load you can run some tests and you can see how many servers you need. In general, based on recent tests, we were able to achieve two million requests per second with just 4 to 5 NCache servers, right? So, you can see how many servers you need in order to handle your application load requirements. Only prereq for NCache is either .NET or .NET Core. It runs on Windows, on Windows Nano, as well as, on Linux servers. Similarly, your applications can be on the on the Windows platform or on the Linux platform.

And, there are some powerful database synchronization features as well. Like I said earlier on, it's not a replacement of your conventional databases. It uses your database, it gets used alongside your database, right? So, some data would be cache but all of your data would always exist in your permanent store which is your relational database. And, I'll talk about how to go about caching in a bit.

3 Commonly Used Distributed Caching Use Cases

Here are some common use cases. General use cases for distributed cache and most prominent is your app data caching. We have Entity Framework Core here.

Application Data Caching

So, first use case which is our most common use case is our App data caching use case. In this, you typically cache data which belongs in the database. Motivation here is that you save expensive database trips as much as possible and benefits that you get are high performance for your data retrieval. Because databases are slow in comparison to in-memory access, so, it's in-memory, so, it's super-fast and then you have multiple servers which are hosting in and serving your request from your application. So, you get more scalability more throughput out of the system and while maintaining low latency that's given for distributed cache & in most cases it's highly available and reliable as well based on the topology that you've chosen, unlike databases which may not be replicated across other servers.

With the App data caching you can cache almost anything. You can use direct APIs. You can cache your domain objects, collections, datasets, single items, results, whichever data that you want to cache and you need that data again you should consider using our object caching and with Entity Framework Core, again you can use direct APIs or extension methods which I will show you.

ASP.NET Session Caching and SignalR Backplane

Then another use case for NCache is it's ASP.NET and ASP.NET Core specific caching. We have our session caching. Session caching in ASP.NET Core is through IDistributedCache as well as to our NCache Session Provider as well. So, that's the second option and then we have SignalR Backplane. That's another option that you can utilize. If you have a SignalR application you can use NCache as a Backplane. This is again a no code change option and then we have response caching, view state and output caching. So, these are all the features which are specific for ASP.NET and in addition to this you can also use App data caching within your web application if needed.

Pub/Sub Messaging and Continuous Queries

And then, we have powerful Pub/Sub Messaging and Continuous Query Events or I would say Pub/Sub Messaging and Events. You can use NCache as your messaging platform, where multiple applications are connected and those applications can talk to one another using our Pub/Sub Messaging. An application can publish messages to NCache, which acts as a communication channel. It has a concept of topics and then these subscriber applications can receive that message if they're connected to that topic. So, they can coordinate with one another sharing data, as well as, custom messages if needed. And we can build social media systems. You can have chat system. You can have leaderboards. You can use it in any kind of industry as needed, where you need to have different components your application coordinating with one another.

Communication between Microservices

Microservices is another use case, where you need to have serverless Microservices applications, interact with one another and they can use NCache for their communication needs and then we also have Continuous Query Events. Regular events where your data gets added, updated or removed. Instead of application sending events, NCache can send events to your applications based on data changes and this could be on all items or on selective items or based on the Continuous Query where you map a data set in NCache and NCache only sends events for that specified data set.

There is a fourth use case as well. We have Full-Text Search feature. It's very common in E-commerce applications. With NCache we've implemented Lucene .NET API. So, if you're interested in Full-Text Search feature, NCache comes fully equipped with that as well.

App Data Caching in EF Core

Let's talk about how you would use caching in a typical Entity Framework Core application.

Setting up Sample application

So, first of all, I have this application. Sample application lined up right here. It's one of the samples which come installed with NCache as well. So, if you go to C:\Program Files\NCache\, these samples are available right here.

.NET and .NET Core samples are separately placed. So, I would go ahead with .NET Core and Entry Framework, we have EF Core sample right here. So, that's the sample I'm using.

distributed-cache-usecases

I've changed it a little bit to demonstrate some of these scenarios which I'm planning to present in this webinar. Otherwise it gets the basic job done for your POC. Right, so, this is the sample. Next thing I would do is I would log on to our demo environment and I would get started by creating a cache cluster for you so that you actually use a cache cluster and take it from there right.

Create a Clustered Cache

So, for cache creation we have our web management tool up and running. I'm going to quickly create one. So, let's go ahead and add a clustered cache. Let's name it democache. I'm going to append a few integers, democache111. You can by the way keep JSON or binary format, as far as, serialization is concerned. There you go. So, you can have Binary or JSON Serialization format. I'm gonna go ahead with Binary, because that's the default option. There are four caching topologies that you can choose from and we have other webinars such as NCache architecture which are specifically targeted for, you know, explaining NCache architecture. So, I'm going to go ahead with Partitioned Replica Cache because this works very well for reads, very well for writes and it's super scalable for read and write request capacity, if you keep adding more servers and it comes equipped with replicas as well, so, there's no downtime or data loss for your application as well.

So, I would keep everything simple, default. Async replication between active partition and its backup. So, I'm going to choose that, it's faster. Size of cache cluster and here I specified servers which are going to host my cache cluster. Like I said I'm going to quickly create a cache because our main focus is Entity Framework Core. Otherwise, in our regular webinars NCache architecture or scaling Azure applications with NCache, those webinars talked about all these features in great details. So, in this particular webinar, I'm just going to go ahead and create a cache quickly. Right, so, I would specify default port TCP/IP and I would start and auto start this cache so that it starts up automatically when your server gets rebooted. So, that's pretty much it.

Simulate Stress and Monitor Cache Statistics

As far as, cache configurations are concerned, you only need to follow this wizard and that would create a cache on multiple servers. I think it started as well. I would open statistics window and then I would open NCache monitor window which is another tool which comes installed with NCache. Currently there's no activity but I can go ahead and run a stress testing tool application. It's called test stress tool and that would simulate some dummy activity, dummy load on my cache cluster. Just to verify that everything, it has been configured properly. Right, so, you can see about, you know, right, Thousand to fifteen hundred requests per second by server one as well as by server two. So, in total its handling about three thousand requests per second and you can review latency. We have very minimal average Microsecond per cache operation latency. I think it's somewhere between, somewhere around four to five Microseconds.

So, this graph would update and unit would go down. Once we, you know, add some more load. Let’s actually go ahead and do that. Right. So, I have another instance running up. Just to show you that now it should show the increased value right here, there you go. So, we have about, previously was handling about fifteen hundred quest per second by each sever. Now it's somewhere between two thousand to three thousand requests per second by each server and you can see average microsecond per cache operation is somewhere around fifty to sixty microseconds per operation. That's a huge performance gain. Considering your applications are not being maxed out or servers are not being maxed out at all. So, you can expect sub millisecond or microsecond responses from your applications when using NCache. So, our environment is configured. Everything is set up. I think we're good to go. Let me stop these tests and let's go ahead and create, review our sample application which talks about caching scenarios.

What EF Core Entities to Cache?

Let's talk about first of all, how to go about caching within Entity Framework Core. What EF Core entities to cache? You have two options, typically. You may have a Single Entity or you have a Query Result, which is an entity collection, right? Single Entity returned or it could be a collection. Single Entity is stored as is. Collection can be stored as a single item or each collection item can be added individually in the cache as well and let's talk about how to go about this.

EF Core Entity Caching Options

Direct NCache APIs. There are two approaches. First of all, you can use NCache APIs directly, right? So, that's something which you can use in Open Source, Enterprise or Professional as well. And, then we have Entity Framework Core Extension Methods which I will spend some time on.

Caching EF Core Single Entity: NCache Direct APIs

Direct APIs. Here is a detail of Direct APIs. Let me show you this from here, right?

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;
}
}

So, typically this is your get customer method. If you're using direct APIs, chances are that you may have a customer ID. So, first of all, you need to construct a cache key, right. So, that's something that you need to do as a must. Because, within NCache everything is stored in a key value pair. Key is your string key, to identify an object. An object part is the actual value, actual property that you want to add in the cache. Actual data that you want to cache for your application.

So, I have come up with this key organization, where I have customer as a keyword and then customer ID and then I provide a runtime parameter which is passed on to me, right? So, that would identify this particular customer right here, with customer ID something uniquely, right? So, that it would uniquely allow you to identify those customers and then you call cache.Get to retrieve items from the cache. Right? So, first of all if you're, you know, using caching, you need to make sure that you have the key constructed and then you first check data to be available in the cache by calling cache.Get and this cache handle is something that is returned when you initialize the cache, right?

So, I'm using this cache right here, NCache.InitializeCache. It allows you to initialize the cache and get connected to it. If you find the items in the cache, which means customer was not null, you've simply return from here. You save expensive trips to the database and that's the main motivation of using the caching system where NCache would have your data. So, you save your expensive trips through the backend database. You don't have to go to the database. But, since it's the first time starting up this application. So, in that case cache would not have the data.

So, in that case you would execute Entity Framework Core, link inside Entity Framework Core. It would return either a single item or a collection. It's a single item scenario and then you call cache.Insert to actually add this for next time usage and return the customer as well. So, next time you would always find this data in the cache as long as it's not being changed or not being synchronized with the data. So, that's our single use of Entity, Direct APIs.

Caching EF Core Entity Collection: NCache Direct APIs

In case of Collections, Direct APIs are very similar.

List GetCustomersByCity (string CustomerCity)
{
	string key = "Customers:City = " + CustomerCity;
	List custList;
	custList = (List)_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;
	}
}     

We have customers by city. Customers city is a runtime parameter. We'll construct a list of customers and try to fetch that collection list from NCache cache.Get, get a list of customers. If that's not null return from here, if it's null we need to retrieve it from the backend database and also add it in the cache for next time usage. So, that's how it is and if you want to store these customer items individually you can also iterate to this custList and individually call cache.Insert by providing unique keys for each collection item as well. So, that's another option that you can utilize.

But you see, you have to first construct a key, get the item from the cache. If it's there, do the null handling and then if it's not there, you get it from the database, execute the data logic and then also add it. So, that's something that you have to do with Direct NCache APIs. It's the most common use case for any typical database caching. For app data caching this is what you would normally do.

Caching EF Core Single Entity - EF Core Extension Methods

But there's another approach, that is through our Extension Methods and that's the main highlight. You can cache entire collection as one item or again cache each collection item separately but through extension methods.

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;
}	

Here is our first extension method that I want to show you. All right, so, it's called From Cache but it does a lot of automation for you, right. It works in such a way that first of all it allows you to construct some caching options, right? So, you first of all create caching options and one of the attributes that I'm introducing at this point is Store As. So, NCache would allow you to choose whether a collection is needed to be stored as a single item, as individual items, collection items. Let’s say, there were 10 items in the collection. So, those would be added as 10 separate items in NCache or you want to store it as a collection which is right here, right? So, that's the second option. In this case it would be stored as one item in the cache.

So, I'm using separate entities. So, if I run this code right here, I have an extension method which says From Cache and if I show you the definition of this it's coming from NCache Alachisoft .NCache.EntityFrameworkCore. That's the main namespace and if I come right here and show you the NuGet Packages. Under Installed we have Alachisoft .NCache.EFCore NuGet package. So, that's the NuGet package that you need to introduce and after that you can get started with these Extension Methods. First of all, it's an out reference cache key, so, cache key is going to be generated by NCache and given to you. So, that's the flexibility and then it just takes the options as a parameter, right. So, it does a lot of automation and behind the scenes it's doing a lot of work for you. FromCache works in such a way that it would automatically check data in the cache first, that's the behavior, if it finds, it doesn't go to the database.

But, if it's not in the cache and then it would go to the database as a rule and get the query executed, result set retrieved and then added in the cache using this cache key being populated and then these caching options set up to that item. So, if you compare it with this one, you don't have to construct the cache key. You don't have to check null handling or getting from the cache yourself. You don't have to insert it in the cache. You just need to use this extension method. So, that makes a lot of things automated and its very simplified programming model for your applications as well.

Caching EF Core Entity Collection: EF Core Extension Methods

And, same case where you would like to store it as a collection, you simply provide the caching options to be collection and in this case, you have a list of customers that you would return. So, you just run the query and then again call FromCache. So, that completes our first extension method and introduction to it.

List GetCustomersByCity (string CustomerCity)
{
List 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;	
}       

What Data to Cache in EF Core?

Next, I will talk about reference and transactional data. This is the main segment.

what-data-to-cache-in-efcore

You would be dealing with applications which have a lot of reads, data with a lot of reads than writes and then there could be applications scenarios where we have reads as well as writes, right? So, for example, lookup data products, employees, which does not change that frequently but it's not static data, it's something that changes but frequency of change is not that great and entire data should exist in the cache. In this particular scenario, I'll explain why and you should expire this after, some hours or days. That’s how to keep cache fresh, that’s another segment.

Then we have transactional data. I will talk about how to handle transactional data caching. It's dynamically created. Orders, accounts, those are examples. It changes very frequently and typically, historically transactional data was not preferred for caching. Because, it was supposed to change and was supposed to go away from your application while your user is active, is only needed at that point. But based on our experience, we highly recommend that you enable caching for transactional data as well. Because, while you're not changing it that data is still active, you may read it a lot of times and if you have millions of users logged in, those would result into millions of requests for that data going back to the database and if it's cached and even it could be two to three requests while it's going to change it's still going to be beneficial from a performance standpoint. So, we highly recommend that you should consider caching some of your transactional if not all.

Caching Reference Data in EF Core

Alright, how to handle reference data caching in EF Core?

caching-reference-data-in-ef-core

I have two-step process for this. You can load entire data in the cache, that's a must-have. So, all of your reference data should be loaded in the cache, that's what we recommend as a must. Why? Because you don't want to go to the database at all, right? You should go ahead and start loading all of your data from the database in NCache and we have extension method that would take care of this right away and then only use cache and avoid database trips.

Step two is always cache as separate entities. That's another tip that I would give you that you should not cache all products or any other, all products as a collection, right? It could be thousands of products or millions of products. But storing them individually would allow you to fetch subsets of the data at a later stage, right. So, for example, you load all products but at a later point from cache you only need discontinued products. So, if you've stored them individually in the cache, let’s say, sixty thousand products, that's the example that I will show you. You can only find the ones that you need at that point. So, you don't need to deal with the entire product dataset and again saving expensive trips to the database.

Caching Reference Data in EF Core: Pre-Load Cache

So, we have extension method called LoadIntoCache, that's next I'm going to show you and I'm going to going to show you a working example of this as well.

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();

}

Now, LoadIntoCache, first of all, caching options should set as ‘store as separate entities’ and then you should run a query which should load all products and then you should call LoadIntoCache and then provide the options and again it would just create cache keys for all of those individually, automatically. And, it would keep loading all those items in the cache and then you can run LINQ queries like this and this LINQ query is against NCache. It’s not using any extension method. It’s calling product in database products where product.Discontinued and we have FromCache only. It's not going to the database at all. It's getting it from NCache directly.

Caching Reference Data in EF Core: Search Reference Data from Cache Only

If you have reference data, you should first load separate entities. Use load into cache to load all products in NCache. Once you've done that you don't have to go to the database. Then you use FromCacheOnly instead of FromCache. First extension method was FromCache which checks in the cache and then goes to database if it's not in the cache. But, LoadIntoCache would load all products and then FromCacheOnly would ensure that it only talks to the cache with an assumption all data is loaded in the cache.

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;

}

Let me run this code, so, that you see this in action. I have a cache which is configured right here for this test and I'm going to show you statistics. I've been playing around with it. 60,000 products are loaded. So, let me just go ahead and clear the contents. Right, so, let's review statistics. Where is my cache? There you go. Right, so, it's zero items and then I will go ahead and run this. It would just cache the single item, then collection all the code examples that I've shown you but I would like to show you how LoadIntoCache works and based on that, I'll put a breakpoint there as well, there you go.

So, first two examples were loading a single item and then loading a collection, the initial code that I had shown you and then this right here is actually loading all products to show you reference data scenario. First of all, it’s storing as separate entities and it's setting up some priorities, some dependency, it's elaborate in terms of that and then it's going to go ahead and load all products, getting it from the database and I highly recommend that you keep running load product, loaded into cache after some intervals, so, that you have data retrieved from the database and load into cache always works against database. It’s always going to go to the database and whatever you have in the database it's going to, its bound to execute against database and fetch that data back to NCache and then use FromCacheOnly afterwards.

So, that's how you deal with reference data. First of all, you store them individually, separately. LoadIntoCache, using this extension method LoadIntoCache, which always execute against database. It does not have cache as a priority. It would always execute against database and then fetch everything back to NCache and then use FromCacheOnly. That's how simple it is.

Caching Transactional Data in EF Core

Transactional data. You can load only working set. It's for result set caching, right?

caching-transactional-data-in-efcore

I personally recommend that if you're interested in customers by a city, orders based on a product, you should have some kind of result set caching and that's what you should do. You should store them as a collection or separate entities based on the size of the results, that if a collection is not that big in size let's say it’s dealing with 100 or 200 items at max, store them as a collection, and but if there are multiple products, multiple orders or customers information, that would categorize as a transactional data. Store them as separate entity. So, that you can get subset out of that and you can maximize your use of caching.

Caching Transactional Data in EF Core - Fetch and Cache as Collection

Use case for this is very simple again. You simply store it as a collection or use FromCache, you don't use FromCacheOnly because you would like to go to the database if it's not in the cache.

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;
}
Caching Transactional Data in EF Core - Fetch and Cache as Separate Entities
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;
}

So far, we have introduced three extension methods. FromCache which works with cache and database automatically and not in the cache you would get it from the database. LoadIntoCache would always execute against database. Fetch objects and bring it to cache and FromCacheOnly always executing against cache and that the only true source of data. It would not go to the database. So, I hope that clarifies a lot of things.

Keeping Cache Fresh

Next segment is based on how to keep cache fresh in Entity Framework Core and that's another important concept that you guys need to understand when dealing with two different sources.

You have caching enabled. You have backend database which is your main data source, persistent data store and then you have caching which also has a copy of data. So, how to ensure that cache is fresh in comparison to database. So, let's have spend some time here.

Keep Cache Fresh: Reference Data

First of all, you should, since you have entire data set in the cache, what if there is a change in the database, right?

keep-cache-fresh-reference-data

So, you need to expire data from the cache and for that we recommend that you use expiration and then automatic reloading of data it could be an option one. So, strategy one is that you use expiration but without a reload. So, that each time data gets expired, it gets automatic reloaded in the cache as well and for that we have this setup right here, Load All Products.

Strategy 1: Use Expiration But with Auto-Reload
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();

}

Let me just show you this from here, right. So, first of all, you store them as separate entities because that's the reference data use case. You load them as all products and then you set up some kind of expiration and then options.SetResyncProvider, there should be a reloading provider and then setup options.IsSyncEnabled to true. So, auto reloading, so that it automatically reloaded in the cache in case it expires, right. So, these are the two properties along with, if you SetResyncProviderName that would automatically set the auto reload flag to true.

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();
        }
    }
}

And, then you need ResyncProvider right here, which is, our sample implementation is right here that I'm showing.

namespace Alachisoft.NCache.EFSampleResyncProvider.Provider
{
    public class EFResncProvider : EFDefaultResyncProvider, 	
    IReadThruProvider
    {
        public override DbContext InitializedDbContext()
        {
            return new NorthwindContext();
        }
    }
}

There you go. You need to implement our IReadThruProvider. Initialize your data source, dispose it at the end and then you LoadFromSource allows you to get the key and based on that key you construct a SQL command and fetch the items from the database and I've given a sample implementation here where we construct a SQL query from the cache key that we have.

Right, so, that key, in this sample implementation is available on GitHub as well. So, it will work in such a way that your items which are loaded in the cache, if I run load into cache one more time they would have expiration attached. So, they would be expired after five to ten hours, whichever is the expiration period and after that this provider would kick in and it would call LoadFromSource using read thru handler automatically and updated data is brought in to NCache. Right, so your cache would automatically be fresh after items are expired from it.

Strategy 2: Don’t Use Expiration, Manually Reload

Second approach which I personally recommend is don't use expression, manually reload by calling LoadIntoCache. And, that's something which is very simple that you should come up with this LoadIntoCache method, if I show you one more time. Keep calling this method after some intervals and don't use any expression. Let's get rid of these.

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

}

So, you know that this is going to be the reference data is only valid for let's say one hour, two hours, five days, week, month. Based on that keep repeating this load all products. This should be called after some intervals, right?

So, that's the idea that you should reload data manually based on an intelligent guess made on the expiry interval, right? So, you should come up with working time set, after which you should automatically call load all products. So, that it automatically gets the data from the backend database and your reference data is kept fresh.

So, I'm going to reiterate these. So, there are two options. If you're using expiration data is going to be removed from the cache. So, you would end up with partial sets, so, you need auto reload as a must. But, if you don't use expiration you would have all the data in the cache all the times for reference data and then you can manually reload in that data after certain intervals. I hope that clarifies.

Keep Cache Fresh: Transactional Data

Next, I would talk about keeping cache fresh for transactional data and that's pretty simple. You should always use short expiration without auto reloading. Because, that's again a short-lived data which may be valid only for five to ten minutes and you should use FromCache, so that if it's not in the cache you should always get it from the back-end database.

And, here is an example of it, where we have customer orders, store them as a collection or individual items, it's entirely up to you. Short expiry if needed or don't use an expiry, or use the expiry and then don't use any auto reload for that matter. So that it's retrieved from the database as soon as it gets expired. I personally recommend using a configurable expiration or come up with the expiration that you know for sure that, that's a working time for this data set. So that after that it would not be needed. So, it should expire automatically and then at that point you would enforce a database access and you automatically get it from the backend database.

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, we've covered how to handle reference data, as well as transactional data. Then we've also covered how to keep cache fresh for reference, as well as transactional data.

Handling Relationships in Cache

One-to-many

A few other things that you may encounter with caching. Handling relationships in cache for EF Core. Again, it's very simple. Include is supported, so if you have region with database regions and then you get region.Territories alongside them, right? So, you can call FromCache and it would store regions separately and territories separately and they would formulate a relationship between region and territories. So, if I show you get region with territories.

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;
}

That's an example right here. Right. So, this region details includes and then we’re using FromCache. So, we'll store regions as well as region territories separately, separate items and then we'll construct a key based dependency. If regions go through a change, territories are also going to be invalidated and vice versa. So, that's how you would handle one-to-one or one-to-many relationships.

Caching Aggregate Operations

Aggregate operations are also supported. So, you can run these Extension Methods with a Deferred First or Default, FromCache. It can be based on Deferred Count, FromCache. So, it would store them as result set, right? So, it doesn't matter if you store them as collection or single items because this is just result of aggregate operations. So, that's another possibility with our Entity Framework.

Caching Aggregate Operations - Returning Entities

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

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

	return shipper;

}

Caching Aggregate Operations - Returning Values

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, then towards the end, I would like to talk about some architectural details about distributed caching. Why you should consider it. It’s highly available, it's super reliable, with replication. It's a peer-to-peer architectured cache. There's no single point of failure. You can add or remove any servers at runtime and clients have connection failover support build to it. So, it's highly available and super reliable with 100% uptime. It comes with many caching topologies, client cache, WAN replication, partitioned and partitioned replicas and I can specifically talk about architectural details if there are any questions otherwise at this point this concludes our presentation.

High Availability

high availability

Caching Topologies

caching-topologies

Client Cache (Near Cache)

client-cache

WAN Replication of Cache

wan-replication

Conclusion

I would like to give a quick recap of this. In this webinar we talked about caching options, direct APIs, Entity Framework Core Extension Methods. So, we have options of choosing between these. We were more focused on Entity Framework Core Extension Methods because that's what we would like to project. It's easier to use. How to tackle reference and transactional data. So, we talked about approaches of loading entire data for reference data and then using separate entities approach and then use cache only for all the data that you have in the cache. For transactional data, we recommend only caching to result set and then use FromCache Extension Method so that you can go to the database if it's not in the cache. And then, for keeping cache fresh we talked about that you should use expiration with auto reload for reference data or don't use expiration but manually reload after certain intervals and for transactional data make sure you use expiration, right? That should be short expiration but without auto reload, so, you can go back to the database and refresh your cache on next usage.

You can always reach us at support@alachisoft.com. If you have any technical queries, you can also reach us at sales@alachisoft.com. If you'd like to take a look at the product, you can download NCache Enterprise for a 30-Day free trial.

What to Do Next?

 
© Copyright Alachisoft 2002 - . All rights reserved. NCache is a registered trademark of Diyatech Corp.