ASP NET Core Azure Functions 入门教程 从零基础到部署的完整实战指南 解决配置与依赖注入常见问题

引言:什么是Azure Functions?
Azure Functions是微软Azure云平台提供的无服务器(Serverless)计算服务。它允许开发者编写小段代码(称为”函数”)来响应各种事件,而无需管理底层的基础设施。当与ASP.NET Core结合时,我们获得了强大的Web开发框架和灵活的事件驱动编程模型的双重优势。
为什么选择ASP.NET Core Azure Functions?
无服务器架构
:自动扩展,按使用量付费
事件驱动
:支持HTTP、队列、定时器、Blob存储等多种触发器
与ASP.NET Core集成
:熟悉的编程模型和依赖注入
多语言支持
:虽然我们专注于C#和ASP.NET Core
无缝Azure集成
:轻松连接其他Azure服务
第一部分:环境准备和项目创建
1.1 必需的开发工具
在开始之前,请确保安装以下工具:
Visual Studio 2022
(推荐)或
Visual Studio Code
Azure Functions Core Tools
(用于本地开发和测试)
Azure CLI
(用于部署和管理)
.NET 6.0 SDK
(或更高版本)
安装Azure Functions Core Tools:
# 使用npm安装(Windows/Mac/Linux)
npm install -g azure-functions-core-tools@4

# 或者使用winget(Windows)
winget install Microsoft.AzureFunctionsCoreTools

# 或者使用brew(Mac)
brew tap azure/functions
brew install azure-functions-core-tools@4
1.2 创建第一个Azure Functions项目
方法1:使用Visual Studio
打开Visual Studio 2022
选择”创建新项目”
搜索”Azure Functions”,选择”Azure Functions”模板
配置项目:
项目名称:
MyFirstFunctionApp
框架:.NET 6.0(长期支持)
Azure Functions版本:v4
选择函数模板:”HTTP触发器”
设置函数名称:
HelloWorld
授权级别:
匿名
(用于测试)
方法2:使用命令行
# 创建项目文件夹
mkdir MyFirstFunctionApp
cd MyFirstFunctionApp

# 初始化Azure Functions项目
func init MyFirstFunctionApp –worker-runtime dotnet –target-framework net6.0

# 创建函数
cd MyFirstFunctionApp
func new –name HelloWorld –template “HTTP trigger” –authlevel anonymous
1.3 项目结构解析
创建的项目结构如下:
MyFirstFunctionApp/
├── MyFirstFunctionApp.csproj # 项目文件
├── HelloWorld.cs # 函数代码
├── host.json # 全局配置
├── local.settings.json # 本地开发配置
└── Properties/
└── launchSettings.json # 调试配置
第二部分:核心概念和基础函数开发
2.1 Azure Functions的核心组件
触发器(Triggers)
触发器定义了函数何时执行。常见的触发器包括:
HTTP触发器
:通过HTTP请求调用
定时触发器
:按Cron表达式定时执行
队列触发器
:当消息到达存储队列时执行
Blob触发器
:当Blob存储中的文件发生变化时执行
Event Grid触发器
:响应Azure事件网格事件
绑定(Bindings)
绑定允许你声明式地连接数据和服务:
输入绑定
:将数据注入函数参数
输出绑定
:从函数参数输出数据
2.2 编写第一个HTTP触发函数
让我们详细分析自动生成的
HelloWorld.cs
文件:
using System.Net;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;

namespace MyFirstFunctionApp
{
public static class HelloWorld
{
[FunctionName(“HelloWorld”)]
public static async Task Run(
[HttpTrigger(AuthorizationLevel.Anonymous, “get”, “post”, Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation(“C# HTTP trigger function processed a request.”);

string name = req.Query[“name”];

string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = Newtonsoft.Json.JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;

string responseMessage = string.IsNullOrEmpty(name)
? “This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.”
: $”Hello, {name}. This HTTP triggered function executed successfully.”;

return new OkObjectResult(responseMessage);
}
}
}
代码详细解析:
FunctionName属性
:标识函数的唯一名称
HttpTrigger属性
:
AuthorizationLevel.Anonymous
:无需认证
“get”, “post”
:支持的HTTP方法
Route = null
:使用默认路由(函数名)
HttpRequest参数
:包含请求的所有信息
ILogger参数
:用于日志记录
返回值
:
IActionResult
类型,支持多种HTTP响应
2.3 本地运行和测试
启动函数
# 在项目目录下运行
func start
或者在Visual Studio中按F5调试。
测试函数
# 使用curl测试GET请求
curl “http://localhost:7071/api/HelloWorld?name=John”

# 使用curl测试POST请求
curl -X POST “http://localhost:7071/api/HelloWorld” \
-H “Content-Type: application/json” \
-d ‘{“name”:”John”}’
预期输出:
Hello, John. This HTTP triggered function executed successfully.
第三部分:ASP.NET Core集成与依赖注入
3.1 理解依赖注入在Azure Functions中的应用
Azure Functions v4+完全支持ASP.NET Core的依赖注入模式。这使得我们可以:
注册自定义服务
使用配置服务
注入数据库上下文
使用日志服务
3.2 创建Startup类实现依赖注入
在项目根目录创建
Startup.cs
文件:
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using System.IO;

[assembly: FunctionsStartup(typeof(MyFirstFunctionApp.Startup))]

namespace MyFirstFunctionApp
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
// 获取配置
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(“local.settings.json”, optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();

// 注册配置
builder.Services.AddSingleton(config);

// 注册自定义服务
builder.Services.AddScoped();
builder.Services.AddSingleton();

// 注册数据库上下文(示例)
// builder.Services.AddDbContext(options =>
// options.UseSqlServer(config.GetConnectionString(“DefaultConnection”)));
}
}
}
需要添加的NuGet包:





3.3 创建自定义服务接口和实现
// IMessageService.cs
public interface IMessageService
{
Task SendAsync(string message);
}

// EmailMessageService.cs
public class EmailMessageService : IMessageService
{
private readonly IConfiguration _config;
private readonly ILogger _logger;

public EmailMessageService(IConfiguration config, ILogger logger)
{
_config = config;
_logger = logger;
}

public async Task SendAsync(string message)
{
// 模拟发送邮件
var smtpServer = _config[“Smtp:Server”];
_logger.LogInformation($”Sending email via {smtpServer}: {message}”);
await Task.Delay(100); // 模拟异步操作
}
}

// ICacheService.cs 和 RedisCacheService.cs
public interface ICacheService
{
Task GetAsync(string key);
Task SetAsync(string key, string value);
}

public class RedisCacheService : ICacheService
{
private readonly IConfiguration _config;
private readonly ILogger _logger;

public RedisCacheService(IConfiguration config, ILogger logger)
{
_config = config;
_logger = logger;
}

public async Task GetAsync(string key)
{
_logger.LogInformation($”Getting from cache: {key}”);
// 实际实现会连接Redis
await Task.Delay(50);
return $”cached_value_for_{key}”;
}

public async Task SetAsync(string key, string value)
{
_logger.LogInformation($”Setting cache: {key} = {value}”);
await Task.Delay(50);
}
}
3.4 在函数中使用依赖注入
现在我们可以创建使用这些服务的函数:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace MyFirstFunctionApp
{
public class MessageFunction
{
private readonly IMessageService _messageService;
private readonly ICacheService _cacheService;
private readonly ILogger _logger;

// 通过构造函数注入服务
public MessageFunction(
IMessageService messageService,
ICacheService cacheService,
ILogger logger)
{
_messageService = messageService;
_cacheService = cacheService;
_logger = logger;
}

[FunctionName(“SendMessage”)]
public async Task SendMessage(
[HttpTrigger(AuthorizationLevel.Anonymous, “post”, Route = “messages”)] HttpRequest req)
{
_logger.LogInformation(“Processing message send request”);

// 从请求体读取消息
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = Newtonsoft.Json.JsonConvert.DeserializeObject(requestBody);
string message = data?.message;

if (string.IsNullOrEmpty(message))
{
return new BadRequestObjectResult(“Please pass a message in the request body”);
}

// 使用注入的服务
await _messageService.SendAsync(message);

// 缓存消息
var cacheKey = $”msg_{DateTime.UtcNow.Ticks}”;
await _cacheService.SetAsync(cacheKey, message);

return new OkObjectResult(new
{
Message = “Message sent successfully”,
CacheKey = cacheKey,
Timestamp = DateTime.UtcNow
});
}

[FunctionName(“GetCachedMessage”)]
public async Task GetCachedMessage(
[HttpTrigger(AuthorizationLevel.Anonymous, “get”, Route = “messages/{cacheKey}”)] HttpRequest req,
string cacheKey)
{
_logger.LogInformation($”Getting cached message for key: {cacheKey}”);

if (string.IsNullOrEmpty(cacheKey))
{
return new BadRequestObjectResult(“Cache key is required”);
}

var cachedValue = await _cacheService.GetAsync(cacheKey);

return new OkObjectResult(new
{
CacheKey = cacheKey,
Value = cachedValue,
Timestamp = DateTime.UtcNow
});
}
}
}
3.5 配置文件设置
更新
local.settings.json
:
{
“IsEncrypted”: false,
“Values”: {
“AzureWebJobsStorage”: “UseDevelopmentStorage=true”,
“FUNCTIONS_WORKER_RUNTIME”: “dotnet”,
“Smtp:Server”: “smtp.example.com”,
“Smtp:Port”: “587”,
“Smtp:Username”: “user@example.com”,
“Smtp:Password”: “password123”
},
“ConnectionStrings”: {
“DefaultConnection”: “Server=.;Database=MyDb;Trusted_Connection=True;”
}
}
第四部分:高级函数开发模式
4.1 定时触发器(Timer Trigger)
定时触发器使用Cron表达式来定义执行时间:
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;

namespace MyFirstFunctionApp
{
public class ScheduledTasks
{
[FunctionName(“DailyCleanup”)]
// Cron表达式:每天凌晨2点执行
// 格式:{秒} {分} {时} {日} {月} {星期}
public void DailyCleanup(
[TimerTrigger(“0 0 2 * * *”)] TimerInfo timer,
ILogger log)
{
log.LogInformation($”Daily cleanup started at: {DateTime.Now}”);

// 执行清理逻辑
// 例如:删除过期数据、清理临时文件等

log.LogInformation(“Daily cleanup completed”);
}

[FunctionName(“EveryMinute”)]
// 每分钟执行一次
public void EveryMinute(
[TimerTrigger(“0 */1 * * * *”)] TimerInfo timer,
ILogger log)
{
log.LogInformation($”Every minute task executed at: {DateTime.Now}”);
}

[FunctionName(“WeekdayReport”)]
// 工作日(周一到周五)上午9点执行
public void WeekdayReport(
[TimerTrigger(“0 0 9 * * 1-5″)] TimerInfo timer,
ILogger log)
{
log.LogInformation($”Generating weekday report at: {DateTime.Now}”);
}
}
}
4.2 队列触发器(Queue Trigger)
队列触发器在消息到达Azure存储队列时执行:
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;

namespace MyFirstFunctionApp
{
public class QueueProcessor
{
[FunctionName(“ProcessOrderQueue”)]
public async Task ProcessOrderQueue(
[QueueTrigger(“orders”, Connection = “AzureWebJobsStorage”)] string orderMessage,
ILogger log)
{
log.LogInformation($”Processing order: {orderMessage}”);

// 解析订单消息
// 处理订单逻辑
await Task.Delay(1000); // 模拟处理时间

log.LogInformation($”Order processed successfully”);
}

[FunctionName(“ProcessOrderQueueWithOutput”)]
public async Task ProcessOrderQueueWithOutput(
[QueueTrigger(“orders”, Connection = “AzureWebJobsStorage”)] string orderMessage,
[Queue(“processed-orders”, Connection = “AzureWebJobsStorage”)] IAsyncCollector processedOrders,
ILogger log)
{
log.LogInformation($”Processing order: {orderMessage}”);

// 处理订单
var processedOrder = $”PROCESSED_{orderMessage}_{DateTime.UtcNow.Ticks}”;

// 输出到另一个队列
await processedOrders.AddAsync(processedOrder);

log.LogInformation($”Order processed and added to output queue”);
}
}
}
4.3 Blob触发器(Blob Trigger)
Blob触发器在Blob存储中的文件发生变化时执行:
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage.Blob;

namespace MyFirstFunctionApp
{
public class BlobProcessor
{
[FunctionName(“ProcessImage”)]
public async Task ProcessImage(
[BlobTrigger(“images/{name}”, Connection = “AzureWebJobsStorage”)] Stream imageStream,
string name,
IDictionary metadata,
ILogger log)
{
log.LogInformation($”Processing image: {name}, Size: {imageStream.Length} bytes”);

// 读取图像元数据
foreach (var meta in metadata)
{
log.LogInformation($”Metadata: {meta.Key} = {meta.Value}”);
}

// 处理图像(例如:缩略图生成)
// 这里使用简单的复制操作
using (var memoryStream = new MemoryStream())
{
await imageStream.CopyToAsync(memoryStream);
memoryStream.Position = 0;

// 保存处理后的图像
// 实际实现会使用Azure.Storage.Blobs包
}

log.LogInformation($”Image {name} processed successfully”);
}

[FunctionName(“ProcessTextFile”)]
public async Task ProcessTextFile(
[BlobTrigger(“texts/{name}”, Connection = “AzureWebJobsStorage”)] Stream textStream,
string name,
Binder binder,
ILogger log)
{
log.LogInformation($”Processing text file: {name}”);

// 读取文件内容
using (var reader = new StreamReader(textStream))
{
string content = await reader.ReadToEndAsync();
log.LogInformation($”File content length: {content.Length}”);

// 使用Binder动态绑定输出
var outputBlob = await binder.BindAsync(
new BlobAttribute($”processed-texts/{name}”, FileAccess.Write));

// 写入处理结果
var processedContent = $”PROCESSED: {content}”;
await outputBlob.UploadTextAsync(processedContent);
}
}
}
}
4.4 使用Binder动态绑定
Binder允许在运行时动态创建绑定:
[FunctionName(“DynamicBinding”)]
public async Task DynamicBinding(
[HttpTrigger(AuthorizationLevel.Anonymous, “post”)] HttpRequest req,
Binder binder,
ILogger log)
{
// 从请求获取文件名和内容
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = Newtonsoft.Json.JsonConvert.DeserializeObject(requestBody);
string filename = data?.filename;
string content = data?.content;

// 动态绑定到Blob输出
var outputBlob = await binder.BindAsync(
new BlobAttribute($”dynamic/{filename}”, FileAccess.Write));

await outputBlob.UploadTextAsync(content);

return new OkObjectResult($”File {filename} saved to blob storage”);
}
第五部分:配置管理详解
5.1 配置源和优先级
Azure Functions的配置按以下优先级加载(从高到低):
local.settings.json
(本地开发)
环境变量
(Azure门户或应用设置)
host.json
(全局配置)
Azure App Configuration
(可选)
5.2 本地配置:local.settings.json
{
“IsEncrypted”: false,
“Values”: {
“AzureWebJobsStorage”: “UseDevelopmentStorage=true”,
“FUNCTIONS_WORKER_RUNTIME”: “dotnet”,

// 自定义应用设置
“AppSettings:AppName”: “MyFunctionApp”,
“AppSettings:Version”: “1.0.0”,
“AppSettings:EnableFeatureX”: “true”,

// 外部服务配置
“SendGrid:ApiKey”: “SG.xxx”,
“Twilio:AccountSid”: “ACxxx”,

// 数据库连接字符串
“ConnectionStrings:SqlDatabase”: “Server=.;Database=MyDb;Trusted_Connection=True;”
},
“ConnectionStrings”: {
“DefaultConnection”: “Server=.;Database=MyDb;Trusted_Connection=True;”
}
}
重要提示
:
local.settings.json
仅在本地开发时使用,不会部署到Azure。在Azure中需要使用应用设置。
5.3 在代码中读取配置
方法1:通过IConfiguration注入
public class ConfigExampleFunction
{
private readonly IConfiguration _config;
private readonly ILogger _logger;

public ConfigExampleFunction(IConfiguration config, ILogger logger)
{
_config = config;
_logger = logger;
}

[FunctionName(“ConfigExample”)]
public IActionResult Run(
[HttpTrigger(AuthorizationLevel.Anonymous, “get”)] HttpRequest req)
{
// 读取简单配置值
var appName = _config[“AppSettings:AppName”];
var version = _config[“AppSettings:Version”];
var enableFeatureX = _config[“AppSettings:EnableFeatureX”];

// 读取连接字符串
var connectionString = _config.GetConnectionString(“DefaultConnection”);

// 使用Options模式(推荐)
var appSettings = new AppSettings();
_config.GetSection(“AppSettings”).Bind(appSettings);

_logger.LogInformation($”App: {appName}, Version: {version}”);

return new OkObjectResult(new
{
AppName = appName,
Version = version,
FeatureXEnabled = enableFeatureX,
ConnectionString = connectionString != null ? “Configured” : “Not Configured”,
Options = appSettings
});
}
}

// 配置模型类
public class AppSettings
{
public string AppName { get; set; }
public string Version { get; set; }
public bool EnableFeatureX { get; set; }
}
方法2:使用环境变量(不推荐,但有时必要)
[FunctionName(“EnvExample”)]
public IActionResult EnvExample(
[HttpTrigger(AuthorizationLevel.Anonymous, “get”)] HttpRequest req)
{
// 直接读取环境变量
var appName = Environment.GetEnvironmentVariable(“AppSettings__AppName”);
var storage = Environment.GetEnvironmentVariable(“AzureWebJobsStorage”);

return new OkObjectResult(new { AppName = appName, Storage = storage });
}
5.4 host.json 全局配置
host.json
文件用于配置函数应用的全局行为:
{
“version”: “2.0”,
“logging”: {
“fileLoggingMode”: “debugOnly”,
“logLevel”: {
“default”: “Information”,
“MyFirstFunctionApp”: “Debug”,
“Function”: “Information”,
“Host”: “Information”
},
“applicationInsights”: {
“samplingSettings”: {
“isEnabled”: true,
“maxTelemetryItemsPerSecond”: 20
}
}
},
“extensionBundle”: {
“id”: “Microsoft.Azure.Functions.ExtensionBundle”,
“version”: “[3.*, 4.0.0)”
},
“functionTimeout”: “00:05:00”,
“extensions”: {
“http”: {
“routePrefix”: “api”,
“maxConcurrentRequests”: 100,
“dynamicThrottlesEnabled”: true
},
“queues”: {
“batchSize”: 16,
“newBatchThreshold”: 8,
“maxPollingInterval”: “00:00:02”
}
}
}
配置说明:
version
:函数运行时版本(2.0或3.0)
logging
:日志级别配置
extensionBundle
:扩展包,自动包含常用绑定
functionTimeout
:函数最大执行时间
extensions
:特定绑定的配置
5.5 Azure门户中的应用设置
部署到Azure后,配置通过”应用设置”管理:
在Azure门户中,转到你的Function App
选择”配置” → “应用设置”
添加或修改设置:
键:
AppSettings:AppName
值:
MyProductionApp
对于连接字符串,使用特殊的命名约定:
键:
SQL_CONNECTION
值:
Server=tcp:…
类型:
SQLServer
5.6 Azure App Configuration(高级)
对于复杂的配置需求,可以使用Azure App Configuration:
public override void Configure(IFunctionsHostBuilder builder)
{
// 从Azure App Configuration加载
var config = new ConfigurationBuilder()
.AddAzureAppConfiguration(options =>
{
options.Connect(Environment.GetEnvironmentVariable(“AppConfig:ConnectionString”))
.Select(“*”)
.ConfigureRefresh(refresh =>
refresh.Register(“Sentinel”, refreshAll: true)
.SetCacheExpiration(TimeSpan.FromMinutes(5)));
})
.Build();

builder.Services.AddSingleton(config);
}
第六部分:依赖注入深度解析
6.1 服务生命周期详解
在Azure Functions中,服务生命周期有三种:
public override void Configure(IFunctionsHostBuilder builder)
{
// 瞬态(Transient):每次请求都创建新实例
builder.Services.AddTransient();

// 作用域(Scoped):每个函数调用创建一个实例
builder.Services.AddScoped();

// 单例(Singleton):整个应用生命周期只创建一个实例
builder.Services.AddSingleton();
}

// 服务实现示例
public class TransientService : ITransientService
{
private readonly Guid _id = Guid.NewGuid();
public string GetId() => _id.ToString();
}

public class ScopedService : IScopedService
{
private readonly Guid _id = Guid.NewGuid();
public string GetId() => _id.ToString();
}

public class SingletonService : ISingletonService
{
private readonly Guid _id = Guid.NewGuid();
public string GetId() => _id.ToString();
}
6.2 在函数中使用不同生命周期的服务
public class LifetimeTestFunction
{
private readonly ITransientService _transient;
private readonly IScopedService _scoped;
private readonly ISingletonService _singleton;

public LifetimeTestFunction(
ITransientService transient,
IScopedService scoped,
ISingletonService singleton)
{
_transient = transient;
_scoped = scoped;
_singleton = singleton;
}

[FunctionName(“LifetimeTest”)]
public async Task Run(
[HttpTrigger(AuthorizationLevel.Anonymous, “get”)] HttpRequest req)
{
// 在同一函数调用中,每个服务的ID应该相同
var transientId1 = _transient.GetId();
var scopedId1 = _scoped.GetId();
var singletonId1 = _singleton.GetId();

// 模拟第二次获取(在同一请求中)
var transientId2 = _transient.GetId();
var scopedId2 = _scoped.GetId();
var singletonId2 = _singleton.GetId();

return new OkObjectResult(new
{
Transient = new { First = transientId1, Second = transientId2, Same = transientId1 == transientId2 },
Scoped = new { First = scopedId1, Second = scopedId2, Same = scopedId1 == scopedId2 },
Singleton = new { First = singletonId1, Second = singletonId2, Same = singletonId1 == singletonId2 }
});
}
}
预期结果
:
Transient
:每次获取都是新实例(ID不同)
Scoped
:同一函数调用中相同(ID相同)
Singleton
:整个应用中相同(ID相同)
6.3 常见服务注册模式
注册数据库上下文
// 使用Entity Framework Core
builder.Services.AddDbContext(options =>
{
var connectionString = config.GetConnectionString(“DefaultConnection”);
options.UseSqlServer(connectionString);
});

// 使用Dapper
builder.Services.AddScoped(sp =>
{
var config = sp.GetRequiredService();
var connectionString = config.GetConnectionString(“DefaultConnection”);
return new SqlConnection(connectionString);
});
注册HTTP客户端
// 基础注册
builder.Services.AddHttpClient();

// 带配置的注册
builder.Services.AddHttpClient(client =>
{
client.BaseAddress = new Uri(“https://api.example.com”);
client.Timeout = TimeSpan.FromSeconds(30);
client.DefaultRequestHeaders.Add(“Accept”, “application/json”);
});

// 带策略的注册
builder.Services.AddHttpClient()
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());

private static IAsyncPolicy GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}
注册缓存服务
// 内存缓存
builder.Services.AddMemoryCache();

// 分布式缓存(Redis)
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = config[“Redis:ConnectionString”];
options.InstanceName = “FunctionApp_”;
});
6.4 在函数方法中使用依赖注入
除了构造函数注入,还可以使用方法注入:
public class MethodInjectionFunction
{
[FunctionName(“MethodInjection”)]
public async Task Run(
[HttpTrigger(AuthorizationLevel.Anonymous, “get”)] HttpRequest req,
[Inject] IMessageService messageService, // 方法注入
ILogger log)
{
await messageService.SendAsync(“Hello from method injection”);
return new OkResult();
}
}
注意
:方法注入需要创建自定义绑定扩展,通常推荐使用构造函数注入。
6.5 处理循环依赖
循环依赖是常见问题,解决方案:
// 问题:ServiceA依赖ServiceB,ServiceB依赖ServiceA
// 解决方案1:使用Lazy
public class ServiceA : IServiceA
{
private readonly Lazy _serviceB;
public ServiceA(Lazy serviceB)
{
_serviceB = serviceB;
}

public void DoWork() => _serviceB.Value.Execute();
}

// 解决方案2:重构设计,引入中介者
public class MediatorService
{
private readonly IServiceA _serviceA;
private readonly IServiceB _serviceB;

public MediatorService(IServiceA serviceA, IServiceB serviceB)
{
_serviceA = serviceA;
_serviceB = serviceB;
}

public void CoordinateWork()
{
_serviceA.DoWork();
_serviceB.DoWork();
}
}
第七部分:配置与依赖注入常见问题及解决方案
7.1 配置相关问题
问题1:配置值在Azure中不生效
症状
:本地运行正常,部署后配置值不正确或为空。
原因分析
:
local.settings.json
不会部署到Azure
Azure中的应用设置键名不匹配
需要重启Function App才能生效
解决方案
:
// 1. 确保配置读取代码健壮
public class RobustConfigFunction
{
private readonly IConfiguration _config;
private readonly ILogger _logger;

public RobustConfigFunction(IConfiguration config, ILogger logger)
{
_config = config;
_logger = logger;
}

[FunctionName(“RobustConfig”)]
public IActionResult Run([HttpTrigger(AuthorizationLevel.Anonymous, “get”)] HttpRequest req)
{
// 1. 检查配置是否存在
var value = _config[“MySetting”];
if (string.IsNullOrEmpty(value))
{
_logger.LogError(“MySetting is not configured”);
return new StatusCodeResult(500);
}

// 2. 提供默认值
var valueWithDefault = _config[“MySetting”] ?? “default_value”;

// 3. 验证配置
if (!bool.TryParse(_config[“FeatureEnabled”], out bool featureEnabled))
{
_logger.LogWarning(“FeatureEnabled is not a valid boolean, defaulting to false”);
featureEnabled = false;
}

return new OkObjectResult(new
{
Value = value,
ValueWithDefault = valueWithDefault,
FeatureEnabled = featureEnabled
});
}
}
部署检查清单
:
[ ] 在Azure门户中检查应用设置是否存在
[ ] 键名是否完全匹配(区分大小写)
[ ] 设置值是否正确
[ ] 是否需要重启Function App
[ ] 检查部署槽位设置是否正确
问题2:连接字符串配置错误
症状
:数据库连接失败,提示”连接字符串格式错误”或”无法连接到服务器”。
解决方案
:
// 1. 安全的连接字符串读取
public class DatabaseFunction
{
private readonly IConfiguration _config;
private readonly ILogger _logger;

public DatabaseFunction(IConfiguration config, ILogger logger)
{
_config = config;
_logger = logger;
}

[FunctionName(“SafeDbConnection”)]
public async Task Run(
[HttpTrigger(AuthorizationLevel.Anonymous, “get”)] HttpRequest req)
{
try
{
// 方式1:通过ConnectionStrings节读取
var connectionString = _config.GetConnectionString(“DefaultConnection”);

// 方式2:通过Values节读取(如果以ConnectionStrings:开头)
var altConnectionString = _config[“ConnectionStrings:DefaultConnection”];

if (string.IsNullOrEmpty(connectionString))
{
_logger.LogError(“Database connection string is not configured”);
return new ObjectResult(“Database configuration missing”) { StatusCode = 500 };
}

// 验证连接字符串格式
if (!connectionString.Contains(“Server=”) && !connectionString.Contains(“Data Source=”))
{
_logger.LogError(“Invalid connection string format”);
return new ObjectResult(“Invalid database configuration”) { StatusCode = 500 };
}

// 尝试连接(示例)
using (var connection = new SqlConnection(connectionString))
{
await connection.OpenAsync();
_logger.LogInformation(“Database connection successful”);
}

return new OkResult();
}
catch (Exception ex)
{
_logger.LogError(ex, “Database connection failed”);
return new ObjectResult(“Database connection failed”) { StatusCode = 500 };
}
}
}
Azure门户配置
:
应用设置:
Key: SQL_CONNECTION
Value: Server=tcp:yourserver.database.windows.net,1433;Database=YourDb;User ID=YourUser;Password=YourPassword;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;
Type: SQLServer
问题3:配置热更新不生效
症状
:修改Azure应用设置后,函数没有立即使用新配置。
原因
:Azure Functions默认不会自动重新加载配置。
解决方案
:
// 1. 实现配置热重载
public class HotReloadConfigFunction
{
private readonly IConfiguration _config;
private readonly ILogger _logger;
private static DateTime _lastConfigCheck = DateTime.MinValue;
private static readonly TimeSpan _reloadInterval = TimeSpan.FromMinutes(5);

public HotReloadConfigFunction(IConfiguration config, ILogger logger)
{
_config = config;
_logger = logger;
}

[FunctionName(“HotReloadConfig”)]
public IActionResult Run([HttpTrigger(AuthorizationLevel.Anonymous, “get”)] HttpRequest req)
{
// 检查是否需要重新加载配置
if (DateTime.UtcNow – _lastConfigCheck > _reloadInterval)
{
_logger.LogInformation(“Reloading configuration…”);
// 触发配置重新加载(需要实现特定逻辑)
_lastConfigCheck = DateTime.UtcNow;
}

var value = _config[“DynamicSetting”];
return new OkObjectResult(new { Value = value, LastCheck = _lastConfigCheck });
}
}

// 2. 使用Azure App Configuration实现自动刷新
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddAzureAppConfiguration(options =>
{
options.Connect(Environment.GetEnvironmentVariable(“AppConfig:ConnectionString”))
.Select(“*”)
.ConfigureRefresh(refresh =>
refresh.Register(“Sentinel”, refreshAll: true)
.SetCacheExpiration(TimeSpan.FromMinutes(5)));
});
}
7.2 依赖注入相关问题
问题1:服务无法解析(InvalidOperationException)
症状
:运行时抛出异常:”Unable to resolve service for type ‘IMyService’ while attempting to activate ‘MyFunction’”
原因
:
服务未注册
接口和实现未正确配对
注册代码未执行
解决方案
:
// Startup.cs – 正确的注册方式
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
// 1. 确保接口和实现正确注册
builder.Services.AddScoped();

// 2. 检查依赖链
// MyService可能依赖其他服务,确保这些服务也已注册
builder.Services.AddScoped(sp =>
{
var config = sp.GetRequiredService();
return new SqlConnection(config.GetConnectionString(“DefaultConnection”));
});

// 3. 注册具体类型(如果需要)
builder.Services.AddScoped();

// 4. 验证注册(可选)
try
{
var serviceProvider = builder.Services.BuildServiceProvider();
serviceProvider.GetRequiredService();
}
catch (Exception ex)
{
// 记录错误
Console.WriteLine($”Service registration error: {ex.Message}”);
}
}
}

// 函数中的正确使用
public class MyFunction
{
private readonly IMyService _myService;

// 构造函数注入
public MyFunction(IMyService myService)
{
_myService = myService ?? throw new ArgumentNullException(nameof(myService));
}

[FunctionName(“MyFunction”)]
public async Task Run(
[HttpTrigger(AuthorizationLevel.Anonymous, “get”)] HttpRequest req)
{
// 使用服务
var result = await _myService.DoWorkAsync();
return new OkObjectResult(result);
}
}
问题2:服务生命周期不匹配
症状
:数据库连接被多个函数共享,导致并发问题;或对象过早释放。
原因
:错误使用了服务生命周期。
解决方案
:
public override void Configure(IFunctionsHostBuilder builder)
{
// 错误示例:将数据库上下文注册为Singleton
// builder.Services.AddSingleton(); // ❌ 错误

// 正确示例:数据库上下文应该是Scoped或Transient
builder.Services.AddScoped(sp =>
{
var config = sp.GetRequiredService();
var options = new DbContextOptionsBuilder()
.UseSqlServer(config.GetConnectionString(“DefaultConnection”))
.Options;
return new MyDbContext(options);
});

// 对于无状态服务,可以使用Singleton
builder.Services.AddSingleton();

// 对于需要请求隔离的服务,使用Scoped
builder.Services.AddScoped();
}

// 生命周期验证函数
public class LifetimeValidationFunction
{
private readonly MyDbContext _dbContext1;
private readonly MyDbContext _dbContext2;
private readonly IMyStatelessService _statelessService1;
private readonly IMyStatelessService _statelessService2;

public LifetimeValidationFunction(
MyDbContext dbContext1,
MyDbContext dbContext2,
IMyStatelessService statelessService1,
IMyStatelessService statelessService2)
{
_dbContext1 = dbContext1;
_dbContext2 = dbContext2;
_statelessService1 = statelessService1;
_statelessService2 = statelessService2;
}

[FunctionName(“LifetimeValidation”)]
public IActionResult Run([HttpTrigger(AuthorizationLevel.Anonymous, “get”)] HttpRequest req)
{
// Scoped服务:同一请求中相同实例
var dbContextSame = ReferenceEquals(_dbContext1, _dbContext2);

// Singleton服务:总是相同实例
var statelessSame = ReferenceEquals(_statelessService1, _statelessService2);

return new OkObjectResult(new
{
DbContextSameInstance = dbContextSame, // 应该是true
StatelessServiceSameInstance = statelessSame, // 应该是true
Message = dbContextSame ? “Correct” : “Check your registrations”
});
}
}
问题3:异步服务注入问题
症状
:尝试在服务构造函数中调用异步方法。
原因
:构造函数不能是异步的。
解决方案
:
// 错误示例:构造函数中调用异步方法
public class BadService
{
public BadService()
{
// ❌ 错误:构造函数不能异步
// var data = await GetDataAsync();
}
}

// 正确示例1:使用Lazy延迟初始化
public class GoodService1
{
private readonly Lazy> _lazyData;

public GoodService1(IConfiguration config)
{
_lazyData = new Lazy>(async () =>
{
// 这里可以异步加载
await Task.Delay(100);
return config[“SomeData”];
});
}

public async Task GetDataAsync()
{
return await _lazyData.Value;
}
}

// 正确示例2:使用初始化模式
public class GoodService2 : IAsyncDisposable
{
private bool _initialized = false;
private string _data;

public async Task InitializeAsync()
{
if (!_initialized)
{
await Task.Delay(100); // 模拟异步操作
_data = “Initialized data”;
_initialized = true;
}
}

public async ValueTask DisposeAsync()
{
// 清理资源
await Task.CompletedTask;
}

public string GetData() => _data;
}

// 在函数中使用
public class MyFunction
{
private readonly GoodService2 _service;

public MyFunction(GoodService2 service)
{
_service = service;
}

[FunctionName(“AsyncInit”)]
public async Task Run(
[HttpTrigger(AuthorizationLevel.Anonymous, “get”)] HttpRequest req)
{
await _service.InitializeAsync();
var data = _service.GetData();
return new OkObjectResult(data);
}
}
问题4:配置与DI结合时的空引用
症状
:在服务构造函数中使用IConfiguration,但配置值为null。
解决方案
:
public class ConfiguredService
{
private readonly IConfiguration _config;
private readonly ILogger _logger;
private readonly string _requiredValue;

public ConfiguredService(IConfiguration config, ILogger logger)
{
_config = config;
_logger = logger;

// 在构造函数中验证配置
_requiredValue = _config[“RequiredSetting”];

if (string.IsNullOrEmpty(_requiredValue))
{
// 记录警告但不抛出异常(允许函数启动)
_logger.LogWarning(“RequiredSetting is not configured”);
}
}

public async Task GetValueAsync()
{
if (string.IsNullOrEmpty(_requiredValue))
{
throw new InvalidOperationException(“RequiredSetting is not configured”);
}

// 使用配置值
return await Task.FromResult(_requiredValue);
}
}

// 在Startup中提供回退值
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.Configure(options =>
{
var config = builder.Services.BuildServiceProvider()
.GetRequiredService();

options.Value = config[“SomeValue”] ?? “DefaultValue”;
});
}
7.3 配置与DI结合的最佳实践
1. 使用Options模式
// 配置模型
public class DatabaseOptions
{
public string ConnectionString { get; set; }
public int CommandTimeout { get; set; } = 30;
public bool EnableRetry { get; set; } = true;
}

public class ApiOptions
{
public string BaseUrl { get; set; }
public string ApiKey { get; set; }
public int TimeoutSeconds { get; set; } = 30;
}

// Startup中注册
public override void Configure(IFunctionsHostBuilder builder)
{
// 绑定配置节到Options
builder.Services.Configure(options =>
{
var config = builder.Services.BuildServiceProvider()
.GetRequiredService();
config.GetSection(“Database”).Bind(options);
});

builder.Services.Configure(options =>
{
var config = builder.Services.BuildServiceProvider()
.GetRequiredService();
config.GetSection(“Api”).Bind(options);
});

// 注册使用Options的服务
builder.Services.AddScoped();
builder.Services.AddScoped();
}

// 服务中使用Options
public class DatabaseService : IDatabaseService
{
private readonly DatabaseOptions _options;
private readonly ILogger _logger;

public DatabaseService(
IOptions options,
ILogger logger)
{
_options = options.Value;
_logger = logger;

// 验证配置
if (string.IsNullOrEmpty(_options.ConnectionString))
{
_logger.LogError(“Database connection string is not configured”);
}
}

public async Task GetConnectionInfoAsync()
{
// 使用配置
return $”Timeout: {_options.CommandTimeout}, Retry: {_options.EnableRetry}”;
}
}

// 函数中使用
public class DataFunction
{
private readonly IDatabaseService _dbService;

public DataFunction(IDatabaseService dbService)
{
_dbService = dbService;
}

[FunctionName(“GetData”)]
public async Task Run(
[HttpTrigger(AuthorizationLevel.Anonymous, “get”)] HttpRequest req)
{
var info = await _dbService.GetConnectionInfoAsync();
return new OkObjectResult(info);
}
}
2. 配置验证
public class ConfigurationValidator
{
public static void ValidateConfiguration(IConfiguration config, ILogger logger)
{
var requiredSettings = new[]
{
“Database:ConnectionString”,
“Api:BaseUrl”,
“Api:ApiKey”
};

foreach (var setting in requiredSettings)
{
var value = config[setting];
if (string.IsNullOrEmpty(value))
{
logger.LogError($”Required configuration ‘{setting}’ is missing”);
// 可以选择抛出异常或记录错误
}
}
}
}

// 在Startup中调用
public override void Configure(IFunctionsHostBuilder builder)
{
var config = builder.Services.BuildServiceProvider()
.GetRequiredService();
var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
var logger = loggerFactory.CreateLogger();

ConfigurationValidator.ValidateConfiguration(config, logger);
}
3. 环境特定配置
public class EnvironmentSpecificConfig
{
private readonly IConfiguration _config;
private readonly IWebHostEnvironment _environment;

public EnvironmentSpecificConfig(IConfiguration config, IWebHostEnvironment environment)
{
_config = config;
_environment = environment;
}

public string GetApiEndpoint()
{
var environment = _environment.EnvironmentName ?? “Development”;

// 根据环境返回不同配置
return environment.ToLower() switch
{
“production” => _config[“Api:ProductionEndpoint”],
“staging” => _config[“Api:StagingEndpoint”],
_ => _config[“Api:DevelopmentEndpoint”]
};
}
}
第八部分:部署到Azure
8.1 部署前准备
1. 创建Azure Function App
使用Azure CLI
:
# 登录Azure
az login

# 创建资源组
az group create –name my-function-rg –location eastus

# 创建存储账户(必需)
az storage account create \
–name mystorageaccount123 \
–location eastus \
–resource-group my-function-rg \
–sku Standard_LRS

# 创建Function App
az functionapp create \
–resource-group my-function-rg \
–consumption-plan-location eastus \
–runtime dotnet \
–functions-version 4 \
–name myfunctionapp123 \
–storage-account mystorageaccount123
使用Azure门户
:
访问portal.azure.com
搜索”Function App”
点击”创建”
配置:
订阅和资源组
函数应用名称:
myfunctionapp123
发布:代码
运行时堆栈:.NET
版本:6
区域:East US
点击”创建”
2. 配置应用设置
使用Azure CLI
:
# 设置应用配置
az functionapp config appsettings set \
–name myfunctionapp123 \
–resource-group my-function-rg \
–settings \
AppSettings:AppName=”MyProductionApp” \
AppSettings:Version=”1.0.0″ \
AppSettings:EnableFeatureX=”true” \
ConnectionStrings:DefaultConnection=”Server=tcp:yourserver.database.windows.net,1433;Database=YourDb;…” \
Smtp:Server=”smtp.sendgrid.net” \
Smtp:Port=”587″ \
Smtp:Username=”apikey” \
Smtp:Password=”SG.your-api-key”
使用Azure门户
:
在Function App中,选择”配置” → “应用设置”
点击”新建”
输入键值对:
名称:
AppSettings:AppName
值:
MyProductionApp
点击”确定”和”保存”
8.2 部署方法
方法1:Visual Studio直接部署
在Visual Studio中右键点击项目
选择”发布”
选择”Azure”
选择”Azure Function App (消耗)”
创建新实例或选择现有实例
点击”发布”
方法2:Azure CLI部署
# 发布项目
func azure functionapp publish myfunctionapp123 –csharp

# 或者使用dotnet publish
dotnet publish -c Release -o ./publish
cd ./publish
func azure functionapp publish myfunctionapp123
方法3:GitHub Actions自动化部署
创建
.github/workflows/deploy.yml
:
name: Deploy to Azure Functions

on:
push:
branches: [ main ]

jobs:
deploy:
runs-on: ubuntu-latest

steps:
– uses: actions/checkout@v2

– name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: ‘6.0.x’

– name: Build
run: |
dotnet restore
dotnet build –configuration Release
dotnet publish -c Release -o publish

– name: Deploy to Azure
uses: Azure/functions-action@v1
with:
app-name: myfunctionapp123
slot-name: ‘production’
publish-profile: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }}
8.3 部署后验证
1. 测试HTTP函数
# 获取函数URL
az functionapp function show \
–resource-group my-function-rg \
–name myfunctionapp123 \
–function-name HelloWorld

# 测试函数
curl “https://myfunctionapp123.azurewebsites.net/api/HelloWorld?name=Production”

# 测试带认证的函数
curl “https://myfunctionapp123.azurewebsites.net/api/SendMessage” \
-X POST \
-H “Content-Type: application/json” \
-d ‘{“message”:”Hello from production”}’
2. 查看日志
# 使用Azure CLI查看实时日志
az functionapp log tail myfunctionapp123

# 在Azure门户中查看
# Function App → Functions → 选择函数 → Monitor → Logs
3. 监控和诊断
在Azure门户中:
概述
:查看函数执行情况
指标
:查看性能指标
活动日志
:查看操作历史
诊断设置
:配置日志流
8.4 常见部署问题
问题1:部署后函数无法访问
检查清单
:
# 1. 检查函数是否已部署
az functionapp function list –name myfunctionapp123 –resource-group my-function-rg

# 2. 检查应用设置
az functionapp config appsettings list –name myfunctionapp123 –resource-group my-function-rg

# 3. 检查函数运行时
az functionapp show –name myfunctionapp123 –resource-group my-function-rg –query state

# 4. 重启函数应用
az functionapp restart –name myfunctionapp123 –resource-group my-function-rg
问题2:依赖注入不工作
解决方案
:
// 确保Startup.cs正确标记
[assembly: FunctionsStartup(typeof(MyFirstFunctionApp.Startup))]

// 检查host.json是否包含扩展包
{
“version”: “2.0”,
“extensionBundle”: {
“id”: “Microsoft.Azure.Functions.ExtensionBundle”,
“version”: “[3.*, 4.0.0)”
}
}

// 检查.csproj文件


net6.0
v4






第九部分:高级主题和最佳实践
9.1 错误处理和重试机制
public class ResilientFunction
{
private readonly ILogger _logger;

public ResilientFunction(ILogger logger)
{
_logger = logger;
}

[FunctionName(“ResilientHttp”)]
public async Task Run(
[HttpTrigger(AuthorizationLevel.Anonymous, “post”)] HttpRequest req)
{
try
{
// 使用Polly实现重试
var retryPolicy = Policy
.Handle()
.OrResult(r => !r.IsSuccessStatusCode)
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
onRetry: (outcome, timespan, retryCount, context) =>
{
_logger.LogWarning($”Retry {retryCount} after {timespan.TotalSeconds}s”);
});

var client = new HttpClient();
var response = await retryPolicy.ExecuteAsync(() =>
client.GetAsync(“https://httpbin.org/status/500”));

if (response.IsSuccessStatusCode)
{
return new OkResult();
}

return new StatusCodeResult(500);
}
catch (Exception ex)
{
_logger.LogError(ex, “Function failed”);
return new ObjectResult(new { error = ex.Message }) { StatusCode = 500 };
}
}
}
9.2 使用Durable Functions实现工作流
// 需要添加包:Microsoft.Azure.WebJobs.Extensions.DurableTask

public class DurableOrchestration
{
[FunctionName(“Orchestrator”)]
public async Task> RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
var outputs = new List();

// 调用活动函数
outputs.Add(await context.CallActivityAsync(“SayHello”, “Tokyo”));
outputs.Add(await context.CallActivityAsync(“SayHello”, “Seattle”));
outputs.Add(await context.CallActivityAsync(“SayHello”, “London”));

return outputs;
}

[FunctionName(“SayHello”)]
public string SayHello([ActivityTrigger] string name, ILogger log)
{
log.LogInformation($”Saying hello to {name}”);
return $”Hello {name}!”;
}

[FunctionName(“HttpStart”)]
public async Task HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, “post”)] HttpRequest req,
[DurableClient] IDurableOrchestrationClient starter,
ILogger log)
{
string instanceId = await starter.StartNewAsync(“Orchestrator”, null);
log.LogInformation($”Started orchestration with ID = ‘{instanceId}’”);

return starter.CreateCheckStatusResponse(req, instanceId);
}
}
9.3 安全最佳实践
public class SecureFunction
{
[FunctionName(“SecureEndpoint”)]
public async Task Run(
[HttpTrigger(AuthorizationLevel.Function, “post”)] HttpRequest req,
ILogger log)
{
// 1. 验证JWT令牌
if (!req.Headers.ContainsKey(“Authorization”))
{
return new UnauthorizedResult();
}

var token = req.Headers[“Authorization”].ToString().Replace(“Bearer “, “”);

// 2. 验证请求体大小
if (req.ContentLength > 1024 * 1024) // 1MB
{
return new BadRequestObjectResult(“Request too large”);
}

// 3. 输入验证
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
if (!IsValidJson(requestBody))
{
return new BadRequestObjectResult(“Invalid JSON”);
}

// 4. 防止SQL注入(如果使用数据库)
// 使用参数化查询,不要拼接SQL

return new OkResult();
}

private bool IsValidJson(string strInput)
{
try
{
Newtonsoft.Json.JsonConvert.DeserializeObject(strInput);
return true;
}
catch
{
return false;
}
}
}
9.4 性能优化
public class PerformanceOptimizedFunction
{
private static readonly HttpClient _httpClient = new HttpClient();
private readonly ICacheService _cacheService;

public PerformanceOptimizedFunction(ICacheService cacheService)
{
_cacheService = cacheService;
}

[FunctionName(“OptimizedEndpoint”)]
public async Task Run(
[HttpTrigger(AuthorizationLevel.Anonymous, “get”)] HttpRequest req)
{
var cacheKey = req.Query[“key”];

// 1. 使用缓存
if (!string.IsNullOrEmpty(cacheKey))
{
var cached = await _cacheService.GetAsync(cacheKey);
if (cached != null)
{
return new OkObjectResult(new { source = “cache”, data = cached });
}
}

// 2. 使用静态HttpClient(避免socket耗尽)
var response = await _httpClient.GetAsync(“https://api.example.com/data”);
var data = await response.Content.ReadAsStringAsync();

// 3. 异步并行处理
var tasks = new[]
{
ProcessDataAsync(data),
SaveToDatabaseAsync(data),
SendNotificationAsync(data)
};

await Task.WhenAll(tasks);

// 4. 缓存结果
if (!string.IsNullOrEmpty(cacheKey))
{
await _cacheService.SetAsync(cacheKey, data);
}

return new OkObjectResult(new { source = “api”, data });
}

private async Task ProcessDataAsync(string data)
{
// 模拟处理
await Task.Delay(100);
}

private async Task SaveToDatabaseAsync(string data)
{
// 模拟数据库保存
await Task.Delay(150);
}

private async Task SendNotificationAsync(string data)
{
// 模拟发送通知
await Task.Delay(50);
}
}
第十部分:故障排除和调试技巧
10.1 本地调试技巧
1. 使用Visual Studio调试
// 在代码中设置断点
[FunctionName(“DebugExample”)]
public async Task Run(
[HttpTrigger(AuthorizationLevel.Anonymous, “get”)] HttpRequest req,
ILogger log)
{
// 设置断点
var name = req.Query[“name”];

// 查看变量值
log.LogInformation($”Name: {name}”);

// 条件断点
if (name == “break”)
{
// 这里会触发断点
Debugger.Break();
}

return new OkObjectResult($”Hello {name}”);
}
2. 使用日志级别
[FunctionName(“LoggingExample”)]
public async Task Run(
[HttpTrigger(AuthorizationLevel.Anonymous, “get”)] HttpRequest req,
ILogger log)
{
// 不同级别的日志
log.LogTrace(“Trace message – detailed debugging”);
log.LogDebug(“Debug message – development information”);
log.LogInformation(“Info message – general information”);
log.LogWarning(“Warning message – something unexpected”);
log.LogError(“Error message – something failed”);
log.LogCritical(“Critical message – severe failure”);

// 结构化日志
var userId = “user123”;
var action = “login”;
log.LogInformation(“User {UserId} performed {Action} at {Timestamp}”,
userId, action, DateTime.UtcNow);

return new OkResult();
}
3. 查看本地日志
运行时,日志会输出到控制台。也可以配置文件日志:
// host.json
{
“logging”: {
“fileLoggingMode”: “debugOnly”,
“logLevel”: {
“default”: “Information”,
“Function”: “Debug”
}
}
}
10.2 Azure中的故障排除
1. 使用Application Insights
// 在Startup.cs中配置
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddApplicationInsightsTelemetry();

// 自定义遥测
builder.Services.AddSingleton();
}

// 自定义遥测初始化器
public class CustomTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
telemetry.Context.Cloud.RoleName = “MyFunctionApp”;
telemetry.Context.GlobalProperties[“Environment”] = “Production”;
}
}
2. 查看实时日志
# 使用Azure CLI
az functionapp log tail myfunctionapp123

# 过滤特定函数的日志
az functionapp log tail myfunctionapp123 | grep “MyFunction”
3. 诊断工具
在Azure门户中:
诊断和解决问题
:自动检测常见问题
指标
:查看执行计数、持续时间、失败率
活动日志
:查看管理操作
10.3 常见错误和解决方案
错误1:函数超时
症状
:函数运行超过5分钟(消耗计划限制)被终止。
解决方案
:
// 1. 优化函数逻辑,减少执行时间
// 2. 使用Durable Functions处理长时间运行的操作
// 3. 考虑升级到高级计划

// 在host.json中增加超时(仅适用于高级计划)
{
“functionTimeout”: “00:10:00” // 10分钟
}
错误2:内存不足
症状
:函数因内存不足而崩溃。
解决方案
:
// 1. 及时释放资源
public async Task Run(HttpRequest req)
{
using (var stream = new MemoryStream())
{
await req.Body.CopyToAsync(stream);
// 处理stream
} // 自动释放

// 2. 避免大对象
// 不要将整个大文件加载到内存
// 使用流式处理

// 3. 分批处理
foreach (var batch in items.Chunk(100))
{
await ProcessBatchAsync(batch);
}

return new OkResult();
}
错误3:并发问题
症状
:多个函数实例同时访问共享资源导致冲突。
解决方案
:
// 1. 使用锁(谨慎使用)
private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);

public async Task Run(HttpRequest req)
{
await _semaphore.WaitAsync();
try
{
// 临界区代码
await ProcessSharedResourceAsync();
}
finally
{
_semaphore.Release();
}

return new OkResult();
}

// 2. 使用队列串行化处理
[FunctionName(“QueueProcessor”)]
public async Task ProcessQueue(
[QueueTrigger(“work-queue”)] string workItem,
ILogger log)
{
// 队列保证了串行处理
await ProcessWorkItemAsync(workItem);
}
总结
通过本教程,你已经学习了:
✅
环境准备和项目创建
✅
核心概念和基础函数开发
✅
ASP.NET Core集成与依赖注入
✅
高级函数开发模式
✅
配置管理详解
✅
依赖注入深度解析
✅
配置与DI常见问题及解决方案
✅
部署到Azure
✅
高级主题和最佳实践
✅
故障排除和调试技巧
下一步建议
实践项目
:创建一个完整的CRUD API
学习Durable Functions
:复杂工作流编排
探索Azure服务集成
:Service Bus、Event Grid等
性能监控
:设置Application Insights警报
安全加固
:实现身份验证和授权
有用的资源
Azure Functions官方文档
.NET Azure Functions参考
Azure Functions扩展绑定
祝你在Azure Functions开发之旅中取得成功!