download files with httpclient

the title of the blog post is self explanatory, but what i want to highlight here is the significance of Path.Combine, most of the time i see code where we concatenate the folder name and the filename for ex:

var path =”C:\\testfolder\\filename.txt”;

this works as expected but imagine moving the code to a linux environment ?? this small line of code will break the app completely.

Path.Combine like Environment.Newline, sets up the path based on your respective OS, for linux it would /mnt/ss/filename.txt, for windows it would be c:\ss\filename.txt

 await DownloadFile(http://sfasfsa.com/safdas/main.txt, Path.Combine( Environment.CurrentDirectory, "filename.txt"));
  private static async Task DownloadFile(string uri, string outputPath)
        {
            if (!Uri.TryCreate(uri, UriKind.Absolute, out _))
                throw new InvalidOperationException("URI is invalid.");

            var stream = await _httpClient.GetStreamAsync(uri);
            await using var fileStream = new FileStream(outputPath, FileMode.Create);
            await stream.CopyToAsync(fileStream);


        }
Advertisement

Azure functions isolated host findings and adding appsettings.json for configuration.

  1. Starting worker process failed, the operation has timed out : Visual studio hasn’t caught up with the function tooling yet, to overcome this issue, install the func cli and run the the command func start, if you are comfortable with VSCODE, you will be at home.
  2. There are lot of great articles out there on how to get started with the azure isolated host functions, but the most complete one that i found is the post Azure Functions and .NET 5: Dependency Injection – DEV Community by Kenichiro Nakamura – DEV Community
  3. There is no documentation out there at least from what I found on how to add configuration Ex: appsettings.json to the isolated functions project. So lets see how we can add this feature.
    1. Add an appsettings file to the root folder and change the file properties to copy
    2. Update the Program.cs class to include the following
  public static void Main()
        {
            var host = new HostBuilder()
                
                .ConfigureAppConfiguration(e =>
                    e.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).AddEnvironmentVariables().Build()
                )
                .ConfigureServices(services =>
                    {
                        services.AddSingleton<DomainFacade>();
                    })
                .ConfigureFunctionsWorkerDefaults()
                .Build();
            host.Run();
        }

3. Update the Function with the following


  public class ConfigurationTest
    {
        private readonly DomainFacade _domainFacade;
        private readonly IConfiguration _configuration;

        public ConfigurationTest(DomainFacade domainFacade, IConfiguration configuration)
        {
            _domainFacade = domainFacade;
            _configuration = configuration;
        }


        [Function("ConfigurationTest")]
        public async Task Run([TimerTrigger("0 */1 * * * *")] FunctionContext context)
        {
            var logger = context.GetLogger("Function1");
            logger.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}" + _configuration["CosmosDb:AccountPrimaryKey"]);

            await _domainFacade.DomainMethod(string someValue );
        }
    }

4. And this will work locally as well as on the cloud, without tinkering with the path of the appsettings, the example is for a timetrigger function, but this will work for any trigger.

Pagination in cosmos db

the following code snippet details on how to do pagination is cosmos db , we just need to pass a pagesize and the continuation token, when we are calling the method for the first time, we can just pass in a null and for the subsequent requests, we can pass in the continuation token, which is returned as a part of the response of this method.

 public async Task<ProductResponse> GetAllProducts(int pageSize, string continuationToken)
        {
            List<Product> products = new List<Product>();
            ProductsResponse productsResponse = new ProductsResponse();
            QueryDefinition queryDef = new QueryDefinition("select * from Prodcuts p");
            string token = null;
            if (!string.IsNullOrWhiteSpace(continuationToken))
                token = continuationToken;

            using FeedIterator<Product> resultSet = _container.GetItemQueryIterator<Product>(queryDefinition: queryDef, continuationToken: token, new QueryRequestOptions { MaxItemCount = pageSize });
            {
                var items = await resultSet.ReadNextAsync();
                foreach (Product item in items)
                {
                    products.Add(item);

                }
  productsResponse.ContinuationToken = items.ContinuationToken;
            }

            productResponse.Products = products;
            return productResponse;
        }

Azure Bicep template for Azure Cosmos DB serverless

Azure Cosmos DB serverless went GA last week, but the documentation has not caught up yet, if you are looking to provision an Azure Cosmos DB serverless account using Bicep you can use the following snippet, the magic here is the capabilities section.

resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2021-04-15' = {
  name: cosmosName
  location: location
  kind: 'GlobalDocumentDB'
  properties: {
    //enableFreeTier: true
    consistencyPolicy: {
      defaultConsistencyLevel: 'Session'
    }
    locations: [
      {
        locationName: location
        failoverPriority: 0
        isZoneRedundant: false
      }
    ]
    capabilities: [
      {
        name: 'EnableServerless'
      }
    ]
    databaseAccountOfferType: 'Standard'
  }
}