Azure Functions are part of Microsoft's serverless computing service, allowing developers to build and deploy event-driven applications without worrying about managing the underlying infrastructure. Here's a brief intro to Azure Functions:
What are Azure Functions?
Azure Functions are small, single-purpose, and event-triggered pieces of code that run in response to various events or triggers. They enable developers to execute code in a serverless environment without provisioning or managing servers.
Key Features:
- Event-driven: Functions can be triggered by various Azure services like Blob Storage, HTTP requests, timers, queues, databases, and more.
- Supported Languages: Azure Functions support multiple programming languages including C#, JavaScript, Python, PowerShell, and TypeScript.
- Scalability: Automatically scales based on demand, allowing cost optimization by paying only for resources used.
- Integration: Easily integrates with other Azure services and third-party services using bindings and triggers.
- Monitoring and Logging: Provides built-in monitoring and logging capabilities for tracking function execution and performance.
Core Concepts:
- Trigger: Defines how a function is invoked. Triggers can be HTTP requests, timers, queue messages, etc.
- Bindings: Allow functions to interact with other services. Input and output bindings simplify integration with Azure services.
- Bindings and Triggers Example: For instance, a function triggered by an HTTP request might use Blob Storage binding to write data to Azure Storage.
Development and Deployment:
- Local Development: Functions can be developed and tested locally using the Azure Functions Core Tools.
- Deployment: Functions can be deployed directly from Visual Studio, Azure CLI, Azure Portal, or through CI/CD pipelines.
Pricing:
Azure Functions follow a consumption-based pricing model, where you're billed based on the number of executions and resources consumed.
Use Cases:
- IoT: Processing sensor data or reacting to IoT events.
- Automation: Executing code in response to changes in databases, files, or schedules.
- Webhooks and APIs: Building APIs or handling HTTP requests.
- Data Processing: Performing data transformations, analytics, and more.
Benefits:
- Cost-Efficient: Pay only for the resources used during function execution.
- Scalability: Automatically scales based on demand.
- Rapid Development: Allows for quick development and deployment of code without managing infrastructure.
Azure Functions offers a versatile platform for developing and deploying small pieces of code that can respond to various events, making it an excellent choice for building serverless applications and microservices in the Azure ecosystem.
Let's build our microservice using Microsoft Azure Functions
Create New Asp.NET Core Web API Project
First, you need to create the Asp.net Core Project using Visual Studio
Create a Data access layer
Now add the .Net Library Project for the creation of the data access layer
Add Model
namespace MicrosoftAzureFunctionInAspNetCoreWebAPI_DAL.Model
{
public class FetchedDataModel
{
public string Id { get; set; }
public int Title { get; set; }
public string Name { get; set; }
public string FilePath { get; set; }
}
}
Add Application Db Context
using Microsoft.EntityFrameworkCore;
using MicrosoftAzureFunctionInAspNetCoreWebAPI_DAL.Model;
namespace MicrosoftAzureFunctionInAspNetCoreWebAPI_DAL.ApplicationDbContext
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<FetchedDataModel> FetchedData { get; set; }
}
}
Add Interface for IDataFetcher
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using MicrosoftAzureFunctionInAspNetCoreWebAPI_DAL.Model;
namespace MicrosoftAzureFunctionInAspNetCoreWebAPI_DAL.IRepository
{
public interface IDataFetcher
{
Task Run([TimerTrigger("0 */5 * * * *")] TimerInfo myTimer, ILogger log);
Task<string> FetchDataFromAPI(string apiUrl);
Task StoreDataInBlobStorage(FetchedDataModel fetchedDataModel);
}
}
Implement the repository for RepositoryDataFetcher
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using MicrosoftAzureFunctionInAspNetCoreWebAPI_DAL.IRepository;
using MicrosoftAzureFunctionInAspNetCoreWebAPI_DAL.Model;
using System.Text.Json;
namespace MicrosoftAzureFunctionInAspNetCoreWebAPI_DAL.Repository
{
public class RepositoryDataFetcher : IDataFetcher
{
private static readonly HttpClient httpClient;
public RepositoryDataFetcher(HttpClient httpClient)
{
httpClient = httpClient ?? new HttpClient();
}
public async Task<string> FetchDataFromAPI(string apiUrl)
{
HttpResponseMessage response = await httpClient.GetAsync(apiUrl);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
public async Task StoreDataInBlobStorage(FetchedDataModel fetchedDataModel)
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse("YourStorageConnectionString");
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference("data-container");
await container.CreateIfNotExistsAsync();
CloudBlockBlob blockBlob = container.GetBlockBlobReference($"{fetchedDataModel.Id}.json");
using(MemoryStream ms = new MemoryStream())
{
JsonSerializer.SerializeAsync(ms, fetchedDataModel);
ms.Position = 0;
await blockBlob.UploadFromStreamAsync(ms);
}
}
public async Task Run([TimerTrigger("0 */5 * * * *")] TimerInfo myTimer, ILogger log)
{
log.LogInformation($"DataFetcherFunction executed at: {DateTime.Now}");
string apiUrl = "https://api.csharpcorner.com/Articledata"; // Replace with your API URL
string dataJson = await FetchDataFromAPI(apiUrl);
FetchedDataModel fetchedData = JsonSerializer.Deserialize<FetchedDataModel>(dataJson);
await StoreDataInBlobStorage(fetchedData);
}
}
}
Add the Service Layer
Add the.NET Library Project for the creation of the Service Layer
Add the Interface for IDataFetcherService
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using MicrosoftAzureFunctionInAspNetCoreWebAPI_DAL.Model;
namespace MicrosoftAzureFunctionInAspNetCoreWebAPI_BAL.IDataService
{
public interface IDataFetcherService
{
Task Run([TimerTrigger("0 */5 * * * *")] TimerInfo myTimer, ILogger? log);
Task<FetchedDataModel> FetchDataFromAPI(string apiUrl);
Task StoreDataInBlobStorage(FetchedDataModel fetchedDataModel);
}
}
Implement the DataFetcherService
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using MicrosoftAzureFunctionInAspNetCoreWebAPI_BAL.IDataService;
using MicrosoftAzureFunctionInAspNetCoreWebAPI_DAL.IRepository;
using MicrosoftAzureFunctionInAspNetCoreWebAPI_DAL.Model;
using System;
using System.Text.Json;
using System.Threading.Tasks;
namespace MicrosoftAzureFunctionInAspNetCoreWebAPI_BAL.DataFetcherService
{
public class DataFetcherService : IDataFetcherService
{
private readonly IDataFetcher _repositoryDataFetcher;
private readonly ILogger _logger;
public DataFetcherService(IDataFetcher repositoryDataFetcher, ILogger<DataFetcherService> logger)
{
_repositoryDataFetcher = repositoryDataFetcher;
_logger = logger;
}
public async Task<FetchedDataModel> FetchDataFromAPI(string apiUrl)
{
try
{
string jsonData = await _repositoryDataFetcher.FetchDataFromAPI(apiUrl);
if(string.IsNullOrEmpty(jsonData))
{
_logger.LogWarning("Received empty data from the API.");
return null;
}
FetchedDataModel fetchedData = JsonSerializer.Deserialize<FetchedDataModel>(jsonData);
return fetchedData;
}
catch(Exception ex)
{
_logger.LogError($"Failed to fetch data from the API: {ex.Message}");
throw;
}
}
public async Task Run([TimerTrigger("0 */5 * * * *")] TimerInfo myTimer,ILogger logger)
{
_logger.LogInformation($"DataFetcherService executed at: {DateTime.Now}");
string apiUrl = "https://api.csharpcorner.com/Articledata"; // Replace with your API URL
try
{
FetchedDataModel fetchedData = await FetchDataFromAPI(apiUrl);
if(fetchedData != null)
{
await StoreDataInBlobStorage(fetchedData);
}
else
{
_logger.LogWarning("Failed to fetch data from the API.");
}
}
catch(Exception ex)
{
_logger.LogError($"Failed to run DataFetcherService: {ex.Message}");
throw;
}
}
public Task StoreDataInBlobStorage(FetchedDataModel fetchedDataModel)
{
try
{
return _repositoryDataFetcher.StoreDataInBlobStorage(fetchedDataModel);
}
catch(Exception ex)
{
_logger.LogError($"Failed to store data in Blob Storage: {ex.Message}");
throw;
}
}
}
}
Now Add the Dependency Injection
Now add the dependencies in the IOC Container
using MicrosoftAzureFunctionInAspNetCoreWebAPI_BAL.DataFetcherService;
using MicrosoftAzureFunctionInAspNetCoreWebAPI_BAL.IDataService;
using MicrosoftAzureFunctionInAspNetCoreWebAPI_DAL.ApplicationDbContext;
using MicrosoftAzureFunctionInAspNetCoreWebAPI_DAL.IRepository;
using MicrosoftAzureFunctionInAspNetCoreWebAPI_DAL.Repository;
using Microsoft.EntityFrameworkCore;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
var configuration = builder.Configuration;
// Add services to the container.
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IDataFetcherService, DataFetcherService>();
builder.Services.AddHttpClient<IDataFetcher, RepositoryDataFetcher>();
builder.Services.AddScoped<IDataFetcher, RepositoryDataFetcher>();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Microservices Development using Microsoft Azure Functions", Version = "v1" });
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if(app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Implement the Presentation Layer
Now Implement the Presentation layer by adding the DataFetcherController
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using MicrosoftAzureFunctionInAspNetCoreWebAPI_BAL.IDataService;
namespace MicrosoftAzureFunctionInAspNetCoreWebAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class DataFetcherController : ControllerBase
{
private readonly IDataFetcherService _dataFetcherService;
private readonly ILogger<DataFetcherController> _logger;
public DataFetcherController(IDataFetcherService dataFetcherService, ILogger<DataFetcherController> logger)
{
_dataFetcherService = dataFetcherService;
_logger = logger;
}
[HttpPost(nameof(FetchAndStoreData))]
public async Task<IActionResult> FetchAndStoreData([TimerTrigger("0 */5 * * * *")] TimerInfo myTimer)
{
try
{
await _dataFetcherService.Run(myTimer,null);
return Ok("Data fetched and stored successfully!");
}
catch(Exception ex)
{
_logger.LogError($"Failed to fetch and store data: {ex.Message}");
return StatusCode(500, "An error occurred while processing the request.");
}
}
}
}
Output
GitHub Project URL
https://github.com/SardarMudassarAliKhan/MicrosoftAzureFunctionInAspNetCoreWebAPI
Conclusion
Azure Functions offers a powerful solution for developers seeking to build and deploy applications in a serverless environment. With event-driven triggers, seamless integration with various Azure services, and support for multiple programming languages, they provide scalability, flexibility, and rapid development opportunities. Their ability to handle diverse use cases, from IoT to data processing and API development, underscores their versatility. Coupled with cost-efficiency and ease of monitoring, Azure Functions stand as a robust choice for creating responsive, scalable, and cost-effective applications in the Azure ecosystem.