ZidUtilities.CommonCode.DataAccess.Sqlite
1.0.0
The owner has unlisted this package.
This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package ZidUtilities.CommonCode.DataAccess.Sqlite --version 1.0.0
NuGet\Install-Package ZidUtilities.CommonCode.DataAccess.Sqlite -Version 1.0.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="ZidUtilities.CommonCode.DataAccess.Sqlite" Version="1.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ZidUtilities.CommonCode.DataAccess.Sqlite" Version="1.0.0" />
<PackageReference Include="ZidUtilities.CommonCode.DataAccess.Sqlite" />
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 ZidUtilities.CommonCode.DataAccess.Sqlite --version 1.0.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: ZidUtilities.CommonCode.DataAccess.Sqlite, 1.0.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 ZidUtilities.CommonCode.DataAccess.Sqlite@1.0.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=ZidUtilities.CommonCode.DataAccess.Sqlite&version=1.0.0
#tool nuget:?package=ZidUtilities.CommonCode.DataAccess.Sqlite&version=1.0.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
CommonCode.DataAccess.Sqlite
SQLite data access layer providing helper methods for executing database operations with ADO.NET and System.Data.SQLite.
Features
- SqliteConnector: SQLite database connector with comprehensive query execution support
- SqliteResponse<T>: Generic response wrapper for all database operations
- ErrorOnResponse: Detailed error tracking and exception handling
- File-based database with zero configuration
- Synchronous and asynchronous query execution
- Automatic transaction management with autoTransact parameter
- Built-in logging system with SystemLog and SystemExceptions tables
- Execution timing and performance tracking
- Cross-platform compatibility
Target Framework
.NET Framework 4.8
Dependencies
- System.Data.SQLite
- CommonCode.DataAccess (for ErrorOnResponse)
Installation
Install the System.Data.SQLite NuGet package:
Install-Package System.Data.SQLite
Core Components
SqliteConnector
The main class for executing SQLite commands. Provides methods for queries, non-queries, scalar operations, and more.
Key Properties:
Connection- The underlying SQLiteConnectionConnectionString- Get/set connection stringLastMessage- Status message ("OK" or error description)Error- Boolean indicating if last operation failedRowsAffected- Rows affected by last non-queryRowsRead- Rows read by last queryLastException- Last general exception thrownLastSqliteException- Last SQLite-specific exceptionTimeOut- Command timeout in seconds (0 = default)OnExecution- Boolean indicating if operation is in progressExecutionLapse- TimeSpan of last operation durationServer- Database file path (DataSource)
SqliteResponse<T>
Generic response wrapper returned by all execute methods.
Properties:
Result- Single result of type TResults- List of results of type TMessage- Status messageErrors- List of ErrorOnResponse objectsIsOK- True if operation succeededIsFailure- True if operation failedErrorOccured- True if errors existExecutionTime- DateTime when response was created
Static Factory Methods:
SqliteResponse<T>.Successful(T result, string message = "Success")SqliteResponse<T>.Successful(List<T> results, string message = "Success")SqliteResponse<T>.Failure(string message, Exception ex = null)
Usage Examples
Creating a Connection
using ZidUtilities.CommonCode.DataAccess.Sqlite;
// Using file path (connection string is created automatically)
string dbPath = @"C:\data\myapp.db";
var connector = new SqliteConnector(dbPath);
// Or using a full connection string
string connStr = $"Data Source={dbPath};Version=3;";
var connector2 = new SqliteConnector(connStr);
// In-memory database (great for testing)
var memConnector = new SqliteConnector(":memory:");
// Test the connection
if (connector.TestConnection())
{
Console.WriteLine("Connected successfully!");
Console.WriteLine($"Database: {connector.Server}");
}
else
{
Console.WriteLine($"Connection failed: {connector.LastMessage}");
}
Creating Tables
var connector = new SqliteConnector(@"C:\data\myapp.db");
string createTableSql = @"
CREATE TABLE IF NOT EXISTS Users (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Username TEXT NOT NULL UNIQUE,
Email TEXT NOT NULL,
IsActive INTEGER DEFAULT 1,
CreatedDate TEXT DEFAULT CURRENT_TIMESTAMP
)";
var response = connector.ExecuteNonQuery(createTableSql);
if (response.IsOK)
{
Console.WriteLine("Table created successfully!");
}
else
{
Console.WriteLine($"Error creating table: {response.Message}");
}
ExecuteNonQuery - INSERT, UPDATE, DELETE
var connector = new SqliteConnector(@"C:\data\myapp.db");
// Simple INSERT without parameters
string insertSql = "INSERT INTO Users (Username, Email) VALUES ('jdoe', 'jdoe@example.com')";
var response = connector.ExecuteNonQuery(insertSql);
if (response.IsOK)
{
Console.WriteLine($"Success! Rows affected: {connector.RowsAffected}");
}
else
{
Console.WriteLine($"Error: {response.Message}");
}
// INSERT with parameters (note: Dictionary<string, string>)
string paramSql = "INSERT INTO Users (Username, Email) VALUES (@username, @email)";
var parameters = new Dictionary<string, string>
{
{ "@username", "janedoe" },
{ "@email", "jane@example.com" }
};
var response2 = connector.ExecuteNonQuery(paramSql, parameters);
// Get the last inserted row ID
string lastIdQuery = "SELECT last_insert_rowid()";
var idResponse = connector.ExecuteScalar(lastIdQuery);
long lastId = Convert.ToInt64(idResponse.Result);
Console.WriteLine($"Inserted row ID: {lastId}");
// UPDATE with parameters
string updateSql = "UPDATE Users SET Email = @email WHERE Username = @username";
var updateParams = new Dictionary<string, string>
{
{ "@email", "newemail@example.com" },
{ "@username", "jdoe" }
};
var response3 = connector.ExecuteNonQuery(updateSql, updateParams);
// DELETE with parameters
string deleteSql = "DELETE FROM Users WHERE Id = @id";
var deleteParams = new Dictionary<string, string> { { "@id", "1" } };
var response4 = connector.ExecuteNonQuery(deleteSql, deleteParams);
ExecuteTable - Query and Return DataTable
var connector = new SqliteConnector(@"C:\data\myapp.db");
// Simple SELECT
string query = "SELECT * FROM Users";
var response = connector.ExecuteTable(query);
if (response.IsOK && response.Result != null)
{
DataTable users = response.Result;
Console.WriteLine($"Retrieved {users.Rows.Count} users");
foreach (DataRow row in users.Rows)
{
Console.WriteLine($"Username: {row["Username"]}, Email: {row["Email"]}");
}
}
// SELECT with parameters
string paramQuery = "SELECT * FROM Users WHERE IsActive = @active";
var queryParams = new Dictionary<string, string>
{
{ "@active", "1" }
};
var response2 = connector.ExecuteTable(paramQuery, queryParams);
// With custom table name
var response3 = connector.ExecuteTable(query, null, false, "MyUsers");
Console.WriteLine($"Table name: {response3.Result.TableName}");
// Query with WHERE clause
string searchQuery = "SELECT * FROM Users WHERE Username LIKE @pattern ORDER BY Username";
var searchParams = new Dictionary<string, string> { { "@pattern", "%doe%" } };
var response4 = connector.ExecuteTable(searchQuery, searchParams);
ExecuteDataSet - Multiple Result Sets
var connector = new SqliteConnector(@"C:\data\myapp.db");
// SQLite executes multiple statements in one command
string multipleSql = @"
SELECT * FROM Users;
SELECT * FROM Orders;
SELECT * FROM Products;";
var response = connector.ExecuteDataSet(multipleSql);
if (response.IsOK && response.Result != null)
{
DataSet ds = response.Result;
Console.WriteLine($"Retrieved {ds.Tables.Count} tables");
if (ds.Tables.Count >= 3)
{
DataTable users = ds.Tables[0];
DataTable orders = ds.Tables[1];
DataTable products = ds.Tables[2];
Console.WriteLine($"Users: {users.Rows.Count}");
Console.WriteLine($"Orders: {orders.Rows.Count}");
Console.WriteLine($"Products: {products.Rows.Count}");
}
}
ExecuteScalar - Single Value
var connector = new SqliteConnector(@"C:\data\myapp.db");
// Get count
string countQuery = "SELECT COUNT(*) FROM Users";
var response = connector.ExecuteScalar(countQuery);
if (response.IsOK && response.Result != null)
{
int count = Convert.ToInt32(response.Result);
Console.WriteLine($"Total users: {count}");
}
// Get max value
string maxQuery = "SELECT MAX(CreatedDate) FROM Users";
var response2 = connector.ExecuteScalar(maxQuery);
if (response2.IsOK && response2.Result != null && response2.Result != DBNull.Value)
{
string maxDate = response2.Result.ToString();
Console.WriteLine($"Latest user created: {maxDate}");
}
// Get single string value
string nameQuery = "SELECT Username FROM Users WHERE Id = @id";
var nameParams = new Dictionary<string, string> { { "@id", "1" } };
var response3 = connector.ExecuteScalar(nameQuery, nameParams);
if (response3.IsOK && response3.Result != null)
{
string username = response3.Result.ToString();
Console.WriteLine($"Username: {username}");
}
// Check if table exists
string tableExistsQuery = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=@tableName";
var tableParams = new Dictionary<string, string> { { "@tableName", "Users" } };
var existsResponse = connector.ExecuteScalar(tableExistsQuery, tableParams);
bool exists = Convert.ToInt32(existsResponse.Result) > 0;
ExecuteColumn - Single Column as List
var connector = new SqliteConnector(@"C:\data\myapp.db");
// Get list of all usernames
string query = "SELECT Username FROM Users ORDER BY Username";
var response = connector.ExecuteColumn(query);
if (response.IsOK && response.Result != null)
{
List<string> usernames = response.Result;
Console.WriteLine($"Found {usernames.Count} usernames:");
foreach (string username in usernames)
{
Console.WriteLine($" - {username}");
}
}
// With parameters
string activeQuery = "SELECT Email FROM Users WHERE IsActive = @active";
var activeParams = new Dictionary<string, string> { { "@active", "1" } };
var response2 = connector.ExecuteColumn(activeQuery, activeParams);
// Get all table names in database
string tablesQuery = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name";
var tablesResponse = connector.ExecuteColumn(tablesQuery);
Automatic Transactions with autoTransact
var connector = new SqliteConnector(@"C:\data\myapp.db");
// Single operation with auto-transaction
// If it fails, it automatically rolls back
string insertSql = "INSERT INTO Users (Username, Email) VALUES (@username, @email)";
var parameters = new Dictionary<string, string>
{
{ "@username", "testuser" },
{ "@email", "test@example.com" }
};
var response = connector.ExecuteNonQuery(insertSql, parameters, autoTransact: true);
if (response.IsOK)
{
Console.WriteLine("Insert committed successfully");
}
else
{
Console.WriteLine("Insert failed and was rolled back");
}
Working with SQLite-Specific Features
var connector = new SqliteConnector(@"C:\data\myapp.db");
// Get database schema information
string pragmaQuery = "PRAGMA table_info(Users)";
var schemaResponse = connector.ExecuteTable(pragmaQuery);
if (schemaResponse.IsOK && schemaResponse.Result != null)
{
foreach (DataRow row in schemaResponse.Result.Rows)
{
Console.WriteLine($"Column: {row["name"]}, Type: {row["type"]}, NotNull: {row["notnull"]}");
}
}
// Get all tables
string tablesQuery = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name";
var tablesResponse = connector.ExecuteColumn(tablesQuery);
// Vacuum database (reclaim space)
var vacuumResponse = connector.ExecuteNonQuery("VACUUM");
// Enable foreign keys (SQLite-specific)
var fkResponse = connector.ExecuteNonQuery("PRAGMA foreign_keys = ON");
// Set journal mode (WAL is recommended for concurrent access)
var walResponse = connector.ExecuteNonQuery("PRAGMA journal_mode = WAL");
// Set synchronous mode
var syncResponse = connector.ExecuteNonQuery("PRAGMA synchronous = NORMAL");
Error Handling Patterns
var connector = new SqliteConnector(@"C:\data\myapp.db");
string query = "SELECT * FROM NonExistentTable";
var response = connector.ExecuteTable(query);
// Check using IsOK property
if (!response.IsOK)
{
Console.WriteLine("Operation failed!");
Console.WriteLine($"Message: {response.Message}");
// Access errors collection
if (response.Errors.Count > 0)
{
foreach (var error in response.Errors)
{
Console.WriteLine($"Error at {error.ExecutionTime}: {error.Message}");
if (error.Exception != null)
{
Console.WriteLine($"Exception: {error.Exception.GetType().Name}");
}
}
}
}
// Alternative: Check Error property on connector
if (connector.Error)
{
Console.WriteLine($"Connector error: {connector.LastMessage}");
// Access specific exception types
if (connector.LastSqliteException != null)
{
Console.WriteLine($"SQLite Error: {connector.LastSqliteException.Message}");
Console.WriteLine($"Result Code: {connector.LastSqliteException.ResultCode}");
}
else if (connector.LastException != null)
{
Console.WriteLine($"General Exception: {connector.LastException.Message}");
}
}
Asynchronous Execution
var connector = new SqliteConnector(@"C:\data\myapp.db");
// Subscribe to events
connector.StartExecution += (query, time) =>
{
Console.WriteLine($"Started at {time}: {query.Substring(0, Math.Min(50, query.Length))}...");
};
connector.FinishExecution += (query, time) =>
{
Console.WriteLine($"Finished at {time}");
};
// Start async query
string query = "SELECT * FROM LargeTable";
connector.AsyncExecuteDataSet(query);
// Check if still executing
while (connector.OnExecution)
{
Console.WriteLine("Still executing...");
System.Threading.Thread.Sleep(100);
}
// Get results
if (connector.AsyncResult == 1)
{
DataSet results = connector.Results;
Console.WriteLine($"Async query returned {results.Tables.Count} tables");
}
else
{
Console.WriteLine($"Async query failed: {connector.LastMessage}");
}
Working with Logging Tables
var connector = new SqliteConnector(@"C:\data\myapp.db");
// Create the logging tables (SystemLog and SystemExceptions)
connector.CreateLogTables();
// Log debug message
var debugResponse = connector.Debug(
comment: "User login attempt",
className: "UserService",
methodName: "Login",
executor: "jdoe",
processType: "Authentication"
);
// Log info message
var infoResponse = connector.Info(
comment: "Configuration loaded",
className: "AppService",
methodName: "Initialize",
executor: "SYSTEM",
processType: "Startup"
);
// Log warning with exception
try
{
throw new Exception("Sample warning");
}
catch (Exception ex)
{
var warnResponse = connector.Warning(
comment: "Operation completed with warnings",
className: "DataService",
methodName: "ProcessData",
executor: "admin",
processType: "DataProcessing",
Ex: ex
);
}
// Log error with exception
try
{
throw new InvalidOperationException("Sample error");
}
catch (Exception ex)
{
var errorResponse = connector.Exception(
comment: "Critical operation failed",
className: "PaymentService",
methodName: "ProcessPayment",
executor: "jdoe",
processType: "Payment",
Ex: ex
);
}
Creating Tables from DataTable Schema
var connector = new SqliteConnector(@"C:\data\myapp.db");
// Create a DataTable with schema
DataTable schema = new DataTable("Products");
schema.Columns.Add("Id", typeof(int)).AutoIncrement = true;
schema.Columns.Add("Name", typeof(string));
schema.Columns.Add("Price", typeof(decimal));
schema.Columns.Add("Stock", typeof(int)).DefaultValue = 0;
schema.Columns.Add("IsActive", typeof(bool)).DefaultValue = true;
schema.Columns["Id"].AutoIncrementSeed = 1;
schema.Columns["Id"].AutoIncrementStep = 1;
schema.Columns["Name"].AllowDBNull = false;
schema.PrimaryKey = new DataColumn[] { schema.Columns["Id"] };
// Create the table in SQLite
connector.CreateTableInSQL(schema);
// Or just get the script
string createScript = SqliteConnector.GetTableScript(schema);
Console.WriteLine(createScript);
Bulk Insert Operations
var connector = new SqliteConnector(@"C:\data\myapp.db");
// Bulk insert with transaction for better performance
string insertSql = "INSERT INTO Users (Username, Email) VALUES (@username, @email)";
var users = new[]
{
new { Username = "user1", Email = "user1@example.com" },
new { Username = "user2", Email = "user2@example.com" },
new { Username = "user3", Email = "user3@example.com" }
};
// Manual transaction for multiple operations
// Note: BeginTransaction, CommitTransaction, RollbackTransaction are private
// So we use SQL statements directly
connector.ExecuteNonQuery("BEGIN TRANSACTION");
try
{
foreach (var user in users)
{
var parameters = new Dictionary<string, string>
{
{ "@username", user.Username },
{ "@email", user.Email }
};
var response = connector.ExecuteNonQuery(insertSql, parameters);
if (!response.IsOK)
{
throw new Exception($"Failed to insert {user.Username}");
}
}
connector.ExecuteNonQuery("COMMIT");
Console.WriteLine($"Inserted {users.Length} records");
}
catch (Exception ex)
{
connector.ExecuteNonQuery("ROLLBACK");
Console.WriteLine($"Bulk insert failed: {ex.Message}");
}
Performance Monitoring
var connector = new SqliteConnector(@"C:\data\myapp.db");
// Set timeout (in seconds)
connector.TimeOut = 30;
// Execute query and measure performance
string query = "SELECT * FROM LargeTable";
var response = connector.ExecuteTable(query);
// Check execution time
TimeSpan duration = connector.ExecutionLapse;
Console.WriteLine($"Query took: {duration.TotalMilliseconds} ms");
Console.WriteLine($"Rows read: {connector.RowsRead}");
// For write operations
string insertSql = "INSERT INTO Logs (Message) VALUES ('Test')";
var insertResponse = connector.ExecuteNonQuery(insertSql);
Console.WriteLine($"Insert took: {connector.ExecutionLapse.TotalMilliseconds} ms");
Console.WriteLine($"Rows affected: {connector.RowsAffected}");
Complete Repository Pattern Example
using ZidUtilities.CommonCode.DataAccess.Sqlite;
using System.Data;
using System.Collections.Generic;
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public bool IsActive { get; set; }
}
public class UserRepository
{
private readonly SqliteConnector _connector;
public UserRepository(string dbPath)
{
_connector = new SqliteConnector(dbPath);
InitializeDatabase();
}
private void InitializeDatabase()
{
string createTable = @"
CREATE TABLE IF NOT EXISTS Users (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Username TEXT NOT NULL UNIQUE,
Email TEXT NOT NULL,
IsActive INTEGER DEFAULT 1,
CreatedDate TEXT DEFAULT CURRENT_TIMESTAMP
)";
_connector.ExecuteNonQuery(createTable);
}
public List<User> GetAllUsers()
{
string query = "SELECT Id, Username, Email, IsActive FROM Users WHERE IsActive = @active";
var parameters = new Dictionary<string, string> { { "@active", "1" } };
var response = _connector.ExecuteTable(query, parameters);
if (!response.IsOK || response.Result == null)
return new List<User>();
var users = new List<User>();
foreach (DataRow row in response.Result.Rows)
{
users.Add(new User
{
Id = Convert.ToInt32(row["Id"]),
Username = row["Username"].ToString(),
Email = row["Email"].ToString(),
IsActive = Convert.ToInt32(row["IsActive"]) == 1
});
}
return users;
}
public bool AddUser(string username, string email)
{
string query = "INSERT INTO Users (Username, Email) VALUES (@username, @email)";
var parameters = new Dictionary<string, string>
{
{ "@username", username },
{ "@email", email }
};
var response = _connector.ExecuteNonQuery(query, parameters, autoTransact: true);
return response.IsOK;
}
public User GetUserById(int userId)
{
string query = "SELECT Id, Username, Email, IsActive FROM Users WHERE Id = @id";
var parameters = new Dictionary<string, string> { { "@id", userId.ToString() } };
var response = _connector.ExecuteTable(query, parameters);
if (!response.IsOK || response.Result == null || response.Result.Rows.Count == 0)
return null;
DataRow row = response.Result.Rows[0];
return new User
{
Id = Convert.ToInt32(row["Id"]),
Username = row["Username"].ToString(),
Email = row["Email"].ToString(),
IsActive = Convert.ToInt32(row["IsActive"]) == 1
};
}
public bool UpdateUser(int userId, string email)
{
string query = "UPDATE Users SET Email = @email WHERE Id = @id";
var parameters = new Dictionary<string, string>
{
{ "@email", email },
{ "@id", userId.ToString() }
};
var response = _connector.ExecuteNonQuery(query, parameters, autoTransact: true);
return response.IsOK && _connector.RowsAffected > 0;
}
public bool DeleteUser(int userId)
{
// Soft delete
string query = "UPDATE Users SET IsActive = 0 WHERE Id = @id";
var parameters = new Dictionary<string, string> { { "@id", userId.ToString() } };
var response = _connector.ExecuteNonQuery(query, parameters, autoTransact: true);
return response.IsOK && _connector.RowsAffected > 0;
}
public int GetUserCount()
{
string query = "SELECT COUNT(*) FROM Users WHERE IsActive = 1";
var response = _connector.ExecuteScalar(query);
if (!response.IsOK || response.Result == null)
return 0;
return Convert.ToInt32(response.Result);
}
}
// Usage
var repo = new UserRepository(@"C:\data\myapp.db");
repo.AddUser("jdoe", "jdoe@example.com");
var users = repo.GetAllUsers();
var user = repo.GetUserById(1);
repo.UpdateUser(1, "newemail@example.com");
SQLite-Specific Considerations
Data Types
- SQLite uses dynamic typing: INTEGER, TEXT, REAL, BLOB, NULL
- Booleans are stored as INTEGER (0 = false, 1 = true)
- Dates are stored as TEXT in ISO8601 format
- Use
Convert.ToInt32(row["IsActive"]) == 1for boolean columns
Concurrent Access
- Enable WAL mode for better concurrent reads/writes:
connector.ExecuteNonQuery("PRAGMA journal_mode = WAL");
Performance Optimization
- Use transactions for bulk operations
- Create indexes on frequently queried columns
- Use appropriate PRAGMA settings
- Vacuum database periodically to reclaim space
File Management
- Database file is created automatically if it doesn't exist
- Use
:memory:for in-memory databases (perfect for testing) - Database file can be copied/backed up while closed
Important Notes
Parameters
- Parameters must be
Dictionary<string, string>, notDictionary<string, object> - All values are passed as strings and converted by ADO.NET
- Always use parameterized queries to prevent SQL injection
Transaction Management
BeginTransaction(),CommitTransaction(), andRollbackTransaction()are private methods- For single operations, use the
autoTransactparameter - For multiple operations, use SQL statements: "BEGIN TRANSACTION", "COMMIT", "ROLLBACK"
Error Handling
- Always check
response.IsOKorresponse.IsFailure - Access errors via
response.Errorscollection - Check
connector.Error,connector.LastMessage,connector.LastException, andconnector.LastSqliteException
Best Practices
- Use parameterized queries for all user input
- Check IsOK before accessing Result
- Handle null values from Result properties
- Enable WAL mode for concurrent access
- Use transactions for bulk operations
- Create indexes for frequently queried columns
- Vacuum database periodically
- Back up database files regularly
Related Projects
- CommonCode.DataAccess: SQL Server implementation with similar API
- CommonCode.DataAccess.ActiveDirectory: Active Directory data access
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET Framework | net48 is compatible. net481 was computed. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
.NETFramework 4.8
- System.Data.SQLite (>= 2.0.2)
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 |
|---|
First release version.