EF Core Extension Methods
NCache provides synchronous and asynchronous extension methods for
caching queries in EF Core. The sync APIs are extension methods provided on IQueryable
interface while the Async APIs are respective invocations of the sync
APIs. The Async APIs return a task instance for these methods.
To utilize the following APIs, include the following namespace in your application:
Alachisoft.NCache.EntityFrameworkCore
NCache’s Entity Framework Core Caching Provider contains the following APIs for caching queries:
Synchronous APIs | Asynchronous APis |
---|---|
FromCache |
FromCacheAsync |
LoadIntoCache |
LoadIntoCacheAsync |
FromCacheOnly |
FromCacheOnlyAsync |
FromCache
Note
This feature is available in NCache Enterprise and Professional editions.
The FromCache
method caches the result set generated against the LINQ query
and returns it upon call. In case the data is not present in the cache, it will
be fetched from the data source and stored in the cache. Upon subsequent
execution of the same query, the result will be fetched from the cache.
If the cache connection cannot be made, the data will be directly fetched from the database. However, after every 60 seconds of the request made, cache connection will be retried. The subsequent requests will try to re-initialize the cache after 60 seconds of the last request. Whenever the cache connection is successfully established, the data will then be fetched from the cache. This also applies to the FromCacheAsync
method.
FromCache
is highly efficient for execution of advanced LINQ queries requiring
more flexibility, and result sets that are used periodically. For example,
customer details can be cached as they are infrequently changed, however
customer orders can be updated regularly.
Examples
- The following example fetches customer details based on a specified customer ID from the database and stores them as a separate entity in cache with specified caching options.
using (var context = new NorthwindContext())
{
var options = new CachingOptions
{
StoreAs = StoreAs.SeperateEntities
};
var resultSet = (from cust in context.Customers
where cust.CustomerId == someCustomerId
select cust).FromCache(options).ToList();
}
- The following example returns the cache key internally generated against the query result set which is stored as a collection in the cache. This key can be saved for future usage purposes like removing the entity/result set from cache.
using (var context = new NorthwindContext())
{
var options = new CachingOptions
{
StoreAs = StoreAs.Collection
};
var resultSet = (from cust in context.Customers
where cust.CustomerId == someCustomerId
select cust).FromCache(out string cacheKey, options);
}
LoadIntoCache
Note
This feature is available in NCache Enterprise and Professional editions.
The LoadIntoCache
API is useful for cases where fresh data from the database
is cached every time the method is called. This fetches a result set from the
source and caches it before returning it. Upon executing the same query again,
the result will be fetched from the source and cached again – meaning existing
cache data is overwritten every time. This is useful as any subsequent
FromCache
calls will also yield fresh data. In case the cache is not initialized, an exception message is thrown.
The LoadIntoCache
method is particularly suitable for data that is frequently
updated like customer orders or any sensitive data like payment details. In such
scenarios, using stale data can result in incorrect business transactions, hence
a fresh copy of the data must be present in the cache at all times.
Examples
- The following example fetches the customer orders from the database and loads the result set into the cache as a collection. It also returns the cache key generated internally which can be saved for future use.
using (var context = new NorthwindContext())
{
var options = new CachingOptions
{
StoreAs = StoreAs.Collection
};
var resultSet = (from custOrder in context.Orders
where custOrder.Customer.CustomerId == someCustomerId
select custOrder)).LoadIntoCache(out string cacheKey, options);
}
- The following example loads the specific order details from the database into the cache as separate entities with caching options specified.
using (var context = new NorthwindContext())
{
var options = new CachingOptions
{
StoreAs = StoreAs.SeperateEntities
};
var resultSet = (from custOrder in context.Orders
where custOrder.Customer.CustomerId == someCustomerId
select custOrder)).LoadIntoCache(options);
}
FromCacheOnly
Note
This feature is only available in NCache Enterprise edition.
For queries which have less frequent updates like customer details, regularly
fetching such data from the data source is costly. Hence, performance can be
enhanced by storing entities as separate entities. FromCacheOnly
only queries
the entities existing in cache and does not approach the data source in any
case. If the entity exists in the cache, the result set will be returned for
all subsequent FromCacheOnly()
calls directly from the cache. If the entity
does not exist in the cache, it will still not be fetched from the data source,
and the LoadIntoCache
or FromCache
call can be made to load the result set
into the cache. In case the cache is not initialized, an exception message is thrown.
Note
This API only works when entities are stored separately, i.e. the caching
option StoreAs
is set to SeperateEntities
.
It is recommended to cache infrequently changing data as separate entities, as
separate entities can be made part of multiple queries. Using FromCacheOnly
,
you can query data whose identifiers you are not aware of but have a criterion
against which they can be queried while adhering to the limitations of
FromCacheOnly
discussed later in this topic.
Important
The entities MUST be indexed in NCache Web Manager before they are used with FromCacheOnly
.
- The following example fetches the customer information from the cache if the
Customer
entity exists. If not, the result set returned will be empty.
using (var context = new NorthwindContext())
{
var resultSet = (from cust in context.Customers
where cust.CustomerId == someCustomerId
select cust).FromCacheOnly();
}
Limitations of FromCacheOnly
FromCacheOnly
supports aggregate functions. However, these are immediate
functions whose result is cached, and not the entities themselves. Hence, NCache
has provided QueryDeferred
which defers the query to resolve so the entities
in the cache are queried to get the result. For more details on deferred APIs,
please refer to the chapter Query Deferred APIs. The following functions are
supported by FromCacheOnly
:
- Group By
- Order By Ascending
- Order By Descending
Select
withGroup By
only- Sum
- Min
- Max
- Average
- Count
Keep in mind the following limitations regarding using LINQ in FromCacheOnly
:
Except for
Count
, all aggregate functions need to be provided with integer values.Count
needs to be provided with an entity to count.More than one aggregate function cannot be used in a single LINQ expression for
FromCacheOnly
.Projection in LINQ expressions for
FromCacheOnly
API can only be performed if aGroupBy
operator and an aggregate function is used.Multiple projections are not supported. Only one attribute can be projected in a LINQ expression (that is carried forward).
using (var context = new NorthwindContext())
{
var min = context.Employees
.Select(e => e.EmployeeId) // Multiple projections have not been performed
.GroupBy(eid => eid) // eId was projected so GroupBy had to be used
.DeferredMin() // MIN provided with a numeric attribute
.FromCacheOnly();
}
GroupBy
can only be used if projection has been done beforehand on the attribute and an aggregate function is being used.GroupBy
has to be the last operator in the LINQ expression except ifOrderBy
is used.- In such cases, the
GroupBy
function can be shifted forward (afterOrderBy
) or backward (beforeOrderBy
).
- In such cases, the
using (var context = new NorthwindContext())
{
var min = context.Employees
.Select(e => e.EmployeeId) // Multiple projections have not been performed
.GroupBy(eid => eid) // eId was projected so GroupBy had to be used
.OrderBy(eid => eid) // OrderBy can be used when GroupBy is used
.DeferredMin() // MIN provided with a numeric attribute
.FromCacheOnly();
}
OrderBy
(both ascending and descending) can only be used in a LINQ expression forFromCacheOnly
ifGroupBy
is used.More than one
GroupBy
andOrderBy
operators cannot be used in a single LINQ expression forFromCacheOnly
.Joins are not supported so the
Include()
method will not work.For LINQ expressions containing
DateTime
instances, it is important that all nodes in a cache cluster have the sameDateTime
format set up. Otherwise, a format exception will be thrown. This problem arises whenDateTime.Now
andDateTime.Parse()
are used in a multi node cache cluster. To avoid it, synchronize theDateTime
format on both machines. Moreover, it is advised to useDateTime.ParseExact()
overDateTime.Parse()
as theParseExact()
API restricts the user to specify a format forDateTime
.
Asynchronous LINQ APIs
The asynchronous APIs perform the same functionality as their synchronous counterparts, but with asynchronous behavior. The parameters passed to these methods are also the same.
Important
Due to its asynchronous nature, cacheKey
is not returned in any of the
asynchronous calls like FromCacheAsync
and LoadIntoCacheAsync
as out
parameters are not allowed with methods with async signature.
FromCacheAsync
If the cache connection cannot be established, the data is directly fetched from the database. However, after every 60 seconds of the request made, cache connection is retried. The subsequent requests will try to re-initialize the cache after 60 seconds of the last request. Whenever the cache connection is successfully established, the data will then be fetched from the cache.
The following example fetches customer details based on a specified customer ID from the database asynchronously, and stores them as a separate entity in cache with specified caching options.
using (var context = new NorthwindContext())
{
var options = new CachingOptions
{
StoreAs = StoreAs.SeperateEntities
};
var task = (from cust in context.Customers
where cust.CustomerId == someCustomerId
select cust).FromCacheAsync(options);
task.Wait();
var resultSet = task.Result.ToList();
}
LoadIntoCacheAsync
The following example loads the specific order details from the database into the cache as separate entities with caching options specified.
using (var context = new NorthwindContext())
{
var options = new CachingOptions
{
StoreAs = StoreAs.SeperateEntities
};
var task = (from custOrder in context.Orders
where custOrder.Customer.CustomerId == someCustomerId
select custOrder)).LoadIntoCacheAsync(options);
task.Wait();
var resultSet = task.Result.ToList();
}
FromCacheOnlyAsync
The following example fetches the customer information only from the cache if
the Customer
entity exists. If not, the result set returned will be empty, as
the database is ignored in this call.
using (var context = new NorthwindContext())
{
var options = new CachingOptions
{
StoreAs = StoreAs.SeperateEntities
};
var task = (from cust in context.Customers
where cust.CustomerId == someCustomerId
select cust).FromCacheOnlyAsync();
task.Wait();
var resultSet = task.Result.ToList();
}
See Also
Caching Options for EF Core Caching
Cache Handle from EF Core Context
Query Deferred APIs for EF Core Caching
Logging Entity Framework Core Caching