Navigation Menu

Skip to content
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

Diseño de un SqlSelectBuilder #2

Open
tolemac opened this issue Jan 30, 2017 · 2 comments
Open

Diseño de un SqlSelectBuilder #2

tolemac opened this issue Jan 30, 2017 · 2 comments

Comments

@tolemac
Copy link
Owner

tolemac commented Jan 30, 2017

La idea inicial era construir consultas SQL de forma programática haciendo uso de expresiones de forma que no haya que escribir nombres de campos como literales protegiéndonos así de fallos a la hora de cambios en la base de datos o el modelo.

Lo primero que hice fue escribir un snippet con lo que sería más o menos la sintaxis para crear consultas. Escribí algo así:

b.SetMainTable<Invoice>("wh", "invoices", "t1");
b.AddJoin<Customer, Invoice>("wh", "customer", "t2", (c, i) => c.Id == i.CustomerId);
b.Where<Invoice, Customer>((i, c) => i.Date < DateTime.Now && i.Serie == "B" && !c.Deleted);
b.OrderBy<Invoice>(i => i.Date).ThenDescend<Customer>(c => c.Id);

Me metí en materia y según iba escribiendo código hice algunas modificaciones.
Este es el código del test con el que iba funcionando:

var b = new SqlBuilder();

b.SetMainTable<Invoice>("WH", "Invoices", "T1");
b.AddJoin<InvoiceDetail>("WH", "InvoiceDetail", "T2")
	.Condition.Set<Invoice>((d, i) => i.Id == d.InvoiceId && d.Price > 0 && i.Total > 0);
b.AddJoin<Customer>("WH", "Customer", "T3")
	.Condition.Set<Invoice>((c, i) => i.CustomerId == c.Id).And(c => !c.Disable);
b.Where.Set<Invoice, Customer>((i, c) => i.Serie == "B" && c.Id == 234);
b.Order.Ascend<Customer>(c => c.Id).Descend<Invoice>(i => i.Date);

Assert.NotNull(b._mainTable);
Assert.Equal(b.Joins.Count, 2);
Assert.Equal(b.Where.ExpressionList.Count, 1);
Assert.Equal(b.Order.ExpressionList.Count, 2);
Assert.Equal(b.Join<InvoiceDetail>().Condition.ExpressionList.Count, 1);
Assert.Equal(b.Join<Customer>().Condition.ExpressionList.Count, 2);

(Código en el branch "SQL" de este repositorio)

El pasado sábado en la #dosconf ví a @panicoenlaxbox usar algo parecido pero sin el uso de extensiones y al preguntarle me comentó que conocía un repo de @eiximenis donde se hacía algo parecido a lo que yo quería.

Estuve mirando ambos cogiendo ideas de uno y otro, MySqlSentenceBuilder de @eiximenis se adapta mejor a la idea que yo tenía sobre lo que quería hacer, pero cuando me puse a estudiar un poco SqlSentence de @panicoenlaxbox me dí cuenta de que lo mismo sería algo bueno soportar texto ... No ser la forma principal de escribir las sentencias sino como en caso extremo poder añadir cualquier cosa con texto ... Siempre puede haber una consulta donde necesitas añadir alguna ñapa que no puedes generar con el builder en cuestión...

Ahí tengo una duda importante, hacer que el builder soporte texto para cubrir situaciones extremas o no aceptar texto y cuando necesites hacer una consulta que no puedes generar con el builder hacerla desde cero en texto.

Bien enumero la lista de requerimientos que me gustaría cumplir:

  • Aceptar expresiones para hacer referencia a los campos.
.AddColumn(i => i.Date, i => i.Total);
  • Aceptar expresiones para definir las clausulas where.
.Where.Set<Invoice, Customer>((i, c) => i.Serie == "B" && c.Id == 234);
  • Soporte a distintos dialectos SQL (Sql Server, MySql, Oracle, ...)
    Implementando el patrón Builder se pueden crear distintos generadores, uno para cada dialecto.
  • Posibilidad de crear configuración por defecto, algo parecido al ModelBuilder de EF pero menos pretencioso. Para hacer algo así:
conf.TableName<Invoice>("Facturas");
conf.ColumnName<Invoice>(i => i.Date, "FechaFactura");
conf.DefaultSchema("Almacen");
conf.Ignore<Invoice>(i => i.Total);

Bueno, creo que es suficiente.
Agradecería cualquier comentario o consejo.

@tolemac
Copy link
Owner Author

tolemac commented Jan 30, 2017

Una cosa más.

Estaba implementándolo de forma que el objeto builder va guardando la configuración que se le va indicando con las instrucciones .AddJoin, AddColumn, ...
Y por último un Generator sería el que viendo la configuración en el builder, y teniendo en cuenta el dialecto, generaría el SQL final.

Al pensar en añadir soporte para texto ya estaba empezando a dudar, lo mismo debería ir traduciendo conforme se va configurando el builder y así aceptar texto sería algo más fácil.

Además el soporte para texto rompe un poco el soporte para cualquier dialecto ... lo mismo lo de soportar texto no es un buen enfoque.

@tolemac
Copy link
Owner Author

tolemac commented Feb 3, 2017

Bueno, creo que este hace exactamente lo que yo quiero:

https://github.com/DomanyDusan/lambda-sql-builder

Le faltan un par de cosas que intentaré implementarle.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant