はじめに
ASP.NET Coreは、Microsoftが提供する最新のWebアプリケーション開発フレームワークです。クロスプラットフォーム対応、高いパフォーマンス、そして豊富な機能を備えており、モダンなWeb開発に最適なツールとして注目を集めています。本記事では、ASP.NET Coreの基礎から実践的な開発手法まで、現役エンジニアの視点で詳しく解説していきます。
この記事を読んだらわかること:
- ASP.NET Coreの基本概念と従来の.NET Frameworkとの違い
- MVCパターンを用いた実践的なWebアプリケーション開発の方法
- パフォーマンス最適化とメモリ使用量の改善テクニック
- セキュリティ対策の実装方法と具体的な防御手段
- Entity Framework Coreを使用したデータベース連携の実践手法
- DockerとCI/CDを活用したモダンなデプロイメント手法
ASP.NET Coreとは何か – モダンWeb開発の新標準
ASP.NET Coreは、Microsoftが開発した最新のWebアプリケーションフレームワークです。従来の.NET Frameworkを基盤とするASP.NETとは異なり、オープンソースでクロスプラットフォーム対応の新しいフレームワークとして設計されています。
従来の.NET Frameworkとの決定的な違い
ASP.NET CoreとASP.NET Frameworkの主要な違いは以下の点にあります:
| 特徴 | ASP.NET Core | ASP.NET Framework |
|---|---|---|
| プラットフォーム | クロスプラットフォーム(Windows, Linux, macOS) | Windowsのみ |
| 依存性 | 完全モジュール化(必要な機能のみ導入可能) | モノリシック(全機能が統合) |
| パフォーマンス | 高速(軽量設計) | 比較的重い |
| 開発方式 | オープンソース | プロプライエタリ |
| デプロイ | 柔軟(自己完結型展開可能) | IIS依存 |
特筆すべき点として、ASP.NET Coreでは必要な機能のみを選択してインストールできる「Pay-for-what-you-use」モデルを採用しています。これにより、アプリケーションの起動時間が短縮され、メモリ使用量も最適化されます。
クロスプラットフォーム対応による新たな可能性
ASP.NET Coreの最大の特徴は、真のクロスプラットフォーム対応です。以下のような開発シナリオが可能になりました:
- 開発環境の自由な選択
- Visual Studio Code(Windows/Mac/Linux)
- Visual Studio(Windows/Mac)
- JetBrains Rider(全プラットフォーム)
- デプロイ先の柔軟な選択
- Linux系サーバー
- Windows Server
- Dockerコンテナ
- クラウドプラットフォーム(Azure, AWS, GCP)
実際の開発では、以下のようなプロジェクト作成コマンドで、どのプラットフォームでも同じように開発を始めることができます:
# 新規Webアプリケーションプロジェクトの作成 dotnet new webapp -n MyFirstAspNetCoreApp # APIプロジェクトの作成 dotnet new webapi -n MyFirstWebApi
高速で軽量な実行環境の実現方法
ASP.NET Coreは、以下の技術的特徴により高いパフォーマンスを実現しています:
Kestrelサーバー
- ネイティブで高性能なWebサーバー
- 非同期I/O処理による高スループット
- メモリ効率の高い実装
最適化されたミドルウェアパイプライン
public void Configure(IApplicationBuilder app)
{
// 必要最小限のミドルウェアのみを使用
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
JITコンパイル最適化
- プラットフォーム特有の最適化
- Tiered Compilationによる実行時最適化
- ReadyToRun形式による起動時間の短縮
効率的なメモリ管理
// Spanを使用した効率的なメモリ操作の例
public void ProcessLargeData(ReadOnlySpan<byte> data)
{
// スタックアロケーションを最小限に抑える
Span<byte> buffer = stackalloc byte[1024];
// データ処理...
}
これらの特徴により、ASP.NET Coreは以下のようなパフォーマンス指標を達成しています:
リクエスト処理速度: 従来の約2倍
メモリ使用量: 最大50%削減
アプリケーション起動時間: 約30%短縮
ASP.NET Coreは、これらの革新的な特徴により、モダンなWeb開発の新標準として急速に普及しています。特に、マイクロサービスアーキテクチャやコンテナ化された環境での開発において、その真価を発揮します。
ASP.NET Coreで作る最新のWebアプリケーション
ASP.NET Coreを使用した最新のWebアプリケーション開発では、モダンな開発手法とベストプラクティスを活用することで、保守性が高く拡張しやすいアプリケーションを構築できます。
新規プロジェクトのセットアップと基本設定
新規プロジェクトの作成から基本設定まで、以下の手順で効率的に開発環境を整えることができます:
プロジェクトの作成
# MVCプロジェクトの作成 dotnet new mvc -n ModernWebApp cd ModernWebApp # 必要なパッケージの追加 dotnet add package Microsoft.EntityFrameworkCore.SqlServer dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
アプリケーション設定の構成
var builder = WebApplication.CreateBuilder(args);
// サービスの追加
builder.Services.AddControllersWithViews();
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
// アプリケーションの構築
var app = builder.Build();
// ミドルウェアの設定
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Model-View-Controllerパターンの実装方法
MVCパターンの効果的な実装例を見ていきましょう:
モデルの定義
public class Product
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
[Range(0, 1000000)]
public decimal Price { get; set; }
public string Description { get; set; }
[DataType(DataType.Date)]
public DateTime CreatedDate { get; set; }
}
コントローラーの実装
public class ProductController : Controller
{
private readonly IProductService _productService;
private readonly ILogger<ProductController> _logger;
public ProductController(IProductService productService, ILogger<ProductController> logger)
{
_productService = productService;
_logger = logger;
}
public async Task<IActionResult> Index()
{
try
{
var products = await _productService.GetAllProductsAsync();
return View(products);
}
catch (Exception ex)
{
_logger.LogError(ex, "製品一覧の取得中にエラーが発生しました");
return StatusCode(500);
}
}
}
ビューの作成
@model IEnumerable<ModernWebApp.Models.Product>
<div class="container">
<h2>製品一覧</h2>
<div class="row">
@foreach (var product in Model)
{
<div class="col-md-4 mb-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">@product.Name</h5>
<p class="card-text">@product.Description</p>
<p class="card-text">
<strong>価格:</strong> @product.Price.ToString("C")
</p>
</div>
</div>
</div>
}
</div>
</div>
依存性注入を活用した疎結合な設計手法
ASP.NET Coreの依存性注入(DI)を活用した実装例:
サービスインターフェースの定義
public interface IProductService
{
Task<IEnumerable<Product>> GetAllProductsAsync();
Task<Product> GetProductByIdAsync(int id);
Task<Product> CreateProductAsync(Product product);
}
サービス実装
public class ProductService : IProductService
{
private readonly ApplicationDbContext _context;
private readonly IMapper _mapper;
public ProductService(ApplicationDbContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
public async Task<IEnumerable<Product>> GetAllProductsAsync()
{
return await _context.Products
.AsNoTracking()
.OrderByDescending(p => p.CreatedDate)
.ToListAsync();
}
// 他のメソッド実装...
}
DIコンテナへの登録
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddAutoMapper(typeof(Program).Assembly);
builder.Services.AddScoped<IEmailService, EmailService>();
// カスタム設定の注入
builder.Services.Configure<EmailSettings>(
builder.Configuration.GetSection("EmailSettings"));
このような実装により、以下のメリットが得られます:
テスト容易性の向上
コンポーネントの再利用性
保守性の向上
実装の柔軟性
ASP.NET Coreのこれらの機能を活用することで、スケーラブルで保守性の高いWebアプリケーションを効率的に開発することができます。
パフォーマンスを最大化するASP.NET Coreの機能
ASP.NET Coreは、高いパフォーマンスを実現するための多様な機能を提供しています。適切な実装方法を理解し、これらの機能を効果的に活用することで、アプリケーションの応答性と処理能力を大幅に向上させることができます。
非同期処理による応答性の向上テクニック
非同期プログラミングを効果的に実装することで、アプリケーションのスケーラビリティと応答性を向上させることができます:
非同期コントローラーアクションの実装
public class ProductController : Controller
{
private readonly IProductService _productService;
public ProductController(IProductService productService)
{
_productService = productService;
}
// 非同期アクションメソッド
public async Task<IActionResult> Index()
{
// ConfigureAwait(false)を使用して不要な同期コンテキストの切り替えを防ぐ
var products = await _productService.GetProductsAsync().ConfigureAwait(false);
return View(products);
}
// 並列処理を活用した非同期処理
public async Task<IActionResult> Dashboard()
{
var tasks = new[]
{
_productService.GetTotalSalesAsync(),
_productService.GetTopProductsAsync(),
_productService.GetRecentOrdersAsync()
};
// 複数の非同期処理を並列実行
await Task.WhenAll(tasks);
var viewModel = new DashboardViewModel
{
TotalSales = tasks[0].Result,
TopProducts = tasks[1].Result,
RecentOrders = tasks[2].Result
};
return View(viewModel);
}
}
非同期ストリーム処理の活用
public async IAsyncEnumerable<Product> GetProductStreamAsync()
{
await using var context = new ProductContext();
var products = context.Products.AsAsyncEnumerable();
await foreach (var product in products)
{
// 大量データを少しずつ処理
yield return product;
}
}
キャッシュ機能の効果的な活用方法
ASP.NET Coreは、複数のレベルでキャッシュを実装できます:
メモリキャッシュの実装
public class CacheProductService : IProductService
{
private readonly IMemoryCache _cache;
private readonly IProductRepository _repository;
public async Task<Product> GetProductByIdAsync(int id)
{
string cacheKey = $"product_{id}";
return await _cache.GetOrCreateAsync(cacheKey, async entry =>
{
entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(10));
entry.SetSlidingExpiration(TimeSpan.FromMinutes(2));
return await _repository.GetProductByIdAsync(id);
});
}
}
分散キャッシュの設定(Redis使用)
public void ConfigureServices(IServiceCollection services)
{
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = Configuration.GetConnectionString("Redis");
options.InstanceName = "ProductCache_";
});
}
レスポンスキャッシュの実装
[ResponseCache(Duration = 60, Location = ResponseCacheLocation.Any)]
public async Task<IActionResult> ProductList()
{
var products = await _productService.GetProductsAsync();
return View(products);
}
メモリ使用量の最適化戦略
メモリ使用量を最適化するための主要な戦略:
オブジェクトプーリングの実装
public class ApiController : Controller
{
private readonly ObjectPool<StringBuilder> _stringBuilderPool;
public ApiController(ObjectPoolProvider poolProvider)
{
_stringBuilderPool = poolProvider.CreateStringBuilderPool();
}
public IActionResult ProcessData(string data)
{
var sb = _stringBuilderPool.Get();
try
{
// StringBuilder を使用した処理
sb.Append("処理結果: ").Append(data);
return Ok(sb.ToString());
}
finally
{
_stringBuilderPool.Return(sb);
}
}
}
効率的なメモリ管理
public class DataProcessor
{
// 大きな配列の効率的な処理
public async Task ProcessLargeDataAsync(Stream stream)
{
const int bufferSize = 4096;
using var memoryStream = new MemoryStream();
Memory<byte> buffer = new byte[bufferSize];
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer)) > 0)
{
await memoryStream.WriteAsync(buffer[..bytesRead]);
}
}
}
これらの最適化技術を適切に組み合わせることで、ASP.NET Coreアプリケーションの性能を最大限に引き出すことができます。
アプリケーションの重要な指標を監視
メモリ使用量
レスポンス時間
スループット
GCの頻度
ボトルネックの特定と対策
プロファイリングツールの活用
パフォーマンステストの実施
継続的なモニタリング
セキュリティ対策の実装ガイド
ASP.NET Coreには、堅牢なセキュリティ機能が組み込まれています。適切な実装により、アプリケーションを様々な脅威から保護することができます。
認証・認可の実装ベストプラクティス
Identity機能の実装
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
// パスワードポリシーの設定
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireUppercase = true;
options.Password.RequireNonAlphanumeric = true;
options.Password.MinLength = 12;
// ロックアウトポリシー
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15);
options.Lockout.MaxFailedAccessAttempts = 5;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
}
JWT認証の実装
public class AuthenticationService : IAuthenticationService
{
private readonly IConfiguration _configuration;
private readonly UserManager<ApplicationUser> _userManager;
public async Task<string> GenerateJwtToken(ApplicationUser user)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Id),
new Claim(ClaimTypes.Email, user.Email),
new Claim(ClaimTypes.Role, "User")
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
_configuration["JWT:SecretKey"]));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _configuration["JWT:Issuer"],
audience: _configuration["JWT:Audience"],
claims: claims,
expires: DateTime.Now.AddHours(1),
signingCredentials: credentials
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
クロスサイトスクリプティング対策の実践
XSS防御の実装
public void ConfigureServices(IServiceCollection services)
{
services.AddAntiforgery(options =>
{
options.Cookie.Name = "X-CSRF-TOKEN";
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
services.Configure<HtmlEncoderOptions>(options =>
{
options.HtmlAttributeEncode = true;
});
}
CSPヘッダーの設定
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
context.Response.Headers.Add(
"Content-Security-Policy",
"default-src 'self'; " +
"script-src 'self' 'unsafe-inline' 'unsafe-eval'; " +
"style-src 'self' 'unsafe-inline';"
);
await next();
});
}
セキュアな設定とデータ保護の方法
データ保護APIの実装
public class SecureDataService
{
private readonly IDataProtector _protector;
public SecureDataService(IDataProtectionProvider provider)
{
_protector = provider.CreateProtector("SecureData.v1");
}
public string ProtectData(string data)
{
return _protector.Protect(data);
}
public string UnprotectData(string protectedData)
{
return _protector.Unprotect(protectedData);
}
}
セキュアな設定管理
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// セキュアな設定の管理
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"path\to\keys"))
.SetDefaultKeyLifetime(TimeSpan.FromDays(90));
// HTTPSリダイレクションの強制
services.AddHttpsRedirection(options =>
{
options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect;
options.HttpsPort = 443;
});
// セッション設定
services.AddSession(options =>
{
options.Cookie.Name = ".MyApp.Session";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.IdleTimeout = TimeSpan.FromMinutes(20);
});
}
}
これらのセキュリティ対策を適切に実装することで、アプリケーションを様々な脅威から保護し、ユーザーデータの安全性を確保することができます。
- 基本的なセキュリティ対策
- HTTPSの強制
- セキュアクッキーの使用
- 適切な認証・認可の実装
- XSS対策の実装
- CSRF対策の実装
- データ保護
- 機密データの暗号化
- セキュアなストレージの使用
- キーの定期的なローテーション
- バックアップの暗号化
- 監視とログ記録
- セキュリティイベントのログ記録
- 異常検知の実装
- 監査ログの保護
実践的なデータベース連携
ASP.NET Coreでのデータベース連携は、主にEntity Framework Core(EF Core)を使用して実装します。効率的なデータアクセスと保守性の高いコードを実現するための実践的な手法を見ていきましょう。
Entity Frameworkを使用したCRUD操作の実装
DbContextの設定
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// リレーションシップの設定
modelBuilder.Entity<Product>()
.HasOne(p => p.Category)
.WithMany(c => c.Products)
.HasForeignKey(p => p.CategoryId);
// インデックスの設定
modelBuilder.Entity<Product>()
.HasIndex(p => p.Name);
// 複合キーの設定
modelBuilder.Entity<OrderItem>()
.HasKey(oi => new { oi.OrderId, oi.ProductId });
}
}
リポジトリパターンの実装
public class ProductRepository : IProductRepository
{
private readonly ApplicationDbContext _context;
public ProductRepository(ApplicationDbContext context)
{
_context = context;
}
// 非同期でのCRUD操作
public async Task<Product> CreateAsync(Product product)
{
await _context.Products.AddAsync(product);
await _context.SaveChangesAsync();
return product;
}
public async Task<Product> UpdateAsync(Product product)
{
_context.Entry(product).State = EntityState.Modified;
await _context.SaveChangesAsync();
return product;
}
public async Task<bool> DeleteAsync(int id)
{
var product = await _context.Products.FindAsync(id);
if (product == null) return false;
_context.Products.Remove(product);
await _context.SaveChangesAsync();
return true;
}
public async Task<IEnumerable<Product>> GetAllAsync()
{
return await _context.Products
.Include(p => p.Category)
.AsNoTracking()
.ToListAsync();
}
}
マイグレーションによるデータベース管理
マイグレーションの作成と適用
# マイグレーションの作成 dotnet ef migrations add InitialCreate # マイグレーションの適用 dotnet ef database update
マイグレーションコードの例
public partial class AddProductInventory : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "StockQuantity",
table: "Products",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.CreateIndex(
name: "IX_Products_StockQuantity",
table: "Products",
column: "StockQuantity");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "StockQuantity",
table: "Products");
}
}
パフォーマンスを考慮したクエリ最適化
効率的なクエリの実装
public class OptimizedProductRepository : IProductRepository
{
private readonly ApplicationDbContext _context;
// クエリの最適化例
public async Task<IEnumerable<ProductSummary>> GetProductSummariesAsync()
{
return await _context.Products
.AsNoTracking() // 読み取り専用クエリの最適化
.Select(p => new ProductSummary // 必要なプロパティのみ選択
{
Id = p.Id,
Name = p.Name,
Price = p.Price,
CategoryName = p.Category.Name
})
.ToListAsync();
}
// ページング処理の実装
public async Task<(List<Product> Items, int TotalCount)> GetPagedAsync(
int pageNumber,
int pageSize)
{
var query = _context.Products
.Include(p => p.Category)
.AsNoTracking();
var totalCount = await query.CountAsync();
var items = await query
.OrderBy(p => p.Name)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
return (items, totalCount);
}
// 複雑な検索条件の実装
public async Task<IEnumerable<Product>> SearchAsync(ProductSearchCriteria criteria)
{
var query = _context.Products.AsQueryable();
if (!string.IsNullOrEmpty(criteria.Name))
query = query.Where(p => p.Name.Contains(criteria.Name));
if (criteria.MinPrice.HasValue)
query = query.Where(p => p.Price >= criteria.MinPrice.Value);
if (criteria.MaxPrice.HasValue)
query = query.Where(p => p.Price <= criteria.MaxPrice.Value);
return await query
.AsNoTracking()
.ToListAsync();
}
}
インデックスの適切な設定
必要なデータのみの取得
N+1問題の回避
クエリの結果キャッシング
トランザクション管理
public async Task<bool> ProcessOrderAsync(Order order)
{
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
// 注文の登録
await _context.Orders.AddAsync(order);
// 在庫の更新
foreach (var item in order.Items)
{
var product = await _context.Products.FindAsync(item.ProductId);
product.StockQuantity -= item.Quantity;
}
await _context.SaveChangesAsync();
await transaction.CommitAsync();
return true;
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
これらの実装方法を適切に組み合わせることで、効率的で保守性の高いデータベース連携を実現できます。
デプロイメントとCI/CDパイプライン
ASP.NET Coreアプリケーションの効率的なデプロイメントと継続的な開発・デリバリーを実現するための実践的な方法を解説します。
Dockerコンテナを使用した展開方法
Dockerfileの作成
# ビルドステージ FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build WORKDIR /src # プロジェクトファイルのコピーと依存関係の復元 COPY ["MyApp.csproj", "./"] RUN dotnet restore # ソースコードのコピーとビルド COPY . . RUN dotnet build -c Release -o /app/build RUN dotnet publish -c Release -o /app/publish # 実行ステージ FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app COPY --from=build /app/publish . EXPOSE 80 ENTRYPOINT ["dotnet", "MyApp.dll"]
docker-compose.ymlの設定
version: '3.8'
services:
webapp:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:80"
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ConnectionStrings__DefaultConnection=Server=db;Database=MyApp;User=sa;Password=YourPassword;
depends_on:
- db
db:
image: mcr.microsoft.com/mssql/server:2019-latest
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=YourPassword
ports:
- "1433:1433"
volumes:
- dbdata:/var/opt/mssql
volumes:
dbdata:
クラウドプラットフォームへのデプロイ手順
Azure App Serviceへのデプロイ設定
# azure-pipeline.yml
trigger:
- main
variables:
azureSubscription: 'Azure Production'
webAppName: 'myapp-production'
vmImageName: 'ubuntu-latest'
stages:
- stage: Build
displayName: Build stage
jobs:
- job: Build
displayName: Build
pool:
vmImage: $(vmImageName)
steps:
- task: DotNetCoreCLI@2
inputs:
command: 'build'
projects: '**/*.csproj'
arguments: '--configuration Release'
- task: DotNetCoreCLI@2
inputs:
command: 'publish'
publishWebProjects: true
arguments: '--configuration Release --output $(Build.ArtifactStagingDirectory)'
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: '$(Build.ArtifactStagingDirectory)'
artifactName: 'webapp'
環境別の設定管理
{
"Production": {
"ConnectionStrings": {
"DefaultConnection": "Server=prod-db;Database=MyApp;..."
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning"
}
},
"ApplicationInsights": {
"ConnectionString": "your-connection-string"
}
}
}
自動テストと継続的デプロイの構築
自動テストの実装
public class ProductControllerTests
{
private readonly IProductService _mockProductService;
private readonly ProductController _controller;
public ProductControllerTests()
{
_mockProductService = Substitute.For<IProductService>();
_controller = new ProductController(_mockProductService);
}
[Fact]
public async Task Index_ReturnsViewResult_WithListOfProducts()
{
// Arrange
var expectedProducts = new List<Product>
{
new Product { Id = 1, Name = "Test Product" }
};
_mockProductService.GetProductsAsync()
.Returns(expectedProducts);
// Act
var result = await _controller.Index();
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsAssignableFrom<IEnumerable<Product>>(
viewResult.Model);
Assert.Equal(expectedProducts.Count, model.Count());
}
}
CI/CDパイプラインの構築(GitHub Actions)
name: .NET Core CI/CD
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
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-restore --verbosity normal
- name: Publish
run: dotnet publish -c Release -o publish
- name: Deploy to Azure Web Apps
uses: azure/webapps-deploy@v2
with:
app-name: 'myapp-production'
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
package: ./publish
環境別の設定ファイル
シークレット管理
環境変数の活用
モニタリングとログの実装
Application Insightsの詳細設定
public void ConfigureServices(IServiceCollection services)
{
// Application Insightsの高度な設定
services.AddApplicationInsightsTelemetry(options =>
{
options.EnableAdaptiveSampling = true;
options.EnableQuickPulseMetricStream = true;
options.EnableDebugLogger = false;
options.EnableHeartbeat = true;
});
// カスタムテレメトリの設定
services.AddSingleton<ITelemetryInitializer, CustomTelemetryInitializer>();
}
public class CustomTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
telemetry.Context.Cloud.RoleName = "MyWebApp";
telemetry.Context.Component.Version = "1.0.0";
if (telemetry is RequestTelemetry request)
{
request.Properties["CustomProperty"] = "CustomValue";
}
}
}
構造化ログの実装
public class LoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<LoggingMiddleware> _logger;
public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
var startTime = DateTime.UtcNow;
// リクエスト情報のログ
_logger.LogInformation(
"Request {Method} {Path} started at {StartTime}",
context.Request.Method,
context.Request.Path,
startTime);
await _next(context);
// レスポンス情報のログ
var duration = DateTime.UtcNow - startTime;
_logger.LogInformation(
"Request completed with status {StatusCode} in {Duration}ms",
context.Response.StatusCode,
duration.TotalMilliseconds);
}
catch (Exception ex)
{
_logger.LogError(
ex,
"Request failed with error: {ErrorMessage}",
ex.Message);
throw;
}
}
}
カスタムメトリクスの実装
public class MetricsService
{
private readonly TelemetryClient _telemetryClient;
private readonly ILogger<MetricsService> _logger;
public MetricsService(TelemetryClient telemetryClient, ILogger<MetricsService> logger)
{
_telemetryClient = telemetryClient;
_logger = logger;
}
public void TrackDatabaseOperation(string operation, TimeSpan duration, bool success)
{
_telemetryClient.TrackMetric(new MetricTelemetry
{
Name = $"Database.{operation}.Duration",
Value = duration.TotalMilliseconds
});
_telemetryClient.TrackEvent($"Database.{operation}",
new Dictionary<string, string>
{
["Success"] = success.ToString(),
["Duration"] = duration.TotalMilliseconds.ToString()
});
}
}
これらの実装により、以下のような利点が得られます:
リアルタイムのパフォーマンス監視
異常検知の自動化
詳細なログ分析が可能
カスタムメトリクスによる業務指標の追跡
ロールバック戦略の実装
ブルー/グリーンデプロイメントの設定(Azure Pipelines)
stages:
- stage: Deploy
jobs:
- deployment: Deploy
environment: production
strategy:
blueGreen:
# プレプロダクションスロットへのデプロイ
- task: AzureWebApp@1
inputs:
azureSubscription: '$(azureSubscription)'
appName: '$(webAppName)'
slotName: 'staging'
package: '$(Pipeline.Workspace)/drop/**/*.zip'
# テストの実行
- task: PowerShell@2
inputs:
targetType: 'inline'
script: |
$statusCode = (Invoke-WebRequest -Uri "https://$(webAppName)-staging.azurewebsites.net/health").StatusCode
if ($statusCode -ne 200) { throw "Health check failed" }
# トラフィックの切り替え
- task: AzureAppServiceManage@0
inputs:
azureSubscription: '$(azureSubscription)'
action: 'Swap Slot'
webAppName: '$(webAppName)'
sourceSlot: 'staging'
targetSlot: 'production'
カナリアリリースの実装
public class CanaryRoutingMiddleware
{
private readonly RequestDelegate _next;
private const double CanaryPercentage = 0.1; // 10%のトラフィック
public async Task InvokeAsync(HttpContext context)
{
var random = new Random();
var isCanary = random.NextDouble() < CanaryPercentage;
if (isCanary)
{
context.Request.Headers["X-Version"] = "canary";
// カナリアバージョンへルーティング
await RouteToCanary(context);
}
else
{
await _next(context);
}
}
}
自動ロールバックの監視設定
public class HealthMonitorService : BackgroundService
{
private readonly ILogger<HealthMonitorService> _logger;
private readonly IDeploymentService _deploymentService;
private int _failureCount = 0;
private const int FailureThreshold = 3;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var isHealthy = await CheckApplicationHealth();
if (!isHealthy)
{
_failureCount++;
_logger.LogWarning("Health check failed. Failure count: {Count}", _failureCount);
if (_failureCount >= FailureThreshold)
{
_logger.LogError("Health check threshold exceeded. Initiating rollback...");
await _deploymentService.RollbackToLastKnownGoodVersion();
}
}
else
{
_failureCount = 0;
}
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
private async Task<bool> CheckApplicationHealth()
{
try
{
// アプリケーションの健全性チェック
// - API エンドポイントの応答確認
// - データベース接続の確認
// - 重要な依存サービスの状態確認
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Health check failed");
return false;
}
}
}
これらの実装により、以下のような利点が得られます:
無停止でのバージョン切り替え
段階的なトラフィック移行
自動化された障害検知と復旧
リスクを最小限に抑えたデプロイメント
これらの実装により、効率的で安全なデプロイメントプロセスを確立できます。
最後に
ASP.NET Coreは、モダンなWeb開発における強力なフレームワークとして、高いパフォーマンスとセキュリティ、優れた拡張性を提供します。基本的な概念から実践的な実装手法まで、本記事で解説した内容を活用することで、効率的で堅牢なWebアプリケーション開発が可能となります。継続的な学習と実践を通じて、ASP.NET Coreの真価を発揮させましょう。

