有没有一种方法可以使用the SQL Server 2012 Microsoft.SqlServer.Dac Namespace来确定数据库是否具有与DacPackage object所描述的模式相同的模式?我看过DacPackage和DacServices的API文档,但是没有运气。我错过了什么吗?

最佳答案

是的,自2012年以来,我一直在使用以下技术。


计算dacpac的指纹。
将该指纹存储在目标数据库中。



  .dacpac只是一个zip文件,其中包含诸如元数据之类的好东西,并且
  型号信息。


这是.dacpac中的屏幕抓图:

c# - 确定数据库是否“等于” DacPackage-LMLPHP

文件model.xml具有如下所示的XML结构

<DataSchemaModel>
    <Header>
        ... developer specific stuff is in here
    </Header>
    <Model>
        .. database model definition is in here
    </Model>
</<DataSchemaModel>



  我们需要做的是从<Model>...</Model>中提取内容
  并将其视为架构的指纹。


“可是等等!”你说。 “ Origin.xml具有以下节点:”

<Checksums>
    <Checksum Uri="/model.xml">EB1B87793DB57B3BB5D4D9826D5566B42FA956EDF711BB96F713D06BA3D309DE</Checksum>
</Checksums>


以我的经验,此<Checksum>节点会更改,而与模型中的架构更改无关。

因此,让我们开始吧。
计算dacpac的指纹。

using System.IO;
using System.IO.Packaging;
using System.Security.Cryptography;
static string DacPacFingerprint(byte[] dacPacBytes)
{
    using (var ms = new MemoryStream(dacPacBytes))
    using (var package = ZipPackage.Open(ms))
    {
        var modelFile = package.GetPart(new Uri("/model.xml", UriKind.Relative));
        using (var streamReader = new System.IO.StreamReader(modelFile.GetStream()))
        {
            var xmlDoc = new XmlDocument() { InnerXml = streamReader.ReadToEnd() };
            foreach (XmlNode childNode in xmlDoc.DocumentElement.ChildNodes)
            {
                if (childNode.Name == "Header")
                {
                    // skip the Header node as described
                    xmlDoc.DocumentElement.RemoveChild(childNode);
                    break;
                }
            }
            using (var crypto = new SHA512CryptoServiceProvider())
            {
                byte[] retVal = crypto.ComputeHash(Encoding.UTF8.GetBytes(xmlDoc.InnerXml));
                return BitConverter.ToString(retVal).Replace("-", "");// hex string
            }
        }
    }
}


现在有了这个指纹,用于应用dacpac的伪代码可以是:

void main()
{
    var dacpacBytes = File.ReadAllBytes("<path-to-dacpac>");
    var dacpacFingerPrint = DacPacFingerprint(dacpacBytes);// see above
    var databaseFingerPrint = Database.GetFingerprint();//however you choose to do this
    if(databaseFingerPrint != dacpacFingerPrint)
    {
        DeployDacpac(...);//however you choose to do this
        Database.SetFingerprint(dacpacFingerPrint);//however you choose to do this
    }
}

07-24 18:25