tag:blogger.com,1999:blog-4885468936559569572024-03-14T02:27:16.171-07:00My small dev worldIt's all about dev here (C# .NET, Windows Azure, SharePoint, Javascript etc.)
Eugenehttp://www.blogger.com/profile/02444967129058674034noreply@blogger.comBlogger4125tag:blogger.com,1999:blog-488546893655956957.post-1595901428794611022013-11-18T15:46:00.001-08:002013-11-18T15:46:04.631-08:00Network адаптер в AzureВ общем, сдуваю пыль с блога. Почему бы и нет? :)<br />
<br />
Работал сегодня с Azure VM. Как-то на автомате уже. Захожу в настройки сети и вырубаю сетевой адаптер. Бац, и тут я понимаю, что машина-то в облаке (facepalm мне) и никакой перезагрузкой адптер уже не включишь. Понятное дело, ни о каком remote desktop уже речи тоже не идет.<br />
<br />
В общем, решить такую проблему не сложно. Меняем размер виртуалки, ажур её пересобирает и врубает адаптер. Ждем. Меняем обратно, чтоб все было как раньше.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqyQZ-0NxdOjWnTX_33lEjVbx5JBGpsehH1dJqSv0z9zOgEv5b4BxcfbHv8IHXSGevV-VFzO7xz20bEqW2X5MnaLSaa79hMRnfrNcUS1MDrjb2Q969_IcJsfZuKKFbWtAaGvMmzB843WjC/s1600/vmsize.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqyQZ-0NxdOjWnTX_33lEjVbx5JBGpsehH1dJqSv0z9zOgEv5b4BxcfbHv8IHXSGevV-VFzO7xz20bEqW2X5MnaLSaa79hMRnfrNcUS1MDrjb2Q969_IcJsfZuKKFbWtAaGvMmzB843WjC/s320/vmsize.png" /></a></div><br />
Eugenehttp://www.blogger.com/profile/02444967129058674034noreply@blogger.com0tag:blogger.com,1999:blog-488546893655956957.post-85388914779487866222013-04-01T12:46:00.000-07:002013-04-01T12:46:30.805-07:00Entity framework - StoreGeneratedPatternДовольно стандартная ситуация, когда в качестве Primary Key в таблице БД SQL Server используется GUID (uniqueidentifier).<br />
Выглядит это вот так:<br />
<pre class='brush: sql'>CREATE TABLE [dbo].[Users]
(
[ID] UNIQUEIDENTIFIER NOT NULL DEFAULT(NEWID()) PRIMARY KEY NONCLUSTERED,
[Name] NVARCHAR(200) NOT NULL
)
</pre>В модели Entity framework выглядеть это будет вот как:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTexMXzGXbw_WouZ1ACTf86mNhWfyUSSGvG1_nnfuYZ400qpxI7M2lLpqByZugMVXJEuG_LN7NPShII0W_2khM1U6NbDXFgEBMY-eNPfvqafICjhFiW30TsEhsYhAX8aUC_PJbnncPzpgL/s1600/model.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTexMXzGXbw_WouZ1ACTf86mNhWfyUSSGvG1_nnfuYZ400qpxI7M2lLpqByZugMVXJEuG_LN7NPShII0W_2khM1U6NbDXFgEBMY-eNPfvqafICjhFiW30TsEhsYhAX8aUC_PJbnncPzpgL/s320/model.png" /></a><br />
<br />
Теперь добавим новую запись в нашу таблицу:<br />
<pre class='brush: csharp'>var entities = new TestEntities();
var newUser = new User { Name = "TestUser" };
entities.Users.AddObject(newUser);
entities.SaveChanges();
</pre><br />
В результате в Management Studio получаем такую картину:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJiTsNrFyCsX4ecFcEaRxDZPh4Lu6zL7wrWJoVR7-6nNJtQKUPFmUBQPbT848KiJajTKughImFDza5wg4Ng53OXE1asbxqv4wdWslT4VfY5bZs-xEu2hqsZudB4FV3iBWPX0kkGJhxNc9_/s1600/addresult.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJiTsNrFyCsX4ecFcEaRxDZPh4Lu6zL7wrWJoVR7-6nNJtQKUPFmUBQPbT848KiJajTKughImFDza5wg4Ng53OXE1asbxqv4wdWslT4VfY5bZs-xEu2hqsZudB4FV3iBWPX0kkGJhxNc9_/s320/addresult.png" /></a><br />
<br />
Не смотря на Default constraint, получаем абсолютно пустой Guid. Оно и логично. Ведь он все же был передан как не Null со стороны Entity framework, так ведь?<br />
И в моем случае я всегда решал эту проблему довольно просто:<br />
<pre class='brush: csharp'>var newUser = new User { ID = Guid.NewGuid(), Name = "TestUser2" };
</pre><br />
Но это довольно таки не практичное решение, так как всегда нужно помнить про заполнение ID. Да и в конце концов, для чего еще Default constraint был создан на стороне SQL Server?<br />
<br />
Немного покопавшись в возможностях Entity framework - нашел интересный параметр StoreGeneratedPattern. <br />
Это обычное перечисление: <br />
1. None - сервер не отвечает за генерацию значения. Зачастую - это значение по умолчанию.<br />
2. Identity - значение генерируется при добавлении новой записи. Работает по умолчанию для колонок, где выбрано IDENTITY.<br />
3. Computed - значение генерируется при добавлении либо изменении записи.<br />
<br />
Если взглянете на первый скриншот в посте, то сможете найти там StoreGeneratedPattern для нашей колонки с ID.<br />
Нам очень подошло бы поведение Identity. Очень жаль, что Entity framework-ом не смог это интерпретировать как Identity самостоятельно. <br />
<br />
Ну да и ладно. Сделаем это сами. Идем в Conceptual Designer, и меняем StoreGeneratedPattern для ID на Identity. <br />
<br />
Можете попробовать добавить запись снова. Все прошло хорошо? Тогда вам повезло и нужный апдейт уже есть на вашей машине. <br />
Но не удивляйтесь, если все свалится с ошибкой в стиле: запись с таким же primary key уже существует. Почему? Скорее всего GUID снова пришел пустым. И если взглянуть на xml, который хранится в edmx файле, то в CSDL вы увидите:<br />
<pre class='brush:xml'><Property Name="ID" Type="Guid" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
</pre><br />
А вот в SSDL это свойство не пробросилось:<br />
<pre class='brush:xml'><Property Name="ID" Type="uniqueidentifier" Nullable="false" />
</pre><br />
Не беда, подумаю многие. И в целом это правда. Ничего не мешает нам добавить нужное свойство в XML руками. В итоге получится вот так:<br />
<pre class='brush:xml'><Property Name="ID" Type="uniqueidentifier" Nullable="false" StoreGeneratedPattern="Identity" />
</pre><br />
Вот только в таком случае придется смириться с тем, что после обновления модели - весь это "кастомный" XML слетит и придется добавлять его заново. Писать свой класс, который будет это хэндлить тоже нет никакого интереса.<br />
<br />
Погуляв немного по microsoft connect оказалось, что баг этот существует еще с далекого 2009-го года.И проявляется даже на 4.4 и 5 версии EF. Но радует, что есть фикс.<br />
Скачать его можно <a href="http://archive.msdn.microsoft.com/KB2561001/Release/ProjectReleases.aspx?ReleaseId=5710" target="_blank">отсюда</a>. Перезагрузки не требует.<br />
<br />
После установки все стало на свои места и работает как положено.<br />
<br />
Удачи!Eugenehttp://www.blogger.com/profile/02444967129058674034noreply@blogger.com0tag:blogger.com,1999:blog-488546893655956957.post-41136888805845782352013-03-31T12:47:00.002-07:002013-04-12T08:19:44.263-07:00Powershell и SQL Azure.Powershell уже давно стал универсальным "орудием труда" для решения различного рода проблем/задач. И команда Windows Azure не отстает. Возможности управления своей подпиской через Powershell постоянно развиваются и модифицируются.<br />
<br />
В данном блог посте речь пойдет конкретно о работе с SQL Azure из Powershell. Основные возможности рассмотрим на примерах, кое-где в сравнении с тем, как это можно сделать через Preview версию management портала.<br />
<br />
Для старта понадобятся:<br />
<br />
1. <a href="http://www.microsoft.com/en-us/download/details.aspx?id=34595" target="_blank">Powershell 3.0</a> - команды для управления SQL Azure выполняются минимум с версии 3.0. <br />
2. <a href="http://go.microsoft.com/?linkid=9811175&clcid=0x409" target="_blank">Windows Azure Powershell</a> - "ажурная" Powershell консоль, с которой в комплекте идут нужные скрипты, которые мы будем импортить. Её можно скачать по указанной ссылке, но лично я пользуюсь Web Platform Installer, что значительно удобнее.<br />
3. Последняя версия Windows Azure SDK. В моем случае - это 1.8. <br />
<br />
Позаботьтесь также об удобном Powershell редакторе. В моем случае - это <a href="http://powergui.org">PowershellGUI</a>. Но подойдет хоть блокнот - лишь бы вам было комфортно.<br />
<br />
Как вы наверняка знаете, Powershell имеет verb-noun синтаксис. Следовательно, существует перечень команд Get/Set/New/Remove.<br />
Пройдемся по списку команд.<br />
<table border="2" bordercolor="black" cellpadding="3" cellspacing="3"><tr> <th>Команда</th> <th>Описание</th> </tr>
<tr> <td><a href="http://msdn.microsoft.com/en-us/library/windowsazure/jj983737.aspx" target="_blank">Get-AzureSqlDatabaseServer</a></td> <td>Возвращает информацию о SQL Azure сервере по указанному имени. Если имя не указано, вернутся все доступные в подписке сервера.</td> </tr>
<tr> <td><a href="http://msdn.microsoft.com/en-us/library/windowsazure/jj983741.aspx" target="_blank">Get-AzureSqlDatabase</a></td> <td>Возвращает базу данных по указанному имени в конексте указанного сервера. Если же имя БД не указано - возвращает все БД на указанном сервере.</td> </tr>
<tr> <td><a href="http://msdn.microsoft.com/en-us/library/windowsazure/jj983742.aspx" target="_blank">Get-AzureSqlDatabaseServerFirewallRule</a></td> <td>Возвращает информацию о SQL Azure сервере по указанному имени. Если имя не указано, вернутся все доступные в подписке сервера.</td> </tr>
<tr> <td><a href="http://msdn.microsoft.com/en-us/library/windowsazure/jj983744.aspx" target="_blank">New-AzureSqlDatabaseServer</a></td> <td>Создает экземпляр Azure SQL Server рамках подписки.</td> </tr>
<tr> <td><a href="http://msdn.microsoft.com/en-us/library/windowsazure/jj983747.aspx" target="_blank">New-AzureSqlDatabase</a></td> <td>Создает новую БД в контексте указанного сервера.</td> </tr>
<tr> <td><a href="http://msdn.microsoft.com/en-us/library/windowsazure/jj983745.aspx" target="_blank">New-AzureSqlDatabaseServerFirewallRule</a></td> <td>Добавляет правило работы firewall для указанного сервера в подписке.</td> </tr>
<tr> <td><a href="http://msdn.microsoft.com/en-us/library/windowsazure/jj983736.aspx" target="_blank">Remove-AzureSqlDatabase</a></td> <td>Удаляет базу данных в указанном контексте.</td> </tr>
<tr> <td><a href="http://msdn.microsoft.com/en-us/library/windowsazure/jj983739.aspx" target="_blank">Remove-AzureSqlDatabaseServer</a></td> <td>Удаляет указанный экземпляр SQL Azure сервера.</td> </tr>
<tr> <td><a href="http://msdn.microsoft.com/en-us/library/windowsazure/jj983740.aspx" target="_blank">Remove-AzureSqlDatabaseServerFirewallRule</a></td> <td>Удаляет добавленное правило для firewall в контексте указанного сервера.</td> </tr>
<tr> <td><a href="http://msdn.microsoft.com/en-us/library/windowsazure/jj983732.aspx" target="_blank">Set-AzureSqlDatabase</a></td> <td>Вносит изменения в настройки указанной БД - имя, макс. размер и т.д.</td> </tr>
<tr> <td><a href="http://msdn.microsoft.com/en-us/library/windowsazure/jj983734.aspx" target="_blank">Set-AzureSqlDatabaseServer</a></td> <td>Вносит изменения в настройки SQL Azure сервера. Сейчас это только возможность изменить пароль администратора.</td> </tr>
<tr> <td><a href="http://msdn.microsoft.com/en-us/library/windowsazure/jj983738.aspx" target="_blank">Set-AzureSqlDatabaseServerFirewallRule</a></td> <td>Вносит изменения в уже созданное firewall правило.</td> </tr>
</table><br />
Некоторые из этих команд рассмотрим ниже. Об остальных же достаточно информации можно найти по указанным ссылкам на MSDN.<br />
<br />
Итак, открывайте свой редактор и поехали.<br />
<br />
<h1>С чего начнем</h1><br />
Первым делом необходимо импортировать модуль, который позволит нам работать с SQL Azure Powershell командами.<br />
<pre class='brush:ps'>Import-Module 'C:\Program Files (x86)\Microsoft SDKs\Windows Azure\PowerShell\Azure\Azure.psd1'
</pre>Расположение этого модуля видно по коду. И вот, собственно, одна из причин, зачем необходимо было установить Azure Powershell.<br />
<br />
<h1>Выбор подписки</h1><br />
Теперь сконфигурируем подписку, с которой планируем работать. Для этого есть два варианта: <br />
1. Импортировать настройки (subscription id, certificate) из <b>publishsettings</b> файла.<br />
2. Установить необходимые параметры, включая сертификат, вручную.<br />
<br />
Воспользуемся первым вариантом. Если у вас есть скачанный pubslishsettings файл - отлично.<br />
Если же нет - один из удобных вариантов его получения - команда <b><u>Get-AzurePublishSettingsFile</u></b>. Она запустит браузер по нужной ссылке, где вы сможете скачать файл с настройками.<br />
Импортированные/заданные настройки в итоге будут храниться в системе: <b>C:\Users\username\AppData\Roaming\Windows Azure Powershell</b>.<br />
Позаботьтесь также, чтобы файл с настройками не был readonly. Я не вдавался в подробности, но в моем случае readonly файл был причиной для ошибок. Поэтому просто стоит принять к сведению.<br />
<br />
Файл готов? Тогда к делу.<br />
<pre class='brush:ps'>Import-AzurePublishSettingsFile "pathToPublishSettingsFile"
Set-AzureSubscription -SubscriptionName "nameOfSubscription"
</pre>Если убрать вторую строку из примера, то будет выбрана подписка по умолчанию. <br />
Прошло без ошибок? Значит мы готовы для работы с SQL Azure через Powershell.<br />
<br />
<h1>Создание сервера</h1><br />
Для начала попробуем получить список доступных в подписке серверов SQL Azure.<br />
<br />
На портале:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeKaN65935oqW8t72tN8SwFN5bLrjYHS9CVA94apF-kCUy27Fay9faJ-UJ8nLvwNWbME_7DvT1vbMZibno6FmwntvYNBYJjqc_KZu_R9j-9Ch6YRNAyLLgfoaJVedGDPmLJxsVILpo1zv2/s1600/serverslist.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeKaN65935oqW8t72tN8SwFN5bLrjYHS9CVA94apF-kCUy27Fay9faJ-UJ8nLvwNWbME_7DvT1vbMZibno6FmwntvYNBYJjqc_KZu_R9j-9Ch6YRNAyLLgfoaJVedGDPmLJxsVILpo1zv2/s320/serverslist.png" /></a><br />
<br />
Через Powershell:<br />
<br />
<pre class='brush: ps'>$availableServers = Get-AzureSqlDatabaseServer
</pre><br />
Затем, создадим экземпляр SQL Azure Server.<br />
<br />
Через портал:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi17bltrIkS6UuVJ5abeIU7k2RBHVRakV4y2cqwJRfqWxBz4uZL6ZB2IIzeBraCNPmQpucebnzlw8ZcQqg7h3e_YMv8bN6HqOkNj8GMVLCQTsTtafkN7NRH96GopxWSkR7vq6TcpioiuvM9/s1600/createServer.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi17bltrIkS6UuVJ5abeIU7k2RBHVRakV4y2cqwJRfqWxBz4uZL6ZB2IIzeBraCNPmQpucebnzlw8ZcQqg7h3e_YMv8bN6HqOkNj8GMVLCQTsTtafkN7NRH96GopxWSkR7vq6TcpioiuvM9/s320/createServer.png" /></a><br />
Как видно, чекбокс, позволяющий Azure Web сервисам из подписки обращаться к новосозданному серверу - включен.<br />
<br />
Через Powershell:<br />
<pre class='brush: ps'>$newSqlServer= New-AzureSqlDatabaseServer -Location "WEST EUROPE" -AdministratorLogin "testUser" -AdministratorLoginPassword "testWorld123"
</pre><br />
<h1>Firewall</h1><br />
По умолчанию Azure SQL Server позвляет к себе подключиться только IP адресам, указанным как разрешенные в firewall.<br />
Зайдем в конфигурацию нового сервера, созданного через Powershell - в правилах пусто. Стоит обратить внимание, что к данному серверу запрещено подключение Azure сервисам из подписки.<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQXF15ypqBL1G2swkcz0HnNj7yTIySM6OkZCCvfDG0pJ9KArV9_IYIpTpn5clCUzVpNw9A0txYH5esTVU-hoD23oXfm-P6JeLQOpYNwj13crTgGJcMTyuCJi9D69g0D70KgFa8D87GZ_Mj/s1600/disallow.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQXF15ypqBL1G2swkcz0HnNj7yTIySM6OkZCCvfDG0pJ9KArV9_IYIpTpn5clCUzVpNw9A0txYH5esTVU-hoD23oXfm-P6JeLQOpYNwj13crTgGJcMTyuCJi9D69g0D70KgFa8D87GZ_Mj/s320/disallow.png" /></a> <br />
Разработчики не предусмотрели такого параметра для New-AzureSqlDatabaseServer, хотя я считаю, что именно там ему и место.<br />
На помощь приходит Azure Firewall. Если добавить правило, в котором в качестве IP Address будет указан <b>0.0.0.0</b>, то Azure интерпретирует его именно как разрешение сервисам внутри облака подключаться к данному экземпляру SQL Server.<br />
<pre class='brush:ps'>$newSqlServer| New-AzureSqlDatabaseServerFirewallRule -RuleName "AzureServices" -StartIpAddress "0.0.0.0" -EndIpAddress "0.0.0.0"
</pre>Также добавим в список правил IP адрес машины, с которой мы планируем далее выполнять наш скрипт. Иначе же при создании контекста все свалится с ошибкой доступа.<br />
<br />
<h1>Работа с контекстом</h1><br />
New-AzureSqlDatabaseServerContext - создает соединение к указанному серверу.<br />
Для подключения нам понадобятся учетные данные администратора, которые мы указывали при создании сервера.<br />
<br />
Создадим объект класса PSCredential и передадим иму имя и пароль. Рекомендую также взглянуть на полезную команду <b>Get-Credential</b>, которая позволит запрашивать имя и пароль непосредственно при выполнении скрипта.<br />
Займемся созданием контекста:<br />
<pre class='brush: ps'>$servercredential = new-object System.Management.Automation.PSCredential("testUser", ("testWorld123"| ConvertTo-SecureString -asPlainText -Force))
#Вариант 1.
$serverContext = $newSqlServer | New-AzureSqlDatabaseServerContext -Credential $serverCredential
#Вариант 2.
$serverContext = $New-AzureSqlDatabaseServerContext -ServerName "ywdddhc9qo" -Credential $serverCredential
</pre>В первом варианте - используем уже имеющуюся переменную, где есть вся информация о нашем сервере. Во втором - имя сервера (возможно также указать имя полностью либо ссылку).<br />
<br />
Контекст получен. Теперь создадим базу данных.<br />
<br />
В Powershell это выглядит вот как:<br />
<pre class='brush: ps'>$testDB = New-AzureSqlDatabase $serverContext -DatabaseName "testDB" –Edition "Web" -MaxSizeGB 1 -Collation "SQL_Latin1_General_CP1_CI_AS"
</pre><br />
Обязательными в данной команде являются контекст и имя новой БД. Если не указать другие параметры, то их значения будут установлены по умолчанию.<br />
<br />
Через портал же это выглядит вот так. И само собой, руками контекст нам получать не надо.<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgy-8hck_sZioaLIPLCHb861dnLWVqE1FvzH6FVWPvJhE_p3a9Qi02mLLK6YhrXeLXSc71G6c1cTgaPfqflRdGi6QXhDhukRFrZH-Dv0TBcpdbC7cdsTiJVcYNHhsgHyr73N_H1Y-jjnIWF/s1600/portalcreatedb.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgy-8hck_sZioaLIPLCHb861dnLWVqE1FvzH6FVWPvJhE_p3a9Qi02mLLK6YhrXeLXSc71G6c1cTgaPfqflRdGi6QXhDhukRFrZH-Dv0TBcpdbC7cdsTiJVcYNHhsgHyr73N_H1Y-jjnIWF/s320/portalcreatedb.png" /></a><br />
Указываем имя, версию (WEB/Business), максимальный размер, collation, подписку и сервер в указанной подписке.<br />
<br />
Теперь получим список с имеющимися на сервере БД. Для этого воспользуемся командой <b><u>Get-AzureSqlDatabase</u></b>. Если имя БД указано, то команда попытается найти конкретную базу. Если же не указано - вернет все имеющиеся.<br />
<pre class='brush: ps'>$allDBs = Get-AzureSqlDatabase $serverContext
</pre><br />
<h1>Выполнение SQL скриптов</h1><br />
Теперь, когда процесс и стандартные возможности работы с SQL Azure ясны, хотелось бы еще написать о работе со структурой базы.<br />
Конечно, можно подключаться к нашей БД через SQL Server Management Studio либо через Management portal, где и выполнять нужный SQL при малейшем изменении. Но если уж автоматизировать, то весь процесс.<br />
<br />
К сожалению, хранить весь SQL в одном файлике - не выйдет. Дело в том, что в SQL Azure есть набор команд, которые могут быть единственными в пакете. Если же это правило нарушить - получим ошибку.<br />
<br />
В целом, не проблема - будем хранить скрипты в нескольких SQL файлах. Теперь вопрос, как, собственно, выполнить эти скрипты? Для этого воспользуемся <b>sqlcmd</b>. <br />
В Powershell для этого есть команда <a href="http://msdn.microsoft.com/en-us/library/cc281720.aspx" target="_blank">Invoke-Sqlcmd</a>. Перед её использованием нужно будет добавить пару оснасток (snap-in).<br />
<br />
<b>Invoke-Sqlcmd</b> позволяет задать файл, в котором лежит SQL скрипт, а также есть возможность вписать непосредственно Query для выполнения. Параметры взаимоисключающие.<br />
В качестве параметра ServerInstance необходимо передавать полное имя нашего сервера, то есть включая "database.windows.net". <br />
<br />
Ну что ж, приступим.<br />
<pre class='brush: ps'>#Добавляем оснастки для работы с SQL Server
Add-pssnapin sqlserverprovidersnapin100
Add-pssnapin sqlservercmdletsnapin100
#Выполняем имеющийся SQL скрипт
$serverName = "ywdddhc9qo" + ".database.windows.net"
Invoke-Sqlcmd -InputFile "pathToSQLFile" -ServerInstance $serverName -Database "testDB" -Username "testUser" -Password "testWorld123"
</pre><br />
<h1>Подведем итоги</h1><br />
Как вы убедились/знаете, все описанное в посте можно сделать и через портал/management studio. Но когда кол-во БД и серверов возрастает либо в вашем распоряжении несколько environment-ов (DEV/QA/ProdDemo/PROD), то на помощь как раз и приходит столь полезный инструмент для автоматизации.<br />
И все самые необходимые возможности команда Windows Azure нам уже предоставила.<br />
<br />
Будем следить за дальнейшим развитием.Eugenehttp://www.blogger.com/profile/02444967129058674034noreply@blogger.com0tag:blogger.com,1999:blog-488546893655956957.post-35273199677861340232013-03-19T14:33:00.000-07:002013-03-19T14:55:44.972-07:00SharePoint SPUtility - особенности отправки почтыТакая простая на первый взгляд задача, как отправка почты, может привнести пару новых багов в вашем проекте :)
Речь пойдет об одном из стандартных подходов в SharePoint 2010 - <b>SPUtility.SendEmail</b>. Описание этого метода можно легко найти на <a href="http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.utilities.sputility.sendemail%28v=office.14%29.aspx" target="_blank">MSDN</a>. Но не надейтесь увидеть там много букв, оно довольно таки скудное.
Начнем с того, что работает, как ожидается.
<pre class="brush: csharp">
SendEmail(SPWeb, Boolean, Boolean, String, String, String)
SendEmail(SPWeb, Boolean, Boolean, String, String, String, Boolean)
</pre>
Проблем и опасений не вызывают. Почта отлично отсылается, если в Central Administration (далее CA) указаны корректные настройки.
<br/>
Но вот интерес вызывают как раз первые две реализации метода:
<pre class="brush: csharp">
SendEmail(SPWeb, StringDictionary, String)
SendEmail(SPWeb, StringDictionary, String, Boolean)
</pre>
Вторым параметром оба метода принимают словарь из строк, чтобы добавить в отправляемое письмо нужный набор header-ов.
Накидаем простенький пример:
<pre class="brush: csharp">
var headers = new StringDictionary
{
{ "to", "samplemail@sample.com" },
{ "subject", "Test subject" },
{ "content-type", "text/html" }
};
SPUtility.SendEmail(web, headers, bodyText);
</pre>
И тут выясняется некая <b>особенность</b>. Если мы передаем свой набор header-ов, то выбор email-a, с которого нужно эту самую почту отправить - работает несколько по другому.
Если в контексте указанного web-а, у пользователя в настройках указан email, то почта попытается уйти именно с email-а пользователя. Если же такой email не указан, то берется адрес, указанный в CA.
Решить эту ситуацию можно довольно просто - необходимо явно добавлять в словарь header "from", где и указывать необходимый адрес отправителя. В данном случае - это адрес из CA.
<pre class="brush: csharp">
var headers = new StringDictionary
{
{ "to", "samplemail@sample.com" },
{ "from", web.Site.WebApplication.OutboundMailSenderAddress },
{ "subject", "Test subject" },
{ "content-type", "text/html" }
};
</pre>
Теперь почта уходит от корректного отправителя.
Eugenehttp://www.blogger.com/profile/02444967129058674034noreply@blogger.com0