【问题标题】:How can I refer to a database deployed from the same DACPAC file, but with a different db name?如何引用从同一个 DACPAC 文件部署但具有不同数据库名称的数据库?
【发布时间】:2016-05-26 23:23:15
【问题描述】:

背景

我有一个多租户方案和一个独特的 Sql Server 项目,该项目将部署到同一台服务器上的多个数据库实例中。每个租户将有一个 db,外加一个“模型”db。

“模型”数据库有三个用途:

  1. 强制某些“系统”数据始终存在于每个租户数据库中
  2. 作为具有特殊权限的用户编辑系统数据的访问点(将准时同步到所有租户)
  3. 创建新租户时,数据库将被复制并附加一个代表租户的新名称

有触发器检查租户数据库中修改/删除的数据是否对应于“模型”数据库中的“系统”数据。如果是这样,则会引发错误,指出无法更改系统数据。

问题

下面是触发器的一部分,用于检查是否允许删除:

IF DB_NAME() <> 'ModelTenant' AND EXISTS
    (
    SELECT
        [deleted].*
    FROM
        [deleted]
            INNER JOIN [---MODEL DB NAME??? ---].[MySchema].[MyTable] [ModelTable]
            ON  [deleted].[Guid] = [ModelTable].[Guid]
    )
BEGIN;
    THROW 50000, 'The DELETE operation on table MyTable cannot be performed. At least one targeted record is reserved by the system and cannot be removed.', 1
END

我似乎找不到应该代替 --- MODEL DB NAME 的东西??? --- 在上面的代码中,可以让项目正确编译。当引用一个完全不同的项目时,我知道该怎么做:使用对用 SQLCMD 变量表示的项目的引用。但在这种情况下,项目引用本质上是同一个项目;仅在不同的数据库上。我似乎无法以这种方式添加自引用。

我能做什么? SSDT 是否为这种情况提供某种支持?

【问题讨论】:

  • 我不明白。您是在尝试查看模型数据库或其中一个租户中的数据吗?如果是模型,只需将该名称放在您拥有它的括号内。如果是租户数据库,这将更具挑战性,因为您可能需要动态 sql。但这取决于代码的上下文。这段代码在哪个数据库上执行?
  • @SeanLange 我不能只将模型数据库的名称放在括号中。该项目将无法编译 DACPAC,并给出“未解决的对象引用”错误。同意,在纯 TSQL 中,将数据库的名称放在那里是可以做的,但我在这里使用 SSDT。 :)
  • 为什么不把你的sql代码移动到一个过程中呢?您需要在某些时候将业务/显示层与数据层分开。
  • @SeanLange 我不能,该过程将无权访问触发器的已删除/插入表。即使这样做了,我仍然需要跨两个数据库的 JOIN。在程序中会有什么不同?
  • 哦,是的......这是一个触发器。我的错。对我来说,这似乎是 DACPAC 中的一个错误,因为语法完全有效。

标签: sql-server visual-studio sql-server-data-tools sqlcmd dacpac


【解决方案1】:

您是否尝试过设置数据库变量?您可以在"Reference aware statements" here 下阅读。然后你可以说:

SELECT * FROM [$(MyModelDb)][MySchema].[MyTable] [ModelTable]

如果您没有针对$(MyModelDb) 的特定项目,您可以选择“通过未解决的引用抑制错误...”选项。自从我使用 SSDT 项目以来已经很久了,但我认为这应该可行。

提示:如果您需要引用 1 表 100 次,您可能会发现创建一个使用数据库变量的 SYNONM 会更好,然后在 SPROC/TRIGGER 中指向该 SYNONM。为什么?因为这样您就不需要部署 SPROC/TRIGGER 来将变量替换为实际值,这样可以使开发更加顺畅。

【讨论】:

  • 现在我目前的解决方案与你的很接近。我正在使用一个虚拟项目作为模拟参考,并启用“抑制参考警告”选项。您的帖子建议可以在没有项目参考的情况下设置此选项。我很想知道这是如何工作的。你能详细说明一下吗?
【解决方案2】:

我不太确定 SSDT 是否特别适合任何相当复杂的项目。我可以想到一两种最有可能实现这一目标的方法(尤其取决于你如何进行发布/部署),但我认为你实际上会失去比你得到的更多。我的意思是:您可以添加步骤以使其正常工作(即赢得战斗),但您将创建一个更复杂的系统,以便让 SSDT 发布一个比它更复杂(且更慢)的系统它需要(即输掉战争)。

在担心 SSDT 之前,让我们先看看为什么需要/希望 SSDT 这样做。您的系统数据与租户数据混合在一起,您需要验证 UPDATEDELETE 操作以确保系统数据不会被修改,识别“系统”数据的唯一方法是匹配它基于 GUID PK 到记录之家 -- ModelDB

关于识别哪些数据属于“系统”而不属于租户的理论是您的主要问题,而不是 SSDT。通过拥有“模型”数据库,您肯定走在了多租户系统的正确轨道上,但将其用于数据验证是一个糟糕的设计选择:除了使用 GUID 作为 PK 已经导致性能下降之外,您还可以进一步通过将所有这些UPDATEDELETE 操作集中到单个争用点来减慢所有这些UPDATEDELETE 操作,因为所有客户端DBS 都需要检查这个公共来源。

最好在每个表中包含一个BIT 字段,该字段混合了系统和租户数据,表示该行是否为“系统”。只需查看 SQL Server 中的系统目录视图:

  • sys.objects 有一个 is_ms_shipped
  • sys.assemblies 去了另一个方向,并有一个is_user_defined column

因此,如果您要向这些表中添加 [IsSystemData] BIT NOT NULL 列,您的触发器逻辑将变为:

IF DB_NAME() <> N'ModelTenant' AND EXISTS
    (
    SELECT del.*
    FROM   [deleted] del
    WHERE  del.[IsSystemData] = 1
    )
BEGIN
    ;THROW 50000, 'The DELETE operation on table MyTable cannot be performed. At least one targeted record is reserved by the system and cannot be removed.', 1;
END;

好处:

  • 不再有 SSDT 问题(至少从这部分开始 ;-)
  • 更快的UPDATEDELETE 操作
  • 减少对共享资源的争用(即ModelDB
  • 代码复杂度更低

【讨论】:

  • 首先,感谢您提供非常详尽的答案。 :) 现在就我而言,我很乐意完全放弃 SSDT,但不幸的是,这不是我的决定。我的老板坚持使用 Visual Studio,而 AFAIK 微软没有为 Sql 开发提供除 SSDT 之外的任何替代方案。另外,我们确实在 DACPAC 和 T4 之上进行了一些投资,以自动生成大量代码。所以我怀疑很快就会放弃所有这些。 :(
  • 另外,model db 还有一个角色我忘了提:我的老板想用它来编辑系统数据并将其同步到所有租户。他想使用我们为用户提供的同一个应用程序,只有一个特殊权限才能允许 1)首先连接到模型数据库和 2)编辑系统数据并绕过阻止它发生的触发器。
  • 还有一件事:“系统”数据不能属于客户端。我们希望强制执行数据完整性,因此数据必须存在于数据库中。
  • @Crono 关于保持 SSDT 并让数据存在于每个客户端数据库中以强制参照完整性:这些都是通过我的建议实现的。我不是说要摆脱 SSDT。
  • 我理解并感谢您提出另一种方法。不幸的是,我在设计规划中的唯一角色是在会议中发出危险信号。最终,以某种方式做事的决定不是我能做出的。 :s 如果是,系统数据将成为脚本的一部分,可用于初始化新租户或“修复”租户。该模型绝不会用作同步看门狗。 :p
【解决方案3】:

作为引用另一个数据库项目的替代方法,您可以生成一个 dacpac,然后在“相同服务器,不同数据库”模式下将 dacpac 引用为数据库引用。

【讨论】:

  • 你的意思是引用同一个项目构建的同一个 DACPAC 文件吗?
  • 是和不是。我的意思是在项目中添加一个独立的 dacpac 文件,然后向它添加一个数据库引用。 dacpac 文件的源可以是项目的构建输出,也可以是使用 SqlPackage.exe 从数据库中提取的 dacpac。无论如何,您可以将该 dacpac 文件的副本放在项目中 *.sql 文件旁边,用作模型的参考。
  • 无论哪种方式,这意味着在某些时候引用的包将包含来自尚未编译的项目的不同元素。这种差异可能会导致构建失败,即使从技术上讲代码是好的。防止这种情况发生的一种方法是忽略此引用的 DACPAC 的未解析对象,但此时我不妨使用一个完全没有元素的虚拟 DACPAC...
猜你喜欢
  • 2018-11-29
  • 2017-10-03
  • 2014-02-20
  • 1970-01-01
  • 2013-11-26
  • 1970-01-01
  • 1970-01-01
  • 2016-09-10
相关资源
最近更新 更多