Micro ORM - 维护您的SQL查询字符串

时间:2011-05-14 11:48:58

标签: sql orm

我不会详细说明为什么我在这个阶段探索使用Micro ORM - 除非说我使用完整的ORM时感到无能为力。背景中发生的事情太多,是自动发生的,而且并非所有事情都是最好的选择。我已经准备好回到原始数据库访问,但我发现了块中的三个新人:Dapper,PetaPoco和Massive。因此,我决定将低级方法与宠物项目结合起来。这是不相关的,但到目前为止,我正在使用PetaPoco。

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

  1. 将SQL查询洒在我需要的任何地方。这是基础设施最重的方法。但是,它在可维护性和可测试性方面都存在问题。

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

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

  4. 我现在主要担心的是,我已经进入了写出完整的ORM所需的所有功能的滑坡,但很糟糕。例如,我现在感觉很合理,我编写或找到一个库来将linq调用转换为SQL语句,以便我可以轻松地按下我的查询或编写可以修饰我传递给它的任何查询的扩展器等等。但这是一个很大的任务,已经由大家伙完成,所以我抵制去那里的冲动。我还希望通过显式写入来保持对发送给数据库的查询的控制。

    那么建议是什么?我应该选择#2选项,还是试着在选项#3上绊倒?我确信我不能在没有脸红的情况下向任何人显示第一个选项中写的任何代码。你还可以推荐其他方法吗?


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

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

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

2 个答案:

答案 0 :(得分:6)

  

我想说将sql放在等效的LINQ查询中,或者将sql用于DataContext.ExecuteQuery。至于那里......嗯,这取决于你,取决于你想要多少分离。 - Dapper的创始人Marc Gravell

See Marc's opinion on the matter

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

答案 1 :(得分:4)

我知道你已经接受了你的答案,但我仍然希望向你展示一个很好的替代方案,也可能对你的情况有所帮助。现在或将来。

使用存储过程时,最好使用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记录。插入和更新通常应返回已添加/更改的数据以反映实际状态。