public async Task <List <Album> > QueryDataWithTransactionAsync(string projectId, string instanceId, string databaseId) { string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; var albums = new List <Album>(); using TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); using var connection = new SpannerConnection(connectionString); // Open the connection, making the implicitly created // transaction read only when it connects to the outer // transaction scope. await connection.OpenAsReadOnlyAsync(); using var cmd = connection.CreateSelectCommand("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"); // Read #1. using (var reader = await cmd.ExecuteReaderAsync()) { while (await reader.ReadAsync()) { Console.WriteLine("SingerId : " + reader.GetFieldValue <string>("SingerId") + " AlbumId : " + reader.GetFieldValue <string>("AlbumId") + " AlbumTitle : " + reader.GetFieldValue <string>("AlbumTitle")); } } // Read #2. Even if changes occur in-between the reads, // the transaction ensures that Read #1 and Read #2 // return the same data. using (var reader = await cmd.ExecuteReaderAsync()) { while (await reader.ReadAsync()) { albums.Add(new Album { AlbumId = reader.GetFieldValue <int>("AlbumId"), SingerId = reader.GetFieldValue <int>("SingerId"), AlbumTitle = reader.GetFieldValue <string>("AlbumTitle") }); } } scope.Complete(); Console.WriteLine("Transaction complete."); return(albums); }