آموزش جامع .NET Aspire از صفر تا صد
⚡ خلاصه سریع — اصل کاریها
اینها مهمترین چیزهایی هستن که اگه هیچی ندونی، فقط اینا رو بدونی کافیه:
- نکته ۱: .NET Aspire یک لایه ارکستراسیون و زیرساختمحور برای اپلیکیشنهای ابری و میکروسرویسی در .NET است.
- نکته ۲: AppHost پروژه اصلی Aspire است که منابع را تعریف میکند: پروژهها، کانتینرها، Redis، SQL Server و...
- نکته ۳: ServiceDefaults سرویسها را با Health Check، OpenTelemetry و Resilience استاندارد میکند.
- نکته ۴: WithReference اتصال بین سرویسها را با تزریق خودکار Connection String برقرار میکند.
- نکته ۵: Dashboard هنگام اجرا، وضعیت منابع، لاگها، متریکها و trace ها را نمایش میدهد.
- نکته ۶: Docker Desktop برای اجرای منابع کانتینری محلی مثل Redis و SQL Server لازم است.
- نکته ۷:
dotnet workload install aspireاولین قدم نصب رسمی Aspire است. - نکته ۸: برای شروع میتوانی از قالب
dotnet new aspire-starterاستفاده کنی.
۳ اشتباه رایج که باید ازشون پرهیز کنی:
- اجرا کردن پروژه بدون Docker Desktop و سپس تعجب از خطای Redis/SQL Server.
- فراموش کردن
MapDefaultEndpoints()وقتی ازAddServiceDefaults()استفاده میکنی. - استفاده از پورت ثابت دستی برای سرویسها بهجای اینکه بذاری Aspire پورتها را مدیریت کند.
اگر فقط ۵ دقیقه وقت داری، اینها رو یاد بگیر:
- AppHost همان ارکستراتور اصلی است.
dotnet workload install aspireنصب را انجام بده.dotnet new aspire-starter -n MyApp && dotnet runپروژه را بساز و اجرا کن.
۱. مقدمه
.NET Aspire چیست؟
.NET Aspire یک فریمورک جدید از خانواده .NET است که برای ساخت اپلیکیشنهای توزیعشده، میکروسرویسی و ابرمحور طراحی شده. به زبان ساده، Aspire به شما کمک میکند:
- چند سرویس مختلف را در یک راهحل تعریف کنی.
- وابستگیها مانند Redis، SQL Server، RabbitMQ، PostgreSQL و... را بهصورت خودکار راهاندازی کنی.
- Connection String و سرویسدیسکاوری را بین سرویسها بهصورت خودکار مدیریت کنی.
- یک Dashboard برای مشاهده لاگها، خطاها، متریکها و trace ها داشته باشی.
- در نهایت همه چیز را برای استقرار روی Azure Container Apps یا Kubernetes آماده کنی.
پس Aspire یک "ارکستراتور توسعه و استقرار" است، نه فقط یک کتابخانه.
چرا باید یادش بگیری؟
در دنیای واقعی، وقتی چند سرویس داری، مشکلات زیر پیش میآید:
- هر سرویس باید جداگانه اجرا شود.
- باید Redis، SQL و... دستی با Docker اجرا شوند.
- Connection Stringها باید دستی set شوند.
- دیباگ و ردیابی خطا بین سرویسها سخت است.
Aspire این مشکلات را خیلی کمتر میکند. تجربه توسعه شبیه اجرای یک پروژه واحد میشود، ولی زیرش یک سیستم توزیعشده واقعی است.
کجا استفاده میشود؟
- ساخت میکروسرویسها
- اپلیکیشنهای ابری آماده استقرار در Azure
- سیستمهای رویدادمحور با RabbitMQ یا Kafka
- پروژههای چندسرویسی که از Redis Cache و SQL استفاده میکنند
- تیمهایی که میخواهند توسعه محلی استاندارد و سریع داشته باشند
۲. پیشنیازها
قبل از شروع، اینها را باید داشته باشی یا بدانی:
دانش قبلی
- زبان C# و فریمورک ASP.NET Core
- مفاهیم ابتدایی Dependency Injection
- آشنایی اولیه با Docker
- آشنایی ابتدایی با REST API و Microservices
- آشنایی ابتدایی با Configuration و Connection String در .NET
ابزارهای لازم
| ابزار | چرا لازم است | حداقل نسخه |
|---|---|---|
| .NET SDK | اجرای پروژههای Aspire | 8 یا 9 |
| Docker Desktop | اجرای کانتینرهای Redis، SQL، RabbitMQ | آخرین نسخه |
| Visual Studio 2022 | توسعه راحتتر | 17.9 به بالا |
| یا VS Code + C# Dev Kit | توسعه سبک | آخرین نسخه |
| Git | مدیریت سورس و قالبها | آخرین نسخه |
اگه Docker Desktop روی سیستم نصب نباشد، بخشهایی که نیاز به Redis یا SQL دارند اجرا نمیشوند.
۳. نصب و راهاندازی
گام ۱: نصب Workload رسمی Aspire
ترمینال را باز کن و دستور زیر را بزن:
dotnet workload update
dotnet workload install aspire
با این کار قالبها و ابزارهای CLI مربوط به Aspire نصب میشوند.
گام ۲: ایجاد اولین پروژه
dotnet new aspire-starter -n MyFirstAspireApp
cd MyFirstAspireApp
ساختار پروژه به این شکل است:
MyFirstAspireApp.sln
├── MyFirstAspireApp.AppHost // ارکستراتور اصلی
├── MyFirstAspireApp.ServiceDefaults // تنظیمات مشترک سرویسها
├── MyFirstAspireApp.ApiService // یک Web API ساده
└── MyFirstAspireApp.Web // یک فرانتاند ساده
گام ۳: اجرای پروژه
dotnet run --project MyFirstAspireApp.AppHost
وقتی اجرا کنی:
- AppHost بالا میآید.
- سرویسهای تعریفشده اجرا میشوند.
- Dashboard روی آدرس
https://localhost:15887یاhttp://localhost:15888باز میشود. - میتوانی وضعیت هر منبع را بهصورت زنده ببینی.
گام ۴: بررسی Dashboard
در Dashboard میتوانی ببینی:
- کدام منابع Running هستند.
- لاگ هر سرویس بهصورت زنده
- trace مربوط به درخواستها
- متریکهای CPU و Memory
۴. مفاهیم پایه
AppHost چیست؟
پروژه AppHost همان قلب Aspire است. در فایل Program.cs این پروژه، منابع مختلف را تعریف میکنی:
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("cache");
var api = builder.AddProject<Projects.ApiService>("api")
.WithReference(cache);
builder.Build().Run();
اینجا:
DistributedApplication.CreateBuilderارکستراتور را میسازد.AddRedisیک Redis محلی با Docker میسازد.AddProjectپروژه API را به عنوان یک منبع معرفی میکند.WithReferenceیعنی API به Redis دسترسی دارد.
ServiceDefaults چیست؟
پروژه ServiceDefaults شامل تنظیمات مشترکی است که همه سرویسها استفاده میکنند. در قالب Aspire، این پروژه متدهایی مثل:
AddServiceDefaults()MapDefaultEndpoints()
را در اختیار سرویسها میگذارد.
Resource چیست؟
در Aspire هر چیزی که در AppHost تعریف میکنی یک Resource است:
| نوع Resource | مثال |
|---|---|
| Project | پروژههای ASP.NET Core |
| Container | Redis، SQL Server، RabbitMQ |
| External | سرویسهای بیرونی یا ابری |
| Parameter | رمزها و مقادیر پیکربندی |
WithReference چه کاری میکند؟
وقتی مینویسی:
var api = builder.AddProject<Projects.ApiService>("api")
.WithReference(cache);
Aspire بهصورت خودکار:
- Connection String مربوط به Redis را در Environment Variable سرویس
apiتزریق میکند. - نام پیشفرض متغیر
ConnectionStrings__cacheخواهد بود. - اگر منابع نیاز به صبر کردن داشته باشند، وابستگی را مدیریت میکند.
۵. مثالهای کد ساده
مثال ۱: یک API با Redis Cache
در AppHost:
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("cache");
var api = builder.AddProject<Projects.ApiService>("api")
.WithReference(cache);
builder.Build().Run();
در ApiService باید بسته Aspire Redis Client را نصب کنی:
dotnet add package Aspire.StackExchange.Redis
سپس در Program.cs سرویس:
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
// این متد ConnectionString به نام "cache" را میخواند
builder.AddRedisClient("cache");
var app = builder.Build();
app.MapDefaultEndpoints();
app.MapGet("/", async (IConnectionMultiplexer redis) =>
{
var db = redis.GetDatabase();
var value = await db.StringIncrementAsync("counter");
return Results.Ok(new { visits = value });
});
app.Run();
توضیح:
AddRedisClient("cache")میداند که Connection String ازConnectionStrings__cacheبیاید.- لازم نیست خودت Docker Redis را دستی اجرا کنی.
- لازم نیست Connection String را در
appsettings.jsonبذاری.
مثال ۲: API با SQL Server
در AppHost:
var builder = DistributedApplication.CreateBuilder(args);
var sql = builder.AddSqlServer("sql")
.AddDatabase("shopdb");
var api = builder.AddProject<Projects.ApiService>("api")
.WithReference(sql);
builder.Build().Run();
در ApiService بستهها را نصب کن:
dotnet add package Aspire.Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
در Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
builder.AddSqlServerDbContext<ShopDbContext>("shopdb");
var app = builder.Build();
app.MapDefaultEndpoints();
app.MapGet("/products", async (ShopDbContext db) =>
{
return await db.Products.ToListAsync();
});
app.Run();
توضیح:
AddSqlServerDbContext<ShopDbContext>("shopdb")بهصورت خودکار DbContext را با Connection String درست رجیستر میکند.- دیگر نیازی به پیکربندی دستی رشته اتصال نیست.
۶. مثال واقعی و کاربردی
پروژه: فروشگاه کوچک eShopMini
این پروژه شامل:
- Web به عنوان فرانتاند
- CatalogApi برای مدیریت محصولات
- OrderingApi برای ثبت سفارش
- Redis برای Cache
- SQL Server برای دیتابیس
- RabbitMQ برای پیامرسانی بین سرویسها
گام ۱: ساخت پروژهها
dotnet new webapi -n CatalogApi
dotnet new webapi -n OrderingApi
dotnet new webapp -n Web
dotnet new aspire-starter -n eShopMini
سپس پروژهها را در Solution قرار بده.
گام ۲: تعریف منابع در AppHost
در Program.cs پروژه AppHost:
var builder = DistributedApplication.CreateBuilder(args);
var sql = builder.AddSqlServer("sqlserver")
.WithDataVolume()
.AddDatabase("CatalogDb");
var redis = builder.AddRedis("redis");
var rabbit = builder.AddRabbitMQ("messaging");
var catalogApi = builder.AddProject<Projects.CatalogApi>("catalog-api")
.WithReference(sql)
.WithReference(redis);
var orderingApi = builder.AddProject<Projects.OrderingApi>("ordering-api")
.WithReference(sql)
.WithReference(rabbit);
var web = builder.AddProject<Projects.Web>("web")
.WithExternalHttpEndpoints()
.WithReference(catalogApi)
.WithReference(orderingApi);
builder.Build().Run();
توضیح:
WithDataVolumeیعنی دادههای SQL Server در طول توسعه محلی از بین نروند.AddDatabase("CatalogDb")یک دیتابیس منطقی داخل SQL Server میسازد.WithExternalHttpEndpointsباعث میشود Web از بیرون قابل دسترسی باشد.WithReference(catalogApi)باعث میشود آدرس سرویس کاتالوگ به Web تزریق شود.
گام ۳: پیکربندی CatalogApi
در Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
builder.AddSqlServerDbContext<CatalogDbContext>("CatalogDb");
builder.AddRedisDistributedCache("redis");
var app = builder.Build();
app.MapDefaultEndpoints();
app.MapGet("/products", async (CatalogDbContext db) =>
{
return await db.Products.ToListAsync();
});
app.MapGet("/products/{id}", async (int id, CatalogDbContext db, IDistributedCache cache) =>
{
var cacheKey = $"product:{id}";
var cached = await cache.GetStringAsync(cacheKey);
if (cached is not null)
{
return Results.Ok(System.Text.Json.JsonSerializer.Deserialize<Product>(cached));
}
var product = await db.Products.FindAsync(id);
if (product is null)
{
return Results.NotFound();
}
await cache.SetStringAsync(cacheKey, System.Text.Json.JsonSerializer.Serialize(product));
return Results.Ok(product);
});
app.Run();
گام ۴: پیکربندی OrderingApi
در Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
builder.AddSqlServerDbContext<OrderingDbContext>("CatalogDb");
builder.AddRabbitMQClient("messaging");
var app = builder.Build();
app.MapDefaultEndpoints();
app.MapPost("/orders", async (Order order, OrderingDbContext db, IConnection connection) =>
{
db.Orders.Add(order);
await db.SaveChangesAsync();
await PublishOrderCreatedEvent(connection, order);
return Results.Created($"/orders/{order.Id}", order);
});
app.Run();
این مثال نشان میدهد چطور Aspire وابستگیهای واقعی را بدون پیکربندی دستی زیاد مدیریت میکند.
۷. مباحث پیشرفته
۱. استفاده از Parameter برای رمزها
var password = builder.AddParameter("sql-password", secret: true);
var sql = builder.AddSqlServer("sql", password);
حالا sql-password یک پارامتر امن است و هنگام استقرار قابل تنظیم است.
۲. استفاده از Container دلخواه
var postgres = builder.AddContainer("postgres", "postgres:16")
.WithDataVolume()
.AddDatabase("mydb");
میتوانی هر ایمیج Docker را مستقیماً اضافه کنی.
۳. تعریف Dependency صریح با WaitFor
var api = builder.AddProject<Projects.Api>("api")
.WaitFor(db)
.WithReference(db);
WaitFor تضمین میکند API فقط وقتی شروع شود که دیتابیس آماده است.
۴. مقیاسدهی با WithReplicas
builder.AddProject<Projects.Api>("api")
.WithReplicas(3);
هنگام استقرار روی Azure Container Apps، سه نمونه اجرا میشود.
۵. گرفتن Manifest برای استقرار
dotnet run --project eShopMini.AppHost --publish manifest
این دستور یک فایل Manifest میسازد که ابزارهای استقرار میتوانند مصرف کنند.
۶. استقرار روی Azure
azd init
azd up
Aspire با Azure Developer CLI هماهنگ است و میتواند منابع را در Azure Container Apps بسازد.
۸. اشتباهات رایج
۱. اجرا بدون Docker Desktop
بسیاری از منابع Aspire مثل Redis، SQL Server و RabbitMQ برای اجرا به Docker نیاز دارند. اگر Docker روشن نباشد، خطای مشابه زیر میبینی:
Docker is not running or is not reachable
۲. فراموش کردن MapDefaultEndpoints
اگر AddServiceDefaults() را بزنی ولی MapDefaultEndpoints() را صدا نزنی، Health Check و /health endpoint واقعاً exposed نمیشوند.
۳. استفاده از پورت ثابت دستی
builder.AddProject<Projects.Api>("api")
.WithEndpoint("http", port: 5000);
چند سرویس ممکن است روی پورت 5000 تداخل پیدا کنند. بهتر است بگذاری Aspire پورتها را مدیریت کند.
۴. اشتباه گرفتن نام Connection String
وقتی مینویسی:
builder.AddProject<Projects.Api>("api")
.WithReference(redis);
نام متغیر محیطی ConnectionStrings__redis است. اگر در سرویس بنویسی AddRedisClient("cache")، کار نمیکند چون نام cache با نام منبع redis یکی نیست.
۵. فراموش کردن Reference پروژه
برای استفاده از Projects.Api در AppHost باید پروژه AppHost به آن پروژه Reference داشته باشد. اگر افزوده نشود، نوع Projects ساخته نمیشود.
۶. استفاده بیش از حد از WithExternalHttpEndpoints
همه سرویسها لازم نیست از بیرون قابل دسترسی باشند. فقط Web یا BFF معمولاً باید external باشند.
۹. بهترین روشها (Best Practices)
۱. تنظیمات مشترک را در ServiceDefaults بگذار
بهجای اینکه هر سرویس Health Check و OpenTelemetry را جداگانه پیکربندی کند، از AddServiceDefaults() استفاده کن.
۲. AppHost را ساده نگه دار
فقط تعریف منابع و روابط را در AppHost بنویس. منطق تجاری را داخل سرویسها نگه دار.
۳. نامگذاری معنیدار برای منابع
redisshopdbcatalog-api
این نامها در Dashboard و متغیرهای محیطی استفاده میشوند.
۴. همیشه از WithReference استفاده کن
باگترین بخش میکروسرویسها Connection String است. بذار Aspire آن را مدیریت کند.
۵. برای منابع Stateful از WithDataVolume استفاده کن
SQL Server یا PostgreSQL محلی باید دیتای خود را بین اجراهای مختلف حفظ کنند.
۶. رمزها را با AddParameter نگه دار
رمزها را مستقیم در کد AppHost قرار نده.
۷. از Health Check استاندارد استفاده کن
با MapDefaultEndpoints() سرویسهایت یک مسیر /health و /alive دارند.
۸. وابستگیها را با WaitFor مشخص کن
اگر یکی از سرویسها باید بعد از دیگری اجرا شود، از WaitFor استفاده کن.
۱۰. منابع و ادامه مسیر
| منبع | آدرس |
|---|---|
| مستندات رسمی .NET Aspire | https://learn.microsoft.com/dotnet/aspire |
| مخزن نمونهها | https://github.com/dotnet/aspire-samples |
| پروژه eShop | https://github.com/dotnet/eshop |
| .NET Aspire Community Toolkit | https://github.com/CommunityToolkit/Aspire |
پیشنهاد میکنم بعد از این آموزش:
- پروژه
aspire-starterرا بساز و اجرا کن. - یک Redis و SQL Server به آن اضافه کن.
- پروژه eShop را clone کن و بررسی کن.
- یک Manifest خروجی بگیر.
- با
azd upروی Azure استقرارش بده.
۱۱. مرجع کامل توابع و متدها (API Deep Dive)
📌 نام متد: AddServiceDefaults()
امضای متد (Signature):
public static IHostApplicationBuilder AddServiceDefaults(
this IHostApplicationBuilder builder)
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| builder | IHostApplicationBuilder | بله | Application builder جاری برای پیکربندی سرویس | builder |
مقدار برگشتی (Return Value):
- نوع:
IHostApplicationBuilder - همان builder ورودی را برمیگرداند تا بتوانی متدهای بعدی را زنجیرهای صدا بزنی.
- همیشه مقدار میدهد، مگر اینکه پیکربندی داخلی خطا بدهد.
کاری که انجام میده (گام به گام):
۱. سرویسدیسکاوری را اضافه میکند.
۲. Health Check های پیشفرض را ثبت میکند.
۳. OpenTelemetry را برای Logging، Metrics و Tracing پیکربندی میکند.
۴. HttpClient را با Resilience استاندارد پیکربندی میکند.
۵. تنظیمات محلی توسعه را ساده میکند.
مثال ساده و قابل اجرا:
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
var app = builder.Build();
app.MapDefaultEndpoints();
app.MapGet("/", () => "Hello from API");
app.Run();
خروجی هنگام اجرا:
Now listening on: http://localhost:5000
مثال واقعی و کاربردی:
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
builder.AddRedisClient("redis");
builder.AddSqlServerDbContext<ShopDbContext>("shopdb");
var app = builder.Build();
app.MapDefaultEndpoints();
app.MapControllers();
app.Run();
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
| InvalidOperationException | اگر سرویسدیسکاوری بدون پیکربندی مناسب اجرا شود | از قالب ServiceDefaults استفاده کن |
| ConfigurationException | اگر OpenTelemetry endpoint نامعتبر باشد | آدرس telemetry را بررسی کن |
Overloadها:
این متد در قالب ServiceDefaults فقط یک امضا دارد، اما میتوانی متدهای سفارشی خودت را بسازی.
اشتباهات رایج:
- ❌ صدا زدن
AddServiceDefaultsبعد ازBuild() - ✅ آن را قبل از ساخت
appصدا بزن - ❌ استفاده از
AddServiceDefaultsبدونMapDefaultEndpoints - ✅ این دو را با هم استفاده کن
متدهای مرتبط:
MapDefaultEndpoints()AddRedisClient()AddSqlServerDbContext()
📌 نام متد: MapDefaultEndpoints()
امضای متد (Signature):
public static WebApplication MapDefaultEndpoints(
this WebApplication app)
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| app | WebApplication | بله | اپلیکیشن ساختهشده که میخواهیم endpoints را اضافه کنیم | app |
مقدار برگشتی (Return Value):
- نوع:
WebApplication - همان
appورودی را برمیگرداند تا endpointهای بیشتری اضافه کنی.
کاری که انجام میده (گام به گام):
۱. endpoint مربوط به سلامت (/health) را ثبت میکند.
۲. endpoint مربوط به زنده بودن (/alive) را ثبت میکند.
۳. این endpointها را برای استفاده Dashboard و Orchestrator آماده میکند.
مثال ساده و قابل اجرا:
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
var app = builder.Build();
app.MapDefaultEndpoints();
app.Run();
خروجی با curl:
curl http://localhost:5000/health
Healthy
مثال واقعی و کاربردی:
در همه سرویسهای واقعی Aspire باید این متد را بعد از Build() فراخوانی کنی:
app.MapDefaultEndpoints();
app.MapControllers();
app.Run();
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
| InvalidOperationException | اگر متد بعد از Run() صدا زده شود |
آن را قبل از Run() فراخوانی کن |
| NullReferenceException | اگر app null باشد |
از Pipeline استاندارد استفاده کن |
Overloadها:
فقط یک امضا دارد.
اشتباهات رایج:
- ❌ فراموش کردن این متد وقتی از
AddServiceDefaults()استفاده میکنی - ✅ همیشه هر دو را با هم استفاده کن
- ❌ اضافه کردن endpoint سفارشی به نام
/healthبعد از این متد - ✅ از نامهای دیگر برای endpointهای سفارشی استفاده کن
متدهای مرتبط:
AddServiceDefaults()MapGet()MapControllers()
📌 نام متد: AddProject<TProject>()
امضای متد (Signature):
public static IResourceBuilder<ProjectResource> AddProject<TProject>(
this IDistributedApplicationBuilder builder,
string name)
where TProject : IProjectMetadata, new()
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| builder | IDistributedApplicationBuilder | بله | builder ارکستراتور Aspire | builder |
| name | string | بله | نام منطقی یکتا برای پروژه | "catalog-api" |
| TProject | نوع جنریک | بله | نوع تولیدشده از پروژه، معمولاً از Projects |
Projects.CatalogApi |
مقدار برگشتی (Return Value):
- نوع:
IResourceBuilder<ProjectResource> - builder مربوط به منبع پروژه را میدهد تا بتوانی
WithReferenceو غیره را صدا بزنی.
کاری که انجام میده (گام به گام):
۱. متادیتای پروژه را از TProject میخواند.
۲. یک ProjectResource جدید میسازد.
۳. آن را در مدل ارکستراتور ثبت میکند.
۴. builder قابل پیکربندی را برمیگرداند.
مثال ساده و قابل اجرا:
var api = builder.AddProject<Projects.ApiService>("api");
builder.Build().Run();
خروجی در Dashboard: یک منبع Project به نام api با وضعیت Running دیده میشود.
مثال واقعی و کاربردی:
var api = builder.AddProject<Projects.CatalogApi>("catalog-api")
.WithReference(db)
.WithReference(redis)
.WithReplicas(3);
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
| ArgumentException | اگر نام تکراری باشد | نام یکتا انتخاب کن |
| InvalidOperationException | اگر TProject متادیتای معتبر نداشته باشد |
مطمئن شو پروژه Reference شده است |
| FileNotFoundException | اگر فایل پروژه یافت نشود | مسیر پروژه را بررسی کن |
Overloadها:
در نسخههای اخیر، overload با Action<ProjectResourceOptions> هم وجود دارد:
builder.AddProject<Projects.Api>("api", options =>
{
// تنظیمات اضافه پروژه
});
اشتباهات رایج:
- ❌ فراموش کردن Reference از AppHost به پروژه موردنظر
- ✅ پروژه را به AppHost اضافه کن
- ❌ استفاده از نام یکسان برای دو منبع
- ✅ نامهای یکتا و معنیدار بده
متدهای مرتبط:
WithReference()WithEndpoint()WaitFor()WithReplicas()
📌 نام متد: AddRedis()
امضای متد (Signature):
public static IResourceBuilder<RedisResource> AddRedis(
this IDistributedApplicationBuilder builder,
string name,
int? port = null,
string? connectionString = null)
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| builder | IDistributedApplicationBuilder | بله | builder ارکستراتور | builder |
| name | string | بله | نام منطقی منبع | "redis" |
| port | int? | خیر | پورت مشخص برای Redis محلی | 6379 |
| connectionString | string? | خیر | اگر داده شود، Redis موجود استفاده میشود بهجای ساخت کانتینر | "localhost:6379" |
مقدار برگشتی (Return Value):
- نوع:
IResourceBuilder<RedisResource> - builder منبع Redis برای استفاده در
WithReference.
کاری که انجام میده (گام به گام):
۱. اگر connectionString داده شود، Redis را به عنوان منبع External ثبت میکند.
۲. در غیر این صورت، یک کانتینر Redis با Docker میسازد.
۳. آن را در مدل ارکستراتور ثبت میکند.
۴. builder را برای پیکربندی بیشتر برمیگرداند.
مثال ساده و قابل اجرا:
var redis = builder.AddRedis("redis");
var api = builder.AddProject<Projects.Api>("api")
.WithReference(redis);
builder.Build().Run();
خروجی در Dashboard: یک منبع Container با نام redis بالا میآید.
مثال واقعی و کاربردی:
استفاده از Redis خارجی:
var redis = builder.AddRedis("redis",
connectionString: builder.Configuration["Redis:ConnectionString"]);
builder.AddProject<Projects.Api>("api")
.WithReference(redis);
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
| ArgumentException | نام تکراری | نام یکتا بده |
| DockerException | اگر Docker در دسترس نباشد | Docker Desktop را اجرا کن |
| InvalidOperationException | اگر port مشخص درگیر باشد | پورت دیگری بده یا خالی بگذار |
Overloadها:
AddRedis(name);
AddRedis(name, port);
AddRedis(name, connectionString);
AddRedis(name, port, connectionString);
اشتباهات رایج:
- ❌ استفاده از
AddRedis("cache")و سپسAddRedisClient("redis")در سرویس - ✅ نامها را یکسان نگه دار
- ❌ اجرا بدون Docker و انتظار Redis محلی
- ✅ Docker Desktop را روشن کن
متدهای مرتبط:
WithReference()AddRedisClient()AddRedisDistributedCache()
📌 نام متد: AddSqlServer()
امضای متد (Signature):
public static IResourceBuilder<SqlServerServerResource> AddSqlServer(
this IDistributedApplicationBuilder builder,
string name,
string? password = null,
int? port = null,
string? connectionString = null)
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| builder | IDistributedApplicationBuilder | بله | builder ارکستراتور | builder |
| name | string | بله | نام منطقی SQL Server | "sqlserver" |
| password | string? | خیر | رمز عبور کاربر SA | "P@ssw0rd" |
| port | int? | خیر | پورت محلی مشخص | 1433 |
| connectionString | string? | خیر | اتصال به SQL Server موجود | "Server=...;..." |
مقدار برگشتی (Return Value):
- نوع:
IResourceBuilder<SqlServerServerResource> - builder مربوط به SQL Server که میتوانی
AddDatabaseرا روی آن صدا بزنی.
کاری که انجام میده (گام به گام):
۱. اگر connectionString داده شود، از SQL Server موجود استفاده میکند.
۲. در غیر این صورت کانتینر SQL Server با Docker میسازد.
۳. در صورت نیاز یک Database منطقی با AddDatabase اضافه میکند.
۴. Connection String مناسب را در اختیار سرویسها میگذارد.
مثال ساده و قابل اجرا:
var sql = builder.AddSqlServer("sql")
.AddDatabase("shopdb");
var api = builder.AddProject<Projects.Api>("api")
.WithReference(sql);
builder.Build().Run();
خروجی در Dashboard: SQL Server و Database زیر آن نمایش داده میشود.
مثال واقعی و کاربردی:
var password = builder.AddParameter("sql-password", secret: true);
var sql = builder.AddSqlServer("sql", password)
.WithDataVolume();
var db = sql.AddDatabase("CatalogDb");
builder.AddProject<Projects.CatalogApi>("catalog-api")
.WithReference(db);
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
| ArgumentException | نام تکراری یا رمز نامعتبر | نام یکتا و رمز معتبر بده |
| DockerException | Docker در دسترس نیست | Docker Desktop را روشن کن |
| InvalidOperationException | AddDatabase قبل از ساخت server صدا زده شود |
از همان builder استفاده کن |
Overloadها:
AddSqlServer(name);
AddSqlServer(name, password);
AddSqlServer(name, password, port);
AddSqlServer(name, connectionString: "...");
اشتباهات رایج:
- ❌ فراموش کردن
AddDatabaseو استفاده ازWithReference(sql) - ✅ اگر میخواهی DbContext بگیری، باید Database را reference کنی
- ❌ استفاده از رمز ضعیف که SQL Server قبول نکند
- ✅ رمز با حروف بزرگ، کوچک، عدد و علامت استفاده کن
متدهای مرتبط:
AddDatabase()WithDataVolume()AddSqlServerDbContext()
📌 نام متد: WithReference()
امضای متد (Signature):
public static IResourceBuilder<TDestination> WithReference<TDestination>(
this IResourceBuilder<TDestination> builder,
IResourceBuilder<IResourceWithConnectionString> source,
string? connectionName = null,
bool optional = false)
where TDestination : IResourceWithEnvironment
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| builder | IResourceBuilder |
بله | منبع مقصد که باید Connection String بگیرد | api |
| source | IResourceBuilder |
بله | منبع مبدأ که Connection String دارد | redis |
| connectionName | string? | خیر | نام سفارشی برای متغیر Connection String | "MyCache" |
| optional | bool | خیر | اگر true باشد، نبود منبع باعث خطا نمیشود | true |
مقدار برگشتی (Return Value):
- نوع:
IResourceBuilder<TDestination> - همان builder مقصد را میدهد تا بتوانی پیکربندی ادامه بدهی.
کاری که انجام میده (گام به گام):
۱. بررسی میکند که منبع مبدأ Connection String دارد.
۲. نام متغیر محیطی را تعیین میکند.
۳. Connection String را به Environment Variable مقصد تزریق میکند.
۴. وابستگی ضمنی بین منابع را ثبت میکند.
مثال ساده و قابل اجرا:
var redis = builder.AddRedis("redis");
var api = builder.AddProject<Projects.Api>("api")
.WithReference(redis);
builder.Build().Run();
خروجی در سرویس API بهصورت متغیر محیطی:
ConnectionStrings__redis=localhost:6379
مثال واقعی و کاربردی:
var legacyDb = builder.AddConnectionString("LegacyDb", "Server=legacy;Database=Old;...");
builder.AddProject<Projects.Migration>("migration")
.WithReference(legacyDb, "LegacyDatabase", optional: true);
در این حالت سرویس Migration از متغیر ConnectionStrings__LegacyDatabase استفاده میکند.
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
| ArgumentException | اگر منبع Connection String نداشته باشد | منبع مناسب بده |
| InvalidOperationException | اگر connectionName تکراری باشد | نام یکتا بده |
| DistributedApplicationException | اگر optional نباشد و منبع در دسترس نباشد | optional را true کن یا منبع را درست کن |
Overloadها:
WithReference(source);
WithReference(source, connectionName);
WithReference(source, connectionName, optional);
اشتباهات رایج:
- ❌ فکر کردن
WithReferenceفقط dependency میسازد و Connection String تزریق نمیکند - ✅
WithReferenceهم dependency و هم Connection String را مدیریت میکند - ❌ استفاده از نام منبع اشتباه در
AddRedisClient("wrongName") - ✅ نام را با
connectionNameیا نام منبع هماهنگ کن
متدهای مرتبط:
WaitFor()WithEnvironment()AddConnectionString()
📌 نام متد: WaitFor()
امضای متد (Signature):
public static IResourceBuilder<T> WaitFor<T>(
this IResourceBuilder<T> builder,
IResourceBuilder<IResource> resource)
where T : IResource
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| builder | IResourceBuilder |
بله | منبعی که باید منتظر بماند | api |
| resource | IResourceBuilder |
بله | منبعی که باید آماده شود | db |
مقدار برگشتی (Return Value):
- نوع:
IResourceBuilder<T> - همان builder مقصد را میدهد.
کاری که انجام میده (گام به گام):
۱. بررسی میکند که منبع مقصد و مبدأ معتبر باشند.
۲. وابستگی استارتآپ را ثبت میکند.
۳. هنگام اجرا، مقصد فقط بعد از Ready شدن مبدأ شروع میشود.
مثال ساده و قابل اجرا:
var db = builder.AddSqlServer("sql").AddDatabase("shopdb");
var api = builder.AddProject<Projects.Api>("api")
.WaitFor(db)
.WithReference(db);
builder.Build().Run();
مثال واقعی و کاربردی:
var migrator = builder.AddProject<Projects.MigrationService>("migrator")
.WaitFor(db)
.WithReference(db);
در اینجا Migration Service فقط بعد از آمادهشدن دیتابیس اجرا میشود.
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
| ArgumentException | اگر منبع به خودش وابسته باشد | منبع دیگری بده |
| InvalidOperationException | اگر وابستگی چرخهای ایجاد شود | گراف وابستگی را ساده کن |
Overloadها:
در برخی نسخهها متد WaitForCompletion برای انتظار برای خاتمه یک Job وجود دارد.
اشتباهات رایج:
- ❌ استفاده از
WaitForبهجایWithReferenceبرای تزریق Connection String - ✅
WithReferenceرا هم صدا بزن - ❌ استفاده از
WaitForبرای منابعی که Health Check ندارند و همیشه Running هستند - ✅ برای ترتیب استارتآپ دقیق از Health Check استفاده کن
متدهای مرتبط:
WithReference()WithHealthCheck()
📌 نام متد: WithReplicas()
امضای متد (Signature):
public static IResourceBuilder<T> WithReplicas<T>(
this IResourceBuilder<T> builder,
int replicas)
where T : IResource
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| builder | IResourceBuilder |
بله | منبعی که باید مقیاسپذیر شود | api |
| replicas | int | بله | تعداد نمونههای موردنیاز | 3 |
مقدار برگشتی (Return Value):
- نوع:
IResourceBuilder<T> - همان builder را میدهد.
کاری که انجام میده (گام به گام):
۱. تعداد replicas را در مدل منبع ذخیره میکند.
۲. هنگام استقرار روی Azure Container Apps یا Kubernetes، این تعداد اعمال میشود.
۳. در حالت محلی معمولاً یک نمونه اجرا میشود.
مثال ساده و قابل اجرا:
var api = builder.AddProject<Projects.Api>("api")
.WithReplicas(3);
builder.Build().Run();
مثال واقعی و کاربردی:
builder.AddProject<Projects.OrderingApi>("ordering-api")
.WithReplicas(5);
هنگام azd up، سرویس OrderingApi با پنج نمونه مستقر میشود.
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
| ArgumentOutOfRangeException | اگر replicas منفی باشد | عدد نامنفی بده |
Overloadها:
فقط یک امضا دارد.
اشتباهات رایج:
- ❌ فکر کردن که
WithReplicasدر اجرای محلی چند نمونه میسازد - ✅ در اجرای محلی معمولاً یک نمونه است؛ مقیاسدهی برای Deployment است
- ❌ استفاده از
WithReplicasبرای دیتابیس - ✅ دیتابیسها معمولاً نباید به این صورت مقیاس شوند
متدهای مرتبط:
AddProject()PublishAsAzureContainerApp()
📌 نام متد: WithEndpoint()
امضای متد (Signature):
public static IResourceBuilder<T> WithEndpoint<T>(
this IResourceBuilder<T> builder,
string name,
int? port = null,
bool isProxied = true,
bool isExternal = false,
string? targetPort = null)
where T : IResourceWithEndpoints
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| builder | IResourceBuilder |
بله | منبع موردنظر | api |
| name | string | بله | نام endpoint | "public-http" |
| port | int? | خیر | پورت مشخص | 8080 |
| isProxied | bool | خیر | آیا از طریق پروکسی Aspire در دسترس باشد | true |
| isExternal | bool | خیر | آیا از بیرون قابل دسترسی باشد | false |
| targetPort | string? | خیر | پورت داخلی هدف در کانتینر | "5000" |
مقدار برگشتی (Return Value):
- نوع:
IResourceBuilder<T> - همان builder را برمیگرداند.
کاری که انجام میده (گام به گام):
۱. یک endpoint با نام مشخص میسازد.
۲. مشخص میکند داخلی یا خارجی باشد.
۳. در صورت نیاز پورت اختصاص میدهد.
۴. در orchestration و Service Discovery ثبت میشود.
مثال ساده و قابل اجرا:
builder.AddProject<Projects.Api>("api")
.WithEndpoint("http", port: 5075);
مثال واقعی و کاربردی:
builder.AddProject<Projects.Web>("web")
.WithEndpoint("public", isExternal: true, targetPort: "8080");
در این حالت Web از بیرون قابل دسترسی است.
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
| ArgumentException | اگر نام endpoint تکراری باشد | نام یکتا بده |
| InvalidOperationException | اگر پورت نامعتبر باشد | پورت معتبر بده |
Overloadها:
در برخی نسخهها overload با Action<EndpointOptions> وجود دارد.
اشتباهات رایج:
- ❌ استفاده از
WithEndpointبرای منبعی که endpoint ندارد - ✅ فقط برای Project و Container مناسب است
- ❌ تنظیم
isExternal = trueبرای API داخلی - ✅ فقط Web یا BFF را
isExternalکن
متدهای مرتبط:
WithExternalHttpEndpoints()WithReference()WithServiceBinding()
۱۲. مقایسه کلاسها/توابع مشابه
| روش | هدف اصلی | تزریق Connection String | ایجاد وابستگی | مناسب برای |
|---|---|---|---|---|
WithReference() |
اتصال بین منابع | بله | بله، بهصورت ضمنی | بیشتر ارتباطها |
WaitFor() |
ترتیب استارتآپ | خیر | بله، صریح | وقتی فقط ترتیب مهم است |
WithEnvironment() |
تزریق متغیر محیطی ساده | خیر | خیر | مقادیر ساده |
AddConnectionString() |
تعریف رشته اتصال دستی | بله | خیر | سرویسهای خارجی |
AddParameter() |
تعریف پارامتر امن | خیر | خیر | رمزها و مقادیر حساس |
WithExternalHttpEndpoints() |
دسترسی بیرونی | خیر | خیر | فرانتاند یا API عمومی |
WithEndpoint() |
تعریف دقیق یک endpoint | خیر | خیر | کنترل پورت و دسترسی |
قانون سرانگشتی
- اگر منبع Connection String دارد و مقصد باید متصل شود →
WithReference - اگر فقط میخواهی صبر کند تا منبع آماده شود →
WaitFor - اگر مقدار ساده مثل
API_KEYرا میخواهی تزریق کنی →WithEnvironment - اگر رمز یا مقدار امن داری →
AddParameter
۱۳. سناریوهای واقعی (Real-World Scenarios)
سناریو ۱: میکروسرویس فروشگاهی
var builder = DistributedApplication.CreateBuilder(args);
var sql = builder.AddSqlServer("sql")
.WithDataVolume()
.AddDatabase("ShopDb");
var redis = builder.AddRedis("redis");
var rabbit = builder.AddRabbitMQ("messaging");
var catalogApi = builder.AddProject<Projects.CatalogApi>("catalog-api")
.WithReference(sql)
.WithReference(redis);
var orderingApi = builder.AddProject<Projects.OrderingApi>("ordering-api")
.WithReference(sql)
.WithReference(rabbit);
var web = builder.AddProject<Projects.Web>("web")
.WithExternalHttpEndpoints()
.WithReference(catalogApi)
.WithReference(orderingApi);
builder.Build().Run();
سناریو ۲: استفاده از منابع ابری بهجای کانتینر محلی
var sql = builder.AddConnectionString("CatalogDb",
"Server=mycloudsql.database.windows.net;Database=Catalog;...");
var redis = builder.AddConnectionString("redis",
"mycloudredis.redis.cache.windows.net:6379,password=...");
var api = builder.AddProject<Projects.Api>("api")
.WithReference(sql)
.WithReference(redis);
builder.Build().Run();
سناریو ۳: پروژه Migration برای دیتابیس
var db = builder.AddSqlServer("sql")
.WithDataVolume()
.AddDatabase("ShopDb");
var migrator = builder.AddProject<Projects.MigrationService>("migrator")
.WaitFor(db)
.WithReference(db);
var api = builder.AddProject<Projects.Api>("api")
.WaitFor(migrator)
.WithReference(db);
builder.Build().Run();
سناریو ۴: استقرار روی Azure Container Apps
azd init
azd up
Aspire Manifest بهصورت خودکار منابع را به Azure Container Apps تبدیل میکند.
۱۴. کارایی و Performance
۱. Health Check استاندارد
AddServiceDefaults() و MapDefaultEndpoints() باعث میشود سیستم از ارسال ترافیک به سرویس ناسالم جلوگیری کند.
۲. Resilience پیشفرض
با AddServiceDefaults()، HttpClient های شما بهصورت خودکار Retry و Timeout بهتری دارند.
۳. جلوگیری از Cold Start غیرضروری با WaitFor
وقتی از WaitFor و Health Check استفاده کنی، سرویسها قبل از آمادگی کامل درخواست نمیگیرند.
۴. مدیریت اتصال به Redis
بستههای Aspire مثل AddRedisClient() از Connection Multiplexing استفاده میکنند و بازدهی بهتری در سناریوهای پرترافیک دارند.
۵. استفاده از WithDataVolume در توسعه
این کار باعث میشود دیتای SQL Server یا PostgreSQL در هر اجرا از بین نرود و سرعت توسعه بهتر باشد.
۶. مقیاسدهی با WithReplicas
برای سرویسهای Stateless میتوانی تعداد replicas را بالا ببری تا throughput بیشتری داشته باشی.
۷. کاهش تاخیر با Service Discovery
Aspire بهجای آدرسهای سخت، از نامهای منطقی استفاده میکند و سرویسدیسکاوری بهصورت خودکار آدرسها را رزولوشن میکند.
۸. غیرفعال کردن Dashboard در Production
در Production معمولاً Dashboard را غیرفعال میکنند تا منابع اضافی مصرف نشود:
builder.ConfigureAspireDashboard(disabled: true);
۱۵. چکلیست یادگیری
سطح مبتدی
- .NET 8 یا 9 SDK نصب شده است.
- Docker Desktop نصب و اجرا شده است.
-
dotnet workload install aspireرا اجرا کردهای. - پروژه با
dotnet new aspire-starterساختهای. - AppHost را اجرا کردهای و Dashboard را دیدهای.
- فایل
Program.csدر AppHost را میفهمی.
سطح متوسط
- یک Redis به AppHost اضافه کردهای.
- یک SQL Server و Database اضافه کردهای.
- از
WithReferenceبرای اتصال منابع استفاده کردهای. - از
WithExternalHttpEndpointsبرای Web استفاده کردهای. -
AddServiceDefaultsوMapDefaultEndpointsرا در سرویسها استفاده کردهای. - Connection String خودکار را در سرویس مصرف کردهای.
سطح پیشرفته
- از
AddParameterبرای رمزها استفاده کردهای. - از
WaitForبرای ترتیب استارتآپ استفاده کردهای. - از
WithDataVolumeبرای دیتابیس محلی استفاده کردهای. - با
WithReplicasمقیاسدهی را پیکربندی کردهای. - Manifest خروجی گرفتهای.
- با
azd upروی Azure استقرار دادهای.
سطح حرفهای
- یک Custom Resource نوشتهای.
- با OpenTelemetry و Trace کار کردهای.
- پروژه eShop را بررسی کردهای.
- استقرار Kubernetes یا ACA با Aspire را انجام دادهای.