<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>David Browne's Web Log</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/dbrowne/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/dbrowne/feed.xml</link>
    <description />
    <lastBuildDate>Thu, 12 Oct 2017 14:06:44 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Simple EF6-style Logging for EF Core</title>
      <link>https://docs.microsoft.com/archive/blogs/dbrowne/simple-logging-for-ef-core</link>
      <pubDate>Fri, 22 Sep 2017 16:24:21 GMT</pubDate>
      <dc:creator><![CDATA[davidbaxterbrowne]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dbrowne/?p=335</guid>
      <description><![CDATA[It took me a while to figure out logging in EF Core, and I missed the simple way you could add...]]></description>
      <content:encoded><![CDATA[It took me a while to figure out logging in EF Core, and I missed the simple way you could add logging for EF6. Remember in EF6 it's as easy as
<pre>using (var db = new BloggingContext())
{
  db.Database.Log = s =&gt; Console.WriteLine(s);
  //. . .
}
</pre>
This is exacerbated that EF Core doesn't support returning the SQL for a query with ToString() like EF6 did. So you have to get the SQL Text through Logging (or IntelliTrace, or Profiler).

In EF Core it's more code, and more complicated, as documented here: <a href="https://docs.microsoft.com/en-us/ef/core/miscellaneous/logging">https://docs.microsoft.com/en-us/ef/core/miscellaneous/logging</a>
<pre>using (var db = new BloggingContext())
{
  var serviceProvider = db.GetInfrastructure();
  var loggerFactory = serviceProvider.GetService();
  loggerFactory.AddProvider(new MyLoggerProvider());
  //. . .
}
</pre>
And what's worse, the log message handling is buried inside the guts of MyLoggerProvider. So I took that sample and reworked so you can add a logger for your EF Core 2 DbContext like this:
<pre>using (var db = new BloggingContext())
{
  db.ConfigureLogging( s =&gt; Console.WriteLine(s) );
  //. . .
}</pre>
You can also filter the logger by LogLevel and/or category with an optional second parameter. Here the logger will capture all Errors and all Queries like this:
<pre>using (var db = new BloggingContext())
{
  db.ConfigureLogging( s =&gt; Console.WriteLine(s) , (c,l) =&gt; l == LogLevel.Error || c == DbLoggerCategory.Query.Name);
  //. . .
}
</pre>
And there's a shortcut to log the Query, Command, and Update categories to capture the all the generated SQL and execution statistics.
<pre>using (var db = new BloggingContext())
{
  db.ConfigureLogging(s =&gt; Console.WriteLine(s), LoggingCategories.SQL);
  //. . .
}
</pre>
Once you configure logging on a DbContext instance it will be enabled on all instances of that DbContext type. Repeated calls to ConfigureLogging() will change the logging for the DbContext type. Behind the scenes there is a single LogProvider for each DbContext type.

Note in a multi-threaded environment changing the logging will affect all new instances of your DbContext type.

It's an extension method so you need to import the namespace with:
<pre>using Microsoft.Samples.EFLogging;
</pre>
And here's the code:
<pre>namespace Microsoft.Samples.EFLogging
{
    using System;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore.Infrastructure;
    using Microsoft.Extensions.Logging;
    using System.Collections.Concurrent;
    using System.Collections.Generic;


    public enum LoggingCategories
    {
        All = 0,
        SQL = 1
    }

    public static class DbContextLoggingExtensions
    {
        public static void ConfigureLogging(this DbContext db, Action&lt;String&gt; logger, Func&lt;string, LogLevel, bool&gt; filter )
        {
            var serviceProvider = db.GetInfrastructure&lt;IServiceProvider&gt;();
            var loggerFactory = (ILoggerFactory)serviceProvider.GetService(typeof(ILoggerFactory));

            LogProvider.CreateOrModifyLoggerForDbContext(db.GetType(), loggerFactory, logger, filter);
        }
        
        public static void ConfigureLogging(this DbContext db, Action&lt;String&gt; logger, 
                                           LoggingCategories categories = LoggingCategories.All)
        {
            var serviceProvider = db.GetInfrastructure&lt;IServiceProvider&gt;();
            var loggerFactory = (LoggerFactory)serviceProvider.GetService(typeof(ILoggerFactory));

            if (categories == LoggingCategories.SQL)
            {
                var SqlCategories = new List&lt;string&gt; { DbLoggerCategory.Database.Command.Name,
                                                       DbLoggerCategory.Query.Name,
                                                       DbLoggerCategory.Update.Name };
                LogProvider.CreateOrModifyLoggerForDbContext(db.GetType(), 
                                                             loggerFactory, 
                                                             logger, 
                                                             (c, l) =&gt; SqlCategories.Contains(c));
            }
            else if (categories == LoggingCategories.All)
            {
                LogProvider.CreateOrModifyLoggerForDbContext(db.GetType(), 
                                                             loggerFactory, logger, 
                                                             (c, l) =&gt; true);
            }
        }
    }
    class LogProvider : ILoggerProvider
    {

        //volatile to allow the configuration to be switched without locking
        public volatile LoggingConfiguration Configuration;
        static bool DefaultFilter(string CategoryName, LogLevel level) =&gt; true;

        static ConcurrentDictionary&lt;Type, LogProvider&gt; providers = new ConcurrentDictionary&lt;Type, LogProvider&gt;();

        public static void CreateOrModifyLoggerForDbContext(Type DbContextType, 
                                                            ILoggerFactory loggerFactory, 
                                                            Action&lt;string&gt; logger, 
                                                            Func&lt;string, LogLevel, bool&gt; filter = null)
        {
            bool isNew = false;
            var provider = providers.GetOrAdd(DbContextType, t =&gt;
              {
                  var p = new LogProvider(logger, filter ?? DefaultFilter);
                  loggerFactory.AddProvider(p);
                  isNew = true;
                  return p;
               }
              );
            if (!isNew)
            {
                provider.Configuration = new LoggingConfiguration(logger, filter ?? DefaultFilter);
            }
            
        }

        public class LoggingConfiguration
        {
            public LoggingConfiguration(Action&lt;string&gt; logger, Func&lt;string, LogLevel, bool&gt; filter)
            {
                this.logger = logger;
                this.filter = filter;
            }
            public readonly Action&lt;string&gt; logger;
            public readonly Func&lt;string, LogLevel, bool&gt; filter;
        }


        private LogProvider(Action&lt;string&gt; logger, Func&lt;string, LogLevel, bool&gt; filter)
        {
            this.Configuration = new LoggingConfiguration(logger, filter);
        }
   
        public ILogger CreateLogger(string categoryName)
        {
            return new Logger(categoryName, this);
        }

        public void Dispose()
        { }

        private class Logger : ILogger
        {
            
            readonly string categoryName;
            readonly LogProvider provider;
            public Logger(string categoryName, LogProvider provider)
            {
                this.provider = provider;
                this.categoryName = categoryName;
            }
            public bool IsEnabled(LogLevel logLevel)
            {
                return true; 
            }

            public void Log&lt;TState&gt;(LogLevel logLevel, EventId eventId, TState state, 
                                    Exception exception, Func&lt;TState, Exception, string&gt; formatter)
            {
                //grab a reference to the current logger settings for consistency, 
                //and to eliminate the need to block a thread reconfiguring the logger
                var config = provider.Configuration;
                if (config.filter(categoryName, logLevel))
                {
                    config.logger(formatter(state, exception));
                }
            }

            public IDisposable BeginScope&lt;TState&gt;(TState state)
            {
                return null;
            }
        }
    }
}</pre>]]></content:encoded>
    </item>
    <item>
      <title>Resource Manager Powershell Script to Enable SQL Server Managed Backup to Azure</title>
      <link>https://docs.microsoft.com/archive/blogs/dbrowne/configure-managed-backup-to-azure</link>
      <pubDate>Mon, 27 Jun 2016 13:33:35 GMT</pubDate>
      <dc:creator><![CDATA[davidbaxterbrowne]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dbrowne/?p=325</guid>
      <description><![CDATA[The information in SQL Server Books Online about how to generate the Shared Access Signature key for...]]></description>
      <content:encoded><![CDATA[The information in SQL Server Books Online about how to generate the Shared Access Signature key for SQL Server Managed Backup to Azure is (at the moment) out-of-date and also not very helpful.

https://msdn.microsoft.com/en-us/library/dn449491.aspx

So here's a Powershell script that creates the SAS key, and also will create the Storage Account and Container if they don't exist.

After creating the SAS key, this script will output a TSQL script to configure managed backup to Azure.

<pre>
$subscriptionName = "My Azure Subscription"
$resourceGroupName = "SQLBackup"
$storageAcctName = "mysqlmanagedbackup"
$containerName= "backups"
$location = "centralus"
$storageSkuName = "Standard_LRS"

Login-AzureRmAccount -SubscriptionName $subscriptionName

$rg = Find-AzureRmResourceGroup | ? Name -eq $resourceGroupName
if ($rg -eq $null)
{
  "Creating Resource Group $resourceGroupName"
  $rg = New-AzureRmResourceGroup $resourceGroupName -Location $location 
}

$sa = Find-AzureRmResource -Name $storageAcctName -ResourceGroupName $resourceGroupName -ResourceType "Microsoft.Storage/storageAccounts"
if ($sa -eq $null)
{
   "Creating Storage Account $storageAcctName"
   $sa = New-AzureRmStorageAccount -ResourceGroupName $resourceGroupName  -Name $storageAcctName -Location $location -SkuName $storageSkuName
}

$storageKey = (Get-AzureRmStorageAccountKey -Name $storageAcctName -ResourceGroupName $resourceGroupName )[0].Value
$context = New-AzureStorageContext -StorageAccountName $storageAcctName -StorageAccountKey $storageKey


if ((get-azurestoragecontainer  -context $context | ? Name -eq $containerName) -eq $null)
{
    Write-Host "Creating New Storage Container  $containerName" 
    new-azurestoragecontainer -name $containerName -permission container -context $context
}

$fullSasToken = New-AzureStorageContainerSASToken -Name $containerName -Permission rwdl -FullUri -Context $context  
$containerUrl = $fullSasToken.Substring(0,$fullSasToken.IndexOf("?"))
$sasToken = $fullSasToken.Substring($fullSasToken.IndexOf("?")+1)


$enableManagedBackupScript = @"
--------------------
---BEGIN TSQL Script
--------------------
CREATE CREDENTIAL [$containerUrl] 
WITH IDENTITY = 'Shared Access Signature', 
     SECRET = '$sasToken' 

GO

EXEC msdb.managed_backup.sp_backup_config_basic   
 @enable_backup = 1,   
 @database_name = null,  
 @container_url = '$containerUrl',   
 @retention_days = 30
   
 --------------------
 ---END TSQL Script
 --------------------
"@


write-host $enableManagedBackupScript

</pre>
]]></content:encoded>
    </item>
    <item>
      <title>Installing SQL Server Express and Restoring a Database With Powershell</title>
      <link>https://docs.microsoft.com/archive/blogs/dbrowne/installing-sql-server-express-and-restoring-a-database-with-powershell</link>
      <pubDate>Sat, 23 Apr 2016 21:27:41 GMT</pubDate>
      <dc:creator><![CDATA[davidbaxterbrowne]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dbrowne/?p=305</guid>
      <description><![CDATA[Here's a powershell script to install SQL Server Express Edition and restore a database from a...]]></description>
      <content:encoded><![CDATA[Here's a powershell script to install SQL Server Express Edition and restore a database from a commmandline.  It's intended to be used as part of an installation script for an application that needs a local SQL Express instance.  But it also demonstrates several SQL Server and Powershell interop features like handling InfoMessages from the server, dealing with resultsets, embedding TSQL commands with Powershell <a href="https://technet.microsoft.com/en-us/library/ee692792.aspx">Here Strings</a>.

Anyway the script assumes that it is sitting in a folder next to the backup file and the SQL Express install media.  You would supply the name of your database to restore and the instance name if you want something other than the default "SQLExpress".

eg
<pre><span style="color: #0000ff">PS C:\install&gt; dir</span>
<span style="color: #0000ff">    Directory: C:\install</span>
<span style="color: #0000ff">Mode                LastWriteTime         Length Name</span>
<span style="color: #0000ff">----                -------------         ------ ----</span>
<span style="color: #0000ff">d-----        4/22/2016   5:29 PM                SQLExpress</span>
<span style="color: #0000ff">-a----        4/23/2016   7:42 AM        2380288 foo.bak</span>
<span style="color: #0000ff">-a----        4/23/2016   4:18 PM           2803 InstallSQLExpressAndRestoreDatabase.ps1</span></pre>
And here's the script:

<span style="color: #000000">InstallSQLExpressAndRestoreDatabase.ps1</span>

<hr />

&nbsp;
<pre>#run this script from an elevated command prompt to install SQL Server Express edition and restore 
$ScriptDir = Split-Path $PSCommandPath</pre>
<pre>#enter database name
$databaseName = "foo"
#enter backup location
$backupLocation = "$ScriptDir\foo.bak"
#enter instance name
$instanceName = "SQLExpress"</pre>
<pre>#---------------------------------------------------------
#              INSTALL SQL SERVER EXPRESS EDITION
#---------------------------------------------------------</pre>
<pre>$serviceAccount = "NT Service\MSSQL`$$($instanceName)"
if ($instanceName -eq "MSSQLSERVER")
{
    $serviceAccount = "NT Service\MSSQLSERVER"
}
$cmd  = @"
"$ScriptDir\SqlExpress\setup.exe" /Q /IACCEPTSQLSERVERLICENSETERMS /ACTION=install  /ROLE=AllFeatures_WithDefaults  /INSTANCENAME=$($instanceName) /SQLSVCACCOUNT="$($serviceAccount)"  
"@</pre>
<pre>write-host "Installing"
write-host $cmd
&amp; cmd.exe /c $cmd</pre>
<pre>#---------------------------------------------------------
#              RESTORE YOUR DATABASE
#---------------------------------------------------------
$constr = "server=(local)\$instanceName;database=master;integrated security=true"</pre>
<pre>$sql = @"
select InstanceDefaultDataPath = serverproperty('InstanceDefaultDataPath'),
select InstanceDefaultLogPath = serverproperty('InstanceDefaultLogPath')
"@
$con = new-object system.data.sqlclient.sqlconnection $constr</pre>
<pre>$handler = [System.Data.SqlClient.SqlInfoMessageEventHandler] {param($s, $a)  
                foreach ($e in $a.Errors) 
                { 
                    write-host "SQL Message (Number: $($e.Number) Severity: $($e.Class) Line Number: $($e.LineNumber)) $($e.Message)" -ForegroundColor Red
                } 
             }; 
$con.add_InfoMessage($handler); 
$con.FireInfoMessageEventOnUserErrors = $true;</pre>
<pre>$con.Open()
$cmd = $con.CreateCommand()</pre>
<pre>$cmd.CommandText = "select InstanceDefaultDataPath = serverproperty('InstanceDefaultDataPath')"
$dataDir = $cmd.ExecuteScalar().ToString()</pre>
<pre>$cmd.CommandText = "select InstanceDefaultDataPath = serverproperty('InstanceDefaultLogPath')"
$logDir = $cmd.ExecuteScalar().ToString()</pre>
<pre>$cmd.CommandText = "restore filelistonly from disk='$backupFile' with file=1";
$rdr = $cmd.ExecuteReader()
$dt = new-object System.Data.DataTable
$dt.Load($rdr)</pre>
<pre>$dataFileName =  ($dt.Rows | where Type -EQ D | select -First 1).LogicalName
$logFileName =   ($dt.Rows | where Type -EQ L | select -First 1).LogicalName</pre>
<pre>$cmd.CommandText = @"</pre>
<pre>restore database [$databaseName] 
  from disk='$backupLocation'
  with 
   MOVE '$($dataFileName)' TO '$($dataDir)$($dataFileName)_Data.mdf', 
   MOVE '$($logFileName)'  TO '$($dataDir)$($logFileName)_Log.ldf'
"@;
$cmd.CommandText
$cmd.ExecuteNonQuery() | out-null</pre>
<pre>write-host "Restore complete"
$con.Close()</pre>]]></content:encoded>
    </item>
    <item>
      <title>TransparentNetworkIPResolution in SQLClient for .NET 4.6.1</title>
      <link>https://docs.microsoft.com/archive/blogs/dbrowne/transparentnetworkipresolution-in-sqlclient-for-net-4-6-1</link>
      <pubDate>Mon, 04 Apr 2016 22:05:42 GMT</pubDate>
      <dc:creator><![CDATA[davidbaxterbrowne]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dbrowne/?p=283</guid>
      <description><![CDATA[A new Connection String property has been added for SqlConnection in .NET 4.6.1....]]></description>
      <content:encoded><![CDATA[A new Connection String property has been added for SqlConnection in .NET 4.6.1.  See

<a href="https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.connectionstring(v=vs.110).aspx">SqlConnection.ConnectionString</a>

But as of this writing the property has not been properly documented.  And the new property is set to true by default:
<blockquote>            var con = new SqlConnection("server=.;Integrated Security=true");
var cb = new SqlConnectionStringBuilder(con.ConnectionString);
Console.WriteLine($"TransparentNetworkIPResolution= {cb.TransparentNetworkIPResolution}");</blockquote>
So this post is a quick preview of the enhanced behavior.

The background to this is that when you deploy an AlwaysOn Availaibility Group with an AG Listener with IPs on two different subnets, then the clients need to be configured with MultiSubnetFailover=true, or else they may experience connection timeouts when the AG Listener is not active on the IP that is returned first by your DNS server.

The initial plan was to change the default for MultiSubnetFailvoer to true, so that the SqlClient attempted a simultaneous connection to both IPs by default.   This was mentioned <a href="https://blogs.msdn.microsoft.com/alwaysonpro/2015/12/01/improved-multisubnet-listener-behavior-with-newly-released-sql-client-provider-in-net-4-6-1/">here</a>. At some point this plan was changed, and instead the default connection behavior was modified to prevent timeouts even without MultiSubnetFailover.

The reason for the timeout before was that the without MultiSubnetFailover, SqlClient would attempt to connect to the first IP returned from the name lookout, and only after that failed would it attempt the other addresses.  This timeout was something like 20sec, and the default connection timeout for SqlConnection is only 15sec.  So the client would either see extreme delays or connection failures.

From spelunking the Reference Source <a href="https://referencesource.microsoft.com/#System.Data/System/Data/SqlClient/TdsParser.cs,67b2c9cce4167aab">here</a>, here's what it looks like the new behavior is: An initial connection attempt to the first-returned IP address is still made, but that attempt is timed-out after only 500ms, and then connection attempts to all the IP addresses are attempted in parallel.

So this new behavior should eliminate the long delays and connection failures for clients who have neglected to set MultiSubnetFailover=true.  They will still suffer a 500ms delay when the AG Listener is not on the first-returned IP address, but once the connection is made it will be pooled and so this should only happen rarely.

It's still a best-practice to set MultiSubnetFailover=true when connection to an AG Listener, but it appears that it's no longer required.

I'll update this thread if I get any more official confirmation or documentation.

David

&nbsp;

&nbsp;

&nbsp;

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>How to create a Linked Server to SQL 2000 from SQL 2012 or later</title>
      <link>https://docs.microsoft.com/archive/blogs/dbrowne/how-to-create-a-linked-server-to-sql-2000-from-sql-2012-or-later</link>
      <pubDate>Fri, 15 May 2015 12:23:00 GMT</pubDate>
      <dc:creator><![CDATA[davidbaxterbrowne]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dbrowne/2015/05/15/how-to-create-a-linked-server-to-sql-2000-from-sql-2012-or-later/</guid>
      <description><![CDATA[So SQL 2000 has been out of support for a while now.&nbsp; But I know some of you still run...]]></description>
      <content:encoded><![CDATA[<p>So SQL 2000 has been out of support for a while now.&nbsp; But I know some of you still run it.&nbsp; As you upgrade your environments you might find you are upgrading a SQL Server 2000, 2005, 2008 or 2008 R2&nbsp;server that has linked server connections to a SQL 2000 server.&nbsp; If so, it can be tricky, since the newer OleDB drivers in SQL Server no longer support connecting to SQL 2000.</p>
<p>Anyway, here's how to do it.&nbsp; You can't use SQLNCLI11, and shouldn't use MSDASQL.&nbsp; If you try to use MSDASQL SQL Server will silently use SQLNCLI11 instead, so that won't work either.&nbsp; So first you need to install an older version of the SQL Native Client OleDB provider, SQLNCLI10.&nbsp; It can be installed side-by-side with SQLNCLI11, and is still supported.</p>
<p>So first download the newest SQL Native Client OleDB provider that still supports connecting to SQL 2000, SQLNCLI10 from here:</p>
<p><a href="https://www.microsoft.com/en-us/download/details.aspx?id=30440" target="_blank">Microsoft&reg; SQL Server&reg; 2008 R2 SP2 Feature Pack </a></p>
<p>&nbsp;</p>
<p>Then set up the linked server like this, after adjusting the server name in sp_addlinkedserver and the login mapping in sp_addlinkedsrvlogin for your environment.</p>
<pre class="scroll"><span style="font-size: 9px;"><code class="mysql"><br /> EXEC sp_MSset_oledb_prop N'SQLNCLI10', N'AllowInProcess', 1<br /> GO<br /> EXEC sp_addlinkedserver @server = N'SQL2000', @srvproduct=N'', @provider=N'SQLNCLI10', @datasrc=N'MySql2000Server'<br /> GO<br /> EXEC sp_serveroption @server=N'SQL2000', @optname=N'collation compatible', @optvalue=N'false'<br /> GO<br /> EXEC sp_serveroption @server=N'SQL2000', @optname=N'data access', @optvalue=N'true'<br /> GO<br /> EXEC sp_serveroption @server=N'SQL2000', @optname=N'dist', @optvalue=N'false'<br /> GO<br /> EXEC sp_serveroption @server=N'SQL2000', @optname=N'pub', @optvalue=N'false'<br /> GO<br /> EXEC sp_serveroption @server=N'SQL2000', @optname=N'rpc', @optvalue=N'true'<br /> GO<br /> EXEC sp_serveroption @server=N'SQL2000', @optname=N'rpc out', @optvalue=N'true'<br /> GO<br /> EXEC sp_serveroption @server=N'SQL2000', @optname=N'sub', @optvalue=N'false'<br /> GO<br /> EXEC sp_serveroption @server=N'SQL2000', @optname=N'connect timeout', @optvalue=N'0'<br /> GO<br /> EXEC sp_serveroption @server=N'SQL2000', @optname=N'collation name', @optvalue=null<br /> GO<br /> EXEC sp_serveroption @server=N'SQL2000', @optname=N'lazy schema validation', @optvalue=N'false'<br /> GO<br /> EXEC sp_serveroption @server=N'SQL2000', @optname=N'query timeout', @optvalue=N'0'<br /> GO<br /> EXEC sp_serveroption @server=N'SQL2000', @optname=N'use remote collation', @optvalue=N'true'<br /> GO<br /> EXEC sp_serveroption @server=N'SQL2000', @optname=N'remote proc transaction promotion', @optvalue=N'true'<br /> GO<br /> EXEC sp_addlinkedsrvlogin @rmtsrvname = N'SQL2000', @locallogin = NULL , @useself = N'False', @rmtuser = N'dw', @rmtpassword = N'P@ssword'<br /> GO</code></span></pre>
<p>Then you can test with a query like</p>
<pre class="scroll"><span style="font-size: 9px;"><code class="mysql"> select * from openquery (sql2000, 'select @@servername a' )<br /> </code></span></pre>
<p>David</p>]]></content:encoded>
    </item>
    <item>
      <title>How to connect to Oracle from a .NET Application</title>
      <link>https://docs.microsoft.com/archive/blogs/dbrowne/how-to-connect-to-oracle-from-a-net-application</link>
      <pubDate>Thu, 24 Jul 2014 12:03:00 GMT</pubDate>
      <dc:creator><![CDATA[davidbaxterbrowne]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dbrowne/2014/07/24/how-to-connect-to-oracle-from-a-net-application/</guid>
      <description><![CDATA[Use this:


ODP.NET, Managed Driver - Official&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...]]></description>
      <content:encoded><![CDATA[<p>Use this:</p>
<div id="stats">
<div class="stat">
<h1><span style="font-size: small;">ODP.NET, Managed Driver - Official</span><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </strong></h1>
</div>
</div>
<p><a href="https://www.nuget.org/packages/Oracle.ManagedDataAccess/">https://www.nuget.org/packages/Oracle.ManagedDataAccess/</a></p>
<p>This short post is just because most of the information you will find on this topic is out-of-date.</p>
<p>Oracle finally wrote a 100% managed code ADO.NET provider and it's available on NuGet.&nbsp; So you can just right-click on your references and grab the NuGet package.&nbsp; Not install, no bitness headaches.</p>
<p>David</p>]]></content:encoded>
    </item>
    <item>
      <title>How to load an Assembly in a SSIS script task that isn't in the GAC</title>
      <link>https://docs.microsoft.com/archive/blogs/dbrowne/how-to-load-an-assembly-in-a-ssis-script-task-that-isnt-in-the-gac</link>
      <pubDate>Wed, 25 Jun 2014 12:10:00 GMT</pubDate>
      <dc:creator><![CDATA[davidbaxterbrowne]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dbrowne/2014/06/25/how-to-load-an-assembly-in-a-ssis-script-task-that-isnt-in-the-gac/</guid>
      <description><![CDATA[The documented and recommended way to reference a custom assembly from an SSIS Script Task or Script...]]></description>
      <content:encoded><![CDATA[<p>The <a title="documented" href="https://msdn.microsoft.com/en-us/library/ms403356.aspx">documented</a> and recommended way to reference a custom assembly from an SSIS Script Task or Script Component is to install it in the Global Assembly Cache (GAC).&nbsp; However this is not always possible or simple to do.</p>
<p>Here's a simple workaround for loading an assembly from an arbitrary location.&nbsp; The idea is to register an <a title="AppDomain.AssemblyResolve" href="https://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve(v=vs.110).aspx">AppDomain.AssemblyResolve</a> event handler.</p>
<p style="padding-left: 30px;">The .NET Framework provides the <span><a href="https://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve(v=vs.110).aspx">AppDomain<span>.</span>AssemblyResolve</a></span> event for applications that require greater control over assembly loading. By handling this event, your application can load an assembly into the load context from outside the normal probing paths, select which of several assembly versions to load, emit a dynamic assembly and return it, and so on. This topic provides guidance for handling the <span><a href="https://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve(v=vs.110).aspx">AssemblyResolve</a></span> event.</p>
<p style="padding-left: 30px;"><a href="https://msdn.microsoft.com/en-us/library/ff527268(v=vs.110).aspx">Resolving Assembly Loads</a></p>
<p>Which does just what we need.&nbsp; The question is where and how to wire it up.&nbsp; The trick is to realize that .NET's Just-in-Time (JIT) compilation is responsible for loading dependent assemblies, and therefore guarantees that the assemblies referenced in a type's method won't be loaded until just before the method is executed.&nbsp; This means that in the type's static constructor we can reliably wire up the AssemblyResolve event that will supply the referenced types.</p>
<p>When you create a SSIS Script Task or Script Component SSIS generates a class for you called ScriptMain, and you can wire-up the event in a <a href="https://msdn.microsoft.com/en-us/library/k9x6w0hc.aspx">Static Constructor</a> for that type.&nbsp; A Static Constructor is guaranteed to be called exactly once "before the first instance is created or any static members are referenced.", so therefore before the dependent assemblies are loaded.</p>
<p>EG:</p>
<pre class="brush: csharp; collapse: true;">[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
     static ScriptMain()
     {
         AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
     }
     static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
     {
         if (args.Name.Contains("ssisHelper"))
         {
             string path = @"c:\temp\";
             return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(path, "ssisHelper.dll"));
         }
         return null;
     }
    . . .</pre>
<p>You can then drop the referenced assemblies in a well-known location on the SSIS server, or use a package variable to refer to the location and pass that in to the Script Task or Script Component.</p>
<p>David</p>]]></content:encoded>
    </item>
    <item>
      <title>Compatible Encryption/Decryption for Windows Store Apps and Full .NET Framework</title>
      <link>https://docs.microsoft.com/archive/blogs/dbrowne/compatible-encryptiondecryption-for-windows-store-apps-and-full-net-framework</link>
      <pubDate>Thu, 19 Jun 2014 12:50:00 GMT</pubDate>
      <dc:creator><![CDATA[davidbaxterbrowne]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dbrowne/2014/06/19/compatible-encryptiondecryption-for-windows-store-apps-and-full-net-framework/</guid>
      <description><![CDATA[I recently needed to encrypt some data in a Windows Store App and decrypt it on the server in a full...]]></description>
      <content:encoded><![CDATA[<p>I recently needed to encrypt some data in a Windows Store App and decrypt it on the server in a full .NET Framework application.&#160; The Encryption APIs available in the two environments are different, so I came up with the following class that implements a compatible pair of encryption and decryption functions to enable encrypting in a Store App and Decrypting on the server or vice versa.</p>  <p>It's a single file, and has conditional compilation directives so it works on both kinds of projects.&#160; It uses AES 256-bit encryption and you must share both a 256bit key and an and initialization vector between your two projects.&#160; Typically the IV is shared in code, and the key is acquired at runtime.</p>  <p>The result is a single object called EncDec in the attached EncDec.cs file. that you new-up with the IV and the key, and then use .Encrypt() and .Decrypt(). All data is passed as strings, base64 endoded if needed.</p>  <p>Here’s how to call it</p>  <pre class="brush: csharp; ruler: true;">    string plainText = &quot;Hello, World!&quot;;
    
    string TestKey = &quot;gFlMfLZu4unBGfBh9weIuLwlcXHSa59vxyUQWM3yh1M=&quot;;
    string TestIV = &quot;rRNgGypocle9VG9bth6kxg==&quot;;
    
    var ed = new EncDec(TestKey, TestIV);
    
    var cypherText = ed.Encrypt(plainText);
    var plainText2 = ed.Decrypt(cypherText);
    </pre>

<p>David</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/10/53/58/07/EncDec.cs" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-components-postattachments/00-10-53-58-07/EncDec.cs">EncDec.cs</a></p>]]></content:encoded>
    </item>
    <item>
      <title>New Sample Published: ObjectDataReader</title>
      <link>https://docs.microsoft.com/archive/blogs/dbrowne/new-sample-published-objectdatareader</link>
      <pubDate>Wed, 11 Jun 2014 13:04:00 GMT</pubDate>
      <dc:creator><![CDATA[davidbaxterbrowne]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dbrowne/2014/06/11/new-sample-published-objectdatareader/</guid>
      <description><![CDATA[I just published a new sample&nbsp;in the&nbsp;MSDN sample gallery:&nbsp; ObjectDataReader.&nbsp;...]]></description>
      <content:encoded><![CDATA[<p>I just published a new sample&nbsp;in the&nbsp;MSDN sample gallery:&nbsp; <a title="ObjectDataReader" href="https://code.msdn.microsoft.com/ObjectDataReader-8476dd72">ObjectDataReader</a>.&nbsp; This is an update of an older sample (EntityDataReader) that is an IDataReader adapter for object collections.&nbsp; This is useful primarily for bulk loading SQL Server from a collection of .NET objects.</p>
<p>ObjectDataReader is intended to be simple and fast.&nbsp; It uses dynamically-compiled expressions to access the object properties, instead of reflection.&nbsp; ObjectDataReader differes from the older EntityDataReader primarily by removing support for automatic flattening of related objects in an object graph.&nbsp; This feature was useful in the early days of Entity Framework, but less so now.&nbsp; Removing this simplified the code and removed the dependency on Entity Framework.</p>
<p>David</p>]]></content:encoded>
    </item>
    <item>
      <title>Copying SQL Server Backups to Windows Azure Storage using AzCopy</title>
      <link>https://docs.microsoft.com/archive/blogs/dbrowne/copying-sql-server-backups-to-windows-azure-storage-using-azcopy</link>
      <pubDate>Fri, 07 Mar 2014 05:01:00 GMT</pubDate>
      <dc:creator><![CDATA[davidbaxterbrowne]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dbrowne/2014/03/07/copying-sql-server-backups-to-windows-azure-storage-using-azcopy/</guid>
      <description><![CDATA[There are at least three easy ways to get your SQL Server backups to Azure storage, which, being...]]></description>
      <content:encoded><![CDATA[<p>There are at least three easy ways to get your SQL Server backups to Azure storage, which, being offsite, bottomless, and reasonably-priced is a good thing.</p>
<p>First you can use SQL Server 2012's native backup to URL function.</p>
<p>Also you can use the Microsoft&reg; SQL Server&reg; Backup to Microsoft Windows&reg; Azure&reg;Tool, available here: <a href="https://www.microsoft.com/en-us/download/details.aspx?id=40740">https://www.microsoft.com/en-us/download/details.aspx?id=40740</a></p>
<p>But both those methods put your backups directly in Azure, which means that if you need your backups locally (and you do), you have to download them.&nbsp; Another downside of this approach is that your backup time depends on your network bandwidth to Azure, so it's not really viable for backing up really large or a really large number of databases.</p>
<p>A&nbsp;better approach is to take your backups to some local disk and copy them to azure.&nbsp;</p>
<p>A StorSimple <a href="https://www.storsimple.com/">https://www.storsimple.com/</a>&nbsp;is a really good option, as it dedupes and encrypts the data before backing it up to Azure, and handles the&nbsp;full lifecycle of the data.&nbsp; Of course with StorSimple you need to&nbsp;install&nbsp;small storage appliance in your datacenter.</p>
<p>If you just want to copy from a local folder to Azure, that's really easy too using AzCopy, available here <a href="https://aka.ms/WaCopy">https://aka.ms/WaCopy</a>&nbsp;.</p>
<p>So use a MaintenancePlan or scheduled job to backup your databases to a local disk or network file share, and then use AzCopy to copy them to Azure.&nbsp; Your backups will be available locally until your Maintence Plan deletes them, and will be available in Azure forever.&nbsp; Plus your backup time will not increase, even if it takes 18 hours to get all your nightly backups sent up to Azure.&nbsp; Just run azcopy after your backups are done.&nbsp; Like this</p>
<p><span style="font-family: courier new,courier;">azcopy.exe backupfolder azure_container_url&nbsp;/destkey:yourkey /S /XO</span></p>
<p>so, something like</p>
<p><span style="font-family: courier new,courier;">"c:\Program Files (x86)\Microsoft SDKs\Windows Azure\AzCopy\azcopy.exe" "C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Backup" "<a href="https://yourstorageaccount.blob.core.windows.net/yourcontainer">https://yourstorageaccount.blob.core.windows.net/yourcontainer</a>" /destkey:AKSxxxxxxxxxxxxxxxxxxxxxxxxxxx /S /XO</span></p>
<p>You can also create a SQL Agent job to run this command.&nbsp; If you do, remember to grant the SQL Agent Service rights to the backup folder.</p>
<p>You will also want to delete some of these backups from Azure at some point according to a long term retention plan.&nbsp; For instance retain daily backups for a week, weekly backups for a month, monthly backups for a year, annual backups for 5 years, where the first backup of the week is the "weeky" backup, the first backup of the month is the "monthly" backtup etc.&nbsp; But that's a subject for another day. . .</p>
<p>David</p>
<p>&nbsp;</p>]]></content:encoded>
    </item>
  </channel>
</rss>