[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-microsoft-migrating-mvc-validation":3,"mdc-936880-key":35,"related-repo-microsoft-migrating-mvc-validation":2014,"related-org-microsoft-migrating-mvc-validation":2112},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":25,"repoUrl":26,"updatedAt":27,"license":28,"forks":29,"topics":30,"repo":31,"sourceUrl":33,"mdContent":34},"migrating-mvc-validation","migrate MVC validation to ASP.NET Core","Migrates ASP.NET Framework validation to ASP.NET Core including data annotations, custom ValidationAttribute classes, ModelState handling, client-side unobtrusive validation, and FluentValidation integration. Use when upgrading projects that use DataAnnotations, IValidatableObject, RemoteAttribute, custom validation attributes with service dependencies, jquery.validate.unobtrusive, or FluentValidation. Also triggers for ModelState.IsValid migration, ApiController automatic 400 responses, ValidationProblemDetails, and client-side validation script setup in Razor views.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"microsoft","Microsoft","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fmicrosoft.png",[12,16,19,22],{"name":13,"slug":14,"type":15},"Validation","validation","tag",{"name":17,"slug":18,"type":15},".NET","net",{"name":20,"slug":21,"type":15},"ASP.NET Core","asp-net-core",{"name":23,"slug":24,"type":15},"Migration","migration",7,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fupgrade-agent-plugins","2026-07-03T16:30:58.332866",null,1,[],{"repoUrl":26,"stars":25,"forks":29,"topics":32,"description":28},[],"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fupgrade-agent-plugins\u002Ftree\u002FHEAD\u002Fplugins\u002Fupgrade-agent\u002Fextenders\u002Fupgrade-dotnet\u002Fupgrade\u002Fskills\u002Flazy\u002Fweb\u002Fmvc\u002Fmigrating-mvc-validation","---\r\nname: migrating-mvc-validation\r\ndescription: >\r\n  Migrates ASP.NET Framework validation to ASP.NET Core including data annotations, custom\r\n  ValidationAttribute classes, ModelState handling, client-side unobtrusive validation, and\r\n  FluentValidation integration. Use when upgrading projects that use DataAnnotations,\r\n  IValidatableObject, RemoteAttribute, custom validation attributes with service dependencies,\r\n  jquery.validate.unobtrusive, or FluentValidation. Also triggers for ModelState.IsValid migration,\r\n  ApiController automatic 400 responses, ValidationProblemDetails, and client-side validation\r\n  script setup in Razor views.\r\nmetadata:\r\n  traits: .NET|CSharp|VisualBasic|DotNetCore\r\n  discovery: lazy\r\n---\r\n\r\n# ASP.NET MVC Validation Migration\r\n\r\n## Overview\r\n\r\nMigrate validation logic from ASP.NET Framework to ASP.NET Core. Most `DataAnnotations` attributes transfer directly, but custom validators with service dependencies, client-side validation infrastructure, `[Remote]` attribute configuration, and `[ApiController]` automatic model validation require targeted changes.\r\n\r\n## Workflow\r\n\r\nTrack progress across these steps:\r\n\r\n```\r\nMigration Progress:\r\n- [ ] Step 1: Audit validation usage\r\n- [ ] Step 2: Migrate data annotations and custom attributes\r\n- [ ] Step 3: Update ModelState handling for API controllers\r\n- [ ] Step 4: Migrate client-side validation scripts\r\n- [ ] Step 5: Migrate Remote validation\r\n- [ ] Step 6: Migrate FluentValidation integration\r\n- [ ] Step 7: Verify validation behavior\r\n```\r\n\r\n### Step 1: Audit Validation Usage\r\n\r\nSearch the project for validation patterns to determine which steps apply:\r\n\r\n| Signal | Search pattern | Indicates |\r\n|--------|---------------|-----------|\r\n| Data annotations | `using System.ComponentModel.DataAnnotations` | Step 2 |\r\n| Custom validators | `: ValidationAttribute` | Step 2 |\r\n| IValidatableObject | `: IValidatableObject` | Step 2 (compatible) |\r\n| ModelState in APIs | `ModelState.IsValid` in `ApiController` classes | Step 3 |\r\n| Client-side validation | `jquery.validate`, `jquery.validate.unobtrusive` | Step 4 |\r\n| Remote validation | `[Remote(` | Step 5 |\r\n| FluentValidation | `AbstractValidator\u003C`, `FluentValidation` | Step 6 |\r\n\r\nSkip steps that have no matching signals.\r\n\r\n### Step 2: Migrate Data Annotations and Custom Attributes\r\n\r\n**Standard data annotations** (`[Required]`, `[StringLength]`, `[Range]`, `[RegularExpression]`, `[Compare]`, `[EmailAddress]`) work identically in ASP.NET Core. No changes needed.\r\n\r\n**`IValidatableObject`** is fully compatible. No changes needed.\r\n\r\n**Custom `ValidationAttribute` subclasses** that override `IsValid(object, ValidationContext)` are compatible, but service resolution changed:\r\n\r\nBefore (ASP.NET Framework — service locator via `DependencyResolver`):\r\n\r\n```csharp\r\npublic class UniqueEmailAttribute : ValidationAttribute\r\n{\r\n    protected override ValidationResult IsValid(object value, ValidationContext validationContext)\r\n    {\r\n        var userService = DependencyResolver.Current.GetService\u003CIUserService>();\r\n        if (userService.EmailExists((string)value))\r\n        {\r\n            return new ValidationResult(ErrorMessage);\r\n        }\r\n        return ValidationResult.Success;\r\n    }\r\n}\r\n```\r\n\r\nAfter (ASP.NET Core — services via `ValidationContext.GetService`):\r\n\r\n```csharp\r\npublic class UniqueEmailAttribute : ValidationAttribute\r\n{\r\n    protected override ValidationResult IsValid(object value, ValidationContext validationContext)\r\n    {\r\n        var userService = (IUserService)validationContext.GetService(typeof(IUserService));\r\n        if (userService.EmailExists((string)value))\r\n        {\r\n            return new ValidationResult(ErrorMessage);\r\n        }\r\n        return ValidationResult.Success;\r\n    }\r\n}\r\n```\r\n\r\nThe key change: replace `DependencyResolver.Current.GetService\u003CT>()` with `validationContext.GetService(typeof(T))`. This works because ASP.NET Core populates `ValidationContext.Items` and the service provider from the DI container automatically.\r\n\r\nFor attributes with complex service dependencies, consider moving the validation logic into a service-layer class or FluentValidation validator instead, to avoid the service locator pattern.\r\n\r\n### Step 3: Update ModelState Handling for API Controllers\r\n\r\n**MVC controllers** (`Controller` base class): `ModelState.IsValid` checks work the same way. No changes needed.\r\n\r\n**API controllers** with `[ApiController]`: ASP.NET Core automatically returns a 400 response with `ValidationProblemDetails` when model state is invalid, before the action method executes. This means manual `ModelState.IsValid` checks are redundant.\r\n\r\nBefore (ASP.NET Framework Web API):\r\n\r\n```csharp\r\n[HttpPost]\r\npublic IHttpActionResult Create(ProductModel model)\r\n{\r\n    if (!ModelState.IsValid)\r\n    {\r\n        return BadRequest(ModelState);\r\n    }\r\n    \u002F\u002F ... create logic\r\n}\r\n```\r\n\r\nAfter (ASP.NET Core with `[ApiController]`):\r\n\r\n```csharp\r\n[HttpPost]\r\npublic IActionResult Create(ProductModel model)\r\n{\r\n    \u002F\u002F ModelState.IsValid check is automatic — invalid requests never reach here\r\n    \u002F\u002F ... create logic\r\n}\r\n```\r\n\r\nThe automatic response returns `ValidationProblemDetails` (RFC 7807) instead of the legacy `ModelState` dictionary format. If existing API clients depend on the old error shape, customize the response factory:\r\n\r\n```csharp\r\nbuilder.Services.AddControllers()\r\n    .ConfigureApiBehaviorOptions(options =>\r\n    {\r\n        options.InvalidModelStateResponseFactory = context =>\r\n        {\r\n            var problems = new ValidationProblemDetails(context.ModelState);\r\n            return new BadRequestObjectResult(problems);\r\n        };\r\n    });\r\n```\r\n\r\nTo disable automatic validation entirely for a specific controller (rare), remove the `[ApiController]` attribute or configure `SuppressModelStateInvalidFilter`:\r\n\r\n```csharp\r\nbuilder.Services.AddControllers()\r\n    .ConfigureApiBehaviorOptions(options =>\r\n    {\r\n        options.SuppressModelStateInvalidFilter = true;\r\n    });\r\n```\r\n\r\n### Step 4: Migrate Client-Side Validation Scripts\r\n\r\nASP.NET Core uses the same `jquery.validate.unobtrusive` library for client-side validation, but script loading setup changed.\r\n\r\n**Add the validation partial.** Create or update `Views\u002FShared\u002F_ValidationScriptsPartial.cshtml`:\r\n\r\n```cshtml\r\n\u003Cscript src=\"~\u002Flib\u002Fjquery-validation\u002Fdist\u002Fjquery.validate.min.js\">\u003C\u002Fscript>\r\n\u003Cscript src=\"~\u002Flib\u002Fjquery-validation-unobtrusive\u002Fjquery.validate.unobtrusive.min.js\">\u003C\u002Fscript>\r\n```\r\n\r\n**Include the partial in views that need client-side validation.** Add at the bottom of each form view or in a `Scripts` section:\r\n\r\n```cshtml\r\n@section Scripts {\r\n    \u003Cpartial name=\"_ValidationScriptsPartial\" \u002F>\r\n}\r\n```\r\n\r\nThe `_Layout.cshtml` must render this section:\r\n\r\n```cshtml\r\n@await RenderSectionAsync(\"Scripts\", required: false)\r\n```\r\n\r\n**Install client libraries.** Use LibMan (the ASP.NET Core default) instead of NuGet or Bower for client-side libraries:\r\n\r\n```\r\nlibman install jquery-validation -p cdnjs -d wwwroot\u002Flib\u002Fjquery-validation\r\nlibman install jquery-validation-unobtrusive -p cdnjs -d wwwroot\u002Flib\u002Fjquery-validation-unobtrusive\r\n```\r\n\r\nOr add entries to `libman.json` if it already exists.\r\n\r\n**Custom client-side validation adapters** that used `$.validator.unobtrusive.adapters.add()` still work, but the registration call must execute after the unobtrusive script loads. Place custom adapter scripts after the `_ValidationScriptsPartial` include.\r\n\r\n### Step 5: Migrate Remote Validation\r\n\r\nThe `[Remote]` attribute exists in ASP.NET Core under `Microsoft.AspNetCore.Mvc` but requires different configuration.\r\n\r\nBefore (ASP.NET Framework):\r\n\r\n```csharp\r\n[Remote(\"IsEmailAvailable\", \"Users\")]\r\npublic string Email { get; set; }\r\n```\r\n\r\nAfter (ASP.NET Core):\r\n\r\n```csharp\r\n[Remote(action: \"IsEmailAvailable\", controller: \"Users\")]\r\npublic string Email { get; set; }\r\n```\r\n\r\nThe attribute syntax is the same, but ensure the validation endpoint:\r\n\r\n1. Returns `JsonResult` — return `Json(true)` for valid, `Json(\"Error message\")` for invalid.\r\n2. Accepts `GET` requests (the default for remote validation AJAX calls).\r\n3. Is accessible without authentication if the form is on a public page.\r\n\r\nExample validation action:\r\n\r\n```csharp\r\n[AcceptVerbs(\"GET\", \"POST\")]\r\npublic IActionResult IsEmailAvailable(string email)\r\n{\r\n    if (_userService.EmailExists(email))\r\n    {\r\n        return Json($\"Email {email} is already in use.\");\r\n    }\r\n    return Json(true);\r\n}\r\n```\r\n\r\nRemote validation depends on `jquery.validate.unobtrusive` — ensure Step 4 is complete.\r\n\r\n### Step 6: Migrate FluentValidation Integration\r\n\r\nIf the project uses FluentValidation, the registration mechanism changed.\r\n\r\nBefore (ASP.NET Framework with `FluentValidation.Mvc`):\r\n\r\n```csharp\r\n\u002F\u002F Global.asax.cs\r\nFluentValidationModelValidatorProvider.Configure();\r\n```\r\n\r\nAfter (ASP.NET Core with `FluentValidation.AspNetCore`):\r\n\r\n```csharp\r\n\u002F\u002F Program.cs\r\nbuilder.Services.AddControllersWithViews()\r\n    .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining\u003CProductValidator>());\r\n```\r\n\r\n**Package change**: Replace `FluentValidation.Mvc` or `FluentValidation.WebApi` with `FluentValidation.AspNetCore` in the project file.\r\n\r\n> **Note:** `AddFluentValidation()` on `IMvcBuilder` was deprecated in FluentValidation 11+. For newer versions, use manual registration:\r\n>\r\n> ```csharp\r\n> builder.Services.AddValidatorsFromAssemblyContaining\u003CProductValidator>();\r\n> builder.Services.AddFluentValidationAutoValidation();\r\n> builder.Services.AddFluentValidationClientsideAdapters(); \u002F\u002F optional: client-side support\r\n> ```\r\n\r\nIndividual `AbstractValidator\u003CT>` implementations require no changes — the validator classes themselves are framework-agnostic.\r\n\r\n### Step 7: Verify Validation Behavior\r\n\r\nAfter migration, verify:\r\n\r\n1. **Build succeeds** with no validation-related compilation errors.\r\n2. **Server-side validation** rejects invalid input and returns appropriate error messages.\r\n3. **Client-side validation** (if used) shows errors before form submission.\r\n4. **API validation** (if applicable) returns the expected error response format.\r\n5. **Remote validation** endpoints respond correctly to AJAX calls.\r\n\r\n## Troubleshooting\r\n\r\n**`ValidationContext.GetService` returns null.** The service is not registered in DI. Register it in `Program.cs` before it can be resolved during validation.\r\n\r\n**Client-side validation not firing.** Ensure `jquery`, `jquery.validate`, and `jquery.validate.unobtrusive` load in that order, and that `_ValidationScriptsPartial` is included in the view's `Scripts` section.\r\n\r\n**API returns `ValidationProblemDetails` instead of the expected error format.** The `[ApiController]` attribute enables automatic model validation with the RFC 7807 response shape. Customize via `InvalidModelStateResponseFactory` or suppress with `SuppressModelStateInvalidFilter` (see Step 3).\r\n\r\n**FluentValidation validators not executing.** Confirm the `FluentValidation.AspNetCore` package is installed and validators are registered via `AddValidatorsFromAssemblyContaining\u003CT>()` or `RegisterValidatorsFromAssemblyContaining\u003CT>()`.\r\n\r\n## Success Criteria\r\n\r\n- Custom `ValidationAttribute` classes use `ValidationContext.GetService` instead of `DependencyResolver`\r\n- API controllers leverage `[ApiController]` automatic validation or explicitly opt out\r\n- Client-side validation scripts load via `_ValidationScriptsPartial` and LibMan\r\n- `[Remote]` validation endpoints return `JsonResult` and accept GET requests\r\n- FluentValidation uses `FluentValidation.AspNetCore` package with correct registration\r\n- No references to `DependencyResolver`, `FluentValidation.Mvc`, or `FluentValidation.WebApi` remain\r\n- Project builds without errors\r\n",{"data":36,"body":40},{"name":4,"description":6,"metadata":37},{"traits":38,"discovery":39},".NET|CSharp|VisualBasic|DotNetCore","lazy",{"type":41,"children":42},"root",[43,52,59,90,96,101,113,120,125,335,340,346,400,413,439,452,569,581,673,702,707,713,737,768,773,848,859,910,930,1007,1027,1070,1076,1088,1105,1130,1148,1178,1191,1205,1215,1224,1237,1263,1269,1288,1293,1316,1321,1343,1348,1400,1405,1480,1492,1498,1503,1515,1538,1549,1580,1611,1672,1685,1691,1696,1747,1753,1776,1821,1860,1892,1898,2008],{"type":44,"tag":45,"props":46,"children":48},"element","h1",{"id":47},"aspnet-mvc-validation-migration",[49],{"type":50,"value":51},"text","ASP.NET MVC Validation Migration",{"type":44,"tag":53,"props":54,"children":56},"h2",{"id":55},"overview",[57],{"type":50,"value":58},"Overview",{"type":44,"tag":60,"props":61,"children":62},"p",{},[63,65,72,74,80,82,88],{"type":50,"value":64},"Migrate validation logic from ASP.NET Framework to ASP.NET Core. Most ",{"type":44,"tag":66,"props":67,"children":69},"code",{"className":68},[],[70],{"type":50,"value":71},"DataAnnotations",{"type":50,"value":73}," attributes transfer directly, but custom validators with service dependencies, client-side validation infrastructure, ",{"type":44,"tag":66,"props":75,"children":77},{"className":76},[],[78],{"type":50,"value":79},"[Remote]",{"type":50,"value":81}," attribute configuration, and ",{"type":44,"tag":66,"props":83,"children":85},{"className":84},[],[86],{"type":50,"value":87},"[ApiController]",{"type":50,"value":89}," automatic model validation require targeted changes.",{"type":44,"tag":53,"props":91,"children":93},{"id":92},"workflow",[94],{"type":50,"value":95},"Workflow",{"type":44,"tag":60,"props":97,"children":98},{},[99],{"type":50,"value":100},"Track progress across these steps:",{"type":44,"tag":102,"props":103,"children":107},"pre",{"className":104,"code":106,"language":50},[105],"language-text","Migration Progress:\n- [ ] Step 1: Audit validation usage\n- [ ] Step 2: Migrate data annotations and custom attributes\n- [ ] Step 3: Update ModelState handling for API controllers\n- [ ] Step 4: Migrate client-side validation scripts\n- [ ] Step 5: Migrate Remote validation\n- [ ] Step 6: Migrate FluentValidation integration\n- [ ] Step 7: Verify validation behavior\n",[108],{"type":44,"tag":66,"props":109,"children":111},{"__ignoreMap":110},"",[112],{"type":50,"value":106},{"type":44,"tag":114,"props":115,"children":117},"h3",{"id":116},"step-1-audit-validation-usage",[118],{"type":50,"value":119},"Step 1: Audit Validation Usage",{"type":44,"tag":60,"props":121,"children":122},{},[123],{"type":50,"value":124},"Search the project for validation patterns to determine which steps apply:",{"type":44,"tag":126,"props":127,"children":128},"table",{},[129,153],{"type":44,"tag":130,"props":131,"children":132},"thead",{},[133],{"type":44,"tag":134,"props":135,"children":136},"tr",{},[137,143,148],{"type":44,"tag":138,"props":139,"children":140},"th",{},[141],{"type":50,"value":142},"Signal",{"type":44,"tag":138,"props":144,"children":145},{},[146],{"type":50,"value":147},"Search pattern",{"type":44,"tag":138,"props":149,"children":150},{},[151],{"type":50,"value":152},"Indicates",{"type":44,"tag":154,"props":155,"children":156},"tbody",{},[157,180,201,223,255,285,307],{"type":44,"tag":134,"props":158,"children":159},{},[160,166,175],{"type":44,"tag":161,"props":162,"children":163},"td",{},[164],{"type":50,"value":165},"Data annotations",{"type":44,"tag":161,"props":167,"children":168},{},[169],{"type":44,"tag":66,"props":170,"children":172},{"className":171},[],[173],{"type":50,"value":174},"using System.ComponentModel.DataAnnotations",{"type":44,"tag":161,"props":176,"children":177},{},[178],{"type":50,"value":179},"Step 2",{"type":44,"tag":134,"props":181,"children":182},{},[183,188,197],{"type":44,"tag":161,"props":184,"children":185},{},[186],{"type":50,"value":187},"Custom validators",{"type":44,"tag":161,"props":189,"children":190},{},[191],{"type":44,"tag":66,"props":192,"children":194},{"className":193},[],[195],{"type":50,"value":196},": ValidationAttribute",{"type":44,"tag":161,"props":198,"children":199},{},[200],{"type":50,"value":179},{"type":44,"tag":134,"props":202,"children":203},{},[204,209,218],{"type":44,"tag":161,"props":205,"children":206},{},[207],{"type":50,"value":208},"IValidatableObject",{"type":44,"tag":161,"props":210,"children":211},{},[212],{"type":44,"tag":66,"props":213,"children":215},{"className":214},[],[216],{"type":50,"value":217},": IValidatableObject",{"type":44,"tag":161,"props":219,"children":220},{},[221],{"type":50,"value":222},"Step 2 (compatible)",{"type":44,"tag":134,"props":224,"children":225},{},[226,231,250],{"type":44,"tag":161,"props":227,"children":228},{},[229],{"type":50,"value":230},"ModelState in APIs",{"type":44,"tag":161,"props":232,"children":233},{},[234,240,242,248],{"type":44,"tag":66,"props":235,"children":237},{"className":236},[],[238],{"type":50,"value":239},"ModelState.IsValid",{"type":50,"value":241}," in ",{"type":44,"tag":66,"props":243,"children":245},{"className":244},[],[246],{"type":50,"value":247},"ApiController",{"type":50,"value":249}," classes",{"type":44,"tag":161,"props":251,"children":252},{},[253],{"type":50,"value":254},"Step 3",{"type":44,"tag":134,"props":256,"children":257},{},[258,263,280],{"type":44,"tag":161,"props":259,"children":260},{},[261],{"type":50,"value":262},"Client-side validation",{"type":44,"tag":161,"props":264,"children":265},{},[266,272,274],{"type":44,"tag":66,"props":267,"children":269},{"className":268},[],[270],{"type":50,"value":271},"jquery.validate",{"type":50,"value":273},", ",{"type":44,"tag":66,"props":275,"children":277},{"className":276},[],[278],{"type":50,"value":279},"jquery.validate.unobtrusive",{"type":44,"tag":161,"props":281,"children":282},{},[283],{"type":50,"value":284},"Step 4",{"type":44,"tag":134,"props":286,"children":287},{},[288,293,302],{"type":44,"tag":161,"props":289,"children":290},{},[291],{"type":50,"value":292},"Remote validation",{"type":44,"tag":161,"props":294,"children":295},{},[296],{"type":44,"tag":66,"props":297,"children":299},{"className":298},[],[300],{"type":50,"value":301},"[Remote(",{"type":44,"tag":161,"props":303,"children":304},{},[305],{"type":50,"value":306},"Step 5",{"type":44,"tag":134,"props":308,"children":309},{},[310,315,330],{"type":44,"tag":161,"props":311,"children":312},{},[313],{"type":50,"value":314},"FluentValidation",{"type":44,"tag":161,"props":316,"children":317},{},[318,324,325],{"type":44,"tag":66,"props":319,"children":321},{"className":320},[],[322],{"type":50,"value":323},"AbstractValidator\u003C",{"type":50,"value":273},{"type":44,"tag":66,"props":326,"children":328},{"className":327},[],[329],{"type":50,"value":314},{"type":44,"tag":161,"props":331,"children":332},{},[333],{"type":50,"value":334},"Step 6",{"type":44,"tag":60,"props":336,"children":337},{},[338],{"type":50,"value":339},"Skip steps that have no matching signals.",{"type":44,"tag":114,"props":341,"children":343},{"id":342},"step-2-migrate-data-annotations-and-custom-attributes",[344],{"type":50,"value":345},"Step 2: Migrate Data Annotations and Custom Attributes",{"type":44,"tag":60,"props":347,"children":348},{},[349,355,357,363,364,370,371,377,378,384,385,391,392,398],{"type":44,"tag":350,"props":351,"children":352},"strong",{},[353],{"type":50,"value":354},"Standard data annotations",{"type":50,"value":356}," (",{"type":44,"tag":66,"props":358,"children":360},{"className":359},[],[361],{"type":50,"value":362},"[Required]",{"type":50,"value":273},{"type":44,"tag":66,"props":365,"children":367},{"className":366},[],[368],{"type":50,"value":369},"[StringLength]",{"type":50,"value":273},{"type":44,"tag":66,"props":372,"children":374},{"className":373},[],[375],{"type":50,"value":376},"[Range]",{"type":50,"value":273},{"type":44,"tag":66,"props":379,"children":381},{"className":380},[],[382],{"type":50,"value":383},"[RegularExpression]",{"type":50,"value":273},{"type":44,"tag":66,"props":386,"children":388},{"className":387},[],[389],{"type":50,"value":390},"[Compare]",{"type":50,"value":273},{"type":44,"tag":66,"props":393,"children":395},{"className":394},[],[396],{"type":50,"value":397},"[EmailAddress]",{"type":50,"value":399},") work identically in ASP.NET Core. No changes needed.",{"type":44,"tag":60,"props":401,"children":402},{},[403,411],{"type":44,"tag":350,"props":404,"children":405},{},[406],{"type":44,"tag":66,"props":407,"children":409},{"className":408},[],[410],{"type":50,"value":208},{"type":50,"value":412}," is fully compatible. No changes needed.",{"type":44,"tag":60,"props":414,"children":415},{},[416,429,431,437],{"type":44,"tag":350,"props":417,"children":418},{},[419,421,427],{"type":50,"value":420},"Custom ",{"type":44,"tag":66,"props":422,"children":424},{"className":423},[],[425],{"type":50,"value":426},"ValidationAttribute",{"type":50,"value":428}," subclasses",{"type":50,"value":430}," that override ",{"type":44,"tag":66,"props":432,"children":434},{"className":433},[],[435],{"type":50,"value":436},"IsValid(object, ValidationContext)",{"type":50,"value":438}," are compatible, but service resolution changed:",{"type":44,"tag":60,"props":440,"children":441},{},[442,444,450],{"type":50,"value":443},"Before (ASP.NET Framework — service locator via ",{"type":44,"tag":66,"props":445,"children":447},{"className":446},[],[448],{"type":50,"value":449},"DependencyResolver",{"type":50,"value":451},"):",{"type":44,"tag":102,"props":453,"children":457},{"className":454,"code":455,"language":456,"meta":110,"style":110},"language-csharp shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","public class UniqueEmailAttribute : ValidationAttribute\n{\n    protected override ValidationResult IsValid(object value, ValidationContext validationContext)\n    {\n        var userService = DependencyResolver.Current.GetService\u003CIUserService>();\n        if (userService.EmailExists((string)value))\n        {\n            return new ValidationResult(ErrorMessage);\n        }\n        return ValidationResult.Success;\n    }\n}\n","csharp",[458],{"type":44,"tag":66,"props":459,"children":460},{"__ignoreMap":110},[461,471,480,489,498,507,516,524,533,542,551,560],{"type":44,"tag":462,"props":463,"children":465},"span",{"class":464,"line":29},"line",[466],{"type":44,"tag":462,"props":467,"children":468},{},[469],{"type":50,"value":470},"public class UniqueEmailAttribute : ValidationAttribute\n",{"type":44,"tag":462,"props":472,"children":474},{"class":464,"line":473},2,[475],{"type":44,"tag":462,"props":476,"children":477},{},[478],{"type":50,"value":479},"{\n",{"type":44,"tag":462,"props":481,"children":483},{"class":464,"line":482},3,[484],{"type":44,"tag":462,"props":485,"children":486},{},[487],{"type":50,"value":488},"    protected override ValidationResult IsValid(object value, ValidationContext validationContext)\n",{"type":44,"tag":462,"props":490,"children":492},{"class":464,"line":491},4,[493],{"type":44,"tag":462,"props":494,"children":495},{},[496],{"type":50,"value":497},"    {\n",{"type":44,"tag":462,"props":499,"children":501},{"class":464,"line":500},5,[502],{"type":44,"tag":462,"props":503,"children":504},{},[505],{"type":50,"value":506},"        var userService = DependencyResolver.Current.GetService\u003CIUserService>();\n",{"type":44,"tag":462,"props":508,"children":510},{"class":464,"line":509},6,[511],{"type":44,"tag":462,"props":512,"children":513},{},[514],{"type":50,"value":515},"        if (userService.EmailExists((string)value))\n",{"type":44,"tag":462,"props":517,"children":518},{"class":464,"line":25},[519],{"type":44,"tag":462,"props":520,"children":521},{},[522],{"type":50,"value":523},"        {\n",{"type":44,"tag":462,"props":525,"children":527},{"class":464,"line":526},8,[528],{"type":44,"tag":462,"props":529,"children":530},{},[531],{"type":50,"value":532},"            return new ValidationResult(ErrorMessage);\n",{"type":44,"tag":462,"props":534,"children":536},{"class":464,"line":535},9,[537],{"type":44,"tag":462,"props":538,"children":539},{},[540],{"type":50,"value":541},"        }\n",{"type":44,"tag":462,"props":543,"children":545},{"class":464,"line":544},10,[546],{"type":44,"tag":462,"props":547,"children":548},{},[549],{"type":50,"value":550},"        return ValidationResult.Success;\n",{"type":44,"tag":462,"props":552,"children":554},{"class":464,"line":553},11,[555],{"type":44,"tag":462,"props":556,"children":557},{},[558],{"type":50,"value":559},"    }\n",{"type":44,"tag":462,"props":561,"children":563},{"class":464,"line":562},12,[564],{"type":44,"tag":462,"props":565,"children":566},{},[567],{"type":50,"value":568},"}\n",{"type":44,"tag":60,"props":570,"children":571},{},[572,574,580],{"type":50,"value":573},"After (ASP.NET Core — services via ",{"type":44,"tag":66,"props":575,"children":577},{"className":576},[],[578],{"type":50,"value":579},"ValidationContext.GetService",{"type":50,"value":451},{"type":44,"tag":102,"props":582,"children":584},{"className":454,"code":583,"language":456,"meta":110,"style":110},"public class UniqueEmailAttribute : ValidationAttribute\n{\n    protected override ValidationResult IsValid(object value, ValidationContext validationContext)\n    {\n        var userService = (IUserService)validationContext.GetService(typeof(IUserService));\n        if (userService.EmailExists((string)value))\n        {\n            return new ValidationResult(ErrorMessage);\n        }\n        return ValidationResult.Success;\n    }\n}\n",[585],{"type":44,"tag":66,"props":586,"children":587},{"__ignoreMap":110},[588,595,602,609,616,624,631,638,645,652,659,666],{"type":44,"tag":462,"props":589,"children":590},{"class":464,"line":29},[591],{"type":44,"tag":462,"props":592,"children":593},{},[594],{"type":50,"value":470},{"type":44,"tag":462,"props":596,"children":597},{"class":464,"line":473},[598],{"type":44,"tag":462,"props":599,"children":600},{},[601],{"type":50,"value":479},{"type":44,"tag":462,"props":603,"children":604},{"class":464,"line":482},[605],{"type":44,"tag":462,"props":606,"children":607},{},[608],{"type":50,"value":488},{"type":44,"tag":462,"props":610,"children":611},{"class":464,"line":491},[612],{"type":44,"tag":462,"props":613,"children":614},{},[615],{"type":50,"value":497},{"type":44,"tag":462,"props":617,"children":618},{"class":464,"line":500},[619],{"type":44,"tag":462,"props":620,"children":621},{},[622],{"type":50,"value":623},"        var userService = (IUserService)validationContext.GetService(typeof(IUserService));\n",{"type":44,"tag":462,"props":625,"children":626},{"class":464,"line":509},[627],{"type":44,"tag":462,"props":628,"children":629},{},[630],{"type":50,"value":515},{"type":44,"tag":462,"props":632,"children":633},{"class":464,"line":25},[634],{"type":44,"tag":462,"props":635,"children":636},{},[637],{"type":50,"value":523},{"type":44,"tag":462,"props":639,"children":640},{"class":464,"line":526},[641],{"type":44,"tag":462,"props":642,"children":643},{},[644],{"type":50,"value":532},{"type":44,"tag":462,"props":646,"children":647},{"class":464,"line":535},[648],{"type":44,"tag":462,"props":649,"children":650},{},[651],{"type":50,"value":541},{"type":44,"tag":462,"props":653,"children":654},{"class":464,"line":544},[655],{"type":44,"tag":462,"props":656,"children":657},{},[658],{"type":50,"value":550},{"type":44,"tag":462,"props":660,"children":661},{"class":464,"line":553},[662],{"type":44,"tag":462,"props":663,"children":664},{},[665],{"type":50,"value":559},{"type":44,"tag":462,"props":667,"children":668},{"class":464,"line":562},[669],{"type":44,"tag":462,"props":670,"children":671},{},[672],{"type":50,"value":568},{"type":44,"tag":60,"props":674,"children":675},{},[676,678,684,686,692,694,700],{"type":50,"value":677},"The key change: replace ",{"type":44,"tag":66,"props":679,"children":681},{"className":680},[],[682],{"type":50,"value":683},"DependencyResolver.Current.GetService\u003CT>()",{"type":50,"value":685}," with ",{"type":44,"tag":66,"props":687,"children":689},{"className":688},[],[690],{"type":50,"value":691},"validationContext.GetService(typeof(T))",{"type":50,"value":693},". This works because ASP.NET Core populates ",{"type":44,"tag":66,"props":695,"children":697},{"className":696},[],[698],{"type":50,"value":699},"ValidationContext.Items",{"type":50,"value":701}," and the service provider from the DI container automatically.",{"type":44,"tag":60,"props":703,"children":704},{},[705],{"type":50,"value":706},"For attributes with complex service dependencies, consider moving the validation logic into a service-layer class or FluentValidation validator instead, to avoid the service locator pattern.",{"type":44,"tag":114,"props":708,"children":710},{"id":709},"step-3-update-modelstate-handling-for-api-controllers",[711],{"type":50,"value":712},"Step 3: Update ModelState Handling for API Controllers",{"type":44,"tag":60,"props":714,"children":715},{},[716,721,722,728,730,735],{"type":44,"tag":350,"props":717,"children":718},{},[719],{"type":50,"value":720},"MVC controllers",{"type":50,"value":356},{"type":44,"tag":66,"props":723,"children":725},{"className":724},[],[726],{"type":50,"value":727},"Controller",{"type":50,"value":729}," base class): ",{"type":44,"tag":66,"props":731,"children":733},{"className":732},[],[734],{"type":50,"value":239},{"type":50,"value":736}," checks work the same way. No changes needed.",{"type":44,"tag":60,"props":738,"children":739},{},[740,745,746,751,753,759,761,766],{"type":44,"tag":350,"props":741,"children":742},{},[743],{"type":50,"value":744},"API controllers",{"type":50,"value":685},{"type":44,"tag":66,"props":747,"children":749},{"className":748},[],[750],{"type":50,"value":87},{"type":50,"value":752},": ASP.NET Core automatically returns a 400 response with ",{"type":44,"tag":66,"props":754,"children":756},{"className":755},[],[757],{"type":50,"value":758},"ValidationProblemDetails",{"type":50,"value":760}," when model state is invalid, before the action method executes. This means manual ",{"type":44,"tag":66,"props":762,"children":764},{"className":763},[],[765],{"type":50,"value":239},{"type":50,"value":767}," checks are redundant.",{"type":44,"tag":60,"props":769,"children":770},{},[771],{"type":50,"value":772},"Before (ASP.NET Framework Web API):",{"type":44,"tag":102,"props":774,"children":776},{"className":454,"code":775,"language":456,"meta":110,"style":110},"[HttpPost]\npublic IHttpActionResult Create(ProductModel model)\n{\n    if (!ModelState.IsValid)\n    {\n        return BadRequest(ModelState);\n    }\n    \u002F\u002F ... create logic\n}\n",[777],{"type":44,"tag":66,"props":778,"children":779},{"__ignoreMap":110},[780,788,796,803,811,818,826,833,841],{"type":44,"tag":462,"props":781,"children":782},{"class":464,"line":29},[783],{"type":44,"tag":462,"props":784,"children":785},{},[786],{"type":50,"value":787},"[HttpPost]\n",{"type":44,"tag":462,"props":789,"children":790},{"class":464,"line":473},[791],{"type":44,"tag":462,"props":792,"children":793},{},[794],{"type":50,"value":795},"public IHttpActionResult Create(ProductModel model)\n",{"type":44,"tag":462,"props":797,"children":798},{"class":464,"line":482},[799],{"type":44,"tag":462,"props":800,"children":801},{},[802],{"type":50,"value":479},{"type":44,"tag":462,"props":804,"children":805},{"class":464,"line":491},[806],{"type":44,"tag":462,"props":807,"children":808},{},[809],{"type":50,"value":810},"    if (!ModelState.IsValid)\n",{"type":44,"tag":462,"props":812,"children":813},{"class":464,"line":500},[814],{"type":44,"tag":462,"props":815,"children":816},{},[817],{"type":50,"value":497},{"type":44,"tag":462,"props":819,"children":820},{"class":464,"line":509},[821],{"type":44,"tag":462,"props":822,"children":823},{},[824],{"type":50,"value":825},"        return BadRequest(ModelState);\n",{"type":44,"tag":462,"props":827,"children":828},{"class":464,"line":25},[829],{"type":44,"tag":462,"props":830,"children":831},{},[832],{"type":50,"value":559},{"type":44,"tag":462,"props":834,"children":835},{"class":464,"line":526},[836],{"type":44,"tag":462,"props":837,"children":838},{},[839],{"type":50,"value":840},"    \u002F\u002F ... create logic\n",{"type":44,"tag":462,"props":842,"children":843},{"class":464,"line":535},[844],{"type":44,"tag":462,"props":845,"children":846},{},[847],{"type":50,"value":568},{"type":44,"tag":60,"props":849,"children":850},{},[851,853,858],{"type":50,"value":852},"After (ASP.NET Core with ",{"type":44,"tag":66,"props":854,"children":856},{"className":855},[],[857],{"type":50,"value":87},{"type":50,"value":451},{"type":44,"tag":102,"props":860,"children":862},{"className":454,"code":861,"language":456,"meta":110,"style":110},"[HttpPost]\npublic IActionResult Create(ProductModel model)\n{\n    \u002F\u002F ModelState.IsValid check is automatic — invalid requests never reach here\n    \u002F\u002F ... create logic\n}\n",[863],{"type":44,"tag":66,"props":864,"children":865},{"__ignoreMap":110},[866,873,881,888,896,903],{"type":44,"tag":462,"props":867,"children":868},{"class":464,"line":29},[869],{"type":44,"tag":462,"props":870,"children":871},{},[872],{"type":50,"value":787},{"type":44,"tag":462,"props":874,"children":875},{"class":464,"line":473},[876],{"type":44,"tag":462,"props":877,"children":878},{},[879],{"type":50,"value":880},"public IActionResult Create(ProductModel model)\n",{"type":44,"tag":462,"props":882,"children":883},{"class":464,"line":482},[884],{"type":44,"tag":462,"props":885,"children":886},{},[887],{"type":50,"value":479},{"type":44,"tag":462,"props":889,"children":890},{"class":464,"line":491},[891],{"type":44,"tag":462,"props":892,"children":893},{},[894],{"type":50,"value":895},"    \u002F\u002F ModelState.IsValid check is automatic — invalid requests never reach here\n",{"type":44,"tag":462,"props":897,"children":898},{"class":464,"line":500},[899],{"type":44,"tag":462,"props":900,"children":901},{},[902],{"type":50,"value":840},{"type":44,"tag":462,"props":904,"children":905},{"class":464,"line":509},[906],{"type":44,"tag":462,"props":907,"children":908},{},[909],{"type":50,"value":568},{"type":44,"tag":60,"props":911,"children":912},{},[913,915,920,922,928],{"type":50,"value":914},"The automatic response returns ",{"type":44,"tag":66,"props":916,"children":918},{"className":917},[],[919],{"type":50,"value":758},{"type":50,"value":921}," (RFC 7807) instead of the legacy ",{"type":44,"tag":66,"props":923,"children":925},{"className":924},[],[926],{"type":50,"value":927},"ModelState",{"type":50,"value":929}," dictionary format. If existing API clients depend on the old error shape, customize the response factory:",{"type":44,"tag":102,"props":931,"children":933},{"className":454,"code":932,"language":456,"meta":110,"style":110},"builder.Services.AddControllers()\n    .ConfigureApiBehaviorOptions(options =>\n    {\n        options.InvalidModelStateResponseFactory = context =>\n        {\n            var problems = new ValidationProblemDetails(context.ModelState);\n            return new BadRequestObjectResult(problems);\n        };\n    });\n",[934],{"type":44,"tag":66,"props":935,"children":936},{"__ignoreMap":110},[937,945,953,960,968,975,983,991,999],{"type":44,"tag":462,"props":938,"children":939},{"class":464,"line":29},[940],{"type":44,"tag":462,"props":941,"children":942},{},[943],{"type":50,"value":944},"builder.Services.AddControllers()\n",{"type":44,"tag":462,"props":946,"children":947},{"class":464,"line":473},[948],{"type":44,"tag":462,"props":949,"children":950},{},[951],{"type":50,"value":952},"    .ConfigureApiBehaviorOptions(options =>\n",{"type":44,"tag":462,"props":954,"children":955},{"class":464,"line":482},[956],{"type":44,"tag":462,"props":957,"children":958},{},[959],{"type":50,"value":497},{"type":44,"tag":462,"props":961,"children":962},{"class":464,"line":491},[963],{"type":44,"tag":462,"props":964,"children":965},{},[966],{"type":50,"value":967},"        options.InvalidModelStateResponseFactory = context =>\n",{"type":44,"tag":462,"props":969,"children":970},{"class":464,"line":500},[971],{"type":44,"tag":462,"props":972,"children":973},{},[974],{"type":50,"value":523},{"type":44,"tag":462,"props":976,"children":977},{"class":464,"line":509},[978],{"type":44,"tag":462,"props":979,"children":980},{},[981],{"type":50,"value":982},"            var problems = new ValidationProblemDetails(context.ModelState);\n",{"type":44,"tag":462,"props":984,"children":985},{"class":464,"line":25},[986],{"type":44,"tag":462,"props":987,"children":988},{},[989],{"type":50,"value":990},"            return new BadRequestObjectResult(problems);\n",{"type":44,"tag":462,"props":992,"children":993},{"class":464,"line":526},[994],{"type":44,"tag":462,"props":995,"children":996},{},[997],{"type":50,"value":998},"        };\n",{"type":44,"tag":462,"props":1000,"children":1001},{"class":464,"line":535},[1002],{"type":44,"tag":462,"props":1003,"children":1004},{},[1005],{"type":50,"value":1006},"    });\n",{"type":44,"tag":60,"props":1008,"children":1009},{},[1010,1012,1017,1019,1025],{"type":50,"value":1011},"To disable automatic validation entirely for a specific controller (rare), remove the ",{"type":44,"tag":66,"props":1013,"children":1015},{"className":1014},[],[1016],{"type":50,"value":87},{"type":50,"value":1018}," attribute or configure ",{"type":44,"tag":66,"props":1020,"children":1022},{"className":1021},[],[1023],{"type":50,"value":1024},"SuppressModelStateInvalidFilter",{"type":50,"value":1026},":",{"type":44,"tag":102,"props":1028,"children":1030},{"className":454,"code":1029,"language":456,"meta":110,"style":110},"builder.Services.AddControllers()\n    .ConfigureApiBehaviorOptions(options =>\n    {\n        options.SuppressModelStateInvalidFilter = true;\n    });\n",[1031],{"type":44,"tag":66,"props":1032,"children":1033},{"__ignoreMap":110},[1034,1041,1048,1055,1063],{"type":44,"tag":462,"props":1035,"children":1036},{"class":464,"line":29},[1037],{"type":44,"tag":462,"props":1038,"children":1039},{},[1040],{"type":50,"value":944},{"type":44,"tag":462,"props":1042,"children":1043},{"class":464,"line":473},[1044],{"type":44,"tag":462,"props":1045,"children":1046},{},[1047],{"type":50,"value":952},{"type":44,"tag":462,"props":1049,"children":1050},{"class":464,"line":482},[1051],{"type":44,"tag":462,"props":1052,"children":1053},{},[1054],{"type":50,"value":497},{"type":44,"tag":462,"props":1056,"children":1057},{"class":464,"line":491},[1058],{"type":44,"tag":462,"props":1059,"children":1060},{},[1061],{"type":50,"value":1062},"        options.SuppressModelStateInvalidFilter = true;\n",{"type":44,"tag":462,"props":1064,"children":1065},{"class":464,"line":500},[1066],{"type":44,"tag":462,"props":1067,"children":1068},{},[1069],{"type":50,"value":1006},{"type":44,"tag":114,"props":1071,"children":1073},{"id":1072},"step-4-migrate-client-side-validation-scripts",[1074],{"type":50,"value":1075},"Step 4: Migrate Client-Side Validation Scripts",{"type":44,"tag":60,"props":1077,"children":1078},{},[1079,1081,1086],{"type":50,"value":1080},"ASP.NET Core uses the same ",{"type":44,"tag":66,"props":1082,"children":1084},{"className":1083},[],[1085],{"type":50,"value":279},{"type":50,"value":1087}," library for client-side validation, but script loading setup changed.",{"type":44,"tag":60,"props":1089,"children":1090},{},[1091,1096,1098,1104],{"type":44,"tag":350,"props":1092,"children":1093},{},[1094],{"type":50,"value":1095},"Add the validation partial.",{"type":50,"value":1097}," Create or update ",{"type":44,"tag":66,"props":1099,"children":1101},{"className":1100},[],[1102],{"type":50,"value":1103},"Views\u002FShared\u002F_ValidationScriptsPartial.cshtml",{"type":50,"value":1026},{"type":44,"tag":102,"props":1106,"children":1110},{"className":1107,"code":1108,"language":1109,"meta":110,"style":110},"language-cshtml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cscript src=\"~\u002Flib\u002Fjquery-validation\u002Fdist\u002Fjquery.validate.min.js\">\u003C\u002Fscript>\n\u003Cscript src=\"~\u002Flib\u002Fjquery-validation-unobtrusive\u002Fjquery.validate.unobtrusive.min.js\">\u003C\u002Fscript>\n","cshtml",[1111],{"type":44,"tag":66,"props":1112,"children":1113},{"__ignoreMap":110},[1114,1122],{"type":44,"tag":462,"props":1115,"children":1116},{"class":464,"line":29},[1117],{"type":44,"tag":462,"props":1118,"children":1119},{},[1120],{"type":50,"value":1121},"\u003Cscript src=\"~\u002Flib\u002Fjquery-validation\u002Fdist\u002Fjquery.validate.min.js\">\u003C\u002Fscript>\n",{"type":44,"tag":462,"props":1123,"children":1124},{"class":464,"line":473},[1125],{"type":44,"tag":462,"props":1126,"children":1127},{},[1128],{"type":50,"value":1129},"\u003Cscript src=\"~\u002Flib\u002Fjquery-validation-unobtrusive\u002Fjquery.validate.unobtrusive.min.js\">\u003C\u002Fscript>\n",{"type":44,"tag":60,"props":1131,"children":1132},{},[1133,1138,1140,1146],{"type":44,"tag":350,"props":1134,"children":1135},{},[1136],{"type":50,"value":1137},"Include the partial in views that need client-side validation.",{"type":50,"value":1139}," Add at the bottom of each form view or in a ",{"type":44,"tag":66,"props":1141,"children":1143},{"className":1142},[],[1144],{"type":50,"value":1145},"Scripts",{"type":50,"value":1147}," section:",{"type":44,"tag":102,"props":1149,"children":1151},{"className":1107,"code":1150,"language":1109,"meta":110,"style":110},"@section Scripts {\n    \u003Cpartial name=\"_ValidationScriptsPartial\" \u002F>\n}\n",[1152],{"type":44,"tag":66,"props":1153,"children":1154},{"__ignoreMap":110},[1155,1163,1171],{"type":44,"tag":462,"props":1156,"children":1157},{"class":464,"line":29},[1158],{"type":44,"tag":462,"props":1159,"children":1160},{},[1161],{"type":50,"value":1162},"@section Scripts {\n",{"type":44,"tag":462,"props":1164,"children":1165},{"class":464,"line":473},[1166],{"type":44,"tag":462,"props":1167,"children":1168},{},[1169],{"type":50,"value":1170},"    \u003Cpartial name=\"_ValidationScriptsPartial\" \u002F>\n",{"type":44,"tag":462,"props":1172,"children":1173},{"class":464,"line":482},[1174],{"type":44,"tag":462,"props":1175,"children":1176},{},[1177],{"type":50,"value":568},{"type":44,"tag":60,"props":1179,"children":1180},{},[1181,1183,1189],{"type":50,"value":1182},"The ",{"type":44,"tag":66,"props":1184,"children":1186},{"className":1185},[],[1187],{"type":50,"value":1188},"_Layout.cshtml",{"type":50,"value":1190}," must render this section:",{"type":44,"tag":102,"props":1192,"children":1194},{"className":1107,"code":1193,"language":1109,"meta":110,"style":110},"@await RenderSectionAsync(\"Scripts\", required: false)\n",[1195],{"type":44,"tag":66,"props":1196,"children":1197},{"__ignoreMap":110},[1198],{"type":44,"tag":462,"props":1199,"children":1200},{"class":464,"line":29},[1201],{"type":44,"tag":462,"props":1202,"children":1203},{},[1204],{"type":50,"value":1193},{"type":44,"tag":60,"props":1206,"children":1207},{},[1208,1213],{"type":44,"tag":350,"props":1209,"children":1210},{},[1211],{"type":50,"value":1212},"Install client libraries.",{"type":50,"value":1214}," Use LibMan (the ASP.NET Core default) instead of NuGet or Bower for client-side libraries:",{"type":44,"tag":102,"props":1216,"children":1219},{"className":1217,"code":1218,"language":50},[105],"libman install jquery-validation -p cdnjs -d wwwroot\u002Flib\u002Fjquery-validation\nlibman install jquery-validation-unobtrusive -p cdnjs -d wwwroot\u002Flib\u002Fjquery-validation-unobtrusive\n",[1220],{"type":44,"tag":66,"props":1221,"children":1222},{"__ignoreMap":110},[1223],{"type":50,"value":1218},{"type":44,"tag":60,"props":1225,"children":1226},{},[1227,1229,1235],{"type":50,"value":1228},"Or add entries to ",{"type":44,"tag":66,"props":1230,"children":1232},{"className":1231},[],[1233],{"type":50,"value":1234},"libman.json",{"type":50,"value":1236}," if it already exists.",{"type":44,"tag":60,"props":1238,"children":1239},{},[1240,1245,1247,1253,1255,1261],{"type":44,"tag":350,"props":1241,"children":1242},{},[1243],{"type":50,"value":1244},"Custom client-side validation adapters",{"type":50,"value":1246}," that used ",{"type":44,"tag":66,"props":1248,"children":1250},{"className":1249},[],[1251],{"type":50,"value":1252},"$.validator.unobtrusive.adapters.add()",{"type":50,"value":1254}," still work, but the registration call must execute after the unobtrusive script loads. Place custom adapter scripts after the ",{"type":44,"tag":66,"props":1256,"children":1258},{"className":1257},[],[1259],{"type":50,"value":1260},"_ValidationScriptsPartial",{"type":50,"value":1262}," include.",{"type":44,"tag":114,"props":1264,"children":1266},{"id":1265},"step-5-migrate-remote-validation",[1267],{"type":50,"value":1268},"Step 5: Migrate Remote Validation",{"type":44,"tag":60,"props":1270,"children":1271},{},[1272,1273,1278,1280,1286],{"type":50,"value":1182},{"type":44,"tag":66,"props":1274,"children":1276},{"className":1275},[],[1277],{"type":50,"value":79},{"type":50,"value":1279}," attribute exists in ASP.NET Core under ",{"type":44,"tag":66,"props":1281,"children":1283},{"className":1282},[],[1284],{"type":50,"value":1285},"Microsoft.AspNetCore.Mvc",{"type":50,"value":1287}," but requires different configuration.",{"type":44,"tag":60,"props":1289,"children":1290},{},[1291],{"type":50,"value":1292},"Before (ASP.NET Framework):",{"type":44,"tag":102,"props":1294,"children":1296},{"className":454,"code":1295,"language":456,"meta":110,"style":110},"[Remote(\"IsEmailAvailable\", \"Users\")]\npublic string Email { get; set; }\n",[1297],{"type":44,"tag":66,"props":1298,"children":1299},{"__ignoreMap":110},[1300,1308],{"type":44,"tag":462,"props":1301,"children":1302},{"class":464,"line":29},[1303],{"type":44,"tag":462,"props":1304,"children":1305},{},[1306],{"type":50,"value":1307},"[Remote(\"IsEmailAvailable\", \"Users\")]\n",{"type":44,"tag":462,"props":1309,"children":1310},{"class":464,"line":473},[1311],{"type":44,"tag":462,"props":1312,"children":1313},{},[1314],{"type":50,"value":1315},"public string Email { get; set; }\n",{"type":44,"tag":60,"props":1317,"children":1318},{},[1319],{"type":50,"value":1320},"After (ASP.NET Core):",{"type":44,"tag":102,"props":1322,"children":1324},{"className":454,"code":1323,"language":456,"meta":110,"style":110},"[Remote(action: \"IsEmailAvailable\", controller: \"Users\")]\npublic string Email { get; set; }\n",[1325],{"type":44,"tag":66,"props":1326,"children":1327},{"__ignoreMap":110},[1328,1336],{"type":44,"tag":462,"props":1329,"children":1330},{"class":464,"line":29},[1331],{"type":44,"tag":462,"props":1332,"children":1333},{},[1334],{"type":50,"value":1335},"[Remote(action: \"IsEmailAvailable\", controller: \"Users\")]\n",{"type":44,"tag":462,"props":1337,"children":1338},{"class":464,"line":473},[1339],{"type":44,"tag":462,"props":1340,"children":1341},{},[1342],{"type":50,"value":1315},{"type":44,"tag":60,"props":1344,"children":1345},{},[1346],{"type":50,"value":1347},"The attribute syntax is the same, but ensure the validation endpoint:",{"type":44,"tag":1349,"props":1350,"children":1351},"ol",{},[1352,1382,1395],{"type":44,"tag":1353,"props":1354,"children":1355},"li",{},[1356,1358,1364,1366,1372,1374,1380],{"type":50,"value":1357},"Returns ",{"type":44,"tag":66,"props":1359,"children":1361},{"className":1360},[],[1362],{"type":50,"value":1363},"JsonResult",{"type":50,"value":1365}," — return ",{"type":44,"tag":66,"props":1367,"children":1369},{"className":1368},[],[1370],{"type":50,"value":1371},"Json(true)",{"type":50,"value":1373}," for valid, ",{"type":44,"tag":66,"props":1375,"children":1377},{"className":1376},[],[1378],{"type":50,"value":1379},"Json(\"Error message\")",{"type":50,"value":1381}," for invalid.",{"type":44,"tag":1353,"props":1383,"children":1384},{},[1385,1387,1393],{"type":50,"value":1386},"Accepts ",{"type":44,"tag":66,"props":1388,"children":1390},{"className":1389},[],[1391],{"type":50,"value":1392},"GET",{"type":50,"value":1394}," requests (the default for remote validation AJAX calls).",{"type":44,"tag":1353,"props":1396,"children":1397},{},[1398],{"type":50,"value":1399},"Is accessible without authentication if the form is on a public page.",{"type":44,"tag":60,"props":1401,"children":1402},{},[1403],{"type":50,"value":1404},"Example validation action:",{"type":44,"tag":102,"props":1406,"children":1408},{"className":454,"code":1407,"language":456,"meta":110,"style":110},"[AcceptVerbs(\"GET\", \"POST\")]\npublic IActionResult IsEmailAvailable(string email)\n{\n    if (_userService.EmailExists(email))\n    {\n        return Json($\"Email {email} is already in use.\");\n    }\n    return Json(true);\n}\n",[1409],{"type":44,"tag":66,"props":1410,"children":1411},{"__ignoreMap":110},[1412,1420,1428,1435,1443,1450,1458,1465,1473],{"type":44,"tag":462,"props":1413,"children":1414},{"class":464,"line":29},[1415],{"type":44,"tag":462,"props":1416,"children":1417},{},[1418],{"type":50,"value":1419},"[AcceptVerbs(\"GET\", \"POST\")]\n",{"type":44,"tag":462,"props":1421,"children":1422},{"class":464,"line":473},[1423],{"type":44,"tag":462,"props":1424,"children":1425},{},[1426],{"type":50,"value":1427},"public IActionResult IsEmailAvailable(string email)\n",{"type":44,"tag":462,"props":1429,"children":1430},{"class":464,"line":482},[1431],{"type":44,"tag":462,"props":1432,"children":1433},{},[1434],{"type":50,"value":479},{"type":44,"tag":462,"props":1436,"children":1437},{"class":464,"line":491},[1438],{"type":44,"tag":462,"props":1439,"children":1440},{},[1441],{"type":50,"value":1442},"    if (_userService.EmailExists(email))\n",{"type":44,"tag":462,"props":1444,"children":1445},{"class":464,"line":500},[1446],{"type":44,"tag":462,"props":1447,"children":1448},{},[1449],{"type":50,"value":497},{"type":44,"tag":462,"props":1451,"children":1452},{"class":464,"line":509},[1453],{"type":44,"tag":462,"props":1454,"children":1455},{},[1456],{"type":50,"value":1457},"        return Json($\"Email {email} is already in use.\");\n",{"type":44,"tag":462,"props":1459,"children":1460},{"class":464,"line":25},[1461],{"type":44,"tag":462,"props":1462,"children":1463},{},[1464],{"type":50,"value":559},{"type":44,"tag":462,"props":1466,"children":1467},{"class":464,"line":526},[1468],{"type":44,"tag":462,"props":1469,"children":1470},{},[1471],{"type":50,"value":1472},"    return Json(true);\n",{"type":44,"tag":462,"props":1474,"children":1475},{"class":464,"line":535},[1476],{"type":44,"tag":462,"props":1477,"children":1478},{},[1479],{"type":50,"value":568},{"type":44,"tag":60,"props":1481,"children":1482},{},[1483,1485,1490],{"type":50,"value":1484},"Remote validation depends on ",{"type":44,"tag":66,"props":1486,"children":1488},{"className":1487},[],[1489],{"type":50,"value":279},{"type":50,"value":1491}," — ensure Step 4 is complete.",{"type":44,"tag":114,"props":1493,"children":1495},{"id":1494},"step-6-migrate-fluentvalidation-integration",[1496],{"type":50,"value":1497},"Step 6: Migrate FluentValidation Integration",{"type":44,"tag":60,"props":1499,"children":1500},{},[1501],{"type":50,"value":1502},"If the project uses FluentValidation, the registration mechanism changed.",{"type":44,"tag":60,"props":1504,"children":1505},{},[1506,1508,1514],{"type":50,"value":1507},"Before (ASP.NET Framework with ",{"type":44,"tag":66,"props":1509,"children":1511},{"className":1510},[],[1512],{"type":50,"value":1513},"FluentValidation.Mvc",{"type":50,"value":451},{"type":44,"tag":102,"props":1516,"children":1518},{"className":454,"code":1517,"language":456,"meta":110,"style":110},"\u002F\u002F Global.asax.cs\nFluentValidationModelValidatorProvider.Configure();\n",[1519],{"type":44,"tag":66,"props":1520,"children":1521},{"__ignoreMap":110},[1522,1530],{"type":44,"tag":462,"props":1523,"children":1524},{"class":464,"line":29},[1525],{"type":44,"tag":462,"props":1526,"children":1527},{},[1528],{"type":50,"value":1529},"\u002F\u002F Global.asax.cs\n",{"type":44,"tag":462,"props":1531,"children":1532},{"class":464,"line":473},[1533],{"type":44,"tag":462,"props":1534,"children":1535},{},[1536],{"type":50,"value":1537},"FluentValidationModelValidatorProvider.Configure();\n",{"type":44,"tag":60,"props":1539,"children":1540},{},[1541,1542,1548],{"type":50,"value":852},{"type":44,"tag":66,"props":1543,"children":1545},{"className":1544},[],[1546],{"type":50,"value":1547},"FluentValidation.AspNetCore",{"type":50,"value":451},{"type":44,"tag":102,"props":1550,"children":1552},{"className":454,"code":1551,"language":456,"meta":110,"style":110},"\u002F\u002F Program.cs\nbuilder.Services.AddControllersWithViews()\n    .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining\u003CProductValidator>());\n",[1553],{"type":44,"tag":66,"props":1554,"children":1555},{"__ignoreMap":110},[1556,1564,1572],{"type":44,"tag":462,"props":1557,"children":1558},{"class":464,"line":29},[1559],{"type":44,"tag":462,"props":1560,"children":1561},{},[1562],{"type":50,"value":1563},"\u002F\u002F Program.cs\n",{"type":44,"tag":462,"props":1565,"children":1566},{"class":464,"line":473},[1567],{"type":44,"tag":462,"props":1568,"children":1569},{},[1570],{"type":50,"value":1571},"builder.Services.AddControllersWithViews()\n",{"type":44,"tag":462,"props":1573,"children":1574},{"class":464,"line":482},[1575],{"type":44,"tag":462,"props":1576,"children":1577},{},[1578],{"type":50,"value":1579},"    .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining\u003CProductValidator>());\n",{"type":44,"tag":60,"props":1581,"children":1582},{},[1583,1588,1590,1595,1597,1603,1604,1609],{"type":44,"tag":350,"props":1584,"children":1585},{},[1586],{"type":50,"value":1587},"Package change",{"type":50,"value":1589},": Replace ",{"type":44,"tag":66,"props":1591,"children":1593},{"className":1592},[],[1594],{"type":50,"value":1513},{"type":50,"value":1596}," or ",{"type":44,"tag":66,"props":1598,"children":1600},{"className":1599},[],[1601],{"type":50,"value":1602},"FluentValidation.WebApi",{"type":50,"value":685},{"type":44,"tag":66,"props":1605,"children":1607},{"className":1606},[],[1608],{"type":50,"value":1547},{"type":50,"value":1610}," in the project file.",{"type":44,"tag":1612,"props":1613,"children":1614},"blockquote",{},[1615,1641],{"type":44,"tag":60,"props":1616,"children":1617},{},[1618,1623,1625,1631,1633,1639],{"type":44,"tag":350,"props":1619,"children":1620},{},[1621],{"type":50,"value":1622},"Note:",{"type":50,"value":1624}," ",{"type":44,"tag":66,"props":1626,"children":1628},{"className":1627},[],[1629],{"type":50,"value":1630},"AddFluentValidation()",{"type":50,"value":1632}," on ",{"type":44,"tag":66,"props":1634,"children":1636},{"className":1635},[],[1637],{"type":50,"value":1638},"IMvcBuilder",{"type":50,"value":1640}," was deprecated in FluentValidation 11+. For newer versions, use manual registration:",{"type":44,"tag":102,"props":1642,"children":1644},{"className":454,"code":1643,"language":456,"meta":110,"style":110},"builder.Services.AddValidatorsFromAssemblyContaining\u003CProductValidator>();\nbuilder.Services.AddFluentValidationAutoValidation();\nbuilder.Services.AddFluentValidationClientsideAdapters(); \u002F\u002F optional: client-side support\n",[1645],{"type":44,"tag":66,"props":1646,"children":1647},{"__ignoreMap":110},[1648,1656,1664],{"type":44,"tag":462,"props":1649,"children":1650},{"class":464,"line":29},[1651],{"type":44,"tag":462,"props":1652,"children":1653},{},[1654],{"type":50,"value":1655},"builder.Services.AddValidatorsFromAssemblyContaining\u003CProductValidator>();\n",{"type":44,"tag":462,"props":1657,"children":1658},{"class":464,"line":473},[1659],{"type":44,"tag":462,"props":1660,"children":1661},{},[1662],{"type":50,"value":1663},"builder.Services.AddFluentValidationAutoValidation();\n",{"type":44,"tag":462,"props":1665,"children":1666},{"class":464,"line":482},[1667],{"type":44,"tag":462,"props":1668,"children":1669},{},[1670],{"type":50,"value":1671},"builder.Services.AddFluentValidationClientsideAdapters(); \u002F\u002F optional: client-side support\n",{"type":44,"tag":60,"props":1673,"children":1674},{},[1675,1677,1683],{"type":50,"value":1676},"Individual ",{"type":44,"tag":66,"props":1678,"children":1680},{"className":1679},[],[1681],{"type":50,"value":1682},"AbstractValidator\u003CT>",{"type":50,"value":1684}," implementations require no changes — the validator classes themselves are framework-agnostic.",{"type":44,"tag":114,"props":1686,"children":1688},{"id":1687},"step-7-verify-validation-behavior",[1689],{"type":50,"value":1690},"Step 7: Verify Validation Behavior",{"type":44,"tag":60,"props":1692,"children":1693},{},[1694],{"type":50,"value":1695},"After migration, verify:",{"type":44,"tag":1349,"props":1697,"children":1698},{},[1699,1709,1719,1728,1738],{"type":44,"tag":1353,"props":1700,"children":1701},{},[1702,1707],{"type":44,"tag":350,"props":1703,"children":1704},{},[1705],{"type":50,"value":1706},"Build succeeds",{"type":50,"value":1708}," with no validation-related compilation errors.",{"type":44,"tag":1353,"props":1710,"children":1711},{},[1712,1717],{"type":44,"tag":350,"props":1713,"children":1714},{},[1715],{"type":50,"value":1716},"Server-side validation",{"type":50,"value":1718}," rejects invalid input and returns appropriate error messages.",{"type":44,"tag":1353,"props":1720,"children":1721},{},[1722,1726],{"type":44,"tag":350,"props":1723,"children":1724},{},[1725],{"type":50,"value":262},{"type":50,"value":1727}," (if used) shows errors before form submission.",{"type":44,"tag":1353,"props":1729,"children":1730},{},[1731,1736],{"type":44,"tag":350,"props":1732,"children":1733},{},[1734],{"type":50,"value":1735},"API validation",{"type":50,"value":1737}," (if applicable) returns the expected error response format.",{"type":44,"tag":1353,"props":1739,"children":1740},{},[1741,1745],{"type":44,"tag":350,"props":1742,"children":1743},{},[1744],{"type":50,"value":292},{"type":50,"value":1746}," endpoints respond correctly to AJAX calls.",{"type":44,"tag":53,"props":1748,"children":1750},{"id":1749},"troubleshooting",[1751],{"type":50,"value":1752},"Troubleshooting",{"type":44,"tag":60,"props":1754,"children":1755},{},[1756,1766,1768,1774],{"type":44,"tag":350,"props":1757,"children":1758},{},[1759,1764],{"type":44,"tag":66,"props":1760,"children":1762},{"className":1761},[],[1763],{"type":50,"value":579},{"type":50,"value":1765}," returns null.",{"type":50,"value":1767}," The service is not registered in DI. Register it in ",{"type":44,"tag":66,"props":1769,"children":1771},{"className":1770},[],[1772],{"type":50,"value":1773},"Program.cs",{"type":50,"value":1775}," before it can be resolved during validation.",{"type":44,"tag":60,"props":1777,"children":1778},{},[1779,1784,1786,1792,1793,1798,1800,1805,1807,1812,1814,1819],{"type":44,"tag":350,"props":1780,"children":1781},{},[1782],{"type":50,"value":1783},"Client-side validation not firing.",{"type":50,"value":1785}," Ensure ",{"type":44,"tag":66,"props":1787,"children":1789},{"className":1788},[],[1790],{"type":50,"value":1791},"jquery",{"type":50,"value":273},{"type":44,"tag":66,"props":1794,"children":1796},{"className":1795},[],[1797],{"type":50,"value":271},{"type":50,"value":1799},", and ",{"type":44,"tag":66,"props":1801,"children":1803},{"className":1802},[],[1804],{"type":50,"value":279},{"type":50,"value":1806}," load in that order, and that ",{"type":44,"tag":66,"props":1808,"children":1810},{"className":1809},[],[1811],{"type":50,"value":1260},{"type":50,"value":1813}," is included in the view's ",{"type":44,"tag":66,"props":1815,"children":1817},{"className":1816},[],[1818],{"type":50,"value":1145},{"type":50,"value":1820}," section.",{"type":44,"tag":60,"props":1822,"children":1823},{},[1824,1836,1838,1843,1845,1851,1853,1858],{"type":44,"tag":350,"props":1825,"children":1826},{},[1827,1829,1834],{"type":50,"value":1828},"API returns ",{"type":44,"tag":66,"props":1830,"children":1832},{"className":1831},[],[1833],{"type":50,"value":758},{"type":50,"value":1835}," instead of the expected error format.",{"type":50,"value":1837}," The ",{"type":44,"tag":66,"props":1839,"children":1841},{"className":1840},[],[1842],{"type":50,"value":87},{"type":50,"value":1844}," attribute enables automatic model validation with the RFC 7807 response shape. Customize via ",{"type":44,"tag":66,"props":1846,"children":1848},{"className":1847},[],[1849],{"type":50,"value":1850},"InvalidModelStateResponseFactory",{"type":50,"value":1852}," or suppress with ",{"type":44,"tag":66,"props":1854,"children":1856},{"className":1855},[],[1857],{"type":50,"value":1024},{"type":50,"value":1859}," (see Step 3).",{"type":44,"tag":60,"props":1861,"children":1862},{},[1863,1868,1870,1875,1877,1883,1884,1890],{"type":44,"tag":350,"props":1864,"children":1865},{},[1866],{"type":50,"value":1867},"FluentValidation validators not executing.",{"type":50,"value":1869}," Confirm the ",{"type":44,"tag":66,"props":1871,"children":1873},{"className":1872},[],[1874],{"type":50,"value":1547},{"type":50,"value":1876}," package is installed and validators are registered via ",{"type":44,"tag":66,"props":1878,"children":1880},{"className":1879},[],[1881],{"type":50,"value":1882},"AddValidatorsFromAssemblyContaining\u003CT>()",{"type":50,"value":1596},{"type":44,"tag":66,"props":1885,"children":1887},{"className":1886},[],[1888],{"type":50,"value":1889},"RegisterValidatorsFromAssemblyContaining\u003CT>()",{"type":50,"value":1891},".",{"type":44,"tag":53,"props":1893,"children":1895},{"id":1894},"success-criteria",[1896],{"type":50,"value":1897},"Success Criteria",{"type":44,"tag":1899,"props":1900,"children":1901},"ul",{},[1902,1925,1937,1949,1966,1978,2003],{"type":44,"tag":1353,"props":1903,"children":1904},{},[1905,1906,1911,1913,1918,1920],{"type":50,"value":420},{"type":44,"tag":66,"props":1907,"children":1909},{"className":1908},[],[1910],{"type":50,"value":426},{"type":50,"value":1912}," classes use ",{"type":44,"tag":66,"props":1914,"children":1916},{"className":1915},[],[1917],{"type":50,"value":579},{"type":50,"value":1919}," instead of ",{"type":44,"tag":66,"props":1921,"children":1923},{"className":1922},[],[1924],{"type":50,"value":449},{"type":44,"tag":1353,"props":1926,"children":1927},{},[1928,1930,1935],{"type":50,"value":1929},"API controllers leverage ",{"type":44,"tag":66,"props":1931,"children":1933},{"className":1932},[],[1934],{"type":50,"value":87},{"type":50,"value":1936}," automatic validation or explicitly opt out",{"type":44,"tag":1353,"props":1938,"children":1939},{},[1940,1942,1947],{"type":50,"value":1941},"Client-side validation scripts load via ",{"type":44,"tag":66,"props":1943,"children":1945},{"className":1944},[],[1946],{"type":50,"value":1260},{"type":50,"value":1948}," and LibMan",{"type":44,"tag":1353,"props":1950,"children":1951},{},[1952,1957,1959,1964],{"type":44,"tag":66,"props":1953,"children":1955},{"className":1954},[],[1956],{"type":50,"value":79},{"type":50,"value":1958}," validation endpoints return ",{"type":44,"tag":66,"props":1960,"children":1962},{"className":1961},[],[1963],{"type":50,"value":1363},{"type":50,"value":1965}," and accept GET requests",{"type":44,"tag":1353,"props":1967,"children":1968},{},[1969,1971,1976],{"type":50,"value":1970},"FluentValidation uses ",{"type":44,"tag":66,"props":1972,"children":1974},{"className":1973},[],[1975],{"type":50,"value":1547},{"type":50,"value":1977}," package with correct registration",{"type":44,"tag":1353,"props":1979,"children":1980},{},[1981,1983,1988,1989,1994,1996,2001],{"type":50,"value":1982},"No references to ",{"type":44,"tag":66,"props":1984,"children":1986},{"className":1985},[],[1987],{"type":50,"value":449},{"type":50,"value":273},{"type":44,"tag":66,"props":1990,"children":1992},{"className":1991},[],[1993],{"type":50,"value":1513},{"type":50,"value":1995},", or ",{"type":44,"tag":66,"props":1997,"children":1999},{"className":1998},[],[2000],{"type":50,"value":1602},{"type":50,"value":2002}," remain",{"type":44,"tag":1353,"props":2004,"children":2005},{},[2006],{"type":50,"value":2007},"Project builds without errors",{"type":44,"tag":2009,"props":2010,"children":2011},"style",{},[2012],{"type":50,"value":2013},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":2015,"total":2111},[2016,2026,2040,2057,2067,2080,2097],{"slug":2017,"name":2017,"fn":2018,"description":2019,"org":2020,"tags":2021,"stars":25,"repoUrl":26,"updatedAt":2025},"converting-to-cpm","migrate .NET projects to Central Package Management","Converts .NET projects and solutions to NuGet Central Package Management (CPM) with Directory.Packages.props. Use when the user wants to centralize, convert, align, or sync NuGet package versions across multiple projects, resolve version conflicts or mismatches, or get versions consistent across a solution or repository. Also triggers when packages are out of sync or drifting across projects.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2022,2023,2024],{"name":17,"slug":18,"type":15},{"name":9,"slug":8,"type":15},{"name":23,"slug":24,"type":15},"2026-07-18T05:14:13.971821",{"slug":2027,"name":2027,"fn":2028,"description":2029,"org":2030,"tags":2031,"stars":25,"repoUrl":26,"updatedAt":2039},"creating-winforms-custom-controls","create custom WinForms controls","Creates custom controls and UserControls for modern WinForms (.NET 6+). Use when deriving from Control, UserControl, Button, TextBox, or other base controls, implementing custom rendering with OnPaint overrides, creating composite controls with child control composition, adding custom properties with [Browsable], [Category], or [DefaultValue] attributes for Designer support, implementing INotifyPropertyChanged for control properties, handling Layout\u002FLayoutEngine for custom sizing, creating scrollable controls with AutoScrollMinSize, implementing dark mode theming, or building List\u002FGrid\u002FTree-like controls. Also triggers for \"custom UserControl\", \"derive from Control\", \"owner-drawn control\", \"Designer properties\", \"[Browsable] attribute\", \"custom WinForms control\", \"LayoutEngine\", \"AutoScroll control\", and \"themed control\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2032,2033,2036],{"name":17,"slug":18,"type":15},{"name":2034,"slug":2035,"type":15},"Windows","windows",{"name":2037,"slug":2038,"type":15},"WinUI","winui","2026-07-03T16:31:30.1325",{"slug":2041,"name":2041,"fn":2042,"description":2043,"org":2044,"tags":2045,"stars":25,"repoUrl":26,"updatedAt":2056},"managing-winforms-high-dpi-layout","design high-DPI responsive WinForms layouts","Implements WinForms high-DPI fluent layouts using TableLayoutPanel, FlowLayoutPanel, and DPI-aware design patterns. Use when creating responsive form layouts that must scale across different DPI settings, implementing Per Monitor V2 (PerMonitorV2) DPI awareness, working with TableLayoutPanel.RowStyles\u002FColumnStyles, using FlowLayoutPanel for dynamic layouts, replacing fixed pixel positioning with container-based layout, troubleshooting DPI scaling issues, or structuring nested layout containers. Also triggers for \"TableLayoutPanel\", \"FlowLayoutPanel\", \"high DPI WinForms\", \"PerMonitorV2\", \"DPI aware layout\", \"responsive WinForms\", \"scale UI\", \"AutoScaleMode\", and \"DPI scaling\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2046,2049,2052,2055],{"name":2047,"slug":2048,"type":15},"Design","design",{"name":2050,"slug":2051,"type":15},"Desktop","desktop",{"name":2053,"slug":2054,"type":15},"UI Components","ui-components",{"name":2034,"slug":2035,"type":15},"2026-07-18T05:14:12.982806",{"slug":2058,"name":2058,"fn":2059,"description":2060,"org":2061,"tags":2062,"stars":25,"repoUrl":26,"updatedAt":2066},"managing-winforms-mvvm","implement MVVM pattern in WinForms","Implements MVVM pattern in WinForms applications (.NET 8+) with ViewModels, Commands, and DataContext. Use when user explicitly requests MVVM implementation, setting up ViewModel with INotifyPropertyChanged or ObservableObject, implementing ICommand or RelayCommand, wiring Form.DataContext, binding controls to ViewModel properties, creating Commands for button clicks, or separating business logic from UI code. Also triggers for \"WinForms MVVM\", \"ViewModel in WinForms\", \"INotifyPropertyChanged\", \"DataContext binding\", \"Command pattern WinForms\", \"ObservableObject\", \"RelayCommand\", and \"testable WinForms\". Also use when fixing existing MVVM code or when guided by winforms-feature-adoption scenario. DO NOT USE FOR: automatic application during version upgrades (opt-in only).\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2063,2064,2065],{"name":17,"slug":18,"type":15},{"name":2034,"slug":2035,"type":15},{"name":2037,"slug":2038,"type":15},"2026-07-18T05:14:11.951511",{"slug":2068,"name":2068,"fn":2069,"description":2070,"org":2071,"tags":2072,"stars":25,"repoUrl":26,"updatedAt":2079},"migrating-aspnet-framework-to-core","migrate ASP.NET Framework to Core","Orchestrates migration of ASP.NET Framework (System.Web) MVC and WebAPI projects to ASP.NET Core. Covers only old .NET Framework web projects — not applicable to ASP.NET Core or modern .NET web projects (those are already on the target stack). Defines the ordered phase sequence (project file, host, config, DI, controllers, middleware, auth, views, cleanup), satellite skill dispatch, and migration unit breakdown for both in-place and side-by-side modes. Use when executing a task that upgrades a project with System.Web dependencies, HttpModules, HttpHandlers, MVC controllers, WebAPI controllers, or Global.asax. Also triggers for \"migrate ASP.NET to Core\", \"upgrade MVC project\", \"convert WebAPI to ASP.NET Core\". Not applicable to class libraries unless they directly reference System.Web.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2073,2074,2075,2078],{"name":17,"slug":18,"type":15},{"name":20,"slug":21,"type":15},{"name":2076,"slug":2077,"type":15},"Backend","backend",{"name":23,"slug":24,"type":15},"2026-07-03T16:30:55.581898",{"slug":2081,"name":2081,"fn":2082,"description":2083,"org":2084,"tags":2085,"stars":25,"repoUrl":26,"updatedAt":2096},"migrating-documentdb-to-cosmos","migrate DocumentDB to Cosmos DB","Migrates from the deprecated Microsoft.Azure.DocumentDB SDK (V2) to the modern Microsoft.Azure.Cosmos SDK (V3) for Azure Cosmos DB. Use ONLY when Microsoft.Azure.DocumentDB has been flagged as deprecated or obsolete and must be replaced — not for version-bump scenarios where the V2 SDK is still supported.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2086,2089,2092,2095],{"name":2087,"slug":2088,"type":15},"Azure","azure",{"name":2090,"slug":2091,"type":15},"Cosmos DB","cosmos-db",{"name":2093,"slug":2094,"type":15},"Database","database",{"name":23,"slug":24,"type":15},"2026-07-03T16:31:21.932144",{"slug":2098,"name":2098,"fn":2099,"description":2100,"org":2101,"tags":2102,"stars":25,"repoUrl":26,"updatedAt":2110},"migrating-global-asax","migrate Global.asax to ASP.NET Core middleware","Migrates Global.asax application lifecycle events to ASP.NET Core middleware, startup configuration, and Program.cs. Use when upgrading ASP.NET Framework apps containing Global.asax or Global.asax.cs files. Triggers for \"migrate Global.asax\", \"convert application events\", \"move Application_Start to Program.cs\", \"replace Global.asax with middleware\", and ASP.NET-to-Core migration involving request lifecycle, error handling, or session management.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2103,2105,2106,2107],{"name":20,"slug":2104,"type":15},"aspnet-core",{"name":9,"slug":8,"type":15},{"name":23,"slug":24,"type":15},{"name":2108,"slug":2109,"type":15},"Modernization","modernization","2026-07-07T06:54:34.226435",19,{"items":2113,"total":2300},[2114,2136,2153,2174,2189,2206,2217,2230,2245,2260,2275,2288],{"slug":2115,"name":2115,"fn":2116,"description":2117,"org":2118,"tags":2119,"stars":2133,"repoUrl":2134,"updatedAt":2135},"rushstack-best-practices","manage Rush monorepos with best practices","Provides best practices and guidance for working with Rush monorepos. Use when the user is working in a Rush-based repository, asks about Rush commands (install, update, build, rebuild), needs help with project selection, dependency management, build caching, subspace configuration, or troubleshooting Rush-specific issues.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2120,2123,2126,2127,2130],{"name":2121,"slug":2122,"type":15},"Engineering","engineering",{"name":2124,"slug":2125,"type":15},"Local Development","local-development",{"name":9,"slug":8,"type":15},{"name":2128,"slug":2129,"type":15},"Project Management","project-management",{"name":2131,"slug":2132,"type":15},"Rush","rush",6484,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Frushstack","2026-04-06T18:34:44.965032",{"slug":2137,"name":2137,"fn":2138,"description":2139,"org":2140,"tags":2141,"stars":2150,"repoUrl":2151,"updatedAt":2152},"azure-ai-agents-persistent-dotnet","build AI agents with Azure .NET SDK","Azure AI Agents Persistent SDK for .NET. Low-level SDK for creating and managing AI agents with threads, messages, runs, and tools. Use for agent CRUD, conversation threads, streaming responses, function calling, file search, and code interpreter. Triggers: \"PersistentAgentsClient\", \"persistent agents\", \"agent threads\", \"agent runs\", \"streaming agents\", \"function calling agents .NET\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2142,2143,2146,2147],{"name":17,"slug":18,"type":15},{"name":2144,"slug":2145,"type":15},"Agents","agents",{"name":2087,"slug":2088,"type":15},{"name":2148,"slug":2149,"type":15},"LLM","llm",2804,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fskills","2026-07-03T16:32:10.297433",{"slug":2154,"name":2154,"fn":2155,"description":2156,"org":2157,"tags":2158,"stars":2150,"repoUrl":2151,"updatedAt":2173},"azure-ai-anomalydetector-java","build anomaly detection applications with Java","Build anomaly detection applications with Azure AI Anomaly Detector SDK for Java. Use when implementing univariate\u002Fmultivariate anomaly detection, time-series analysis, or AI-powered monitoring.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2159,2162,2163,2166,2169,2170],{"name":2160,"slug":2161,"type":15},"Analytics","analytics",{"name":2087,"slug":2088,"type":15},{"name":2164,"slug":2165,"type":15},"Data Analysis","data-analysis",{"name":2167,"slug":2168,"type":15},"Java","java",{"name":9,"slug":8,"type":15},{"name":2171,"slug":2172,"type":15},"Monitoring","monitoring","2026-05-13T06:14:16.261754",{"slug":2175,"name":2175,"fn":2176,"description":2177,"org":2178,"tags":2179,"stars":2150,"repoUrl":2151,"updatedAt":2188},"azure-ai-contentsafety-java","build content moderation applications with Azure AI","Build content moderation applications with Azure AI Content Safety SDK for Java. Use when implementing text\u002Fimage analysis, blocklist management, or harm detection for hate, violence, sexual content, and self-harm.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2180,2183,2184,2185],{"name":2181,"slug":2182,"type":15},"AI Infrastructure","ai-infrastructure",{"name":2087,"slug":2088,"type":15},{"name":2167,"slug":2168,"type":15},{"name":2186,"slug":2187,"type":15},"Security","security","2026-07-07T06:53:31.293235",{"slug":2190,"name":2190,"fn":2191,"description":2192,"org":2193,"tags":2194,"stars":2150,"repoUrl":2151,"updatedAt":2205},"azure-ai-contentsafety-py","detect harmful content with Azure AI Content Safety","Azure AI Content Safety SDK for Python. Use for detecting harmful content in text and images with multi-severity classification.\nTriggers: \"azure-ai-contentsafety\", \"ContentSafetyClient\", \"content moderation\", \"harmful content\", \"text analysis\", \"image analysis\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2195,2196,2199,2200,2201,2204],{"name":2087,"slug":2088,"type":15},{"name":2197,"slug":2198,"type":15},"Compliance","compliance",{"name":2148,"slug":2149,"type":15},{"name":9,"slug":8,"type":15},{"name":2202,"slug":2203,"type":15},"Python","python",{"name":2186,"slug":2187,"type":15},"2026-07-18T05:14:23.017504",{"slug":2207,"name":2207,"fn":2208,"description":2209,"org":2210,"tags":2211,"stars":2150,"repoUrl":2151,"updatedAt":2216},"azure-ai-language-conversations-py","implement conversational language understanding with Python","Implement Conversational Language Understanding (CLU) using the azure-ai-language-conversations Python SDK. Use when working with ConversationAnalysisClient to analyze conversation intent and entities, building NLP features, or integrating language understanding into applications.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2212,2213,2214,2215],{"name":2160,"slug":2161,"type":15},{"name":2087,"slug":2088,"type":15},{"name":2148,"slug":2149,"type":15},{"name":2202,"slug":2203,"type":15},"2026-07-31T05:54:29.068751",{"slug":2218,"name":2218,"fn":2219,"description":2220,"org":2221,"tags":2222,"stars":2150,"repoUrl":2151,"updatedAt":2229},"azure-ai-translation-text-py","translate text using Azure AI services","Azure AI Text Translation SDK for real-time text translation, transliteration, language detection, and dictionary lookup. Use for translating text content in applications.\nTriggers: \"text translation\", \"translator\", \"translate text\", \"transliterate\", \"TextTranslationClient\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2223,2226,2227,2228],{"name":2224,"slug":2225,"type":15},"API Development","api-development",{"name":2087,"slug":2088,"type":15},{"name":9,"slug":8,"type":15},{"name":2202,"slug":2203,"type":15},"2026-07-18T05:14:16.988376",{"slug":2231,"name":2231,"fn":2232,"description":2233,"org":2234,"tags":2235,"stars":2150,"repoUrl":2151,"updatedAt":2244},"azure-ai-vision-imageanalysis-py","analyze images with Azure AI Vision","Azure AI Vision Image Analysis SDK for captions, tags, objects, OCR, people detection, and smart cropping. Use for computer vision and image understanding tasks.\nTriggers: \"image analysis\", \"computer vision\", \"OCR\", \"object detection\", \"ImageAnalysisClient\", \"image caption\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2236,2237,2240,2243],{"name":2087,"slug":2088,"type":15},{"name":2238,"slug":2239,"type":15},"Computer Vision","computer-vision",{"name":2241,"slug":2242,"type":15},"Images","images",{"name":2202,"slug":2203,"type":15},"2026-07-18T05:14:18.007737",{"slug":2246,"name":2246,"fn":2247,"description":2248,"org":2249,"tags":2250,"stars":2150,"repoUrl":2151,"updatedAt":2259},"azure-appconfiguration-java","manage configuration with Azure App Configuration","Azure App Configuration SDK for Java. Centralized application configuration management with key-value settings, feature flags, and snapshots.\nTriggers: \"ConfigurationClient java\", \"app configuration java\", \"feature flag java\", \"configuration setting java\", \"azure config java\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2251,2252,2255,2258],{"name":2087,"slug":2088,"type":15},{"name":2253,"slug":2254,"type":15},"Configuration","configuration",{"name":2256,"slug":2257,"type":15},"Feature Flags","feature-flags",{"name":2167,"slug":2168,"type":15},"2026-07-03T16:32:01.278468",{"slug":2261,"name":2261,"fn":2262,"description":2263,"org":2264,"tags":2265,"stars":2150,"repoUrl":2151,"updatedAt":2274},"azure-cosmos-rust","build applications with Azure Cosmos DB","Azure Cosmos DB library for Rust (NoSQL API). Document CRUD, containers, and globally distributed data.\nTriggers: \"cosmos db rust\", \"CosmosClient rust\", \"document crud rust\", \"NoSQL rust\", \"partition key rust\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2266,2267,2268,2271],{"name":2090,"slug":2091,"type":15},{"name":2093,"slug":2094,"type":15},{"name":2269,"slug":2270,"type":15},"NoSQL","nosql",{"name":2272,"slug":2273,"type":15},"Rust","rust","2026-07-31T05:54:27.021432",{"slug":2276,"name":2276,"fn":2262,"description":2277,"org":2278,"tags":2279,"stars":2150,"repoUrl":2151,"updatedAt":2287},"azure-cosmos-ts","Azure Cosmos DB JavaScript\u002FTypeScript SDK (@azure\u002Fcosmos) for data plane operations. Use for CRUD operations on documents, queries, bulk operations, and container management. Triggers: \"Cosmos DB\", \"@azure\u002Fcosmos\", \"CosmosClient\", \"document CRUD\", \"NoSQL queries\", \"bulk operations\", \"partition key\", \"container.items\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2280,2281,2282,2283,2284],{"name":2090,"slug":2091,"type":15},{"name":2093,"slug":2094,"type":15},{"name":9,"slug":8,"type":15},{"name":2269,"slug":2270,"type":15},{"name":2285,"slug":2286,"type":15},"TypeScript","typescript","2026-07-03T16:31:19.368382",{"slug":2289,"name":2289,"fn":2290,"description":2291,"org":2292,"tags":2293,"stars":2150,"repoUrl":2151,"updatedAt":2299},"azure-data-tables-java","build table storage applications with Java","Build table storage applications with Azure Tables SDK for Java. Use when working with Azure Table Storage or Cosmos DB Table API for NoSQL key-value data, schemaless storage, or structured data at scale.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2294,2295,2296,2297,2298],{"name":2087,"slug":2088,"type":15},{"name":2090,"slug":2091,"type":15},{"name":2093,"slug":2094,"type":15},{"name":2167,"slug":2168,"type":15},{"name":2269,"slug":2270,"type":15},"2026-05-13T06:14:17.582229",267]