PakValidate.DataAnnotations 1.2.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package PakValidate.DataAnnotations --version 1.2.0
                    
NuGet\Install-Package PakValidate.DataAnnotations -Version 1.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PakValidate.DataAnnotations" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PakValidate.DataAnnotations" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="PakValidate.DataAnnotations" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PakValidate.DataAnnotations --version 1.2.0
                    
#r "nuget: PakValidate.DataAnnotations, 1.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package PakValidate.DataAnnotations@1.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=PakValidate.DataAnnotations&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=PakValidate.DataAnnotations&version=1.2.0
                    
Install as a Cake Tool

🇵🇰 PakValidate

A comprehensive .NET validation library for Pakistani data formats. Validate CNIC, mobile numbers, NTN, IBAN, postal codes, landline numbers, vehicle plates, and STRN — with rich metadata extraction.

NuGet License: MIT

Features

Validator Validates Extracts
CNIC 13-digit national identity card Gender, Province, Locality Code
Mobile Pakistani mobile numbers (all formats) Carrier (Jazz/Telenor/Zong/Ufone), E.164 format
NTN National Tax Number (FBR) Standard vs CNIC-based type
IBAN Pakistani bank accounts (MOD-97) Bank name, Account number
Postal Code 5-digit postal codes Region/City
Landline City landline numbers Area code, City
Vehicle Plate Registration plates Registration city
STRN Sales Tax Registration Number RTO Jurisdiction

Installation

# Core library
dotnet add package PakValidate

# FluentValidation extensions (optional)
dotnet add package PakValidate.FluentValidation

# Data Annotations attributes (optional)
dotnet add package PakValidate.DataAnnotations

Quick Start

using PakValidate;

// Simple validation
bool isValid = Pak.Cnic.IsValid("35202-1234567-1"); // true

// Rich validation with metadata
var result = Pak.Cnic.Validate("35202-1234567-1");
if (result.IsValid)
{
    Console.WriteLine(result.Gender());    // Male
    Console.WriteLine(result.Province());  // Punjab
    Console.WriteLine(result.Formatted()); // 35202-1234567-1
}

// Mobile number with carrier detection
var mobile = Pak.Mobile.Validate("03001234567");
Console.WriteLine(mobile.Carrier());       // Jazz

// IBAN with bank identification
var iban = Pak.Iban.Validate("PK36SCBL0000001123456702");
Console.WriteLine(iban.BankName());        // Standard Chartered Pakistan

// Implicit bool conversion
if (Pak.Mobile.Validate(phoneNumber))
{
    // Valid!
}

Validation Approaches

Choose the approach that best fits your needs:

1. Direct API (Core Package)

using PakValidate;

var result = Pak.Cnic.Validate("35202-1234567-1");
result.ThrowIfInvalid();                              // Throw if invalid
var gender = result.Gender();                         // Extract metadata

// Batch validation
var batch = Pak.ValidateAll(
    (nameof(model.Cnic), () => Pak.Cnic.Validate(model.Cnic)),
    (nameof(model.Mobile), () => Pak.Mobile.Validate(model.Mobile))
);
foreach (var (field, error) in batch.GetErrors())
    Console.WriteLine($"{field}: {error}");

2. FluentValidation

using PakValidate.FluentValidation;
using FluentValidation;

public class UserValidator : AbstractValidator<User>
{
    public UserValidator()
    {
        RuleFor(x => x.IdCard).NotEmpty().IsValidCnic();
        RuleFor(x => x.PhoneNumber).IsValidPakistaniMobile();
        RuleFor(x => x.BankAccount).IsValidPakistaniIban();
    }
}

// Use in controller/service
var validator = new UserValidator();
var result = validator.Validate(user);
if (!result.IsValid)
{
    foreach (var error in result.Errors)
        ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
}

Available methods: .IsValidCnic(), .IsValidPakistaniMobile(), .IsValidNtn(), .IsValidPakistaniIban(), .IsValidPakistaniPostalCode(), .IsValidPakistaniLandline(), .IsValidPakistaniVehiclePlate(), .IsValidStrn(), .IsValidPakistaniPhone()

3. Data Annotations

using PakValidate.DataAnnotations;
using System.ComponentModel.DataAnnotations;

public class User
{
    [Required]
    [PakCnic]
    public string IdCard { get; set; }

    [PakMobile]
    public string? PhoneNumber { get; set; }

    [PakIban]
    public string? BankAccount { get; set; }
}

// Validate
var context = new ValidationContext(user);
var results = new List<ValidationResult>();
if (!Validator.TryValidateObject(user, context, results, validateAllProperties: true))
{
    foreach (var error in results)
        ModelState.AddModelError(error.MemberNames.First(), error.ErrorMessage);
}

Available attributes: [PakCnic], [PakMobile], [PakNtn], [PakIban], [PakPostalCode], [PakLandline], [PakVehiclePlate], [PakStrn]

Note: Attributes return true for null. Use [Required] separately for mandatory fields.

Validators

CNIC

var result = Pak.Cnic.Validate("35202-1234567-1");
// Accepts: 35202-1234567-1, 3520212345671

// Metadata: Gender, Province, LocalityCode, Formatted
Console.WriteLine(result.Gender());     // Male
Console.WriteLine(result.Province());   // Punjab

Province codes: 1=KP, 2=FATA/Merged, 3=Punjab, 4=Sindh, 5=Balochistan, 6=Islamabad, 7=GB, 8=AJK

Mobile Number

var result = Pak.Mobile.Validate("03001234567");
// Accepts: 03001234567, 0300-1234567, +923001234567, 923001234567

// Metadata: Carrier, LocalFormat, InternationalFormat, E164, Prefix
Console.WriteLine(result.Carrier());              // Jazz
Console.WriteLine(result.InternationalFormat());  // +923001234567

Carriers: Jazz, Telenor, Zong, Ufone, SCO

NTN

var result = Pak.Ntn.Validate("1234567-8");
// Accepts: Standard (1234567-8) or CNIC-based (13-digit)

// Metadata: Type, Formatted

IBAN

var result = Pak.Iban.Validate("PK36SCBL0000001123456702");

// Metadata: BankCode, BankName, AccountNumber, CheckDigits, Formatted
Console.WriteLine(result.BankName());  // Standard Chartered Pakistan

Supports: 25+ Pakistani banks (HBL, UBL, MCB, Meezan, Allied, Bank Alfalah, etc.)

Postal Code

var result = Pak.PostalCode.Validate("44000");
// Metadata: Region, RegionPrefix

Console.WriteLine(result.Region());  // Islamabad

Landline

var result = Pak.Landline.Validate("051-1234567");
// Accepts: 021-12345678, 051-1234567, +92-51-1234567

// Metadata: AreaCode, City, LocalFormat, InternationalFormat

Vehicle Plate

var result = Pak.VehiclePlate.Validate("LEA-1234");
// Accepts: LEA-1234, RI-5678, ISB-123, G-1234 (including government plates)

// Metadata: Prefix, Number, RegistrationCity, Formatted

STRN

var result = Pak.Strn.Validate("1312345678901");
// 13-digit Sales Tax Registration Number

// Metadata: Jurisdiction, RegionCode, Formatted

Extension Methods

Metadata Access

All validation results support property-style metadata access:

var result = Pak.Cnic.Validate("35202-1234567-1");

string? gender = result.Gender();      // Cleaner than result["Gender"]
string? province = result.Province();
string? formatted = result.Formatted();

// Works with any validator
var mobile = Pak.Mobile.Validate("03001234567");
var carrier = mobile.Carrier();
var e164 = mobile.E164();

// Generic access for any metadata
string? value = result.GetMetadata("CustomKey");

Error Handling

var result = Pak.Cnic.Validate(cnic);

// Throw if invalid
result.ThrowIfInvalid();

// Check if invalid (inverse of IsValid)
if (result.IsInvalid())
    Console.WriteLine(result.ErrorMessage);

// Get error with default
string error = result.GetErrorOrDefault("Invalid input");

Result Mapping

Transform validation results using functional patterns:

// Extract metadata on success
string? gender = Pak.Cnic.Validate(cnic).Map(
    r => r.Gender(),
    error => null
);

// Complex transformation
var mobileInfo = Pak.Mobile.Validate(phone).Map(
    r => new { Carrier = r.Carrier(), Format = r.LocalFormat() },
    error => null
);

// Execute side effects
Pak.Cnic.Validate(cnic).Match(
    r => Console.WriteLine($"Valid: {r.Gender()}"),
    error => Console.WriteLine($"Invalid: {error}")
);

Batch Validation

Validate multiple fields and collect all errors:

var batch = Pak.ValidateAll(
    (nameof(model.Cnic), () => Pak.Cnic.Validate(model.Cnic)),
    (nameof(model.Mobile), () => Pak.Mobile.Validate(model.Mobile)),
    (nameof(model.Iban), () => Pak.Iban.Validate(model.Iban))
);

// Check all results
if (batch.IsValid) { /* all passed */ }
if (batch.IsInvalid()) { /* at least one failed */ }

// Get specific field error
var cnicError = batch.GetError(nameof(model.Cnic));

// Iterate all errors
foreach (var (field, error) in batch.GetErrors())
    Console.WriteLine($"{field}: {error}");

// Throw if any failed
batch.ThrowIfInvalid();

Returns:

  • IsValid — true if all validations passed
  • Errors — dictionary of failed fields (only failures)
  • Results — all validation results by field name

Custom Property Names

Batch validation works with any property naming:

public class User
{
    public string IdCard { get; set; }      // Different name!
    public string PhoneNumber { get; set; }
    public string BankAccount { get; set; }
}

var batch = Pak.ValidateAll(
    (nameof(User.IdCard), () => Pak.Cnic.Validate(user.IdCard)),
    (nameof(User.PhoneNumber), () => Pak.Mobile.Validate(user.PhoneNumber)),
    (nameof(User.BankAccount), () => Pak.Iban.Validate(user.BankAccount))
);

// Errors keyed by YOUR property names
if (!batch.IsValid)
{
    foreach (var (field, error) in batch.GetErrors())
        ModelState.AddModelError(field, error);
}

ValidationResult

Every Validate() call returns a ValidationResult:

public class ValidationResult
{
    bool IsValid { get; }              // Pass/fail
    string? ErrorMessage { get; }      // Error if invalid
    string? Sanitized { get; }         // Cleaned input (digits only)
    IReadOnlyDictionary<string, string> Metadata { get; } // Extracted data

    // Implicit bool conversion
    public static implicit operator bool(ValidationResult result);
}

Supported Frameworks

  • .NET 6.0
  • .NET 7.0
  • .NET 8.0
  • .NET 9.0
  • .NET 10.0

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.1 91 2/23/2026
1.3.0 90 2/23/2026
1.2.0 84 2/23/2026
1.1.0 88 2/23/2026