Chapter 32: Building custom MVC and Razor Pages components
This chapter introduces ways to extend Razor views and request validation with custom Tag Helpers, view components, custom validation attributes, and FluentValidation.
After this chapter you will be able to
- Custom Razor component options. Describe the chapter's two ways to extend Razor views: custom Tag Helpers for generating or conditionally changing HTML, and view components for independent page sections with their own logic and data access.
- Domain-specific validation attributes. Explain why an application may need custom DataAnnotations validation attributes, and describe that the chapter shows both a simple custom attribute and an attribute that uses services from the DI container.
- FluentValidation replacement approach. Describe FluentValidation as an alternative to the built-in attribute-based validation framework, and explain that it separates binding models from validation rules, which can make some validation logic easier to build.
- Custom Tag Helper purpose. Explain that custom Razor Tag Helpers let an application customize the HTML produced by Razor, including adding new elements, altering or removing existing markup, and transforming contained Razor content.
- Tag Helper activation and benefits. Describe how a Tag Helper augments an HTML tag when its matching attribute is present, using the Input Tag Helper's asp-for attribute as an example. Explain that Tag Helpers reduce duplicated markup, preserve HTML-oriented readable templates rather than replacing tags, and receive IDE IntelliSense support.
- Tag Helper rendering implementation. Describe how SystemInfoTagHelper derives from TagHelper and implements Process() so Razor invokes it when it finds the target element. Explain that Process() configures a div, its tag mode, and its inner HTML through TagHelperOutput.
- Element naming and attributes. Describe how the SystemInfoTagHelper class name maps to the Razor element <system-info> by removing the TagHelper suffix and converting the remaining name to kebab case. Explain that IncludeMachine and IncludeOS are exposed as Razor-settable attributes with HtmlAttributeName, and that HtmlTargetElement can override the target element name.
- Safe encoding and registration. Explain how to safely render generated system values by HTML-encoding them before passing them to SetHtmlContent(), or by passing unencoded input to SetContent() for automatic encoding. Describe registering the helper in _ViewImports.cshtml with @addTagHelper using either its fully qualified type and assembly or a wildcard for all helpers in an assembly.
- Attribute-based conditional rendering. Explain how IfTagHelper is applied through an if attribute on an existing HTML element and how its Boolean property is bound to that attribute value. Describe the different rendered outcomes when the value is true versus false.
- Output suppression and execution order. Describe how IfTagHelper removes content when RenderContent is false by setting TagHelperOutput.TagName to null and calling SuppressOutput(). Explain why its Order is set to int.MinValue when other Tag Helpers may be attached to the same element.
- Markdown Tag Helper rendering. Describe how a Markdown Tag Helper transforms Markdown placed inside a <markdown> element into rendered HTML. Explain that it first renders its child Razor content, then converts the resulting Markdown string with Markdig, replaces the child content with HTML, and removes the wrapping <markdown> element.
- Tag Helper component boundaries. Distinguish small, isolated reusable Tag Helper functionality from larger application-specific UI sections or components that call business-logic services. State that view components are the recommended choice for the larger cases.
- View component purpose. Explain that a view component encapsulates the business logic, database access, and rendering needed for a small page section that is independent of the main Razor Page or view. Distinguish it from a partial view, which encapsulates rendering of a view model but is not suited to independent business logic.
- Component implementation and invocation. Describe how a custom view component is typically implemented as a public, non-nested, nonabstract class derived from ViewComponent with an InvokeAsync method returning Task<IViewComponentResult>. Explain that it is invoked directly from a Razor view or layout with vc: Tag Helper syntax, and that InvokeAsync parameters are supplied as kebab-case element properties rather than through model binding.
- Component rendering results. Describe the rendering results a view component can produce: it normally uses View() to render a component partial view, can use Content() to return HTML-encoded text, and cannot return HTTP status codes or redirects. Explain how the recipe component selects an Unauthenticated template for anonymous users and the default template with database-loaded view models for authenticated users.
- Custom currency validation attribute. Describe how a custom ValidationAttribute can restrict CurrencyFrom and CurrencyTo to an allowed set of currency codes. Explain that the attribute is applied to binding-model properties and overrides IsValid() to return ValidationResult.Success for an allowed value or a ValidationResult containing an error for an invalid one.
- Service resolution in attributes. Explain why constructor dependency injection cannot be used in a ValidationAttribute: .NET attribute constructors can receive only constant values. Describe resolving a registered ICurrencyProvider from ValidationContext with GetService<T>() or GetRequiredService<T>() as the necessary service-locator workaround for a dynamic list of allowed currencies.
- FluentValidation as an alternative. Explain that Razor Pages and MVC use DataAnnotations-based validation by default, but that their validation framework can be replaced with FluentValidation. Describe FluentValidation as an open-source framework that defines validation rules in separate validator code rather than as attributes on binding-model properties.
- Reasons for separate validators. Describe the advantages of FluentValidation's separate, strongly typed validators: they avoid attribute-related dependency-injection limitations, make multi-property rules easier, are generally easier to test, and can better support single responsibility. Distinguish these benefits from the opposing preference to keep a model and its validation rules together with DataAnnotations.
- Standalone validator classes. Describe how FluentValidation moves validation rules out of a binding model and into a separate validator class per model, typically derived from AbstractValidator<T>. Explain that the model can retain the same structure while having no validation attributes.
- Fluent property rules. Explain that a validator defines property rules with RuleFor() followed by chained methods such as NotEmpty(), forming a fluent interface. Describe how Must() can hold custom or arbitrarily complex validation logic directly in the validator.
- Injected validator dependencies. Explain that FluentValidation validators are created through dependency injection, so a validator can receive services such as ICurrencyProvider directly in its constructor. Distinguish this from a custom DataAnnotations attribute obtaining a service through service location and an injected context object.
- FluentValidation application setup. Describe how to add FluentValidation to an MVC or Razor Pages application: install the FluentValidation.AspNetCore package, call AddFluentValidationAutoValidation() in Program.cs, and make validators available through dependency injection.
- DataAnnotations migration control. Explain that ASP.NET Core runs both DataAnnotations and FluentValidation validation unless DisableDataAnnotationsValidation is set to true; describe why disabling DataAnnotations is recommended after a migration rather than while validation is intentionally split.
What it builds on
- Using partial views to encapsulate markup (chapter 17) · The idea of partial views as reusable, encapsulated Razor markup.
- Catering to editors with Tag Helpers (chapter 18) · The role of Tag Helpers in making Razor markup easier to author.
- Validating binding models (chapter 16) · The use of validation for Razor Pages binding models, including DataAnnotations-based validation.
- Using services from the DI container (chapter 8) · Resolving and using services registered in the dependency injection container.
- Introducing Razor templates (chapter 17) · Razor templates as the mechanism for generating HTML.
- The Input and Textarea Tag Helpers (chapter 18) · The Input Tag Helper and its asp-for attribute.
- Using C# in Razor templates (chapter 17) · Mixing C# with Razor templates to generate dynamic HTML.
- Using conditional markup with the Environment Tag Helper (chapter 18) · the Environment Tag Helper for conditionally rendering markup by environment
Section by section
Introduction
This chapter introduces ways to extend Razor views and request validation with custom Tag Helpers, view components, custom validation attributes, and FluentValidation.
- Custom Razor component options: Describe the chapter's two ways to extend Razor views: custom Tag Helpers for generating or conditionally changing HTML, and view components for independent page sections with their own logic and data access.
Why it matters: These extensions let you package reusable UI behavior instead of putting all rendering and page-specific logic directly in a Razor Page or view. - Domain-specific validation attributes: Explain why an application may need custom DataAnnotations validation attributes, and describe that the chapter shows both a simple custom attribute and an attribute that uses services from the DI container.
Why it matters: Custom attributes allow declarative validation rules to express requirements specific to an application's domain. - FluentValidation replacement approach: Describe FluentValidation as an alternative to the built-in attribute-based validation framework, and explain that it separates binding models from validation rules, which can make some validation logic easier to build.
Why it matters: Choosing a separate validation-rule approach can improve the organization of complex validation when attributes are not the preferred fit.
Key terms: Tag Helpers, custom Tag Helpers, view components, partial views, Razor views, DataAnnotations, validation attribute, DI container, binding models, FluentValidation
32.1 Creating a custom Razor Tag Helper
This section introduces custom Razor Tag Helpers as a way to augment, remove, or transform HTML markup while retaining readable Razor templates and IDE support.
- Custom Tag Helper purpose: Explain that custom Razor Tag Helpers let an application customize the HTML produced by Razor, including adding new elements, altering or removing existing markup, and transforming contained Razor content.
Why it matters: Tag Helpers provide a reusable way to express common UI behavior directly in familiar HTML-like markup. - Tag Helper activation and benefits: Describe how a Tag Helper augments an HTML tag when its matching attribute is present, using the Input Tag Helper's asp-for attribute as an example. Explain that Tag Helpers reduce duplicated markup, preserve HTML-oriented readable templates rather than replacing tags, and receive IDE IntelliSense support.
Why it matters: Understanding how Tag Helpers attach to ordinary HTML helps you recognize when they are clearer and more maintainable than embedding repeated Razor or C# logic.
Key terms: Tag Helpers, custom Tag Helpers, Razor templates, HTML output, Input Tag Helper, asp-for, PageModel, IntelliSense, HTML Helpers
32.1.1 Printing environment information with a custom Tag Helper
This section builds and registers a SystemInfoTagHelper that renders selectable, safely encoded server information in Razor views, partials, and layouts.
- System information diagnostic purpose: Explain why a system-information Tag Helper can help identify the server, environment, or deployed application context that rendered a page, especially in production or a server farm. State why exposing this information may be inappropriate in production.
Why it matters: Diagnostic output can make deployment troubleshooting easier, but server details should not be unnecessarily exposed to users. - Tag Helper rendering implementation: Describe how SystemInfoTagHelper derives from TagHelper and implements Process() so Razor invokes it when it finds the target element. Explain that Process() configures a div, its tag mode, and its inner HTML through TagHelperOutput.
Why it matters: This is the basic pattern for making a custom Tag Helper turn Razor markup into generated HTML. - Element naming and attributes: Describe how the SystemInfoTagHelper class name maps to the Razor element <system-info> by removing the TagHelper suffix and converting the remaining name to kebab case. Explain that IncludeMachine and IncludeOS are exposed as Razor-settable attributes with HtmlAttributeName, and that HtmlTargetElement can override the target element name.
Why it matters: Naming and attribute binding let authors use a custom C# component naturally and with typed Razor markup. - Safe encoding and registration: Explain how to safely render generated system values by HTML-encoding them before passing them to SetHtmlContent(), or by passing unencoded input to SetContent() for automatic encoding. Describe registering the helper in _ViewImports.cshtml with @addTagHelper using either its fully qualified type and assembly or a wildcard for all helpers in an assembly.
Why it matters: Correct registration makes the helper available to Razor, while correct encoding prevents XSS and malformed output.
Key terms: SystemInfoTagHelper, server farm, environment, Environment Tag Helper, TagHelper, Process(), TagHelperOutput, Content.SetHtmlContent(), TagMode, <system-info>
32.1.2 Creating a custom Tag Helper to conditionally hide elements
This section builds an attribute-based Razor Tag Helper that conditionally removes an existing HTML element and its contents without wrapping markup in a C# if statement.
- Attribute-based conditional rendering: Explain how IfTagHelper is applied through an if attribute on an existing HTML element and how its Boolean property is bound to that attribute value. Describe the different rendered outcomes when the value is true versus false.
Why it matters: This lets Razor templates conditionally show content while keeping the markup readable and editable as HTML. - Output suppression and execution order: Describe how IfTagHelper removes content when RenderContent is false by setting TagHelperOutput.TagName to null and calling SuppressOutput(). Explain why its Order is set to int.MinValue when other Tag Helpers may be attached to the same element.
Why it matters: Removing unwanted output before other Tag Helpers run avoids doing unnecessary processing on an element that will not appear in the page. - Custom Tag Helper registration: State that a custom Tag Helper must be registered in _ViewImports.cshtml using the @addTagHelper directive before Razor templates can use it.
Why it matters: Registration makes the custom conditional attribute available to Razor views and pages.
Key terms: IfTagHelper, if attribute, RenderContent, Tag Helper, Process(), TagHelperOutput.TagName, SuppressOutput(), Order, int.MinValue, _ViewImports.cshtml
32.1.3 Creating a Tag Helper to convert Markdown to HTML
This section builds a custom Markdown Tag Helper that renders its child Razor content, converts it to HTML with Markdig, and explains where Tag Helpers fit compared with view components.
- Markdown Tag Helper rendering: Describe how a Markdown Tag Helper transforms Markdown placed inside a <markdown> element into rendered HTML. Explain that it first renders its child Razor content, then converts the resulting Markdown string with Markdig, replaces the child content with HTML, and removes the wrapping <markdown> element.
Why it matters: This pattern lets a reusable Tag Helper inspect and transform the content written between its start and end tags. - Asynchronous child-content processing: Explain why the Markdown Tag Helper implements ProcessAsync rather than Process. State that it calls the asynchronous GetChildContentAsync method, so the calling Tag Helper method must also be asynchronous to avoid problems such as thread starvation.
Why it matters: Keeping asynchronous calls asynchronous avoids blocking behavior that can harm an application's ability to serve requests. - Tag Helper component boundaries: Distinguish small, isolated reusable Tag Helper functionality from larger application-specific UI sections or components that call business-logic services. State that view components are the recommended choice for the larger cases.
Why it matters: Choosing the right UI component keeps small markup transformations from turning into oversized, hard-to-maintain Tag Helpers.
Key terms: Markdown Tag Helper, Markdig, GetChildContentAsync, ProcessAsync, Razor content, Process, thread starvation, Tag Helpers, view components, business-logic services
32.2 View components: Adding logic to partial views
This section explains when to use view components and how to build, invoke, and render isolated Razor UI sections that combine business logic, data access, and presentation.
- View component purpose: Explain that a view component encapsulates the business logic, database access, and rendering needed for a small page section that is independent of the main Razor Page or view. Distinguish it from a partial view, which encapsulates rendering of a view model but is not suited to independent business logic.
Why it matters: View components keep the main page focused on its primary content while making independently dynamic sections easier to organize and test. - Component implementation and invocation: Describe how a custom view component is typically implemented as a public, non-nested, nonabstract class derived from ViewComponent with an InvokeAsync method returning Task<IViewComponentResult>. Explain that it is invoked directly from a Razor view or layout with vc: Tag Helper syntax, and that InvokeAsync parameters are supplied as kebab-case element properties rather than through model binding.
Why it matters: This is the practical pattern for placing an isolated component into a Razor Page or layout and passing it the inputs it needs. - Component rendering results: Describe the rendering results a view component can produce: it normally uses View() to render a component partial view, can use Content() to return HTML-encoded text, and cannot return HTTP status codes or redirects. Explain how the recipe component selects an Unauthenticated template for anonymous users and the default template with database-loaded view models for authenticated users.
Why it matters: Understanding the restricted result types and template selection lets you design a component around rendering content rather than handling an HTTP response. - Component view locations and isolation: State that component templates are stored under Views/Shared/Components/ComponentName/TemplateName or Pages/Shared/Components/ComponentName/TemplateName, with Default.cshtml used when no template name is specified. Describe that a component is isolated from the containing Razor Page and its layout sections, though its own views can use their own layout and sections.
Why it matters: Correct template placement allows Razor to find component views, while understanding isolation prevents attempts to share layout sections across component and page contexts.
Key terms: view component, partial view, business logic, rendering logic, dependency injection, ViewComponent, InvokeAsync, IViewComponentResult, vc: prefix, kebab-case
32.3 Building a custom validation attribute
This section shows how to build a custom DataAnnotations validation attribute for domain-specific rules and how to obtain validation services through ValidationContext when needed.
- Custom currency validation attribute: Describe how a custom ValidationAttribute can restrict CurrencyFrom and CurrencyTo to an allowed set of currency codes. Explain that the attribute is applied to binding-model properties and overrides IsValid() to return ValidationResult.Success for an allowed value or a ValidationResult containing an error for an invalid one.
Why it matters: Custom attributes keep domain-specific input rules declarative and alongside the model properties they protect. - Validation context and execution: Explain that DataAnnotations validation runs after model binding and before an action or Razor Page handler executes, with the framework calling IsValid() for each ValidationAttribute. Describe how ValidationContext supplies the property value and context information, including ObjectInstance for comparing a property with other values on the top-level model.
Why it matters: Knowing when validation runs and what context it receives helps you place rules correctly and implement cross-property checks. - Server and client validation: Distinguish the server-side validation provided automatically by the custom attribute from client-side validation. State that server-side validation is sufficient for security and data consistency, while client-side validation gives immediate user feedback but requires JavaScript-library-specific integration such as a jQuery Validation adapter.
Why it matters: Client-side checks improve usability, but server-side validation remains the protection that must be enforced. - Service resolution in attributes: Explain why constructor dependency injection cannot be used in a ValidationAttribute: .NET attribute constructors can receive only constant values. Describe resolving a registered ICurrencyProvider from ValidationContext with GetService<T>() or GetRequiredService<T>() as the necessary service-locator workaround for a dynamic list of allowed currencies.
Why it matters: This workaround lets declarative validation use application services when hardcoded attribute arguments are not enough.
Key terms: DataAnnotations, ValidationAttribute, CurrencyCodeAttribute, IsValid(), ValidationResult, ValidationResult.Success, filter pipeline, model binding, ValidationContext, ObjectInstance
32.4 Replacing the validation framework with FluentValidation
This section introduces FluentValidation as a replaceable alternative to DataAnnotations validation in Razor Pages and MVC, explaining its benefits and trade-offs.
- FluentValidation as an alternative: Explain that Razor Pages and MVC use DataAnnotations-based validation by default, but that their validation framework can be replaced with FluentValidation. Describe FluentValidation as an open-source framework that defines validation rules in separate validator code rather than as attributes on binding-model properties.
Why it matters: Choosing a validation framework affects where validation rules live and how ASP.NET Core validates incoming models. - Reasons for separate validators: Describe the advantages of FluentValidation's separate, strongly typed validators: they avoid attribute-related dependency-injection limitations, make multi-property rules easier, are generally easier to test, and can better support single responsibility. Distinguish these benefits from the opposing preference to keep a model and its validation rules together with DataAnnotations.
Why it matters: The trade-off helps you choose whether separating validation logic is worth the added structure in an application. - Validation framework scope: State that validation is part of ASP.NET Core model binding, while minimal APIs have no built-in validation framework and can use a chosen framework such as DataAnnotations or another alternative. Contrast this freedom with Razor Pages and MVC, where DataAnnotations validation is built in by default.
Why it matters: Different ASP.NET Core application styles provide different starting points for validating request data.
Key terms: FluentValidation, DataAnnotations, Razor Pages, MVC, binding model, validator, dependency injection, DataAnnotations attributes, strongly typed, single-responsibility principle
32.4.1 Comparing FluentValidation with DataAnnotations attributes
This section compares FluentValidation with DataAnnotations by showing how standalone fluent validators make custom, reusable, dependency-injected, and cross-property validation easier.
- Standalone validator classes: Describe how FluentValidation moves validation rules out of a binding model and into a separate validator class per model, typically derived from AbstractValidator<T>. Explain that the model can retain the same structure while having no validation attributes.
Why it matters: Separating rules from models is the fundamental design difference between FluentValidation and DataAnnotations attributes. - Fluent property rules: Explain that a validator defines property rules with RuleFor() followed by chained methods such as NotEmpty(), forming a fluent interface. Describe how Must() can hold custom or arbitrarily complex validation logic directly in the validator.
Why it matters: The fluent rule syntax is how FluentValidation expresses both ordinary and custom validation without relying on custom attributes. - Reusable typed validation rules: Describe how a validation rule used in multiple validators can be extracted into an extension method, such as currency-code validation, and then reused. Explain that FluentValidation prevents applying a rule to an inappropriate property type, unlike a DataAnnotations attribute that could be placed on Quantity.
Why it matters: Reusable, type-appropriate rules reduce duplication and prevent invalid combinations of attributes and properties. - Injected validator dependencies: Explain that FluentValidation validators are created through dependency injection, so a validator can receive services such as ICurrencyProvider directly in its constructor. Distinguish this from a custom DataAnnotations attribute obtaining a service through service location and an injected context object.
Why it matters: Constructor injection makes a validator's required services explicit and avoids the extra indirection of service location in validation attributes. - Cross-property validation: Describe how the overload of Must() can receive both the model and the current property value, enabling rules that compare properties, such as requiring CurrencyTo to differ from CurrencyFrom. State that equivalent DataAnnotations validation is possible but needs more ceremony and is generally harder to test.
Why it matters: Many real validation rules concern relationships between values, not just one property in isolation.
Key terms: FluentValidation, binding model, AbstractValidator<T>, DataAnnotations attributes, RuleFor(), NotEmpty(), Must(), fluent interface, CurrencyConverterModelValidator, extension method
32.4.2 Adding FluentValidation to your application
This section explains how to install, configure, register, and organize FluentValidation validators in an MVC or Razor Pages application.
- FluentValidation application setup: Describe how to add FluentValidation to an MVC or Razor Pages application: install the FluentValidation.AspNetCore package, call AddFluentValidationAutoValidation() in Program.cs, and make validators available through dependency injection.
Why it matters: These steps replace ASP.NET Core's normal validation integration with FluentValidation's validator-based approach. - Validator registration options: Distinguish manual validator registration from FluentValidation's automatic registration option, which can register custom validators across the application.
Why it matters: Choosing automatic discovery can reduce repetitive DI registration, while manual registration lets you choose each validator's scope. - DataAnnotations migration control: Explain that ASP.NET Core runs both DataAnnotations and FluentValidation validation unless DisableDataAnnotationsValidation is set to true; describe why disabling DataAnnotations is recommended after a migration rather than while validation is intentionally split.
Why it matters: Avoiding two active validation systems keeps validation rules from being divided between separate places. - Validator code organization: Describe the section's organizational preference: validators can live anywhere if registered with DI, but placing them near their models is preferred; for Razor Pages binding models, the validator can be a nested class of the PageModel alongside the InputModel.
Why it matters: Keeping a validator close to its model makes related page input and validation logic easier to find.
Key terms: FluentValidation.AspNetCore, AddFluentValidationAutoValidation(), Program.cs, DI container, validators, manual registration, automatic registration, DisableDataAnnotationsValidation, DataAnnotations, FluentValidation
32.5 Summary
This summary recaps how to extend Razor rendering and model validation with custom Tag Helpers, view components, validation attributes, and FluentValidation.
Concepts explained in this chapter
- FluentValidationFluentValidation is a .NET validation library that defines object validation rules with a fluent programming interface.
- view componentViewComponent is the ASP.NET Core base class from which custom view component classes can derive.
- validationValidation is the application framework process that checks model data against rules to determine whether it is acceptable.
- validation attributeA custom validation attribute is an application-defined attribute that implements a specialized validation rule, typically by deriving from ValidationAttribute.
- Tag HelpersTag Helpers are ASP.NET Core components that add server-side processing behavior to HTML-like elements in Razor files.
- TagHelperOutputTagHelperOutput is the ASP.NET Core object representing the generated element, attributes, and content produced by a Tag Helper.
- AbstractValidatorAbstractValidator is the FluentValidation base class that supplies infrastructure and methods for defining validation rules for a model.
- MustMust is a FluentValidation rule method that applies a predicate to implement custom validation logic.
- RuleForRuleFor is a FluentValidation method that begins defining validation rules for a model property or member.
- @addTagHelper directiveThe @addTagHelper directive is a Razor directive that registers Tag Helpers for use in Razor files.
- dataannotations attributeDataAnnotations is the .NET attribute-based framework for declaring metadata and validation rules on types and their members.
- IsValidIsValid is the validation method that evaluates a value and returns the result of applying a validation rule.
- ValidationContextValidationContext is a .NET object that provides contextual information about the object and member being validated.
- AddFluentValidationAutoValidationAddFluentValidationAutoValidation is an ASP.NET Core service-registration method that enables automatic FluentValidation integration for MVC and Razor Pages.
- FluentValidation.AspNetCoreFluentValidation.AspNetCore is a NuGet package that integrates FluentValidation with ASP.NET Core application validation.
- HTML escapingHTML encoding is the transformation of characters in text into representations that browsers treat as literal content rather than markup or executable code.
- ProcessProcess is the synchronous TagHelper method that generates a Tag Helper's output.
- SetHtmlContentSetHtmlContent is a TagHelperOutput method that assigns HTML content without automatically encoding it.
- TagHelperTagHelper is the ASP.NET Core base class from which custom Tag Helper components derive.
- _ViewImports.cshtml_ViewImports.cshtml is a conventionally named Razor file whose directives and imports are implicitly applied to Razor views and pages in its directory and descendant directories.
- model bindingA model binder is the framework component that locates request values, converts them to appropriate types, and assigns them to bound objects or parameters.
- client-side validationClient-side validation is validation performed in client software, typically a browser, before submitted data reaches the server.
- direct dependencyAn explicit dependency is a dependency declared by a component's public construction or method interface and supplied from outside the component.
- fluent interfaceA fluent API is a chainable programming interface that configures entity mappings through method calls rather than declarative attributes.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
