如何在开发存储过程时使用依赖注入?

时间:2014-02-26 02:16:14

标签: stored-procedures dependency-injection

在数据库中存储过程通常用于处理业务逻辑。 (辩论说逻辑应该在哪里,但这不是主题) 当写入越来越多的SP时,系统变得非常复杂。我认为的主要原因是依赖关系(SP1依赖于SP2,SP2依赖于SP3 ......) 在OO世界中,存在依赖注入模式以及许多IoC容器(例如Spring)来解决依赖性问题。 在SP世界中,这个模式可以应用吗?怎么样?有什么工具吗?

2 个答案:

答案 0 :(得分:4)

依赖注入在支持多态的环境中最有意义:您有一个可以拥有众多实现之一的接口,并且可以将适当实现的发现卸载到某个框架。

在SQL中,存储过程接口与其实现之间没有距离 - 您可以使用过程定义接口。当您在共同签名中实际定义多个过程可互换时,很难想到一个案例。

实际上,对于依赖注入,您还需要一些键来告诉系统您想要注入的是什么。在OOP语言中,接口通常是关键。但您可能不希望将SP签名作为密钥。如果你按名称查找程序,那么它与直接执行它有何不同?

OOP存在的原因以及为什么它是管理复杂性的流行方式。依赖注入建立在其核心概念之上,似乎并不适用于程序环境。

答案 1 :(得分:0)

虽然不推荐。有一种方法可以在SQL环境中使用依赖注入(使用存储过程,函数,视图甚至表)。它涉及很多动态SQL,触发器,并在一段时间后变得非常混乱。但是,仅仅为了回答这个问题,这里就是:

首先,您必须确定要注入的依赖项类型以及访问方式。让我们说,对于这个例子,它是一个公司ID,你想要根据公司执行不同的对象,同时在更高级别的程序中保持不可知,并遵循D.R.Y(不要重复自己)。

因此,您需要一种方法来存储此公司ID,无论您身在何处以及传入的内容都可以随时检索该公司ID。您不希望始终将公司ID传递给SQL对象,所以你使用CONTEXT_INFO。这是一个通过线程保持不变的值,并且对于该线程是唯一的。

SET @Context = CAST(15 AS VARBINARY(128))
SET CONTEXT_INFO @Context;

现在你可以像这样访问上下文:

SELECT @Context = CONTEXT_INFO();
SET @CompanyID = CAST(@Context AS INT)

到目前为止你和我在一起吗? 这是它开始变得非常混乱的地方 使用公司特定对象的名称为自己创建一个表。

CREATE TABLE CompanyObjectRegistry (ObjectSchema VARCHAR(100), ObjectName VARCHAR(100), CompanyID)

将您的对象存储在此处:

INSERT CompanyObjectRegistry
VALUES ('CompanyFifteen', 'DoSomethingCool', 15),
     ('CompanyTwelve', 'DoSomethingCool', 12);

然后创建顶级过程:

CREATE PROCEDURE DoSomethingCool (@Data VARCHAR(10), @Param2 INT) 
AS 
DECLARE @CompanyID INT = CAST(CONTEXT_INFO() AS INT),
    @SQL NVARCHAR(MAX) = '
    EXEC [SCHEMA].DoSomethingCool ''' + @Data + ''', ' 
      + CAST(Param2 AS VARCHAR(10)) + ';'
SET
SELECT @SQL = REPLACE(@SQL, '[SCHEMA]', ObjectSchema)
FROM CompanyObjectRegistry
WHERE CompanyID = @CompanyID

EXEC(@SQL)

由于DSQL,它的开销很小,而且无法编译。您可以使用“生成”过程来消除部分开销,该过程从CompanyObjectRegistry的内容自动生成此“基本”过程并使用IF语句。

如果需要,可以在CompanyObjectRegistry中添加触发器,并在编辑/添加其中一个基础对象时生成基础对象。您可以使用DDL触发器。

以下是如何为表格执行此操作:

DECLARE @TableName VARCHAR(100) = 'DoSomethingCool';
SET @SQL = 'CREATE VIEW ' + @TableName + ' AS '
+ (SELECT '
            SELECT * FROM ' + ObjectSchema + '.' + ObjectName + ' 
            WHERE ' + CAST(CompanyID AS VARCHAR(10)) + ' = @CompanyID
            UNION ALL ' -- You would have to remove the last 10 characters from the result
    FROM CompanyObjectRegistry
    WHERE ObjectName = @TableName
    FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)')

EXEC (@SQL)

可以以相同的方式注入视图,只需从每个视图中选择*即可。 功能很难做到,因为你不能做上述任何一种方法。您必须使用函数的标题并生成正文。

这需要很多时间。所以如果你有选择......不要。