title: How to configure amazon s3 client for multi region in .net core?
body: <p>I have asp.net core application. Given S3 urls, the application needs to download file. These S3 urls may belongs to different AWS regions in the US. </p>
  
  <pre><code>  public class Downloader
    {
        public void DownloadFile(string s3Url, string destFolder)
        {
            //download files
        }
    }
  </code></pre>
  
  <p>The <code>DownloadFile()</code> method gets called <strong>concurrently</strong>. Each url pass to this method may belong to different region.</p>
  
  <p>AWS documentation shows <a href="http://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-netcore.html" rel="nofollow noreferrer">how to Configure the AWS SDK for .NET with .NET Core</a>. In my case AWS credentials are stored in profile file on server and the same credentials can be used across all US regions. So appsettings.json looks like</p>
  
  <pre><code>"AWS": {    
      "Profile": "default",
      "ProfilesLocation": "C:\\aws\\awsprofile"
    },
  </code></pre>
  
  <p><strong>Issue</strong><br>
  Since URLS can belong to different region, i cannot follow documentation code. I <strong>cannot</strong> register <code>IAmazonS3</code> with DI framework and inject that instance into <code>Downloader</code> to download files from different regions. Because <code>IAmazonS3</code> instance tried to a particular region.</p>
  
  <p><strong>Solution</strong><br>
  So i created a factory which provides instance of <code>IAmazonS3</code> given region name.</p>
  
  <pre><code>public interface IS3ClientFactory : IDisposable
  {
      IAmazonS3 GetS3Client(RegionEndpoint region);
  }
  
  public class S3ClientFactory : IS3ClientFactory
  {
      private bool _disposed = false;
      private IDictionary&lt;string, IAmazonS3&gt; _container = null;
  
      private S3ClientFactory()
      {
          _container = new Dictionary&lt;string, IAmazonS3&gt;();
      }
      public static IS3ClientFactory Configure(AWSOptions option, RegionEndpoint[] regions)
      {
          var factory = new S3ClientFactory();
          foreach (RegionEndpoint region in regions)
          {
              option.Region = region;
              factory._container.Add(region.SystemName, option.CreateServiceClient&lt;IAmazonS3&gt;());
          }
          return factory;
      }
  
      public IAmazonS3 GetS3Client(RegionEndpoint region)
      {            
          return _container[region.SystemName];
      }
  
      public void Dispose()
      {
          Dispose(true);
          GC.SuppressFinalize(this);
      }
  
      protected virtual void Dispose(bool disposing)
      {
          if (!_disposed)
          {
              if (disposing)
              {
                  if (_container != null &amp;&amp; _container.Any())
                  {
                      foreach (var s3Client in _container)
                      {
                          if (s3Client.Value != null)
                          {
                              s3Client.Value.Dispose();
                          }
                      }
                  }
                  _disposed = true;
              }
          }
      }
  }
  </code></pre>
  
  <p>and in startup.cs register factory with DI</p>
  
  <pre><code>services.AddSingleton&lt;IS3ClientFactory&gt;(S3ClientFactory.Configure(Configuration.GetAWSOptions(),
                  new RegionEndpoint[]
                  {
                      RegionEndpoint.USWest1,
                      RegionEndpoint.USWest2,
                      RegionEndpoint.USEast1,
                      RegionEndpoint.USEast2
                  }));
  </code></pre>
  
  <p>and <code>Downloader</code> class would look like</p>
  
  <pre><code>public class Downloader : IDownloader
  {        
      private readonly IS3ClientFactory _factory;
      public Downloader(IS3ClientFactory factory)
      {
         _factory = factory;        
      }
  
      public void DownloadFile(string s3Url, string destFolder)
      {
          var s3Uri = new AmazonS3Uri(s3Url);            
          var s3Client = _factory.GetS3Client(s3Uri.Region);
  
          // use s3Client to download file
      }
  
  }
  </code></pre>
  
  <p><strong>Questions</strong></p>
  
  <ol>
  <li><p>In <code>S3ClientFactory</code>'s Configure method i am dynamically assigning <code>RegionEndpoint</code> to <code>AWSOptions</code> and then call <code>option.CreateServiceClient&lt;IAmazonS3&gt;()</code> is this a correct way of creating region specific instance of <code>IAmazonS3</code>? The code need to be unit testable so i could not use <code>new AmazonS3Client(RegionEndpoint)</code>. </p>
  
  <pre><code>foreach (RegionEndpoint region in regions)
  {
      option.Region = region;
      factory._container.Add(region.SystemName, option.CreateServiceClient&lt;IAmazonS3&gt;());
  } 
  </code></pre></li>
  <li><p>Is it okay to have singleton instance of <code>IAmazonS3</code>?</p></li>
  </ol>