Dapper的SQL查询生成器

时间:2012-10-21 21:19:55

标签: c# sql orm dapper

您正在尝试找到具有最佳性能的ORM,以便在我们的新项目中使用。我最后的选择变成了Dapper。我们还需要让我们的应用程序包含以下功能(至少),这些功能阻止我们将SQL查询的硬编码传递给Dapper

  1. 独立于数据库
  2. 运行时实体定义
  3. 我考虑过为Dapper编写一个SQL生成器,但我不确定我所遵循的方法是最好的:

    1. 使用方法签名声明接口。该实现对应于要使用的数据库系统(SQL Server / MySQL / PostgreSql / DB2 / Oracle / etc ......)。
    2. 使用以下格式创建数据库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>
      
    3. 使用上面提供的XML生成类/实体(允许在运行时扩展模式)。创建的对象是POCO。

    4. 实现循环当前实体属性的方法(使用reflexion)并生成属性不为null的SQL语句:

      String GetInsert(object currentEntity)
      return:
      "INSERT INTO Foo (ID, Name) VALUES (1, 'BooBoo')"
      
    5. 实施至少包括

      SELECT
      INSERT INTO
      UPDATE
      DELETE
      JOIN /*(using references like the KooID above)*/
      WHERE /*(using filter expressions)*/
      
    6. 你能想到这种方法的任何倒退/劣势吗?你能推荐一些改进吗?

      谢谢!

2 个答案:

答案 0 :(得分:1)

使用MEF代替配置

如果您解除所有这些配置并仅对这些提供程序进行硬编码并使用MEF来发现应用程序中包含哪些配置并使用该配置,该怎么办?那么当你要连接到另一个数据库时,你会编写一个新的提供者并替换提供者的程序集?然后MEF将完成剩下的工作。

添加新实体,无需重新编译即可进行生产

但是,当你在评论中添加更多细节时,我想说你尝试这样做的方式是要走的路,除了我引入一些变化:

  1. 数据库提供程序的可发现性仍然可以使用MEF实现,因此您只需将提供程序程序集删除到bin文件夹,您的应用程序就会使用它。这当然也可以通过配置来完成。您可以决定如何实例化正确的提供者。

  2. 您的示例数据库架构似乎有自己定义的语法。也许宁可使用已经过验证的东西,并允许标准化,也可能更复杂的定义。

  3. 您的UI(视图或您使用的任何内容)实际上是可以使用shcema XML而不是POCO的模板。 POCO对象只会数据传送到UI。

  4. 由于您将在您的应用中广泛使用反射,因此可能会大大降低速度。我建议你使用更好(=更快)的方法。 Mark Gravell Look at this library on NuGet

  5. 生成和消耗设计时未知实体

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

    正如您所提到的,您的UI实际上可以读取相同的数据模式UI,并根据从数据库中读取的POCO实例填充它。只要在没有其他业务规则或用户界面流程的情况下将您的应用程序纯粹面向数据,这一切都很好。

答案 1 :(得分:0)

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

    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