This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
GeoIP2-dotnet is MaxMind's official .NET client library for:
- GeoIP2/GeoLite2 Web Services: Country, City, and Insights endpoints
- GeoIP2/GeoLite2 Databases: Local MMDB file reading for various database types (City, Country, ASN, Anonymous IP, Anonymous Plus, ISP, etc.)
The library provides both web service clients and database readers that return strongly-typed model objects containing geographic, ISP, anonymizer, and other IP-related data.
MaxMind.GeoIP2/
├── Responses/ # Response models (CityResponse, InsightsResponse, etc.)
├── Model/ # Data models (City, Location, Traits, etc.)
├── Exceptions/ # Custom exceptions for error handling
├── Http/ # HTTP client infrastructure
├── DatabaseReader.cs # Local MMDB file reader
├── WebServiceClient.cs # HTTP client for MaxMind web services
└── I*Provider/I*Client interfaces for testing and DI
MaxMind.GeoIP2.UnitTests/
├── TestData/ # Test MMDB databases (via submodule)
├── DatabaseReaderTests.cs
├── WebServiceClientTests.cs
└── DeserializationTests.cs
Model and response classes use C# records with init properties and three
attribute types:
[JsonPropertyName]for JSON field mapping (System.Text.Json)[MapKey("field_name")]for MaxMind DB field mapping[MapKey("field_name", true)]for nested sub-objects (constructs from a sub-map rather than a scalar field)[Inject("field_name")]injects metadata like IP address[Network]injects network information for the IP
No constructors needed — deserialization uses property initialization.
Some features are only available in newer .NET versions (e.g., DateOnly in
.NET 6+). When adding features that use newer .NET types, ensure backward
compatibility with .NET Standard 2.0/2.1.
For date fields stored as strings in MMDB databases, use a backing field with an
internal string property for deserialization. Invalid values should throw a
GeoIP2Exception with enough context to identify the field and bad value:
#if NET6_0_OR_GREATER
private DateOnly? _networkLastSeen;
[JsonInclude]
[JsonPropertyName("network_last_seen")]
public DateOnly? NetworkLastSeen
{
get => _networkLastSeen;
init => _networkLastSeen = value;
}
[JsonIgnore]
[MapKey("network_last_seen")]
internal string? NetworkLastSeenString
{
get => _networkLastSeen?.ToString("o");
init => _networkLastSeen = value == null ? null
: DateOnly.TryParse(value, out var result) ? result
: throw new GeoIP2Exception(
$"Could not parse 'network_last_seen' value '{value}' as a valid date.");
}
#endif- DatabaseReader: Reads MMDB files via
MaxMind.Db. Thread-safe, reuse across lookups. May throwAddressNotFoundExceptionorInvalidOperationException. - WebServiceClient: Uses
HttpClient. Thread-safe, reuse for connection pooling. Supports sync and async. Supports DI viaIOptions<WebServiceClientOptions>andIHttpClientFactory.
GeoIP2Exception
├── AddressNotFoundException // IP not found in database or blocked
├── AuthenticationException // Invalid credentials
├── InvalidRequestException // Invalid request parameters
├── OutOfQueriesException // Query limit exceeded
├── PermissionRequiredException // Service requires higher tier
└── HttpException // Network/HTTP errors
# Build all projects
dotnet build MaxMind.GeoIP2
dotnet build MaxMind.GeoIP2.UnitTests
dotnet build MaxMind.GeoIP2.Benchmark
# Run all tests
dotnet test MaxMind.GeoIP2.UnitTests/MaxMind.GeoIP2.UnitTests.csproj
# Run specific test class
dotnet test --filter "FullyQualifiedName~DatabaseReaderTests"
# Run benchmark
dotnet run -f net8.0 -p MaxMind.GeoIP2.Benchmark/MaxMind.GeoIP2.Benchmark.csproj- Test databases are in
MaxMind.GeoIP2.UnitTests/TestData/MaxMind-DB/(git submodule) MAXMIND_TEST_BASE_DIRenv var must point to the test project directory (set automatically in CI)
-
Add the property with appropriate attributes:
[JsonInclude] [JsonPropertyName("field_name")] [MapKey("field_name")] // If supported by database public TypeName? FieldName { get; init; }
-
No constructor changes needed — adding a new
initproperty with a default value is not a breaking change. -
Update tests with assertions for the new field
-
Update
releasenotes.mdwith the change
- Determine if it extends an existing response (
AbstractCityResponse,AbstractCountryResponse,AbstractResponse) - Follow patterns from similar responses
- Add corresponding method in
DatabaseReader.csandIGeoIP2DatabaseReader.cs - Add web service method (if applicable) in
WebServiceClient.csandIGeoIP2WebServicesClient.cs - Provide comprehensive XML documentation for all public members
- Add unit tests in
DatabaseReaderTests.csorWebServiceClientTests.cs
- Use
[Obsolete]with helpful messages pointing to alternatives - Keep deprecated functionality working
- Update
releasenotes.mdwith deprecation notices - Use
#pragma warning disable 618around internal uses of deprecated APIs
Always update releasenotes.md for user-facing changes:
## X.Y.Z (YYYY-MM-DD)
- A new `PropertyName` property has been added to
`MaxMind.GeoIP2.Model.ModelClass`. This provides information about...
- The `OldProperty` property in `MaxMind.GeoIP2.Model.ModelClass` has been
marked `Obsolete`. Please use `NewProperty` instead.
- **BREAKING:** Description of any breaking changes (major versions only).- Removing or renaming existing properties
- Changing the type of existing properties
- Changing default values of existing properties
- TreatWarningsAsErrors and EnforceCodeStyleInBuild are enabled — code style violations and warnings are build errors
- Use
.editorconfigsettings for formatting - XML documentation required for all public types and members
- Prefer alphabetical ordering for
initproperties unless there is a preexisting logical grouping
- Target Frameworks: net10.0, net9.0, net8.0, netstandard2.1, netstandard2.0
- Language Version: C# 14.0
- Key dependencies and their versions are defined in
MaxMind.GeoIP2.csproj.
- API Documentation
- GeoIP2 Web Services Docs
- GeoIP2 Database Docs
- MaxMind DB Format
- GitHub Issues: https://github.com/maxmind/GeoIP2-dotnet/issues
Last Updated: 2026-03-20