C# Dapper的SQL查询生成器

C# Dapper的SQL查询生成器,c#,sql,orm,dapper,C#,Sql,Orm,Dapper,Hi试图在我们的新项目中找到性能最好的ORM。我最后的选择变得得体了。我们还需要让我们的应用程序包括(至少)以下特性,这些特性防止我们硬编码SQL查询以传递给Dapper 独立于数据库 运行时实体定义 我曾考虑为Dapper编写一个SQL生成器,但我不确定我采用的方法是否是最好的: 声明具有签名方法的接口。该实现对应于要使用的数据库系统(SQL Server/MySQL/PostgreSql/DB2/Oracle/etc…) 使用以下格式创建数据库XML架构: <sqltable name

Hi试图在我们的新项目中找到性能最好的ORM。我最后的选择变得得体了。我们还需要让我们的应用程序包括(至少)以下特性,这些特性防止我们硬编码SQL查询以传递给Dapper

  • 独立于数据库
  • 运行时实体定义
  • 我曾考虑为Dapper编写一个SQL生成器,但我不确定我采用的方法是否是最好的:

  • 声明具有签名方法的接口。该实现对应于要使用的数据库系统(SQL Server/MySQL/PostgreSql/DB2/Oracle/etc…)
  • 使用以下格式创建数据库XML架构:

    <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>
    
  • 实施将至少包括

    SELECT
    INSERT INTO
    UPDATE
    DELETE
    JOIN /*(using references like the KooID above)*/
    WHERE /*(using filter expressions)*/
    
  • 你能想到这种方法有什么缺点吗?你能推荐一些改进吗

    谢谢大家!

    使用MEF而不是配置 如果您不考虑所有这些配置,只对这些提供程序进行硬编码,并使用MEF来发现应用程序中包含哪些提供程序并使用该提供程序,该怎么办?然后,当您连接到另一个DB时,您会编写一个新的提供程序并替换提供程序的程序集吗?然后,MEF将完成其余的工作

    在生产过程中添加新实体而无需重新编译 但是,当您在评论中添加了更多细节时,我想说,您尝试这样做的方式是可行的,除了我将介绍一些更改:

  • 数据库提供者的可发现性仍然可以使用MEF实现,因此您所要做的就是将提供者程序集放到您的bin文件夹中,您的应用程序将使用该程序集。当然,这也可以通过配置来完成。由您决定如何实例化正确的提供者

  • 您的示例数据库模式似乎有自己定义的语法。也许更愿意使用已经被证明的东西,允许标准化,也可能更复杂的定义

  • 您的UI(视图或您使用的任何东西)实际上是可以使用shcema XML而不是POCO的模板。POCO对象将只向UI传递数据

  • 由于你将在你的应用程序中广泛使用反射,它可能会大大降低速度。我建议你采用更好(更快)的方法。马克·格雷威尔

  • 生成和共享设计时未知实体 在设计期间(从编译代码的角度来看),数据实体将是未知的,因为这些POCO将在运行时从XML模式生成。除非您的应用程序纯粹是面向数据库的(如直接操作数据库中的表),否则我看不出您将如何使用硬编码UI使用这些实体

    正如您所提到的,您的UI实际上能够读取相同的数据模式UI,并基于从数据库读取的POCO实例填充它。只要您的应用程序完全面向数据,而不需要额外的业务规则或用户界面流程,就可以了。

    使用MEF而不是配置 如果您不考虑所有这些配置,只对这些提供程序进行硬编码,并使用MEF来发现应用程序中包含哪些提供程序并使用该提供程序,该怎么办?然后,当您连接到另一个DB时,您会编写一个新的提供程序并替换提供程序的程序集吗?然后,MEF将完成其余的工作

    在生产过程中添加新实体而无需重新编译 但是,当您在评论中添加了更多细节时,我想说,您尝试这样做的方式是可行的,除了我将介绍一些更改:

  • 数据库提供者的可发现性仍然可以使用MEF实现,因此您所要做的就是将提供者程序集放到您的bin文件夹中,您的应用程序将使用该程序集。当然,这也可以通过配置来完成。由您决定如何实例化正确的提供者

  • 您的示例数据库模式似乎有自己定义的语法。也许更愿意使用已经被证明的东西,允许标准化,也可能更复杂的定义

  • 您的UI(视图或您使用的任何东西)实际上是可以使用shcema XML而不是POCO的模板。POCO对象将只向UI传递数据

  • 由于你将在你的应用程序中广泛使用反射,它可能会大大降低速度。我建议你采用更好(更快)的方法。马克·格雷威尔

  • 生成和共享设计时未知实体 在设计期间(从编译代码的角度来看),数据实体将是未知的,因为这些POCO将在运行时从XML模式生成。除非您的应用程序纯粹是面向数据库的(如直接操作数据库中的表),否则我看不出您将如何使用硬编码UI使用这些实体


    正如您所提到的,您的UI实际上能够读取相同的数据模式UI,并基于从数据库读取的POCO实例填充它。只要您的应用程序完全面向数据,而不需要额外的业务规则或用户界面流程,这一切都没有问题。

    为了返回最终文本,我提出了我的解决方案:

        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
    
    createPROCEDURE[dbo]。[Helper\u CreatePocoFromTableName]
    @tableName varchar(100)
    作为
    开始
    不计数;
    声明@codeLines表(lineId int,lineText varchar(4000))
    插入@codeLines
    选择rowNr=ROW_NUMBER()(按rowNr排序),Property Column from(
    选择1作为行编号,“公共类”++@tableName++“{”作为属性列
    联合
    选择rowNr=2,'public'+a1.NewType+''+a1.COLUMN_NAME+'{get;set;}'作为PropertyColumn
    --,*添加注释,以便获得可复制粘贴的输出
    从…起
    (
    /*使用top是因为我是按顺序排列的。
    为子查询排序的唯一方法是在其上放置一个top*/
    选择前100%
    列名称,
    数据类型,
    你是纳拉布吗
    
        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