【问题标题】:Micro ORM - maintaining your SQL query stringsMicro ORM - 维护你的 SQL 查询字符串
【发布时间】:2011-05-14 11:48:58
【问题描述】:

我不会详细说明我在这个阶段探索使用 Micro ORM 的原因 - 只是说当我使用完整的 ORM 时我感到无能为力。有太多的事情在后台自动发生,并不是所有的事情都是最好的选择。我已经准备好回到原始数据库访问,但我发现了三个新人:Dapper、PetaPoco 和 Massive。所以我决定在一个宠物项目中尝试一下低层次的方法。这无关紧要,但到目前为止,我正在使用 PetaPoco。

无论如何,我都无法决定如何维护我将从更高级别使用的 SQL 字符串。我能想到的主要解决方案有以下三种:

  1. 将 SQL 查询分散在我需要的任何地方。这是最不重基础设施的方法。但是,它在可维护性和可测试性方面都受到影响。

  2. 将查询使用限制为某些服务类。这有助于可维护性,我需要实现的基础设施仍然很低。也可以构建这些服务类,以便为测试目的轻松模拟。

  3. 准备一些类以使系统具有一定的灵活性。我已经开始走这条路了。我实现了一个 Repository 接口和一个依赖于数据库的 Repository 类。我还构建了一些小接口来捕获可以传递给我的存储库的 GetMany() 方法的 SQL 查询。现在所有的查询都作为单独的类实现,我可能需要更多的接口来增加数据库的独立性——也许在将查询装饰成分页和排序查询时有一些灵活性(同样,这也会使它们在处理不同的数据库时更加灵活)。

我现在主要担心的是,我已经进入了编写完整 ORM 所需的所有函数的滑坡,但是很糟糕。例如,我现在感觉很明智,我编写或找到一个库来将 linq 调用转换为 SQL 语句,这样我就可以轻松地处理我的查询,或者编写可以装饰我传递给它的任何查询的扩展程序,等等。但这是一个很大的问题任务,而且已经被大佬们完成了,所以我忍住了要去那里的冲动。我还想保留对我发送到数据库的查询的控制 - 通过显式编写它们。

那么建议是什么?我应该选择#2选项,还是尝试偶然发现选项#3?我敢肯定,我不能不脸红地向任何人展示第一个选项中编写的任何代码。您还有其他方法可以推荐吗?


编辑:在我提出这个问题后,我意识到还有另一个选项,与这三个选项有些正交:存储过程。将所有查询作为存储过程放入数据库中似乎有一些优点。它们被保存在一个中心位置,而不是通过代码传播(尽管维护是一个问题 - 参数可能会不同步)。对数据库方言的依赖是自动解决的:如果你移动数据库,你移植所有的存储过程,你就完成了。还有安全方面的好处。

使用存储过程选项,备选方案 1 和 2 似乎更合适一些。似乎没有足够的实体来保证选项 3 - 但仍然可以将过程调用命令与数据库访问代码分开。

我已经实现了不带存储过程的选项 3,以及带有存储过程的选项 2,似乎后者更适合我(以防有人对问题的结果感兴趣)。

【问题讨论】:

  • 在深入研究细节之前...您检查过 ibatis.net 吗?
  • 这里没有适合所有尺寸的答案,这实际上取决于您拥有的项目类型....您也可以选择混合搭配
  • @p.marino 我做到了,我也被它诱惑了。我更喜欢我提到的三个接口。不过,我最终可能会将我的宠物项目移植到那里,看看感觉如何。
  • @Sam 我同意你的看法。如果您愿意,可以将其视为一次钓鱼探险。我对 DB 应用程序领域有些陌生,如果有我还没有看到的选项,我会很高兴有机会检查它们。
  • 不确定这是否有帮助,但我们使用抽象工厂模式管理我们的 sql 连接(对我们的需求非常灵活,希望它也能让其他人受益?):stackoverflow.com/questions/2969841/…跨度>

标签: sql orm


【解决方案1】:

我会说将 sql 放在您将放置等效 LINQ 查询的位置,或者将 sql 放在 DataContext.ExecuteQuery 的位置。至于那在哪里……嗯,这取决于你,取决于你想要多少分离。 - Marc Gravell,Dapper 的创造者

See Marc's opinion on the matter

我认为关键是,您不应该真正重用 SQL。如果你的逻辑被重用,那么它应该被包装在一个可以从多个地方调用的方法中。

【讨论】:

  • 谢谢,我没有看到这个问题 - 可能是在我阅读了包含“dapper”一词的问题后提出的。
【解决方案2】:

我知道您已经接受了您的答案,但我仍然想向您展示一个不错的替代方案,它也可能对您的情况有所帮助。现在或将来。

使用存储过程时最好使用 T4

我倾向于在我的项目中使用存储过程,即使它没有使用 PetaPoco、Dapper 或 Massive(项目在这些出现之前就开始了)。它使用 BLToolkit 代替。反正。我没有编写方法来运行存储过程并编写代码来提供存储过程参数,而是使用 written a T4 template 为我生成代码。

每当存储过程发生变化(一些可能被添加/删除,参数添加/删除/重命名/重新输入),我的代码将在编译时中断,因为方法调用将不再匹配它们的签名。

我将我的存储过程保存在一个文件中(以便它们获得版本控制)。如果您在一个多开发人员团队中工作,将存储过程分别放在自己的文件中可能是明智的。它使更新变得不那么痛苦。我在一些项目中经历过,只要 SP 的数量不是很大,它就可以正常工作。您可以根据与它们相关的实体将它们重组到文件夹中。

无论如何。维护与存储过程有关,代码更改只需单击 Visual Studio 中的一个按钮,即可一次转换所有 T4。您不必搜索使用这些过程的方法。编译时会报错。少担心一件事。

所以不要写

using (var db = new DbManager())
{
    return db
        .SetSpCommand(
            "Person_SaveWithRelations",
            db.Parameter("@Name", name),
            db.Parameter("@Email", email),
            db.Parameter("@Birth", birth),
            db.Parameter("@ExternalID", exId))
        .ExecuteObject<Person>();
}

还有一堆魔术字符串,我可以简单地写:

using (var db = new DataManager())
{
    return db
        .Person
        .SaveWithRelations(name, email, birth, exId)
        .ExecuteObject<Person>();
}

这在编译时更好、更清晰,并提供智能感知,因此在开发时也更快。

好消息是存储过程可能会变得非常复杂并且可以做很多事情。在上面的示例中,我检查了一些数据,插入人员记录和一些相关的记录,最后返回新插入的 Person 记录。插入和更新通常应返回为反映实际状态而添加/更改的数据。

【讨论】:

  • 非常好的主意。我只见过 T4 一次,我认为它是我不会使用的东西。这个答案告诉我,我不应该太快忽略这些事情。我一定会在玩具项目中尝试一下,以感受一下。现在,我在存储过程之间来回切换并在代码中保留查询。这种方法可能会给存储过程+1。 :)
  • @Dysaster:我不喜欢法式沙拉酱...如果您知道我的意思... ;) 事实是我不太喜欢将 TSQL 与 C# 代码混合因为我讨厌将 Javascript/HTML 与 C# 代码混合。存储过程应该是它们自己空间的一部分,也不应该与 C# 混合使用。 T4 可以透明地将它们转换为可用的形式,从而消除忘记输入错误等人为错误因素(前提是您的 T4 没有错误)。
猜你喜欢
  • 1970-01-01
  • 2013-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-24
  • 1970-01-01
  • 2021-07-05
  • 2021-02-05
相关资源
最近更新 更多