@@ -22,25 +22,25 @@ namespace NuGetGallery.Infrastructure
2222 private const string IndexPartitionKey = "INDEX" ;
2323 private const string IndexRowKey = "0" ;
2424
25- private CloudTable _tableRef ;
25+ private readonly string _tableName ;
26+ private readonly bool _readAccessGeoRedundant ;
27+ private readonly Func < string > _connectionStringFactory ;
2628
27- public AzureEntityList ( string connStr , string tableName , bool readAccessGeoRedundant )
29+ public AzureEntityList ( Func < string > connectionStringFactory , string tableName , bool readAccessGeoRedundant )
2830 {
29- var tableClient = CloudStorageAccount . Parse ( connStr ) . CreateCloudTableClient ( ) ;
30- if ( readAccessGeoRedundant )
31- {
32- tableClient . DefaultRequestOptions . LocationMode = LocationMode . PrimaryThenSecondary ;
33- }
34- _tableRef = tableClient . GetTableReference ( tableName ) ;
31+ _tableName = tableName ?? throw new ArgumentNullException ( nameof ( tableName ) ) ;
32+ _readAccessGeoRedundant = readAccessGeoRedundant ;
33+ _connectionStringFactory = connectionStringFactory ?? throw new ArgumentNullException ( nameof ( connectionStringFactory ) ) ;
3534
35+ var tableRef = GetTableReference ( ) ;
3636 // Create the actual Azure Table, if it doesn't yet exist.
37- bool newTable = _tableRef . CreateIfNotExists ( ) ;
37+ bool newTable = tableRef . CreateIfNotExists ( ) ;
3838
3939 // Create the Index if it doesn't yet exist.
4040 bool needsIndex = newTable ;
4141 if ( ! newTable )
4242 {
43- var indexResult = _tableRef . Execute (
43+ var indexResult = tableRef . Execute (
4444 TableOperation . Retrieve < Index > ( IndexPartitionKey , IndexRowKey ) ) ;
4545
4646 needsIndex = ( indexResult . HttpStatusCode == 404 ) ;
@@ -49,7 +49,7 @@ public AzureEntityList(string connStr, string tableName, bool readAccessGeoRedun
4949 if ( needsIndex )
5050 {
5151 // Create the index
52- var result = _tableRef . Execute (
52+ var result = tableRef . Execute (
5353 TableOperation . Insert ( new Index
5454 {
5555 Count = 0 ,
@@ -102,7 +102,8 @@ public T this[long index]
102102 string partitionKey = FormatPartitionKey ( page ) ;
103103 string rowKey = FormatRowKey ( row ) ;
104104
105- var response = _tableRef . Execute ( TableOperation . Retrieve < T > ( partitionKey , rowKey ) ) ;
105+ var tableRef = GetTableReference ( ) ;
106+ var response = tableRef . Execute ( TableOperation . Retrieve < T > ( partitionKey , rowKey ) ) ;
106107 if ( response . HttpStatusCode == 404 )
107108 {
108109 throw new ArgumentOutOfRangeException ( nameof ( index ) , index , CoreStrings . Http404NotFound ) ;
@@ -129,8 +130,10 @@ public T this[long index]
129130 value . PartitionKey = FormatPartitionKey ( page ) ;
130131 value . RowKey = FormatRowKey ( row ) ;
131132
133+ var tableRef = GetTableReference ( ) ;
134+
132135 // Just do an unconditional update - if you wanted any *real* benefit of atomic update then you would need a more complex method signature that calls you back when optimistic updates fail ETAG checks!
133- _tableRef . Execute ( TableOperation . Replace ( value ) ) ;
136+ tableRef . Execute ( TableOperation . Replace ( value ) ) ;
134137 }
135138 }
136139
@@ -161,13 +164,14 @@ public long Add(T entity)
161164 /// </summary>
162165 public IEnumerator < T > GetEnumerator ( )
163166 {
167+ var tableRef = GetTableReference ( ) ;
164168 for ( long page = 0 ; ; page ++ )
165169 {
166170 string partitionKey = FormatPartitionKey ( page ) ;
167171 var chunkQuery = new TableQuery < T > ( ) . Where (
168172 TableQuery . GenerateFilterCondition ( "PartitionKey" , QueryComparisons . Equal , partitionKey ) ) ;
169173
170- var chunk = _tableRef . ExecuteQuery ( chunkQuery ) . ToArray ( ) ;
174+ var chunk = tableRef . ExecuteQuery ( chunkQuery ) . ToArray ( ) ;
171175
172176 foreach ( var item in chunk )
173177 {
@@ -191,6 +195,7 @@ public IEnumerable<T> GetRange(long pos, int n)
191195 int done = 0 ;
192196 long page = pos / 1000 ;
193197 long offset = pos % 1000 ;
198+ var tableRef = GetTableReference ( ) ;
194199 while ( done < n )
195200 {
196201 string partitionKey = FormatPartitionKey ( page ) ;
@@ -201,7 +206,7 @@ public IEnumerable<T> GetRange(long pos, int n)
201206 TableOperators . And ,
202207 TableQuery . GenerateFilterCondition ( "RowKey" , QueryComparisons . GreaterThanOrEqual , rowKey ) ) ) ;
203208
204- var chunk = _tableRef . ExecuteQuery ( chunkQuery ) . ToArray ( ) ;
209+ var chunk = tableRef . ExecuteQuery ( chunkQuery ) . ToArray ( ) ;
205210 if ( chunk . Length == 0 )
206211 {
207212 break ; // Reached the end of the list
@@ -235,9 +240,10 @@ private long AtomicIncrementCount()
235240 // 2) use ETAG to do a conditional +1 update
236241 // 3) retry if that optimistic concurrency attempt failed
237242 long pos = - 1 ; // To avoid compiler warnings, grr - should never be returned
243+ var tableRef = GetTableReference ( ) ;
238244 DoReplaceWithRetry ( ( ) =>
239245 {
240- var result1 = _tableRef . Execute (
246+ var result1 = tableRef . Execute (
241247 TableOperation . Retrieve < Index > ( IndexPartitionKey , IndexRowKey ) ) ;
242248
243249 ThrowIfErrorStatus ( result1 ) ;
@@ -260,6 +266,7 @@ private long AtomicIncrementCount()
260266 private void InsertIfNotExistsWithRetry < T2 > ( Func < T2 > valueGenerator ) where T2 : ITableEntity
261267 {
262268 TableResult storeResult ;
269+ var tableRef = GetTableReference ( ) ;
263270 do
264271 {
265272 var entity = valueGenerator ( ) ;
@@ -268,7 +275,7 @@ private void InsertIfNotExistsWithRetry<T2>(Func<T2> valueGenerator) where T2 :
268275 // - the dummy MERGES with existing data instead of overwriting it, so no data loss.
269276 // 2) Use its ETAG to conditionally replace the item
270277 // 3) return true if success, false to allow retry on failure
271- var dummyResult = _tableRef . Execute (
278+ var dummyResult = tableRef . Execute (
272279 TableOperation . InsertOrMerge ( new HazardEntry
273280 {
274281 PartitionKey = entity . PartitionKey ,
@@ -281,7 +288,7 @@ private void InsertIfNotExistsWithRetry<T2>(Func<T2> valueGenerator) where T2 :
281288 }
282289
283290 entity . ETag = dummyResult . Etag ;
284- storeResult = _tableRef . Execute ( TableOperation . Replace ( entity ) ) ;
291+ storeResult = tableRef . Execute ( TableOperation . Replace ( entity ) ) ;
285292 }
286293 while ( storeResult . HttpStatusCode == 412 ) ;
287294 ThrowIfErrorStatus ( storeResult ) ;
@@ -290,17 +297,19 @@ private void InsertIfNotExistsWithRetry<T2>(Func<T2> valueGenerator) where T2 :
290297 private void DoReplaceWithRetry < T2 > ( Func < T2 > valueGenerator ) where T2 : ITableEntity
291298 {
292299 TableResult storeResult ;
300+ var tableRef = GetTableReference ( ) ;
293301 do
294302 {
295- storeResult = _tableRef . Execute ( TableOperation . Replace ( valueGenerator . Invoke ( ) ) ) ;
303+ storeResult = tableRef . Execute ( TableOperation . Replace ( valueGenerator . Invoke ( ) ) ) ;
296304 }
297305 while ( storeResult . HttpStatusCode == 412 ) ;
298306 ThrowIfErrorStatus ( storeResult ) ;
299307 }
300308
301309 private Index ReadIndex ( )
302310 {
303- var response = _tableRef . Execute ( TableOperation . Retrieve < Index > ( IndexPartitionKey , IndexRowKey ) ) ;
311+ var tableRef = GetTableReference ( ) ;
312+ var response = tableRef . Execute ( TableOperation . Retrieve < Index > ( IndexPartitionKey , IndexRowKey ) ) ;
304313 return ( Index ) response . Result ;
305314 }
306315
@@ -333,6 +342,16 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
333342 return GetEnumerator ( ) ;
334343 }
335344
345+ private CloudTable GetTableReference ( )
346+ {
347+ var tableClient = CloudStorageAccount . Parse ( _connectionStringFactory ( ) ) . CreateCloudTableClient ( ) ;
348+ if ( _readAccessGeoRedundant )
349+ {
350+ tableClient . DefaultRequestOptions . LocationMode = LocationMode . PrimaryThenSecondary ;
351+ }
352+ return tableClient . GetTableReference ( _tableName ) ;
353+ }
354+
336355 class HazardEntry : ITableEntity
337356 {
338357 private const string PlaceHolderPropertyName = "Place_Held" ;
0 commit comments