Capas de acceso a datos .NET escalables de verdad: el batido perfecto para el rendimiento de tus bases de datos
SOLIDQ SUMMIT MADRID 2017
#SQSummit17
Enrique Catalá | Mentor | ecatala@solidq.com | @enriquecatala
Guillermo Perez | DEV | gperez@solidq.com |
Capas de acceso a datos .NET escalables de
verdad: el batido perfecto para el rendimiento
de tus bases de datos
SOLIDQ SUMMIT MADRID 2017
Tipología de
acceso
Por conjuntos Por cursores
Patrones de
bajo nivel
Dinámico
Adhoc
Parametrizable
Estático
Stored
procedures
Arquitecturas
Modelo
conectividad
Conectada
Desconectada
Modelo de
desarrollo
Manual
ORM
SOLIDQ SUMMIT MADRID 2017
Fundamentos
• Tiempo de desarrollo
• Diferencia de roles
• Desarrollador vs. DBA
• Independencia de la
aplicación
• Uso de modelo de
datos
• Abstracción de servidor
de base de datos
Másmotivos
• Rechazo a T-SQL
• Manejo de cadenas
para acceso a datos
• Errores en tiempo de
ejecución vs.
Compilación
• Uso de lenguajes más
familiares
• Es más “cool”
SOLIDQ SUMMIT MADRID 2017
Librería Método ¿Qué hace?
Query.PlanCompiler
.PlanCompiler
Compile(cqt.DbCommandTree ctree,
…….)
Compila query hacia SQL Server. Coste
de compilación. Aquí generalmente es
donde debemos poner esfuerzos en
optimizar linq
Objects.Elinq.Compi
ledELinqQueryState
GetExecutionPlan(MergeOption?
forMergeOption)
Obtener plan de ejecución ya
compilado previamente
Objects.Elinq.ELinq
QueryState
GetExecutionPlan(MergeOption?
forMergeOption)
Obtener plan de ejecución todavía no
compilado
Objects.ObjectQuer
y<T>
GetResults(MergeOption?
forMergeOption)
Obtiene datos. Incluye datos
GetExecutionPlan, Compile y el coste de
materializar incluido. Es el coste total
SOLIDQ SUMMIT MADRID 2017
public class MyDbContext : DbContext
{
//...
public DbSet<Product> Products
{
get; set;
}
}
var product = _dbContext.Products.Find(4);
var product2 = _dbContext.Products
.AsNoTracking()
.Where(p => p.Id == 4)
.FirstOrDefault();Implementación Dapper y EF Core
SOLIDQ SUMMIT MADRID 2017
using(var _dapper = new SqlConnection(config.ConnectionString))
{
_dapper.Open();
var product = _dapper.Query<Product>(@"
SELECT
Id,Name,Col1,Col2
FROM [dbo].[Products]
WHERE Id = @ProductId",
new
{
ProductId = 4
}).FirstOrDefault();
}
Implementación Dapper y EF Core
SOLIDQ SUMMIT MADRID 2017
var order = _dbContext.Orders
.AsNoTracking()
.Include(o => o.Details)
.ThenInclude(d => d.Product)
.Include(o => o.Customer)
.Where(o => o.Id == 4);
Implementación Dapper y EF Core
Hierarchy Load
SOLIDQ SUMMIT MADRID 2017
var orders = _dapper.Query<Order, Customer, Order>(
"/* Order join with customer */“
,(order, customer) => {
order.Customer = customer;
return order;
}
, queryParam
);
var details = _dapper.Query<OrderDetail, Product,OrderDetail>(
"/* detail join with product */“
, (detail, product) => {
detail.Product = product;
return detail;
}
, queryParam
, splitOn: "OrderId,Id“
);
foreach (var order in orders)
{
order.Details = detailResult
.Where(d => d.OrderId == order.Id)
.ToList();
}
Implementación Dapper y EF Core
Hierarchy Load
SOLIDQ SUMMIT MADRID 2017
var orders = _dapper.Query<Order, Customer, Order>(
"/* Order join with customer */“
,(order, customer) => {
order.Customer = customer;
return order;
}
, queryParam
);
var details = _dapper.Query<OrderDetail, Product,OrderDetail>(
"/* detail join with product */“
, (detail, product) => {
detail.Product = product;
return detail;
}
, queryParam
, splitOn: "OrderId,Id“
);
//Potencialmente peligroso sin un "lock“. Si ponemos un lock, lo
hacemos monothread
Parallel.ForEach(orders, (order) =>
{
order.Details = detailResult
.Where(d => d.OrderId == order.Id)
.ToList();
});
Implementación Dapper y EF Core
Hierarchy Load
SOLIDQ SUMMIT MADRID 2017
var ordersList = _dapper.Query<Order, Customer, Order>(
/*
...
*/
);
var orders = new ConcurrentDictionary<int, Order>(
ordersList.ToDictionary(o => o.Id, o => o)
);
var detailsList = _dapper.Query<OrderDetail, Product,OrderDetail>(
/*
...
*/
);
var details = new ConcurrentDictionary<int,
IEnumerable<OrderDetail>>(
detailResult
.GroupBy(d => d.OrderId)
.ToDictionary(
d => d.FirstOrDefault().OrderId,
d => d.AsEnumerable()
)
);
Parallel.ForEach(orders, (o) => {
if (details.ContainsKey(o.Key))
o.Value.Details = details[o.Key].ToList();
}
);
Implementación Dapper y EF Core
Hierarchy Load
SOLIDQ SUMMIT MADRID 2017
• Evita dependencia entre clases con el uso de
Interfaces
Arquitectura de
aplicación
• Evita procesados fila a fila
Capas de acceso a
datos
• La estructura de datos importa, no es todo BBDD
Complejidad
algorítmica
• Ya no estamos en los 90. Hasta tu móvil tiene 8
núcleos ☺
Consideraciones de
concurrencia
• Elige con criterio y úsalo bienORMs