SQL Azure 导入导出服务 / bacpac / dac / 提取数据层应用程序






4.90/5 (3投票s)
如何从代码中使用 Dac Export 服务
引言
它有很多名字,但对我来说,它只有一件事...
SQL Azure 推出以来一直非常需要的 SQL Azure 备份解决方案。
起初,他们试图告诉我们不需要备份,因为他们的数据中心非常安全,不会出现任何问题,他们可能是对的 - 他们永远不会丢失我们的数据。但是 - 这是一个很大的但是 - 我们备份数据的主要原因之一是为了防止人为错误。因此,除非我们可以创建版本化的备份,否则我们的数据很容易受到攻击。
有很多半吊子的解决方案。SqlAzureMW、Sync Framework 等等。但是它们都不能让你安排备份,也不能让你的用户在执行某些可能损坏数据的操作之前创建即时备份。直到现在...我希望这个新的导出服务最终能够满足我们的需求。
开始之前
看起来 该服务 - 它是一个 HTTP 端点,可以将你的整个数据库导出到 Azure 存储帐户 - 需要由“服务器级别主体登录名 (由预配过程创建)”运行。 有很多文档表明存在一些权限组合,允许其他用户执行该命令并触发备份。 但是,我一直无法让任何其他用户能够执行它。 也许是因为大多数这些文档(包括服务器返回的 HTTP 错误)似乎都是为 denali (主要是本地版本) 编写的。 即使我可以让它工作,我也不想将这些权限授予以交互方式使用数据库的任何用户 - 更不用说每个用户了。
这样我们就只剩下在安全服务器上创建一个服务,使用我的个人登录名执行命令来触发备份的选项。 因为备份的位置将被硬编码到服务中(即不是参数),以及我的 Azure 存储访问密钥。 我可以很放心地允许任何人触发备份,而不用担心它会成为安全风险。 因为我知道这些 bacpac 文件的确切去向,并且它们不会被重定向到错误的人手中。
话虽如此... 你如何调用这个 http 端点?
好东西
Redgate 有一个免费的实用程序,可以获取你的参数并代表你发出调用。 他们甚至有一个命令行,你可以将其放在批处理文件中。 但是,如果你想将其包含在你的应用程序中(或在你的服务中运行),该怎么办?
Dac 团队在 CodePlex 上发布了一个示例项目,但没有文档。
我发现该示例项目过于复杂,无法展示基本概念。 所以我整合了一些东西,供任何试图弄清楚的人使用。
该服务有一个通用端点 http://dacdc.cloudapp.net/DACWebService.svc 和一些特定区域的端点,例如美国中北部 - https://ch1prod-dacsvc.azure.com/DACWebService.svc。
该服务是一个类似 REST 的服务,因此你不能仅仅使用 WCF 直接调用它。 你需要使用 HTTP 库。 但是,该服务要求参数以 XML 文档的形式传递。 创建正确格式的 XML 的最简单方法可能是从服务下载合约。
你可以使用 svcutil.exe 或在 VS 中“添加服务引用”来执行此操作。 这些工具中的任何一个都将为服务中的数据合约创建代理包装器。 它们都不会创建通常与 WCF 客户端代理一起出现的 app.config 部分(因为该服务不是那种意义上的 WCF 服务)。 它们的操作合约代理也没有任何用处。
一旦你拥有了与 Datacontracts 匹配的类,以下 C# 代码片段应该就是你所需要的。
我重构了 CodePlex 项目,使逻辑更直接,并删除了所有分散示例的部分。
我还使用了任务库来包装异步模式,我发现它更容易理解,这样它就可以为 C# 5 中的 asnyc 和 await 关键字做好准备。
public class BackupUsingDacService
    {
        public BackupUsingDacService(Credentials credentials)
        {
            _Credentials = credentials;
        }
        private Credentials _Credentials;
        const string _EndPoint = 
    "https://ch1prod-dacsvc.azure.com/DACWebService.svc"; // North Central US
        const string _DbServerName = "###.database.windows.net";
        public Task<Task<Guid>> Backup()
        {
            var ImportExport = new DacImportExport.ExportInput
                {
                    BlobCredentials = new DacImportExport.BlobStorageAccessKeyCredentials
                    {
                        Uri = "http://###.blob.core.windows.net/sqlbackup/coc_" + 
                DateTime.Now.ToString() + ".bacpac",
                        StorageAccessKey = "###"
                    },
                    ConnectionInfo = new DacImportExport.ConnectionInfo
                    {
                        ServerName = _DbServerName,
                        DatabaseName = _Credentials.InitialCatalog,
                        UserName = _Credentials.UserName,
                        Password = _Credentials.Password
                    }
                };
            var req = (HttpWebRequest)WebRequest.Create
            ( new Uri( _EndPoint + "/Export" ) );
            req.Method = "POST";
            req.ContentType = "application/xml";
            return Task.Factory.FromAsync<Stream>
        ( req.BeginGetRequestStream, req.EndGetRequestStream, req )
                .ContinueWith( ar =>
                {
                    DataContractSerializer serializer = 
            new DataContractSerializer( typeof( DacImportExport.ExportInput ) );
                    serializer.WriteObject( ar.Result, ImportExport );
                    ar.Result.Close();
                    var req2 = ar.AsyncState as HttpWebRequest;
                    return Task.Factory.FromAsync<WebResponse>
            ( req2.BeginGetResponse, req2.EndGetResponse, req2 )
                        .ContinueWith<Guid>( ar2 =>
                        {
                            Guid resultGuid = Guid.Empty;
                            try
                            {
                                Encoding enc = Encoding.GetEncoding( 1252 );
                                using (var responseStream = new StreamReader
                ( ar2.Result.GetResponseStream(), enc ))
                                {
                                    using (XmlDictionaryReader reader = 
                    XmlDictionaryReader.CreateTextReader
                    ( responseStream.BaseStream, 
                    new XmlDictionaryReaderQuotas() ))
                                    {
                                        DataContractSerializer dcs = 
                    new DataContractSerializer( typeof( Guid ) );
                                        resultGuid = (Guid)dcs.ReadObject( reader, true );
                                    }
                                }
                                ar2.Result.Close();
                            } catch (WebException ex)
                            {
                                if ((ex.Response != null) && 
                (ex.Response is HttpWebResponse))
                                {
                                    HttpWebResponse response = 
                    ex.Response as HttpWebResponse;
                                    Console.WriteLine( "Service Error: {0} {1} ", 
                response.StatusCode, response.StatusDescription );
                                } else
                                {
                                    Console.WriteLine( "Service Error: {0}", ex.Message );
                                }
                            }
                            return resultGuid;
                        } );
                } );
        }
历史
- 2011 年 11 月 21 日:首次发布

