Generador de consultas SQL para Dapper

c# dapper orm sql

Pregunta

Hola, estaba tratando de encontrar el ORM con el mejor rendimiento para usar en nuestro nuevo proyecto. Mi elección final se convirtió en Dapper. También necesitamos que nuestra aplicación incluya las siguientes funciones (al menos) que nos impiden codificar por completo las consultas SQL para pasarlas a Dapper.

  1. Base de datos independiente
  2. Definición de las entidades de tiempo de ejecución

Pensé en escribir un generador de SQL para Dapper, pero no estoy seguro de que el enfoque que estoy siguiendo sea el mejor:

  1. Declare una interfaz con la firma de métodos. La implementación corresponde al sistema de base de datos que se utilizará (SQL Server / MySQL / PostgreSql / DB2 / Oracle / etc ...).
  2. Crear esquema XML de base de datos usando el siguiente formato:

    <sqltable name="Foo">
        <sqlfield name="ID" primarykey="1" />
        <sqlfield name="Name" />
        <sqlfield name="Surname />
        <sqlfield name="etc" />
        <sqlreference name"KooID" table="Koo" field="ID" />
    </sqltable>
    
  3. Genere clases / entidades utilizando el XML proporcionado anteriormente (permitiendo la extensión del esquema en tiempo de ejecución). Los objetos creados son POCO.

  4. Implementar métodos que repiten las propiedades de la entidad actual (usando reflexión) y generar sentencias SQL donde la propiedad no es nula:

    String GetInsert(object currentEntity)
    return:
    "INSERT INTO Foo (ID, Name) VALUES (1, 'BooBoo')"
    
  5. La implementación incluirá al menos

    SELECT
    INSERT INTO
    UPDATE
    DELETE
    JOIN /*(using references like the KooID above)*/
    WHERE /*(using filter expressions)*/
    

¿Puedes pensar en retrocesos / desventajas de este enfoque? ¿Me puede recomendar alguna mejora?

¡Gracias!

Respuesta aceptada

Usar MEF en lugar de configuraciones

¿Qué sucede si descarta todas estas configuraciones y solo codifica estos proveedores y utiliza MEF para descubrir cuál está incluido en su aplicación y usar esa? ¿Entonces, cuando te conectas a una base de datos diferente, escribirías un nuevo proveedor y reemplazarías el ensamble del proveedor? MEF haría el resto.

Agregar nuevas entidades de producción directa sin recompilación

Pero a medida que agregaste algunos detalles más en los comentarios, me gustaría decir que la forma en que tratas de hacerlo es el camino a seguir, salvo que introduciría algunos cambios:

  1. La capacidad de detección del proveedor de la base de datos aún podría implementarse utilizando MEF, por lo que lo único que tendrías que hacer es soltar el ensamblaje del proveedor en tu carpeta bin y tu aplicación lo usaría. Esto también puede hacerse por configuración. Depende de usted decidir cómo crear una instancia del proveedor correcto.

  2. Su esquema de base de datos de ejemplo parece tener una sintaxis definida por usted mismo. Tal vez más bien usar algo que ya está probado y permite una definición estandarizada y probablemente también más compleja.

  3. Su UI (vistas o lo que sea que use) en realidad serán plantillas que pueden consumir XML shcema y no POCO. Los objetos POCO solo entregarán datos a la IU.

  4. Como usará la reflexión de manera extensiva en su aplicación, probablemente la desacelere considerablemente. Sugiero que uses enfoques mejores (= más rápidos). Mire esta biblioteca en NuGet por Mark Gravell.

Generando y consumiendo entidades desconocidas en tiempo de diseño

Las entidades de datos serán desconocidas durante el tiempo de diseño (desde la perspectiva del código compilado) porque esas POCO se deben generar a partir del esquema XML en tiempo de ejecución. A menos que su aplicación esté exclusivamente orientada a la base de datos (como en la manipulación directa de tablas en la base de datos), no veo cómo consumirá estas entidades con la interfaz de usuario codificada.

Como mencionas, tu IU realmente podrá leer la misma IU de esquema de datos y poblarla en función de las instancias de POCO leídas de la base de datos. Eso está bien siempre y cuando tenga sentido que su aplicación esté puramente orientada a datos sin reglas comerciales adicionales o procesos de interfaz de usuario.


Respuesta popular

propongo mi solución, para devolver el texto final:

    createPROCEDURE [dbo].[Helper_CreatePocoFromTableName]    
    @tableName varchar(100)
AS
BEGIN
SET NOCOUNT ON;
declare @codeLines table (lineId int, lineText varchar(4000))

insert into @codeLines
Select  rowNr = ROW_NUMBER() over(order by rowNr), PropertyColumn from (
    SELECT 1 as rowNr, 'public class ' + @tableName + ' {' as PropertyColumn
    UNION
    SELECT  rowNr =2 , 'public ' + a1.NewType + ' ' + a1.COLUMN_NAME + ' {get;set;}' as PropertyColumn
    -- ,* comment added so that i get copy pasteable output
     FROM 
    (
        /*using top because i'm putting an order by ordinal_position on it. 
        putting a top on it is the only way for a subquery to be ordered*/
        SELECT TOP 100 PERCENT
        COLUMN_NAME,
        DATA_TYPE,
        IS_NULLABLE,
        CASE 
            WHEN DATA_TYPE = 'varchar' THEN 'string'
            WHEN DATA_TYPE = 'nvarchar' THEN 'string' 
            WHEN DATA_TYPE = 'char' THEN 'string'
            WHEN DATA_TYPE = 'nchar' THEN 'string'
            WHEN DATA_TYPE = 'datetime' AND IS_NULLABLE = 'NO' THEN 'DateTime'
            WHEN DATA_TYPE = 'datetime' AND IS_NULLABLE = 'YES' THEN 'DateTime?'
            WHEN DATA_TYPE = 'smalldatetime' AND IS_NULLABLE = 'NO' THEN 'DateTime'
            WHEN DATA_TYPE = 'datetime2' AND IS_NULLABLE = 'NO' THEN 'DateTime'
            WHEN DATA_TYPE = 'smalldatetime' AND IS_NULLABLE = 'YES' THEN 'DateTime?'
            WHEN DATA_TYPE = 'datetime2' AND IS_NULLABLE = 'YES' THEN 'DateTime?'
            WHEN DATA_TYPE = 'int' AND IS_NULLABLE = 'YES' THEN 'int?'
            WHEN DATA_TYPE = 'int' AND IS_NULLABLE = 'NO' THEN 'int'
            WHEN DATA_TYPE = 'smallint' AND IS_NULLABLE = 'NO' THEN 'Int16'
            WHEN DATA_TYPE = 'smallint' AND IS_NULLABLE = 'YES' THEN 'Int16?'
            WHEN DATA_TYPE = 'decimal' AND IS_NULLABLE = 'NO' THEN 'decimal'
            WHEN DATA_TYPE = 'decimal' AND IS_NULLABLE = 'YES' THEN 'decimal?'
            WHEN DATA_TYPE = 'numeric' AND IS_NULLABLE = 'NO' THEN 'decimal'
            WHEN DATA_TYPE = 'numeric' AND IS_NULLABLE = 'YES' THEN 'decimal?'
            WHEN DATA_TYPE = 'money' AND IS_NULLABLE = 'NO' THEN 'decimal'
            WHEN DATA_TYPE = 'money' AND IS_NULLABLE = 'YES' THEN 'decimal?'
            WHEN DATA_TYPE = 'bigint' AND IS_NULLABLE = 'NO' THEN 'long'
            WHEN DATA_TYPE = 'bigint' AND IS_NULLABLE = 'YES' THEN 'long?'
            WHEN DATA_TYPE = 'tinyint' AND IS_NULLABLE = 'NO' THEN 'byte'
            WHEN DATA_TYPE = 'tinyint' AND IS_NULLABLE = 'YES' THEN 'byte?'
            WHEN DATA_TYPE = 'char' THEN 'string'                       
            WHEN DATA_TYPE = 'timestamp' THEN 'byte[]'
            WHEN DATA_TYPE = 'varbinary' THEN 'byte[]'
            WHEN DATA_TYPE = 'bit' AND IS_NULLABLE = 'NO' THEN 'bool'
            WHEN DATA_TYPE = 'bit' AND IS_NULLABLE = 'YES' THEN 'bool?'
            WHEN DATA_TYPE = 'xml' THEN 'string'
        END AS NewType
        FROM INFORMATION_SCHEMA.COLUMNS 
        WHERE TABLE_NAME = @tableName
        ORDER BY ORDINAL_POSITION
        ) AS a1 
    UNION 
    SELECT 1000 as rowNr,  '} // class ' + @tableName
    ) as t Order By rowNr asc


declare @max int=(select max(lineId) from @codeLines)

-- assembly result 
declare @i int=1
declare @res nvarchar(max)=''

while(@i<=@max)
begin
  set @res = @res +(select lineText +'
  ' from @codeLines l where l.lineId=@i )

  set @i=@i+1
end

select classCode=@res
END


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow