Dapper 的 SQL 查询生成器
Posted
技术标签:
【中文标题】Dapper 的 SQL 查询生成器【英文标题】:SQL Query Generator for Dapper 【发布时间】:2012-10-21 21:19:55 【问题描述】:Hi 试图找到在我们的新项目中使用的性能最佳的 ORM。我的最终选择变成了 Dapper。我们还需要让我们的应用程序(至少)包含以下功能,这些功能可以防止我们对 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>
使用上面提供的 XML 生成类/实体(允许在运行时扩展模式)。创建的对象是 POCO。
实现在当前实体的属性上循环的方法(使用反射)并生成属性不为空的 SQL 语句:
String GetInsert(object currentEntity)
return:
"INSERT INTO Foo (ID, Name) VALUES (1, 'BooBoo')"
实施将至少包括
SELECT
INSERT INTO
UPDATE
DELETE
JOIN /*(using references like the KooID above)*/
WHERE /*(using filter expressions)*/
您能想到这种方法的任何倒退/缺点吗?你能推荐一些改进吗?
谢谢!
【问题讨论】:
你是否看过 dapper 的 contrib/rainbow 部分,它们旨在提供自动 CRUD 支持(其中 dapper “核心”只是查询/执行)?另外:如果它没有您需要的功能......完全有可能它不是正确的选择工具。另外:您可以使用多个工具(也许 dapper 用于读取密集型显示页面,另一个 ORM 用于数据编辑页面) 我将选择这款经典的方形钉圆孔。另外,如果你没有看过 petapoco,它很简洁,并带有一个 poco 生成器来处理类。 【参考方案1】:使用 MEF 代替配置
如果您取消所有这些配置,只对这些providers 进行硬编码,然后使用 MEF 来发现您的应用程序中包含哪个配置并使用该配置,该怎么办?然后,当您要连接到不同的数据库时,您会编写一个新的提供程序并替换提供程序的程序集吗?然后 MEF 将完成剩下的工作。
在生产过程中添加新实体而无需重新编译
但是,当您在 cmets 中添加了更多细节时,我想说的是,您尝试这样做的方式是可行的,除了我要介绍一些更改:
仍然可以使用 MEF 实现数据库提供程序可发现性,因此您只需将提供程序程序集放到您的 bin 文件夹中,您的应用程序就会使用它。这当然也可以通过配置来完成。如何实例化正确的提供者由您决定。
您的示例数据库模式似乎具有您自己定义的语法。也许宁愿使用已经证明的东西,允许标准化,也可能更复杂的定义。
您的 UI(视图或您使用的任何东西)实际上是可以使用 shcema XML 而不是 POCO 的模板。 POCO 对象只会向用户界面传递数据。
由于您将在应用程序中广泛使用反射,它可能会大大减慢它的速度。我建议您使用更好(=更快)的方法。 Look at this library on NuGet Mark Gravell。
生成和使用设计时未知实体
在设计期间(从编译代码的角度来看)数据实体将是未知的,因为这些 POCO 将在运行时从 XML 模式生成。除非您的应用程序纯粹是面向数据库的(如直接操作数据库中的表),否则我看不出您将如何使用硬编码 UI 来使用这些实体?
正如您提到的,您的 UI 实际上能够读取相同的数据架构 UI,并根据从数据库读取的 POCO 实例填充它。这一切都很好,只要您的应用程序完全面向数据而不需要额外的业务规则或用户界面流程是有意义的。
【讨论】:
您将失去运行时实体定义功能。您无法猜测用户将使用哪些名称来对其进行硬编码。 运行时定义到底是什么意思?这是否意味着您将编辑您的实体配置文件并重新启动您的应用程序,或者是否有其他方式来添加新实体? 我的意思是,在我的应用程序中,用户能够定义自己的数据对象类型(即实体)。我愿意将每个实体存储在单独的表中,因此只需使用新实体模式扩展 XML 文件,其余部分将由预期的 SQL 生成器处理。 @MoslemBenDhaou:1. 你认为如何在运行时创建强类型 POCO?并使用它们? 2. 您如何期望那些 POCO 被现有 UI 消耗,而在设计时不知道这些 POCO,因为它们还不存在? 3. 我认为您的计划是在添加新实体时不重新编译您的应用程序,对吧? 您提出的关于使用硬编码 UI 操作实体的观点很好。正如您所说,我当前(第 1 步)的需求几乎是数据操作或面向数据的应用程序。我将使用通用视图和流程来处理数据和触发事件。我可能不得不尽快想出一个解决方案来调整设计。感谢您的意见!【参考方案2】:我提出我的解决方案,以返回最终文本:
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
【讨论】:
以上是关于Dapper 的 SQL 查询生成器的主要内容,如果未能解决你的问题,请参考以下文章
调试 Dapper Contrib(或任何 ORM) - 如何在 Visual Studio 中查看生成的 SQL - 例如更新?