"哥本哈士奇(aspnetx)排"这个项目名称看似无厘头,实际上暗含了技术圈的幽默与深意。作为一名在.NET领域摸爬滚打多年的开发者,我第一眼就认出了这个梗——它巧妙地将ASP.NET Core框架与流行文化中的"哈士奇"形象相结合,暗示这是一个关于ASP.NET Core技术栈的"奇葩"问题排查指南。
在真实的开发场景中,ASP.NET Core应用经常会遇到各种"拆家式"的诡异问题——就像哈士奇拆家一样让人猝不及防。这些问题往往不按常理出牌,常规的排查方法难以奏效,需要开发者具备特殊的"驯犬技巧"。
最常见的"哈士奇行为"发生在依赖注入容器中。比如这个经典场景:
csharp复制services.AddScoped<IDogService, HuskyService>();
services.AddSingleton<IDogService, BulldogService>();
你以为最后注册的BulldogService会生效?实际运行时可能会发现HuskyService突然"拆家"——因为不同生命周期的服务注册可能产生冲突。我在三个不同项目中踩过这个坑,异常堆栈根本不会指向这里。
重要提示:永远用TryAdd系列方法防止重复注册,如
services.TryAddScoped<IDogService, HuskyService>()
中间件顺序问题堪称ASP.NET Core的"撒手没"时刻。比如:
csharp复制app.UseRouting();
app.UseStaticFiles();
app.UseEndpoints(...);
把静态文件中间件放在路由之后,你的静态文件请求可能会像脱缰的哈士奇一样消失得无影无踪。正确的顺序应该是:
csharp复制app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(...);
JSON配置加载时的环境变量覆盖行为经常让人发出"这TM是狼是狗"的感叹:
json复制{
"ConnectionStrings": {
"Default": "Server=localhost"
}
}
当你在生产环境用ASPNETCORE_ENVIRONMENT=Production启动时,配置系统会像哈士奇学狼嚎一样,突然加载appsettings.Production.json覆盖原有配置。如果没做好配置分层设计,这种静默覆盖会导致半夜被叫起来"找狗"。
ASP.NET Core的日志系统是你的"狗毛分析仪"。配置结构化日志时一定要加上这些关键信息:
csharp复制builder.Logging.AddJsonConsole(options => {
options.IncludeScopes = true;
options.TimestampFormat = "HH:mm:ss";
options.JsonWriterOptions = new JsonWriterOptions { Indented = true };
});
这样当日志中出现"沙发被拆"的异常时,你至少能知道是哪个时间点、哪个作用域下的"哈士奇"干的。
当应用出现"半夜跑酷"式的性能问题时,需要准备完整的"遛狗装备":
这是我常用的性能分析命令组合:
bash复制dotnet tool install -g dotnet-counters
dotnet counters monitor System.Runtime Microsoft.AspNetCore.Hosting -p <PID>
必须像给哈士奇戴GPS项圈一样配置健康检查:
csharp复制builder.Services.AddHealthChecks()
.AddCheck<DatabaseHealthCheck>("database")
.AddUrlGroup(new Uri("http://cache-service"), "cache");
app.MapHealthChecks("/healthz");
处理SIGTERM信号时,要给"哈士奇"足够的反应时间:
csharp复制builder.WebHost.ConfigureShutdownTimeout(TimeSpan.FromSeconds(30));
同时记得在Program.cs中注册应用停止事件:
csharp复制var app = builder.Build();
var lifetime = app.Services.GetRequiredService<IHostApplicationLifetime>();
lifetime.ApplicationStopping.Register(() => {
// 回收资源、关闭连接等操作
});
某次线上事故中,我们的API内存持续增长,像哈士奇囤积玩具一样。通过dotnet-dump分析发现是缓存组件没有设置过期时间:
csharp复制// 错误示范
services.AddMemoryCache();
// 正确做法
services.AddMemoryCache(options => {
options.SizeLimit = 1024 * 1024 * 100; // 100MB
options.CompactionPercentage = 0.5;
});
当你的API被前端调用时,可能会遇到CORS问题,就像哈士奇总想越狱。正确的CORS配置应该是:
csharp复制builder.Services.AddCors(options => {
options.AddPolicy("AllowFrontend", policy => {
policy.WithOrigins("https://example.com")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
app.UseCors("AllowFrontend");
使用dotnet watch时要特别注意:
bash复制dotnet watch run --environment Development
但要注意某些配置变更(如launchSettings.json)需要重启才能生效,就像哈士奇突然不想走路时你得抱它回家。
在VS Code中推荐这些调试配置:
json复制{
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net7.0/YourApp.dll",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
]
}
Dockerfile中常见的坑:
dockerfile复制# 错误示范 - 会产生巨大镜像
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /app
FROM mcr.microsoft.com/dotnet/aspnet:7.0
COPY --from=build /app .
ENTRYPOINT ["dotnet", "YourApp.dll"]
# 正确做法 - 多阶段构建
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["YourApp.csproj", "."]
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS final
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "YourApp.dll"]
deployment.yaml中需要特别注意这些参数:
yaml复制apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: your-app
image: your-registry/your-app:latest
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 15
periodSeconds: 20
resources:
limits:
memory: "500Mi"
cpu: "500m"
在Startup.cs中配置:
csharp复制builder.Services.AddMetrics();
app.UseHttpMetrics();
// Program.cs
app.MapMetrics();
然后配置prometheus.yml:
yaml复制scrape_configs:
- job_name: 'aspnet-app'
scrape_interval: 15s
static_configs:
- targets: ['your-app:80']
使用Sentry等工具时要注意:
csharp复制builder.WebHost.UseSentry(options => {
options.Dsn = "your-dsn";
options.MaxRequestBodySize = RequestSize.Always;
options.SendDefaultPii = true;
options.TracesSampleRate = 0.2;
});
对于静态内容:
csharp复制app.UseStaticFiles(new StaticFileOptions {
OnPrepareResponse = ctx => {
ctx.Context.Response.Headers.Append(
"Cache-Control", "public,max-age=31536000");
}
});
对于API响应:
csharp复制[ResponseCache(Duration = 30, Location = ResponseCacheLocation.Client)]
public IActionResult Get() {
return Ok(data);
}
使用Dapper时的最佳实践:
csharp复制public async Task<IEnumerable<Dog>> GetDogsAsync() {
using var connection = new SqlConnection(_config.GetConnectionString("Default"));
return await connection.QueryAsync<Dog>(
"SELECT * FROM Dogs WHERE Breed = @Breed",
new { Breed = "Husky" });
}
EF Core的优化建议:
csharp复制services.AddDbContext<AppDbContext>(options => {
options.UseSqlServer(config.GetConnectionString("Default"))
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
.EnableSensitiveDataLogging(false);
});
使用AspNetCoreRateLimit:
csharp复制services.AddMemoryCache();
services.Configure<IpRateLimitOptions>(options => {
options.GeneralRules = new List<RateLimitRule> {
new RateLimitRule {
Endpoint = "*",
Limit = 100,
Period = "1m"
}
};
});
services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
对于传统表单:
html复制<form method="post">
@Html.AntiForgeryToken()
<!-- 表单内容 -->
</form>
对于API:
csharp复制services.AddAntiforgery(options => {
options.HeaderName = "X-CSRF-TOKEN";
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
使用xUnit测试控制器:
csharp复制public class DogsControllerTests {
[Fact]
public async Task Get_ReturnsListOfDogs() {
// Arrange
var mockRepo = new Mock<IDogRepository>();
mockRepo.Setup(repo => repo.GetAllAsync())
.ReturnsAsync(GetTestDogs());
var controller = new DogsController(mockRepo.Object);
// Act
var result = await controller.Get();
// Assert
var viewResult = Assert.IsType<OkObjectResult>(result);
var model = Assert.IsAssignableFrom<IEnumerable<Dog>>(viewResult.Value);
Assert.Equal(2, model.Count());
}
}
Program.cs中:
csharp复制if (builder.Environment.IsDevelopment()) {
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
}
// 测试项目中的WebApplicationFactory
public class CustomWebApplicationFactory : WebApplicationFactory<Program> {
protected override void ConfigureWebHost(IWebHostBuilder builder) {
builder.ConfigureServices(services => {
services.RemoveAll<DbContextOptions<AppDbContext>>();
services.AddDbContext<AppDbContext>(options => {
options.UseInMemoryDatabase("TestDb");
});
});
}
}
yaml复制name: Build and Deploy
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Test
run: dotnet test --no-build --configuration Release
- name: Publish
run: dotnet publish -c Release -o ./publish
- name: Docker Build
run: docker build -t your-app:${{ github.sha }} .
bicep复制param location string = resourceGroup().location
param appName string = 'yourapp'
resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
name: 'asp-${appName}'
location: location
sku: {
name: 'P1v2'
tier: 'PremiumV2'
}
}
resource webApp 'Microsoft.Web/sites@2022-03-01' = {
name: appName
location: location
properties: {
serverFarmId: appServicePlan.id
siteConfig: {
linuxFxVersion: 'DOTNETCORE|7.0'
alwaysOn: true
}
}
}
.devcontainer/devcontainer.json:
json复制{
"name": "ASP.NET Core",
"image": "mcr.microsoft.com/dotnet/sdk:7.0",
"forwardPorts": [5000, 5001],
"customizations": {
"vscode": {
"extensions": [
"ms-dotnettools.csharp",
"ms-azuretools.vscode-docker"
]
}
},
"postCreateCommand": "dotnet restore"
}
global.json确保团队统一SDK版本:
json复制{
"sdk": {
"version": "7.0.203",
"rollForward": "latestPatch"
}
}
dotnet-tools.json共享团队工具:
json复制{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "7.0.5",
"commands": ["dotnet-ef"]
},
"dotnet-reportgenerator-globaltool": {
"version": "5.1.10",
"commands": ["reportgenerator"]
}
}
}
使用Serilog+Seq的配置:
csharp复制builder.Host.UseSerilog((ctx, lc) => lc
.WriteTo.Console()
.WriteTo.Seq("http://localhost:5341")
.Enrich.WithProperty("Application", ctx.HostingEnvironment.ApplicationName)
.Enrich.FromLogContext()
.ReadFrom.Configuration(ctx.Configuration));
在Seq中可以用这种查询找错误:
code复制@Level = 'Error' and Application = 'YourApp' |
order by @Timestamp desc |
take 100
OpenTelemetry配置示例:
csharp复制builder.Services.AddOpenTelemetry()
.WithTracing(tracerProviderBuilder => {
tracerProviderBuilder
.AddSource("YourApp")
.SetResourceBuilder(ResourceBuilder
.CreateDefault()
.AddService("YourApp"))
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddOtlpExporter();
});
在PageModel中处理表单:
csharp复制public class ContactModel : PageModel {
[BindProperty]
public ContactForm Input { get; set; }
public void OnGet() { }
public IActionResult OnPost() {
if (!ModelState.IsValid) {
return Page();
}
// 处理表单提交
return RedirectToPage("/ThankYou");
}
}
对应的Razor页面:
html复制@page
@model ContactModel
<form method="post">
<div class="form-group">
<label asp-for="Input.Name"></label>
<input asp-for="Input.Name" class="form-control">
<span asp-validation-for="Input.Name"></span>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Program.cs配置:
csharp复制builder.Services.AddHttpClient("ServerAPI",
client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
组件中调用API:
csharp复制@inject IHttpClientFactory HttpClientFactory
<button @onclick="LoadData">Load</button>
@code {
private async Task LoadData() {
var http = HttpClientFactory.CreateClient("ServerAPI");
data = await http.GetFromJsonAsync<Dog[]>("api/dogs");
}
}
使用Refit声明式HTTP客户端:
csharp复制public interface IDogServiceApi {
[Get("/api/dogs/{id}")]
Task<Dog> GetDogAsync(int id);
}
// 注册
builder.Services.AddRefitClient<IDogServiceApi>()
.ConfigureHttpClient(c => c.BaseAddress = new Uri("http://dog-service"));
csharp复制services.AddCap(x => {
x.UseSqlServer(config.GetConnectionString("Default"));
x.UseRabbitMQ(options => {
options.HostName = config["RabbitMQ:Host"];
});
x.SucceedMessageExpiredAfter = 24 * 3600;
});
// 发布消息
[NonAction]
[CapSubscribe("dogs.created")]
public async Task HandleDogCreated(DogCreatedEvent @event) {
// 处理事件
}
csharp复制public class Kennel : IAggregateRoot {
private readonly List<Dog> _dogs = new();
public Guid Id { get; private set; }
public IReadOnlyCollection<Dog> Dogs => _dogs.AsReadOnly();
public void AddDog(string name, string breed) {
if (_dogs.Count >= 10) {
throw new KennelFullException();
}
_dogs.Add(new Dog(name, breed));
}
}
csharp复制public class DogAdoptedEvent : IDomainEvent {
public Guid DogId { get; }
public DateTime AdoptedDate { get; }
public DogAdoptedEvent(Guid dogId) {
DogId = dogId;
AdoptedDate = DateTime.UtcNow;
}
}
// 在聚合方法中发布
public void Adopt() {
// ...其他逻辑
AddDomainEvent(new DogAdoptedEvent(Id));
}
csharp复制[MemoryDiagnoser]
public class DogServiceBenchmarks {
private IDogService _service;
[GlobalSetup]
public void Setup() {
_service = new DogService(new TestDogRepository());
}
[Benchmark]
public async Task<List<Dog>> GetAllDogsAsync() {
return await _service.GetAllDogsAsync();
}
}
test.js脚本示例:
javascript复制import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 100 },
{ duration: '1m', target: 500 },
{ duration: '20s', target: 0 },
],
};
export default function () {
const res = http.get('http://localhost:5000/api/dogs');
check(res, {
'status was 200': (r) => r.status == 200,
'response time < 200ms': (r) => r.timings.duration < 200,
});
sleep(1);
}
.editorconfig配置示例:
ini复制[*.cs]
dotnet_diagnostic.CA1303.severity = error
dotnet_diagnostic.CA2007.severity = warning
dotnet_diagnostic.CA1056.severity = none
# 命名规则
dotnet_naming_rule.interface_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.interface_should_be_pascal_case.symbols = interface
dotnet_naming_rule.interface_should_be_pascal_case.style = pascal_case
使用coverlet收集覆盖率:
bash复制dotnet test --collect:"XPlat Code Coverage" --settings coverlet.runsettings
coverlet.runsettings文件:
xml复制<?xml version="1.0" encoding="utf-8" ?>
<RunSettings>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName="XPlat code coverage">
<Configuration>
<Format>cobertura</Format>
<Include>[YourApp]*</Include>
<Exclude>[*.Tests]*</Exclude>
</Configuration>
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>
推荐的分支策略:
code复制main - 生产环境代码(保护分支)
release/* - 预发布分支
feature/* - 功能开发分支
hotfix/* - 紧急修复分支
每个PR必须检查:
csharp复制builder.Services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Dog API", Version = "v1" });
c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "YourApp.xml"));
});
app.UseSwagger();
app.UseSwaggerUI(c => {
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Dog API V1");
});
docfx.json配置示例:
json复制{
"metadata": [
{
"src": [
{
"files": ["**/*.csproj"],
"exclude": ["**/bin/**", "**/obj/**"]
}
],
"dest": "api"
}
],
"build": {
"content": [
{
"files": ["api/**.yml", "api/index.md"]
},
{
"files": ["docs/**/*.md", "docs/**/*.yml"]
}
],
"resource": [
{
"files": ["images/**"]
}
],
"dest": "_site"
}
}
csharp复制public class ApiExceptionFilter : IExceptionFilter {
public void OnException(ExceptionContext context) {
var problemDetails = new ProblemDetails {
Title = "An error occurred",
Status = StatusCodes.Status500InternalServerError,
Detail = context.Exception.Message,
Instance = context.HttpContext.Request.Path
};
if (context.Exception is DogNotFoundException) {
problemDetails.Status = StatusCodes.Status404NotFound;
problemDetails.Title = "Dog not found";
}
context.Result = new ObjectResult(problemDetails) {
StatusCode = problemDetails.Status
};
context.ExceptionHandled = true;
}
}
// 注册
builder.Services.AddControllers(options => {
options.Filters.Add<ApiExceptionFilter>();
});
csharp复制builder.Services.AddHealthChecks()
.AddCheck<DatabaseHealthCheck>("database", failureStatus: HealthStatus.Degraded);
app.MapHealthChecks("/healthz", new HealthCheckOptions {
ResponseWriter = async (context, report) => {
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonSerializer.Serialize(new {
status = report.Status.ToString(),
checks = report.Entries.Select(e => new {
name = e.Key,
status = e.Value.Status.ToString(),
duration = e.Value.Duration.TotalMilliseconds,
exception = e.Value.Exception?.Message
})
}));
}
});
appsettings.Production.json示例:
json复制{
"Logging": {
"LogLevel": {
"Default": "Warning",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"Default": "Server=prod-db;Database=DogApp;User Id=prod-user;"
}
}
使用Azure Key Vault:
csharp复制builder.Configuration.AddAzureKeyVault(
new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
new DefaultAzureCredential());
或者使用本地用户机密:
bash复制dotnet user-secrets init
dotnet user-secrets set "ConnectionStrings:Default" "Server=localhost;..."
使用Azure DevOps的发布管道:
yaml复制- stage: DeployGreen
jobs:
- deployment: Deploy
environment: 'production-green'
strategy:
rolling:
maxParallel: 2
deploy:
steps:
- script: echo "Deploying to green slot"
- stage: SwitchTraffic
dependsOn: DeployGreen
condition: succeeded()
jobs:
- job: Switch
steps:
- script: echo "Switching traffic to green slot"
使用Kubernetes的渐进式发布:
yaml复制apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: dog-service
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: dog-service
service:
port: 80
analysis:
interval: 1m
threshold: 5
maxWeight: 50
stepWeight: 10
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
- name: request-duration
thresholdRange:
max: 500
interval: 1m
csharp复制builder.Services.AddMetrics();
app.UseMetricsAllMiddleware();
app.UseMetricsAllEndpoints();
// 自定义指标
var dogsAdoptedCounter = new CounterOptions {
Name = "dogs_adopted_total",
Help = "Total number of dogs adopted"
};
Metrics.DefaultRegistry.Measure.Counter.Increment(dogsAdoptedCounter);
csharp复制builder.Services.AddOpenTelemetry()
.WithTracing(builder => {
builder.AddAspNetCoreInstrumentation(options => {
options.Enrich = (activity, eventName, rawObject) => {
if (eventName == "OnStartActivity") {
if (rawObject is HttpRequest httpRequest) {
activity.SetTag("http.user_agent", httpRequest.Headers["User-Agent"]);
}
}
};
});
});
bundleconfig.json示例:
json复制[
{
"outputFileName": "wwwroot/css/site.min.css",
"inputFiles": [
"wwwroot/css/site.css",
"wwwroot/lib/bootstrap/dist/css/bootstrap.css"
]
},
{
"outputFileName": "wwwroot/js/site.min.js",
"inputFiles": [
"wwwroot/js/site.js",
"wwwroot/lib/jquery/dist/jquery.js"
],
"minify": {
"enabled": true,
"renameLocals": true
},
"sourceMap": false
}
]
csharp复制app.UseImageSharp(new ImageSharpMiddlewareOptions {
OnPrepareResponse = ctx => {
ctx.Context.Response.Headers["Cache-Control"] = "public,max-age=31536000";
}
});
csharp复制builder.Services.AddProgressiveWebApp();
// manifest.json
{
"name": "Dog App",
"short_name": "DogApp",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#1E88E5",
"icons": [
{
"src": "/images/icon-192.png",
"sizes": "192x192",
"type": "image/png"
}
]
}
使用Bootstrap的断点:
html复制<div class="d-none d-md-block">
<!-- 只在中等及以上屏幕显示 -->
</div>
Resources/Controllers/DogController.en.resx:
xml复制<data name="DogNotFound" xml:space="preserve">
<value>Dog not found</value>
</data>
Resources/Controllers/DogController.zh-CN.resx:
xml复制<data name="DogNotFound" xml:space="preserve">
<value>未找到狗狗</value>
</data>
csharp复制app.UseRequestLocalization(options => {
var supportedCultures = new[] { "en", "zh-CN" };
options.AddSupportedCultures(supportedCultures)
.AddSupportedUICultures(supportedCultures)
.SetDefaultCulture("en");
});
html复制<button class="btn btn-primary" aria-label="Adopt this dog">
<i class="fas fa-paw"></i> Adopt
</button>
javascript复制window.matchMedia('(prefers-contrast: more)').addEventListener('change', e => {
document.body.classList.toggle('high-contrast', e.matches);
});
使用NuGet更新检查:
bash复制dotnet list package --outdated
或者使用GitHub Dependabot:
yaml复制version: 2
updates:
- package-ecosystem: "nuget"
directory: "/"
schedule:
interval: "weekly"
使用ObsoleteAttribute标记:
csharp复制[Obsolete("Use GetDogV2 instead", false)]
public Dog GetDog(int id) {
// 旧实现
}
public Dog GetDogV2(int id) {
// 新实现
}
配合构建警告转换为错误:
xml复制<PropertyGroup>
<WarningsAsErrors>CS0618</WarningsAsErrors>
</PropertyGroup>
csharp复制app.Use(async (context, next) => {
context.Response.Headers.Add("Content-Security-Policy",
"default-src 'self'; script-src 'self' 'unsafe-inline' cdn.example.com");
await next();
});
csharp复制app.UseSecurityHeaders(policies => {
policies.AddFrameOptionsDeny()
.AddXssProtectionBlock()
.AddContentTypeOptionsNoSniff()
.AddReferrerPolicyStrictOriginWhenCrossOrigin()
.AddContentSecurityPolicy(builder => {
builder.AddDefaultSrc().Self();
builder.AddScriptSrc().Self().UnsafeInline();
});
});
使用SQL Agent作业或Azure自动化:
sql复制-- 完整备份
BACKUP DATABASE [DogApp]
TO DISK = N'D:\Backups\DogApp_Full.bak'
WITH COMPRESSION, STATS = 10;
csharp复制public class AppStateSnapshot {
public DateTime Timestamp { get; set; }
public int TotalDogs { get; set; }
public int ActiveUsers { get; set; }
public static async Task<AppStateSnapshot> CreateAsync(AppDbContext db) {
return new AppStateSnapshot {
Timestamp = DateTime.UtcNow,
TotalDogs = await db.Dogs.CountAsync(),
ActiveUsers = await db.Users.CountAsync(u => u.LastActive > DateTime.UtcNow.AddDays(-1))
};
}
}
使用弹性缩放:
json复制{
"sku": {
"name": "P1v2",
"tier": "PremiumV2",
"size": "P1v2",
"family": "P",
"capacity": 1
},
"properties": {
"targetWorkerCount": 1,
"targetWorkerSizeId": 0,
"minimumElasticInstanceCount": 1,
"maximumElasticInstanceCount": 3
}
}
csharp复制public class DogArchiveService {
public async Task ArchiveOldDogsAsync(DateTime cutoffDate) {
var oldDogs = await _db.Dogs
.Where(d => d.LastUpdated < cutoffDate)
.ToListAsync();
await _archiveDb.Dogs.AddRangeAsync(oldDogs);
_db.Dogs.RemoveRange(oldDogs);
await _db.SaveChangesAsync();
await _archiveDb.SaveChangesAsync();
}
}
markdown复制### 功能完整性
- [ ] 是否实现了所有需求功能
- [ ] 是否有未完成的TODO注释
### 代码质量
- [ ] 是否遵循了团队编码规范
- [ ] 是否有明显的性能问题
- [ ] 是否有安全漏洞风险
### 测试覆盖