|
using System; |
|
using System.Collections.Generic; |
|
using System.Data.Entity; |
|
using System.Data.Entity.Core.Objects.DataClasses; |
|
using System.Diagnostics; |
|
using System.Linq; |
|
using System.Runtime.Serialization; |
|
using System.Text; |
|
using System.Threading.Tasks; |
|
using Library; |
|
|
|
namespace Demo |
|
{ |
|
class Program |
|
{ |
|
|
|
|
|
static void Main(string[] args) |
|
{ |
|
FunctionalTest(); |
|
PerformanceTest(); |
|
} |
|
|
|
private static void FunctionalTest() |
|
{ |
|
Console.WriteLine("Functional test"); |
|
|
|
ObjectCanBeRetreivedAfterAddingItToTheDbContext(); |
|
ObjectCannotBeRetreivedAfterDeletingItFromTheDbContext(); |
|
NestedObjectCanBeRetrieved(); |
|
NestedObjectCanBeRetrievedLateBound(); |
|
} |
|
|
|
private static void ObjectCanBeRetreivedAfterAddingItToTheDbContext() |
|
{ |
|
var customerIds = new IdGenerator(); |
|
using (var db = new MyDatabase()) |
|
{ |
|
var finder = new FasterFind(db); |
|
db.Customers.Add(new Customer() |
|
{ |
|
Id = customerIds.Next(), |
|
Name = "Some name", |
|
Orders = new List<Order>() |
|
}); |
|
Debug.Assert(finder.Find<Customer>(customerIds.All().First()) != null); |
|
Debug.Assert(finder.Find<Order>(customerIds.All().First()) == null); |
|
} |
|
} |
|
private static void ObjectCannotBeRetreivedAfterDeletingItFromTheDbContext() |
|
{ |
|
var customerIds = new IdGenerator(); |
|
using (var db = new MyDatabase()) |
|
{ |
|
var finder = new FasterFind(db); |
|
var customer = db.Customers.Add(new Customer() |
|
{ |
|
Id = customerIds.Next(), |
|
Name = "Some name", |
|
Orders = new List<Order>() |
|
}); |
|
db.SaveChanges(); |
|
db.Customers.Remove(customer); |
|
Debug.Assert(finder.Find<Customer>(customerIds.All().First()) == null); |
|
db.SaveChanges(); |
|
Debug.Assert(finder.Find<Customer>(customerIds.All().First()) == null); |
|
} |
|
} |
|
private static void NestedObjectCanBeRetrieved() |
|
{ |
|
var customerIds = new IdGenerator(); |
|
var orderIds = new IdGenerator(); |
|
using (var db = new MyDatabase()) |
|
{ |
|
var finder = new FasterFind(db); |
|
var customer = db.Customers.Add(new Customer() |
|
{ |
|
Id = customerIds.Next(), |
|
Name = "Some name", |
|
Orders = new List<Order>() |
|
{ |
|
new Order() |
|
{ |
|
Id = orderIds.Next(), |
|
} |
|
} |
|
}); |
|
Debug.Assert(finder.Find<Order>(orderIds.All().First()) != null); |
|
db.SaveChanges(); |
|
Debug.Assert(finder.Find<Order>(orderIds.All().First()) != null); |
|
} |
|
} |
|
private static void NestedObjectCanBeRetrievedLateBound() |
|
{ |
|
// WARNING THIS IS THE ONLY USE-CASE THAT THIS |
|
// IMPLEMENTATION DOES NOT SUPPORT - UNLESS SAVED WITH SAVECHANGES |
|
var customerIds = new IdGenerator(); |
|
var orderIds = new IdGenerator(); |
|
using (var db = new MyDatabase()) |
|
{ |
|
var finder = new FasterFind(db); |
|
var customer = db.Customers.Add(new Customer() |
|
{ |
|
Id = customerIds.Next(), |
|
Name = "Some name", |
|
Orders = new List<Order>() |
|
}); |
|
customer.Orders.Add(new Order() |
|
{ |
|
Customer = customer, |
|
Id = orderIds.Next(), |
|
}); |
|
// This is the case where EF can find the object |
|
// and our implementation cannot find it |
|
// but EF is slow because of this 'only' case. |
|
Debug.Assert(finder.Find<Order>(orderIds.All().First()) == null); |
|
Debug.Assert(db.Orders.Find(orderIds.All().First()) != null); |
|
|
|
db.SaveChanges(); |
|
Debug.Assert(finder.Find<Order>(orderIds.All().First()) != null); |
|
} |
|
} |
|
|
|
private static void PerformanceTest() |
|
{ |
|
Console.WriteLine("Performance test"); |
|
var customerIds = new IdGenerator(); |
|
var orderIds = new IdGenerator(); |
|
InitializeDatabase(customerIds, orderIds); |
|
|
|
FasterFind ff = null; |
|
Func<MyDatabase, Guid, Customer> ff_customer = (db, id) => (ff ?? (ff = new FasterFind(db))).Find<Customer>(id); |
|
Func<MyDatabase, Guid, Customer> find_customer = (db, id) => db.Customers.Find(id); |
|
Func<MyDatabase, Guid, Customer> fod_customer = (db, id) => db.Customers.FirstOrDefault(c => c.Id == id); |
|
Func<MyDatabase, Guid, Customer> fod_customer_ant = (db, id) => db.Customers.AsNoTracking().FirstOrDefault(c => c.Id == id); |
|
|
|
Func<MyDatabase, Guid, Order> ff_order = (db, id) => (ff ?? (ff = new FasterFind(db))).Find<Order>(id); |
|
Func<MyDatabase, Guid, Order> find_order = (db, id) => db.Orders.Find(id); |
|
Func<MyDatabase, Guid, Order> fod_order = (db, id) => db.Orders.FirstOrDefault(c => c.Id == id); |
|
Func<MyDatabase, Guid, Order> fod_order_ant = (db, id) => db.Orders.AsNoTracking().FirstOrDefault(c => c.Id == id); |
|
|
|
ExecutePerformanceTest(customerIds, orderIds, " 0% loaded - ff ", ff_customer, ff_order); |
|
ff = null; |
|
ExecutePerformanceTest(customerIds, orderIds, " 0% loaded - find ", find_customer, find_order); |
|
ExecutePerformanceTest(customerIds, orderIds, " 0% loaded - fod ", fod_customer, fod_order); |
|
ExecutePerformanceTest(customerIds, orderIds, " 0% loaded - fodnt", fod_customer_ant, fod_order_ant); |
|
|
|
ExecutePerformanceTest(customerIds, orderIds, " 50% loaded - ff ", ff_customer, ff_order, db => InitializeHalf(db, customerIds, orderIds)); |
|
ff = null; |
|
ExecutePerformanceTest(customerIds, orderIds, " 50% loaded - find ", find_customer, find_order, db => InitializeHalf(db, customerIds, orderIds)); |
|
ExecutePerformanceTest(customerIds, orderIds, " 50% loaded - fod ", fod_customer, fod_order, db => InitializeHalf(db, customerIds, orderIds)); |
|
ExecutePerformanceTest(customerIds, orderIds, " 50% loaded - fodnt", fod_customer_ant, fod_order_ant, db => InitializeHalf(db, customerIds, orderIds)); |
|
|
|
ExecutePerformanceTest(customerIds, orderIds, "100% loaded - ff ", ff_customer, ff_order, (db) => InitializeFull(db)); |
|
ff = null; |
|
ExecutePerformanceTest(customerIds, orderIds, "100% loaded - find ", find_customer, find_order, (db) => InitializeFull(db)); |
|
ExecutePerformanceTest(customerIds, orderIds, "100% loaded - fod ", fod_customer, fod_order, (db) => InitializeFull(db)); |
|
ExecutePerformanceTest(customerIds, orderIds, "100% loaded - fodnt", fod_customer_ant, fod_order_ant, (db) => InitializeFull(db)); |
|
} |
|
|
|
private static void InitializeDatabase(IdGenerator customerIds, IdGenerator orderIds) |
|
{ |
|
using (var db = new MyDatabase()) |
|
{ |
|
for (int i = 0; i < 500; i++) |
|
{ |
|
var customer = db.Customers.Add(new Customer() |
|
{ |
|
Id = customerIds.Next(), |
|
Name = "Some name", |
|
Orders = new List<Order>() |
|
}); |
|
for (int j = 0; j < 3; j++) |
|
{ |
|
customer.Orders.Add(new Order() |
|
{ |
|
Id = orderIds.Next(), |
|
Customer = customer |
|
}); |
|
} |
|
} |
|
db.SaveChanges(); |
|
} |
|
} |
|
|
|
private static void InitializeFull(MyDatabase db) |
|
{ |
|
db.Customers.Include(z => z.Orders).ToArray(); |
|
} |
|
|
|
private static void InitializeHalf(MyDatabase db, IdGenerator customerIds, IdGenerator orderIds) |
|
{ |
|
db.Customers.Take(customerIds.All().Count() / 2).ToArray(); |
|
db.Orders.Take(orderIds.All().Count() / 2).ToArray(); |
|
} |
|
|
|
static void ExecuteOnNewDbContext(Action<MyDatabase> action) |
|
{ |
|
using (var database = new MyDatabase()) |
|
{ |
|
action(database); |
|
} |
|
} |
|
|
|
static void ExecutePerformanceTest(IdGenerator customers, |
|
IdGenerator orders, |
|
string title, |
|
Func<MyDatabase, Guid, Customer> customer, |
|
Func<MyDatabase, Guid, Order> order, |
|
Action<MyDatabase> setup = null |
|
) |
|
{ |
|
ExecuteOnNewDbContext(db => |
|
{ |
|
setup?.Invoke(db); |
|
using (new PerformanceScope($"Customers - {title}")) |
|
{ |
|
foreach (var id in customers.All()) |
|
{ |
|
customer(db, id); |
|
} |
|
} |
|
using (new PerformanceScope($"Orders - {title}")) |
|
{ |
|
foreach (var id in orders.All()) |
|
{ |
|
order(db, id); |
|
} |
|
} |
|
}); |
|
} |
|
} |
|
} |