Skip to content

Add support for caching fetched relations with Criteria #2090

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 6, 2020
Next Next commit
Add support for caching fetched relations with Criteria
  • Loading branch information
bahusoid authored and fredericDelaporte committed Apr 4, 2020
commit b971b1e63188586520d4b770df3e25539f4fceef
4 changes: 2 additions & 2 deletions src/NHibernate.DomainModel/Northwind/Entities/Northwind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ public IQueryable<Role> Role
get { return _session.Query<Role>(); }
}

public IEnumerable<IUser> IUsers
public IQueryable<IUser> IUsers
{
get { return _session.Query<IUser>(); }
}
}
}
}
102 changes: 102 additions & 0 deletions src/NHibernate.DomainModel/Northwind/Entities/NorthwindQueryOver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NHibernate.Linq;

namespace NHibernate.DomainModel.Northwind.Entities
{
public class NorthwindQueryOver
{
private readonly ISession _session;

public NorthwindQueryOver(ISession session)
{
_session = session;
}

public IQueryOver<Customer, Customer> Customers
{
get { return _session.QueryOver<Customer>(); }
}

public IQueryOver<Product, Product> Products
{
get { return _session.QueryOver<Product>(); }
}

public IQueryOver<Shipper, Shipper> Shippers
{
get { return _session.QueryOver<Shipper>(); }
}

public IQueryOver<Order, Order> Orders
{
get { return _session.QueryOver<Order>(); }
}

public IQueryOver<OrderLine, OrderLine> OrderLines
{
get { return _session.QueryOver<OrderLine>(); }
}

public IQueryOver<Employee, Employee> Employees
{
get { return _session.QueryOver<Employee>(); }
}

public IQueryOver<ProductCategory, ProductCategory> Categories
{
get { return _session.QueryOver<ProductCategory>(); }
}

public IQueryOver<Timesheet, Timesheet> Timesheets
{
get { return _session.QueryOver<Timesheet>(); }
}

public IQueryOver<Animal, Animal> Animals
{
get { return _session.QueryOver<Animal>(); }
}

public IQueryOver<Mammal, Mammal> Mammals
{
get { return _session.QueryOver<Mammal>(); }
}

public IQueryOver<User, User> Users
{
get { return _session.QueryOver<User>(); }
}

public IQueryOver<PatientRecord, PatientRecord> PatientRecords
{
get { return _session.QueryOver<PatientRecord>(); }
}

public IQueryOver<State, State> States
{
get { return _session.QueryOver<State>(); }
}

public IQueryOver<Patient, Patient> Patients
{
get { return _session.QueryOver<Patient>(); }
}

public IQueryOver<Physician, Physician> Physicians
{
get { return _session.QueryOver<Physician>(); }
}

public IQueryOver<Role, Role> Role
{
get { return _session.QueryOver<Role>(); }
}

public IQueryOver<IUser, IUser> IUsers
{
get { return _session.QueryOver<IUser>(); }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by AsyncGenerator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------


using System.Linq;
using NHibernate.Cfg;
using NHibernate.DomainModel.Northwind.Entities;
using NUnit.Framework;

namespace NHibernate.Test.Criteria.ReadonlyTests
{
using System.Threading.Tasks;
[TestFixture]
public class QueryOverCacheableTestsAsync : CriteriaNorthwindReadonlyTestCase
{
//Just for discoverability
private class CriteriaCacheableTest{}

protected override void Configure(Configuration cfg)
{
cfg.SetProperty(Environment.UseQueryCache, "true");
cfg.SetProperty(Environment.GenerateStatistics, "true");
base.Configure(cfg);
}

[Test]
public async Task QueryIsCacheableAsync()
{
Sfi.Statistics.Clear();
await (Sfi.EvictQueriesAsync());

var x = await (db.Customers.Cacheable().ListAsync());
var x2 = await (db.Customers.Cacheable().ListAsync());

Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(1), "Unexpected execution count");
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(1), "Unexpected cache put count");
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "Unexpected cache hit count");
}

[Test]
public async Task QueryIsCacheable2Async()
{
Sfi.Statistics.Clear();
await (Sfi.EvictQueriesAsync());

var x = await (db.Customers.Cacheable().ListAsync());
var x2 = await (db.Customers.ListAsync());

Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(2), "Unexpected execution count");
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(1), "Unexpected cache put count");
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0), "Unexpected cache hit count");
}

[Test]
public async Task QueryIsCacheableWithRegionAsync()
{
Sfi.Statistics.Clear();
await (Sfi.EvictQueriesAsync());
await (Sfi.EvictQueriesAsync("test"));
await (Sfi.EvictQueriesAsync("other"));

var x = await (db.Customers.Cacheable().CacheRegion("test").ListAsync());
var x2 = await (db.Customers.Cacheable().CacheRegion("test").ListAsync());
var x3 = await (db.Customers.Cacheable().CacheRegion("other").ListAsync());

Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(2), "Unexpected execution count");
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(2), "Unexpected cache put count");
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "Unexpected cache hit count");
}


[Test]
public async Task CanBeCombinedWithFetchAsync()
{

Sfi.Statistics.Clear();
await (Sfi.EvictQueriesAsync());

await (db.Customers
.Cacheable()
.ListAsync());

await (db.Orders
.Cacheable()
.ListAsync());

await (db.Customers
.Fetch(SelectMode.Fetch, x => x.Orders)
.Cacheable()
.ListAsync());

await (db.Orders
.Fetch(SelectMode.Fetch, x => x.OrderLines)
.Cacheable()
.ListAsync());

var customer = await (db.Customers
.Fetch(SelectMode.Fetch, x => x.Address)
.Where(x => x.CustomerId == "VINET")
.Cacheable()
.SingleOrDefaultAsync());

customer = await (db.Customers
.Fetch(SelectMode.Fetch, x => x.Address)
.Where(x => x.CustomerId == "VINET")
.Cacheable()
.SingleOrDefaultAsync());

Assert.That(NHibernateUtil.IsInitialized(customer.Address), Is.True, "Expected the fetched Address to be initialized");
Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(5), "Unexpected execution count");
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(5), "Unexpected cache put count");
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "Unexpected cache hit count");
}

[Test]
public async Task FetchIsCacheableAsync()
{
Sfi.Statistics.Clear();
await (Sfi.EvictQueriesAsync());

Order order;
order = (await (db.Orders
.Fetch(
SelectMode.Fetch,
x => x.Customer,
x => x.OrderLines,
x => x.OrderLines.First().Product,
x => x.OrderLines.First().Product.OrderLines)
.Where(x => x.OrderId == 10248)
.Cacheable()
.ListAsync()))
.First();

AssertFetchedOrder(order);

Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(1), "Unexpected execution count");
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(1), "Unexpected cache put count");
Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(1), "Unexpected cache miss count");

Sfi.Statistics.Clear();
Session.Clear();

order = (await (db.Orders
.Fetch(
SelectMode.Fetch,
x => x.Customer,
x => x.OrderLines,
x => x.OrderLines.First().Product,
x => x.OrderLines.First().Product.OrderLines)
.Where(x => x.OrderId == 10248)
.Cacheable()
.ListAsync()))
.First();

AssertFetchedOrder(order);

Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(0), "Unexpected execution count");
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(0), "Unexpected cache put count");
Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(0), "Unexpected cache miss count");
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "Unexpected cache hit count");
}

[Test]
public async Task FutureFetchIsCacheableAsync()
{
Sfi.Statistics.Clear();
await (Sfi.EvictQueriesAsync());
var multiQueries = Sfi.ConnectionProvider.Driver.SupportsMultipleQueries;

Order order;

db.Orders
.Fetch(SelectMode.Fetch, x => x.Customer)
.Where(x => x.OrderId == 10248)
.Cacheable()
.Future();

order = db.Orders
.Fetch(
SelectMode.Fetch,
x => x.OrderLines,
x => x.OrderLines.First().Product,
x => x.OrderLines.First().Product.OrderLines)
.Where(x => x.OrderId == 10248)
.Cacheable()
.Future()
.ToList()
.First();

AssertFetchedOrder(order);

Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(multiQueries ? 1 : 2), "Unexpected execution count");
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(2), "Unexpected cache put count");
Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(2), "Unexpected cache miss count");

Sfi.Statistics.Clear();
Session.Clear();

db.Orders
.Fetch(SelectMode.Fetch, x => x.Customer)
.Where(x => x.OrderId == 10248)
.Cacheable()
.Future();

order = db.Orders
.Fetch(
SelectMode.Fetch,
x => x.OrderLines,
x => x.OrderLines.First().Product,
x => x.OrderLines.First().Product.OrderLines)
.Where(x => x.OrderId == 10248)
.Cacheable()
.Future()
.ToList()
.First();

AssertFetchedOrder(order);

Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(0), "Unexpected execution count");
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(0), "Unexpected cache put count");
Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(0), "Unexpected cache miss count");
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(2), "Unexpected cache hit count");
}

private static void AssertFetchedOrder(Order order)
{
Assert.That(NHibernateUtil.IsInitialized(order.Customer), Is.True, "Expected the fetched Customer to be initialized");
Assert.That(NHibernateUtil.IsInitialized(order.OrderLines), Is.True, "Expected the fetched OrderLines to be initialized");
Assert.That(order.OrderLines, Has.Count.EqualTo(3), "Expected the fetched OrderLines to have 3 items");
var orderLine = order.OrderLines.First();
Assert.That(NHibernateUtil.IsInitialized(orderLine.Product), Is.True, "Expected the fetched Product to be initialized");
Assert.That(NHibernateUtil.IsInitialized(orderLine.Product.OrderLines), Is.True, "Expected the fetched OrderLines to be initialized");
}
}
}
Loading