既然我们拥有Micro ORM,内联SQL是否仍被列为不良做法?


26

这是一个开放式的问题,但我想提出一些意见,因为我成长于一个以内联SQL脚本为标准的世界,然后我们都非常了解基于SQL注入的问题以及sql当时多么脆弱在各处进行字符串操作。

然后是ORM的曙光,您在其中向ORM解释查询并让它生成自己的SQL,在很多情况下,这种查询不是最佳方法,但又安全又容易。有关ORM或数据库抽象层的另一个好处是,SQL是在考虑数据库引擎的情况下生成的,因此我可以将Hibernate / Nhibernate与MSSQL,MYSQL一起使用,而我的代码从未更改,它只是一个配置细节。

现在快速发展到今天,Micro ORM似乎正在赢得更多开发人员的支持,我想知道为什么我们似乎在整个内联sql主题上都掉头了。

我必须承认,我确实喜欢没有ORM配置文件的想法,并且能够以更优化的方式编写查询,但是感觉就像我向诸如SQL注入之类的旧漏洞敞开了怀抱,而且我也将自己束缚于一个数据库引擎,因此,如果我希望我的软件支持多个数据库引擎,则需要做更多的字符串黑客攻击,这似乎开始使代码变得不可读且更脆弱。(就在有人提到它之前,我知道您可以在大多数micro orms中使用基于参数的参数,这在大多数情况下都可以防止sql注入)

那么人们对此事有何看法?在这种情况下,我将Dapper用作微型ORM,在这种情况下将NHibernate用作常规ORM,但是在每个领域中的大多数情况都非常相似。

我所说的内联SQL是源代码中的SQL字符串。曾经有过关于源代码中SQL字符串的设计争论,这有损于逻辑的基本意图,这就是为什么静态类型的linq样式查询如此流行的原因,它仍然仅是一种语言,但是在一页中说C#和Sql现在,您的原始源代码中混合了2种语言。为了澄清起见,SQL注入只是使用sql字符串的已知问题之一,我已经提到过可以通过基于参数的查询阻止这种情况的发生,但是我着重指出了在源代码中根植SQL查询的其他问题,例如缺少DB Vendor抽象以及在基于字符串的查询上丢失任何级别的编译时错误捕获功能,这些都是我们设法通过ORM的高级查询功能来解决的问题,

因此,我不太关注各个突出的问题,而更全局的是,由于大多数Micro ORM使用这种机制,现在再次将SQL字符串直接再次包含在源代码中变得越来越容易被接受。

这是一个类似的问题,它具有一些不同的观点,尽管更多是关于没有微规范上下文的内联sql:

/programming/5303746/is-inline-sql-hard-coding


1
您是否认为您可以以不征求意见的方式改写这个问题?意见征询与Stack Exchange的问答格式不太吻合。

您始终可以在单独的类中抽象“ Micro ORM”查询,在必要时可以在更改DBMS时交换正在执行的查询。
CodeCaster

还没有听说过“ Micro ORM”一词。您是说不尝试完全取代SQL的ORM,还是有些不同?
哈维尔

没关系,经过一番谷歌搜索,这似乎是一个.net东西。
哈维尔

1
@GrandmasterB:请解释为什么。我已经在回答中回避了这个问题,因为我认为这是一个权衡的问题。
罗伯特·哈维

Answers:


31

您所说的“内联SQL”实际上应该称为“不带参数化的字符串连接”,并且不必为了安全地使用Micro ORM而这样做。

考虑以下Dapper示例:

string sql = "SELECT * from user_profile WHERE FirstName LIKE @name;";
var result = connection.Query<Profile>(sql, new {name = "%"+name+"%"});

即使正在进行字符串连接,它也已完全参数化。看到@符号了吗?

还是这个例子:

var dog = connection.Query<Dog>("select Age = @Age, Id = @Id", 
          new { Age = (int?)null, Id = guid });

这大致等效于以下ADO.NET代码:

List<Dog> dog = new List<Dog>();
using(var cmd = connection.CreateCommand()) {
    cmd.CommandText = "select Age = @Age, Id = @Id";
    cmd.Parameters.AddWithValue("Age", DBNull.Value);
    cmd.Parameters.AddWithValue("Id", guid);
    using(var reader = cmd.ExecuteReader()) {
        while(reader.Read()) {
            int age = reader.ReadInt32("Age");
            int id = reader.ReadInt32("Id");
            dog.Add(new Dog { Age = age, Id = id });
        }
    }
}

如果您需要的灵活性更大,Dapper提供了SQL模板和一个AddDynamicParms()函数。所有SQL注入安全。

那么为什么要首先使用SQL字符串呢?

嗯,出于相同的原因,您将在任何其他ORM中使用自定义SQL。也许ORM是代码生成次优的SQL,您需要对其进行优化。也许您想做一些本机上很难在ORM中完成的事情,例如UNION。或者,也许您只是想避免生成所有那些代理类的复杂性。

如果您确实不想在Dapper中为每个CRUD方法编写SQL字符串(谁这样做),则可以使用以下库:

https://github.com/ericdc1/Dapper.SimpleCRUD/

这将为您提供极其简单明了的CRUD,同时仍为您提供手写SQL语句的灵活性。记住,上面的ADO.NET示例是每个人在ORM出现之前所做的方式。Dapper只是薄薄的单板。


很好的例子,我已经提到过您可以通过参数来回避SQL注入,因为这只是一个较大的问题,还添加了一个编辑,阐明了使用术语“ 内联sql”时的含义。
Grofit

我在回答中添加了一些细节。
罗伯特·哈维

2
有关SimpleCRUD的信息+1并不存在。
Grofit

在Java中,你会发现的MyBatis比休眠或EclipseLink的和ligther。它的处理方式是使用XML中的预定义语句(而不是对代码进行硬编码)。它还添加了一些有趣的功能,作为增强最终SQL的前提。我们最近使用了一个API网络,该网络具有很高的并发性,并且业务并不复杂,并且运行良好。
2016年

2

我喜欢sql,无法忍受看到它被字符串文字砍掉了,所以我写了一个VS扩展名,让您在c#项目中使用真实的sql文件。在自己的文件中编辑sql可以使您智能化列和表,语法验证,测试执行,执行计划以及其他内容。保存文件时,我的扩展程序会生成c#包装器类,因此您永远不必编写一行连接代码,命令代码,参数或读取器代码。因为没有其他方法,所以对查询都进行了参数化,并且已经生成了用于单元测试的存储库和POCO,并对输入参数和结果进行了智能感知。

而且我还免费提供了牛排刀……当专家需要来重新开发开发人员的sql时,她正在查看一个真正的sql文件,并且不需要接触任何C#。当她返回并整理数据库时,删除了一些列,对缺失列的引用由于c#中的编译错误而直接跳出。数据库就像解决方案中的另一个可以入侵的项目一样,它的界面可以在代码中发现。如果解决方案可以编译,则说明所有查询都在工作。没有由于无效的转换,无效的列名或无效查询而导致的运行时错误。

回顾我们仍在使用的dapper,我不敢相信在2016年这是数据访问中最酷的事情。运行时msil生成在所有情况下都是聪明的,但是所有错误都是运行时错误,并且字符串操作(如出于任何其他目的的字符串操作)是耗时,易碎且容易出错的。不得不在POCO中重复输入列名,这是多么干燥的事情,您必须手工编写和维护这些列名。

所以你去了。如果您想了解一下一个闻所未闻的数据访问技术,而这是闻所未闻的开发人员在业余时间(带着一个年幼的孩子和许多其他爱好)工作的地方,那就在这里


您似乎想了很多“创新”,但这与Entity Framework中的ExecuteStoreQueryExecuteStoreCommand有何不同?
罗伯特·哈维

我没有参加EF或SQL辩论。我的事情是给想使用SQL的人的。因此,作为执行SQL的一种方式,ExecuteStoreQuery()将为您填充POCO,但是您仍然需要通过外观来定义POCO。它并没有帮助您将sql放入文件或创建参数。您的查询无法在代码中发现或无法测试。删除列不会在您的应用程序中产生编译错误。我可以继续,但我担心自己会重复自己的内容:-)也许您可能需要3分钟才能观看youtube视频。它是法语的,请降低声音!英语来了。
bbsimonbb

EF具有自动生成所有POCO的能力。我在这里想念什么?Dapper和Massive的真正创新之处在于,您根本不需要POCO。您可以使用dynamic
罗伯特·哈维

我对EF几乎一无所知。ExecuteStoreQuery()将填充一个实体。它会根据查询生成一个实体。实体是从数据库对象生成的(反之亦然),而不是从查询生成的。如果您的查询与现有实体不符,则必须编写POCO,不是吗?
bbsimonbb

1
:-)必须说,当我自由地提出自己的最佳想法时,我对广告收费变得很敏感。这是我最新的侵略性商业行为。到3分钟时,您应该知道我是否在做某事。
bbsimonbb

1

通过参数化查询以及存储库设计模式,您可以完全控制查询内容以防止注入攻击。在这种情况下,内联SQL除了对于大型和复杂查询的可读性外,不是什么问题,无论如何应将其放在存储过程中。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.