1

嗨,他试图找到性能最好的 ORM,以便在我们的新项目中使用。我的最终选择变成了 Dapper。我们还需要让我们的应用程序包含以下功能(至少),这些功能可以防止我们对 SQL 查询进行硬编码以传递给 Dapper

  1. 独立于数据库
  2. 运行时实体定义

我想过为 Dapper 编写一个 SQL 生成器,但我不确定我遵循的方法是最好的:

  1. 声明一个带有方法签名的接口。该实现对应于要使用的数据库系统(SQL Server/ MySQL/ PostgreSql/ DB2/ Oracle/ 等...)。
  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. 实现在当前实体的属性上循环的方法(使用反射)并生成属性不为空的 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)*/
    

你能想到这种方法的任何倒退/缺点吗?你能推荐一些改进吗?

谢谢!

4

2 回答 2

1

使用 MEF 代替配置

如果您取消所有这些配置,只对这些提供程序进行硬编码,并使用 MEF 来发现您的应用程序中包含哪一个并使用那个会怎样?然后,当您要连接到不同的数据库时,您会编写一个新的提供程序并替换提供程序的程序集吗?然后 MEF 将完成剩下的工作。

在生产过程中添加新实体而无需重新编译

但是,当您在评论中添加了更多详细信息时,我想说的是,您尝试这样做的方式是可行的,除了我要引入一些更改:

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

  2. 您的示例数据库模式似乎具有您自己定义的语法。也许宁愿使用已经证明并允许标准化并且可能也更复杂的定义的东西。

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

  4. 由于您将在您的应用程序中广泛使用反射,它可能会大大减慢它。我建议您使用更好(=更快)的方法。查看Mark Gravell在 NuGet 上的这个库。

生成和使用设计时未知实体

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

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

于 2012-10-22T07:21:41.787 回答
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
于 2015-01-30T13:13:24.553 回答