DbfDataReader 2.2.0

dotnet add package DbfDataReader --version 2.2.0
                    
NuGet\Install-Package DbfDataReader -Version 2.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="DbfDataReader" Version="2.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DbfDataReader" Version="2.2.0" />
                    
Directory.Packages.props
<PackageReference Include="DbfDataReader" />
                    
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 DbfDataReader --version 2.2.0
                    
#r "nuget: DbfDataReader, 2.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 DbfDataReader@2.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=DbfDataReader&version=2.2.0
                    
Install as a Cake Addin
#tool nuget:?package=DbfDataReader&version=2.2.0
                    
Install as a Cake Tool

DbfDataReader

CI NuGet NuGet MyGet Build Status

DbfDataReader is a small fast .Net Core library for reading dBase, xBase, Clipper and FoxPro database files

Usage, to get summary info:

var dbfPath = "path\\file.dbf";
using (var dbfTable = new DbfTable(dbfPath, Encoding.UTF8))
{
    var header = dbfTable.Header;

    var versionDescription = header.VersionDescription;
    var hasMemo = dbfTable.Memo != null;
    var recordCount = header.RecordCount;

    foreach (var dbfColumn in dbfTable.Columns)
    {
        var name = dbfColumn.ColumnName;
        var columnType = dbfColumn.ColumnType;
        var length = dbfColumn.Length;
        var decimalCount = dbfColumn.DecimalCount;
    }
}

and to iterate over the rows:

var skipDeleted = true;

var dbfPath = "path/file.dbf";
using (var dbfTable = new DbfTable(dbfPath, Encoding.UTF8))
{        
    var dbfRecord = new DbfRecord(dbfTable);

    while (dbfTable.Read(dbfRecord))
    {
        if (skipDeleted && dbfRecord.IsDeleted)
        {
            continue;
        }

        foreach (var dbfValue in dbfRecord.Values)
        {
            var stringValue = dbfValue.ToString();
            var obj = dbfValue.GetValue();
        }
    }
}

There is also an implementation of DbDataReader:

var options = new DbfDataReaderOptions
{
    SkipDeletedRecords = true
    // Encoding = EncodingProvider.GetEncoding(1252);
};

var dbfPath = "path/file.dbf";
using (var dbfDataReader = new DbfDataReader(dbfPath, options))
{
    while (dbfDataReader.Read())
    {
        var valueCol1 = dbfDataReader.GetString(0);
        var valueCol2 = dbfDataReader.GetDecimal(1);
        var valueCol3 = dbfDataReader.GetDateTime(2);
        var valueCol4 = dbfDataReader.GetInt32(3);
    }
}

which also means you can bulk copy to MS SqlServer:

var options = new DbfDataReaderOptions
{
    SkipDeletedRecords = true
    // Encoding = EncodingProvider.GetEncoding(1252);
};

var dbfPath = "path/file.dbf";
using (var dbfDataReader = new DbfDataReader(dbfPath, options))
{
    using (var bulkCopy = new SqlBulkCopy(connection))
    {
        bulkCopy.DestinationTableName = "DestinationTableName";

        try
        {
            bulkCopy.WriteToServer(dbfDataReader);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error importing: dbf file: '{dbfPath}', exception: {ex.Message}");
        }
    }
}

Records can also be read asynchronously — DbfDataReader overrides ReadAsync, and DbfTable has ReadAsync/ReadRecordAsync counterparts. Each record is fetched with a single buffered asynchronous read and parsed in memory:

var dbfPath = "path/file.dbf";
using (var dbfDataReader = new DbfDataReader(dbfPath))
{
    while (await dbfDataReader.ReadAsync(cancellationToken))
    {
        var valueCol1 = dbfDataReader.GetString(0);
    }
}

Records can be accessed randomly by zero-based index using Seek, available on both DbfTable and DbfDataReader. The index of the record most recently read is available as RecordIndex:

var dbfPath = "path/file.dbf";
using (var dbfDataReader = new DbfDataReader(dbfPath))
{
    dbfDataReader.Seek(41); // position at the 42nd record

    if (dbfDataReader.Read())
    {
        var recordIndex = dbfDataReader.RecordIndex; // 41
        var valueCol1 = dbfDataReader.GetString(0);
    }
}

Visual FoxPro compound index files (.cdx) can be opened and searched, and search results combined with Seek to jump straight to the matching records — CdxKeyEntry.RecordIndex converts the index's one-based record numbers to the zero-based indexes Seek expects:

using DbfDataReader.Cdx;

var dbfPath = "path/file.dbf";
var cdxPath = "path/file.cdx";

using (var dbfTable = new DbfTable(dbfPath))
using (var cdxFile = new CdxFile(cdxPath, dbfTable.CurrentEncoding))
{
    var tagNames = cdxFile.TagNames;             // the named indexes ("tags") in the file

    var index = cdxFile.GetIndex("CONTACT_ID");  // one tag; index.KeyExpression describes the key
    var dbfRecord = new DbfRecord(dbfTable);

    foreach (var entry in index.Search("C0000000042"))
    {
        dbfTable.Seek(entry.RecordIndex);
        dbfTable.Read(dbfRecord);
        // dbfRecord now holds the matching row
    }
}

CdxIndex also supports EnumerateEntries() (full in-order scan), Count(), and a Search(Func<byte[], int>) overload for range or prefix searches. Current limitations: only ascending indexes with byte-wise (MACHINE collation) character keys are searchable, index key expressions are exposed as text but not evaluated, and index entries include deleted records (check DbfRecord.IsDeleted after seeking).

Rows can be mapped straight to your own types. DbfTable.Query<T>() is a typed query builder whose Where/OrderBy lambdas are translated into the same query engine — the type's public settable properties define which columns are read, matched by name (exact first, then case-insensitively):

public class GpsPoint
{
    public string Point_ID { get; set; }
    public decimal? Max_PDOP { get; set; }
    public DateTime? Date_Visit { get; set; }
}

using (var dbfTable = new DbfTable("path/dbase_03.dbf"))
{
    var points = dbfTable.Query<GpsPoint>()
        .Where(p => p.Max_PDOP >= 3.5m && p.Point_ID.StartsWith("A"))
        .OrderByDescending(p => p.Date_Visit)
        .Take(10)
        .ToList(); // also foreach, First/FirstOrDefault, Count, ToListAsync, AsAsyncEnumerable
}

Supported inside Where: comparisons, &&/||/!, == null/!= null (translated to IS [NOT] NULL), string.StartsWith/EndsWith/Contains (translated to LIKE), and collection.Contains(p.Column) (translated to IN). Deleted records are skipped unless .IncludeDeleted() is called. Anything that cannot be translated throws NotSupportedException — nothing falls back to silent in-memory evaluation.

There is also an implementation of DbConnection so you can query a folder of files e.g.

var dbConnection = new DbfDbConnection(string.Empty, string.Empty);
dbConnection.ConnectionString = $"Folder=./test/fixtures;SkipDeletedRecords=false";
dbConnection.Open();

var dbCommand = dbConnection.CreateCommand();
dbCommand.CommandText = "select * from dbase_03.dbf;";

var reader = await dbCommand.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
    var valueCol1 = reader.GetString(0);
    var valueCol11 = reader.GetDecimal(10);
}

The command text supports column lists with optional aliases, a row limit, WHERE clauses with named (@name) or positional (?) parameters, and ORDER BY:

select top 10 Point_ID as id, Date_Visit from dbase_03.dbf
select Point_ID, Max_PDOP from dbase_03.dbf limit 5
select Point_ID from dbase_03.dbf where Max_PDOP >= 3.5 and Date_Visit >= '1997-01-01'
select Point_ID from dbase_03.dbf where Point_ID like 'A%' or Point_ID in ('B1', 'B2')
select * from dbase_03.dbf where Point_ID = @id
select top 5 Point_ID from dbase_03.dbf where Max_PDOP >= 3.5 order by Max_PDOP desc, Point_ID
select count(*) from dbase_03.dbf where Max_PDOP >= 3.5
var command = (DbfDbCommand)dbConnection.CreateCommand();
command.CommandText = "select Point_ID from dbase_03.dbf where Point_ID = @id";
command.Parameters.AddWithValue("@id", "A1");
var value = command.ExecuteScalar();

DbfDbConnection also offers the same row-to-type mapping over SQL text in the style of Dapper — and the provider is Dapper-compatible if you prefer the real thing:

var points = dbConnection.Query<GpsPoint>(
    "select Point_ID, Max_PDOP, Date_Visit from dbase_03.dbf where Point_ID = @id",
    new { id = "A1" });
var ids = dbConnection.Query<string>("select Point_ID from dbase_03.dbf"); // scalar rows
// also QueryAsync<T> and QueryFirstOrDefault<T>

Predicates support =, <>, !=, <, <=, >, >=, BETWEEN, IN, LIKE (% and _), IS [NOT] NULL, AND/OR/NOT and parentheses. String comparisons are ordinal, case-sensitive and ignore trailing spaces; comparisons involving NULL follow SQL three-valued logic; date columns compare against 'yyyy-MM-dd' or 'yyyy-MM-dd HH:mm:ss' strings. ORDER BY sorts with the same comparison rules (multiple keys, ASC/DESC, select-list aliases allowed, nulls first ascending) using a stable in-memory sort of the matching rows; TOP/LIMIT applies after the sort. COUNT(*) is the one supported aggregate, and it counts as cheaply as it safely can: without a WHERE it scans record status bytes only (header record counts are not trusted); when an index covers the WHERE exactly, the count comes from the index without reading any rows; otherwise rows are read and filtered without being projected. The same fast paths back DbfQuery<T>.Count().

When a sidecar compound index (file.cdx) exists next to the table, queries use it automatically: equality, range and BETWEEN predicates on indexed character, integer, numeric, double and date columns (plus prefix LIKE on character columns) become index seeks, and an ORDER BY matching an index tag — ascending or descending — reads in index order instead of sorting (descending order is served by reversing the ascending tag). This applies to SQL text and to the Query<T> builder alike. The planner is conservative — index tags with dBASE UNIQUE or FOR filters, descending keys, expression keys, unsupported key types (datetime, currency), or non-ASCII character search values fall back to a full table scan, and the full WHERE clause is always re-applied to every row an index returns. Set UseIndexes=false in the connection string (or call .WithoutIndexes() on the builder) to force scans, and use DbfDbCommand.ExplainPlan() or DbfQuery<T>.ExplainPlan() to see which path a query takes:

var command = (DbfDbCommand)dbConnection.CreateCommand();
command.CommandText = "select * from setup.dbf where KEY_NAME = 'CONTACTS'";
Console.WriteLine(command.ExplainPlan()); // index seek (=) on tag 'KEY_NAME'

The connection string supports the options available in DbfDataReaderOptions:

  • Folder - the folder containing the files to be queried
    • required
    • string
  • Encoding - the encoding to be used
    • optional
    • string
    • defaults to null and uses the language from the DBF header
    • valid Encoding web name from Encoding e.g. 'ascii'
  • ReadFloatsAsDecimals - whether to read floats as decimals
    • optional
    • boolean
    • defaults to false
  • SkipDeletedRecords - whether to skip deleted records
    • optional
    • boolean
    • defaults to true
  • StringTrimming - string timming behaviour
    • optional
    • string
    • defaults to 'None'
    • one of 'None', 'Trim', 'TrimStart', 'TrimEnd'
  • UseIndexes - whether to use sidecar .cdx compound indexes automatically
    • optional
    • boolean
    • defaults to true

Used by

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  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 was computed.  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 was computed.  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 was computed.  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. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on DbfDataReader:

Package Downloads
SwagOverFlow.Data

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.2.0 953 7/6/2026
2.1.0 133 7/6/2026
2.0.0 106 7/6/2026
1.1.0 31,952 3/4/2026
1.0.0 3,038 2/23/2026
0.9.0 276,774 6/12/2023
0.8.0 85,541 7/5/2021
0.7.0 52,835 2/19/2021
0.6.0 1,834 2/19/2021
0.5.8 9,763 2/18/2021
0.5.7 1,317 2/18/2021
0.5.6 2,573 2/16/2021
0.5.5 1,436 2/16/2021
0.5.4 242,652 3/2/2020
0.5.3 15,094 2/17/2020
0.5.2 113,891 11/20/2018
0.5.1 4,484 10/9/2018
0.5.0 2,650 9/25/2018
0.4.3 2,660 9/3/2018
Loading failed