如何建议使用ORM代替存储过程?


31

我在一家仅使用存储过程进行所有数据访问的公司工作,这使我们的本地数据库保持同步非常烦人,因为每次提交都必须运行新的proc。过去,我曾经使用过一些基本的ORM,但是我发现使用它的经验要好得多,更干净。我想向开发经理和团队中的其他成员建议我们考虑使用某种ORM进行未来开发(团队中的其他成员仅熟悉存储过程,而从未使用过其他东西)。当前的体系结构是.NET 3.5,类似于.NET 1.1,具有“神类”,它们使用ActiveRecord的奇怪实现并返回在代码隐藏文件中循环的未类型化数据集-这些类的工作方式如下:

class Foo { 
    public bool LoadFoo() { 
        bool blnResult = false;
        if (this.FooID == 0) { 
            throw new Exception("FooID must be set before calling this method.");
        }

        DataSet ds = // ... call to Sproc
        if (ds.Tables[0].Rows.Count > 0) { 
            foo.FooName = ds.Tables[0].Rows[0]["FooName"].ToString();
            // other properties set
            blnResult = true;
        }
        return blnResult;
    }
}

// Consumer
Foo foo = new Foo();
foo.FooID = 1234;
foo.LoadFoo();
// do stuff with foo...

几乎没有任何设计模式的应用。根本没有任何测试(其他人都不知道如何编写单元测试,并且通过手动加载网站并四处浏览来完成测试)。查看我们的数据库,我们有:199个表,13个视图,多达926个存储过程和93个函数。大约有30个左右的表用于批处理作业或外部物品,其余的用于我们的核心应用程序。

在这种情况下,甚至值得采用其他方法吗?我说的是向前发展,因为“因为它可以工作”,所以我们不允许重构现有代码,因此我们不能更改现有类以使用ORM,但是我不知道我们多久添加一次全新的模块添加/修复当前模块的过程,因此我不确定ORM是否是正确的方法(对存储过程和数据集的投入过多)。如果是正确的选择,我该如何提出使用它的理由?我想到的唯一好处就是拥有更简洁的代码(尽管可能不是,因为当前的体系结构不是 考虑到ORM的构建,因此我们基本上是将ORM移植到将来的模块上,但旧模块仍将使用DataSet),而不必记住已经运行了哪些过程脚本以及需要运行哪些过程脚本,等等,仅此而已,我不知道这样的论点有多么令人信服。可维护性是另一个问题,但除了我以外,似乎没有人关心。


8
听起来您比仅仅说服团队使用ORM还要麻烦得多。在我看来,您的团队并不了解某些良好的开发实践(例如,设计模式,单元测试)。这些是您需要解决的更重要的问题。
伯纳德

6
具有讽刺意味的是,我认为在大约5年的发展中,我只遇到了少数几个了解设计模式和单元测试之类的人员/团队。通常我是公司中唯一了解这些事情的人。
韦恩·莫利纳

3
@Wayne M:我觉得有点令人不安,但是对此我也并不感到惊讶。
伯纳德

2
我发现这非常令人沮丧。当您提出建议并得到“头灯中的鹿”外观表示其他人对您所谈论的内容或任何人为什么会考虑这样做的模棱两可的想法时,这很奇怪。我过去曾发生过多次。
韦恩·莫利纳

2
我是存储过程的忠实拥护者,因此我的评论有偏见,但我完全不同意整个前提。您喜欢ORM,并且想使用它就可以了。但是,团队的其他成员都可以使用存储过程。为什么要强迫他们达到自己喜欢的程度?
2011年

Answers:


47

存储过程很糟糕,它们通常很慢,并且效率与普通客户端代码差不多。

[提速通常是由于客户端和存储过程接口的设计方式以及将事务编写为短而集中的SQL突发的方式。]

存储过程是放置代码的最糟糕的地方之一。根据通常随机的规则,它将您的应用程序分为两种语言和平台。

[此问题的评分为-30分,因为很多人认为存储过程具有神奇的功能,尽管它们会引起问题,但仍必须使用它。]

将所有存储过程代码移至客户端将使所有人的工作变得更加轻松。

您仍然必须不时更新架构和ORM模型。但是,架构更改与ORM更改是隔离的,从而允许应用程序和数据库架构之间具有一定的独立性。

重写它们时,您将能够测试,修复,维护,理解和调整所有这些存储过程。您的应用程序将以相同的方式运行,并且变得不那么脆弱,因为您不再需要采用两种不同的技术。

ORM并不是魔术,而且出色的数据库设计技能对于使其正常运行至关重要。

此外,由于对事务边界的考虑不周,带有大量客户端SQL的程序可能会变慢。存储过程看起来很快的原因之一是存储过程强制非常非常仔细地设计事务。

ORM不会神奇地强制进行仔细的事务设计。事务设计仍然必须像编写存储过程时一样仔细地进行。


19
1个,因为存储过程是共苦与工作
加里·罗维

3
:'(我没有问题,在存储过程中这只是一个抽象层给我。
迈克·韦勒

3
如果我们创建SP,则数据库引擎会将其存储为编译形式,并创建执行路径,以使其尽快执行。但是ORM每次都会发送SQL,而SQL需要由数据库引擎编译和运行。我认为使用ORM而不是存储过程会更慢。
DeveloperArnab

4
滑稽。我们仅仅因为SPEED而从ORM切换回存储过程。不是我们需要编程的时间,而是ORM处理诸如实现对象,查找对象,在不同客户端上更新之类的时间。SP那里快得多。一个例子:使用新的现代ORM从数据库读取30.000个对象需要……哦。2分钟后超时。调用存储过程并获得结果-2秒。是的-存在许多技巧,例如通过分页将DB的呼叫中断减少到很多,但是,如果只是可以使用ORM或使用ORM,则有很大的不同
Offler 2015年

2
@DeveloperArnab:也许在20年前是对的,但是现代的DB引擎非常复杂,可以识别以前执行的查询和重用执行计划,如今的速度差异如此之小,几乎无法保证SP会带来额外的麻烦。
whatsisname

20

存储过程很好,它们既快速又高效,是放置与数据相关的代码的理想场所。将所有这些代码移至客户端将使您作为客户端开发人员稍微容易一些(略微,因为在提交更改时仍必须更新架构和ORM模型),但是您将丢失所有现有代码,并使得考虑到所有这些sql技能的丧失,您的应用程序速度更慢,而且可能更脆弱。

我想知道DBA是否坐在那里说:“哦,每次提交,我都必须再次拉低客户机,我们应该将所有代码移入DB表单中”。

在您的情况下,您应该能够用其他人替换现有的自​​定义ORM(即您的有趣的类),而不会改变您的代码隐藏代码的编写方式。您也可以保留SP,因为大多数(全部?)ORM会愉快地调用它们。因此,我建议使用ORM替换那些“ Foo”类,然后从那里开始。我不建议您更换SP。

PS。似乎在代码隐藏类中有很多通用代码,这使它们本身成为一种设计模式。您认为设计模式首先是什么!(好的,可能不是最好的,甚至不是一个好的,但它仍然是DP)

编辑:现在有了Dapper,避免在重量级ORM上进行存储的任何理由都消失了。


3
+1,显而易见的第一步是移至ORM并将其用于将现有存储过程的结果映射到对象。摆脱所有这些ds.Tables[0].Rows[0]["FooName"].ToString()废话。经理喜欢存储过程吗?他得到保留它们。但是,将所有重复的样板代码移动到由LINQ to SQL生成的内容中在物理上是不可能的,这是一件坏事。
Carson63000

11
好吧,我不敢相信您的帖子中有多少“错误”。您与DBA抱怨必须提取代码的比较是不合适的,而且完全没有意义。首先,数据库是一个服务,旨在存储和检索数据,这是故事的结尾。顾名思义,LOGIC进入业务逻辑层,这是API代码的一部分。应用更脆弱,远离存储吗?您是认真的还是只是拖钓?TDD一个具有存储逻辑的应用程序,然后告诉我这有多有趣。同样,ORM也不意味着要切换。数据库是,因为它们只是一个服务。
Matteo Mosca,2012年

5
我真的无法理解为什么有人认为数据库是一切神圣的圣地。考虑一下。当第三方公司向您提供服务时,他们是让您直接访问其数据库,还是在API后对其进行屏蔽?要使用热门示例,请使用任何Google服务。开发人员可以连接到他们的公共API,甚至不知道它下面是哪个数据库,或者根本就没有数据库。这样便可以设计出可靠且解耦的软件。数据库并不意味着应用程序可以直接访问数据库。中间层在那里。
Matteo Mosca,2012年

5
@Matteo Mosca:当然可以,但是中间件如何访问数据库...它是数据库的客户端。“客户端”并不表示“ GUI”或“桌面应用”。应用程序层中有很多灰色区域-您是否将验证放在GUI或服务器/业务逻辑中?它应该放在服务器中进行干净的设计,但这对于性能和用户响应能力而言确实很差。同样,当数据库提高性能和(在这种情况下)数据正确性更好时,通常会在数据库中放入一些逻辑。
gbjbaanb 2012年

4
对不起,我仍然不同意。在您必须将应用程序部署在不同的环境中的那一天,您没有使用过相同的数据库技术,这时您会发现自己的应用程序无法运行,因为在新数据库中没有所有这些存储过程..即使重新创建它们(这很麻烦),由于SQL方言等的不同,它们也可能是不同的。通过标准(例如SOAP或REST)公开的具有逻辑和验证功能的集中式API可以解决此问题问题,并增加了可测试性,版本控制和一致性。
Matteo Mosca

13

您似乎正在试图带领您的团队从一个极端(存储过程和数据集)过渡到另一个极端(成熟的ORM)。我认为您还可以设置其他更多增量更改,以实现改进数据访问层的代码质量,而您的团队可能更愿意接受。

您发布的未完成的活动记录实现代码不是特别复杂-我建议您研究易于理解和实现的存储库模式,该模式在.NET开发人员中非常流行。这种模式通常与ORM关联,但是使用普通的ADO.NET创建存储库同样容易。

至于DataSet的-糟糕!如果您返回静态(甚至动态)类型的对象,则使用类库将更加容易。我相信这个插图比我能更好地解释了我对DataSet的看法。

另外,您可以放弃存储的proc,而无需跳转到ORM-使用参数化SQL没错。实际上,除非您有复杂的过程可以节省多次往返服务器的时间,否则我绝对会偏爱使用存储过程。当我打开旧数据库并看到无休止的CRUD程序列表时,我也很讨厌它。

我不劝阻ORM的使用-我通常在我从事的大多数项目中都使用它们。但是,我可以理解为什么在尝试向这个项目中介绍一个项目时可能会遇到很多摩擦,而您的团队对此表示友好,听起来好像他们在8年前就停止学习新知识了。话虽这么说,我一定会看一看新的“微型ORM”,例如Dapper(用于为该站点提供支持)和Massive,它们都非常易于使用,并且与SQL相比,它们更易于访问SQL。典型的ORM,您的团队可能更愿意接受。


2
我认为问题只是在编写应用程序时,.NET没有真正的ORM记录,所以它是通过另一种方式(ASP.NET 1.1)完成的,没有人想到过以其他方式进行(或其他任何方式)直到几个月前我加入。
韦恩·莫利纳

1
Dapper +1。我刚刚开始在项目中使用它,它非常容易实现和使用。我已经是大粉丝了。
SLoret

4

我处在类似的情况下,实际上我们的硬件,功能和政策都倾向于数据库方面,因此一切都通过存储过程进行。不幸的是,它们给编码人员带来了痛苦,尤其是在涉及元数据和代码生成时,因为存储过程中没有像表那样丰富的元数据。

无论如何,您仍然可以使用存储的proc编写优美,简洁的代码。我目前正在使用所有存储过程自己实现存储库模式。我建议您在FluentAdo.net上从数据读取器映射回您的业务对象时提出一个绝妙的主意。我采纳了这个想法,并将其重新用于我自己的解决方案。


3

JFTR-我是一个PHP较低的开发人员,但这听起来像是一个主要的政治问题。

考虑到支持应用程序的“烂摊子”规模,那么- 除了最佳实践之外 -根除它会产生巨大的开销。听起来好像是在重写区域上。

您能否保证所建议的替代方案会产生收益,以证明业务成本合理?我怀疑这家企业的投资回报率可能很难卖给企业。除非应用程序不稳定,或者您不能从财务角度证明大修的优点,否则可能会很难。

ORM是SPROCS 的唯一替代品吗?在完整的ORM和普通SQL之间有两种设计模式。也许您可以通过将这些SPROCS逐渐从DB移入DBAL来启动该过程。当然,随着时间的流逝,它会发展成自制ORM的危险-但您距离目标还差一步。


2

几年前,我们从SP切换到ORM。

在一种情况下,我们必须更新80个表。使用entlib和SP,旧的估算模型将为此估算80小时。我们在10中做到了:)

它使我们用于开发数据访问层的时间减少了80%。


1
而且您无法编写表更新的脚本,为什么呢?
2011年

这是在内存中获取一个复杂的对象,并将其保存为关系数据库以进行报告。我们确实编写了一个对对象进行反射以创建表结构的程序。
Shiraz Bhaiji 2011年

-1

在我看来,更根本的问题是您的部署无法正常,自动地工作。

设置一个连续的构建引擎可能会更容易,该引擎知道每次提交后如何根据需要操作数据库。

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.