Webiant Logo Webiant Logo
  1. No results found.

    Try your search with a different keyword or use * as a wildcard.

SqLiteNopDataProvider.cs

using System.Data;
using System.Data.Common;
using System.Linq.Expressions;
using System.Text;
using LinqToDB;
using LinqToDB.Data;
using LinqToDB.DataProvider;
using LinqToDB.DataProvider.SQLite;
using LinqToDB.Tools;
using Microsoft.Data.Sqlite;
using Nop.Core;
using Nop.Core.ComponentModel;
using Nop.Data;
using Nop.Data.DataProviders;
namespace Nop.Tests;

/// 
/// Represents the SQLite data provider
/// 
public partial class SqLiteNopDataProvider : BaseDataProvider, INopDataProvider
{
    #region Consts

    //it's quite fast hash (to cheaply distinguish between objects)
    private const string HASH_ALGORITHM = "SHA1";
    private static DataConnection _dataContext;

    private static readonly ReaderWriterLockSlim _locker = new();

    #endregion

    #region Methods

    public void CreateDatabase(string collation, int triesToConnect = 10)
    {
        ExecuteNonQueryAsync("PRAGMA journal_mode=WAL;").Wait();
    }

    /// 
    /// Gets a connection to the database for a current data provider
    /// 
    /// Connection string
    /// Connection to a database
    protected override DbConnection GetInternalDbConnection(string connectionString)
    {
        ArgumentException.ThrowIfNullOrEmpty(connectionString);

        return new SqliteConnection(string.IsNullOrEmpty(connectionString)
            ? DataSettingsManager.LoadSettings().ConnectionString
            : connectionString);
    }

    /// 
    /// Inserts record into table. Returns inserted entity with identity
    /// 
    /// 
    /// 
    /// Inserted entity
    public override TEntity InsertEntity(TEntity entity)
    {
        using (new ReaderWriteLockDisposable(_locker))
        {
            entity.Id = DataContext.InsertWithInt32Identity(entity);
            return entity;
        }
    }

    /// 
    /// Insert a new entity
    /// 
    /// Entity type
    /// Entity
    /// Entity
    public override Task InsertEntityAsync(TEntity entity)
    {
        InsertEntity(entity);

        return Task.FromResult(entity);
    }

    /// 
    /// Updates record in table, using values from entity parameter.
    /// Record to update identified by match on primary key value from obj value.
    /// 
    /// Entity with data to update
    /// Entity type
    public override Task UpdateEntityAsync(TEntity entity)
    {
        using (new ReaderWriteLockDisposable(_locker))
            DataContext.Update(entity);

        return Task.CompletedTask;
    }

    /// 
    /// Updates records in table, using values from entity parameter.
    /// Records to update are identified by match on primary key value from obj value.
    /// 
    /// Entities with data to update
    /// Entity type
    /// A task that represents the asynchronous operation
    public override async Task UpdateEntitiesAsync(IEnumerable entities)
    {
        foreach (var entity in entities)
            await UpdateEntityAsync(entity);
    }

    /// 
    /// Updates records in table, using values from entity parameter.
    /// Records to update are identified by match on primary key value from obj value.
    /// 
    /// Entities with data to update
    /// Entity type
    public override void UpdateEntities(IEnumerable entities)
    {
        foreach (var entity in entities)
            UpdateEntity(entity);
    }

    /// 
    /// Deletes record in table. Record to delete identified
    /// by match on primary key value from obj value.
    /// 
    /// Entity for delete operation
    /// Entity type
    public override Task DeleteEntityAsync(TEntity entity)
    {
        using (new ReaderWriteLockDisposable(_locker))
            DataContext.Delete(entity);

        return Task.CompletedTask;
    }

    /// 
    /// Performs delete records in a table
    /// 
    /// Entities for delete operation
    /// Entity type
    public override Task BulkDeleteEntitiesAsync(IList entities)
    {
        using (new ReaderWriteLockDisposable(_locker))
        {
            foreach (var entity in entities)
                DataContext.Delete(entity);
        }

        return Task.CompletedTask;
    }

    /// 
    /// Performs delete records in a table by a condition
    /// 
    /// A function to test each element for a condition.
    /// Entity type
    public override Task BulkDeleteEntitiesAsync(Expression> predicate)
    {
        return Task.FromResult(DataContext.GetTable()
            .Where(predicate).Delete());
    }

    /// 
    /// Performs bulk insert operation for entity collection.
    /// 
    /// Entities for insert operation
    /// Entity type
    public override Task BulkInsertEntitiesAsync(IEnumerable entities)
    {
        using (new ReaderWriteLockDisposable(_locker))
            DataContext.BulkCopy(new BulkCopyOptions(), entities.RetrieveIdentity(DataContext));

        return Task.CompletedTask;
    }

    /// 
    /// Gets the name of a foreign key
    /// 
    /// Foreign key table
    /// Foreign key column name
    /// Primary table
    /// Primary key column name
    /// Name of a foreign key
    public string CreateForeignKeyName(string foreignTable, string foreignColumn, string primaryTable, string primaryColumn)
    {
        return "FK_" + HashHelper.CreateHash(Encoding.UTF8.GetBytes($"{foreignTable}_{foreignColumn}_{primaryTable}_{primaryColumn}"), HASH_ALGORITHM);
    }

    /// 
    /// Gets the name of an index
    /// 
    /// Target table name
    /// Target column name
    /// Name of an index
    public string GetIndexName(string targetTable, string targetColumn)
    {
        return "IX_" + HashHelper.CreateHash(Encoding.UTF8.GetBytes($"{targetTable}_{targetColumn}"), HASH_ALGORITHM);
    }

    /// 
    /// Returns queryable source for specified mapping class for current connection,
    /// mapped to database table or view.
    /// 
    /// Entity type
    /// Queryable source
    public override IQueryable GetTable()
    {
        using (new ReaderWriteLockDisposable(_locker, ReaderWriteLockType.Read))
            return DataContext.GetTable();
    }

    /// 
    /// Get the current identity value
    /// 
    /// Entity
    /// Integer identity; null if cannot get the result
    public Task GetTableIdentAsync() where TEntity : BaseEntity
    {
        using (new ReaderWriteLockDisposable(_locker, ReaderWriteLockType.Read))
        {
            var tableName = DataContext.GetTable().TableName;

            var result = DataContext.Query($"select seq from sqlite_sequence where name = \"{tableName}\"")
                .FirstOrDefault();

            return Task.FromResult(result ?? 1);
        }
    }

    /// 
    /// Checks if the specified database exists, returns true if database exists
    /// 
    /// Returns true if the database exists.
    public bool DatabaseExists()
    {
        return true;
    }

    /// 
    /// Creates a backup of the database
    /// 
    public virtual Task BackupDatabaseAsync(string fileName)
    {
        throw new DataException("This database provider does not support backup");
    }

    /// 
    /// Restores the database from a backup
    /// 
    /// The name of the backup file
    public virtual Task RestoreDatabaseAsync(string backupFileName)
    {
        throw new DataException("This database provider does not support backup");
    }

    /// 
    /// Re-index database tables
    /// 
    public Task ReIndexTablesAsync()
    {
        using (new ReaderWriteLockDisposable(_locker))
            DataContext.Execute("VACUUM;");

        return Task.CompletedTask;
    }

    /// 
    /// Build the connection string
    /// 
    /// Connection string info
    /// Connection string
    public string BuildConnectionString(INopConnectionStringInfo nopConnectionString)
    {
        ArgumentNullException.ThrowIfNull(nopConnectionString);

        if (nopConnectionString.IntegratedSecurity)
            throw new NopException("Data provider supports connection only with password");

        var builder = new SqliteConnectionStringBuilder
        {
            DataSource = CommonHelper.DefaultFileProvider.MapPath($"~/App_Data/{nopConnectionString.DatabaseName}.sqlite"),
            Password = nopConnectionString.Password,
            Mode = SqliteOpenMode.ReadWrite,
            Cache = SqliteCacheMode.Shared
        };

        return builder.ConnectionString;
    }

    /// 
    /// Set table identity (is supported)
    /// 
    /// Entity
    /// Identity value
    public Task SetTableIdentAsync(int ident) where TEntity : BaseEntity
    {
        using (new ReaderWriteLockDisposable(_locker))
        {
            var tableName = DataContext.GetTable().TableName;

            DataContext.Execute($"update sqlite_sequence set seq = {ident} where name = \"{tableName}\"");
        }

        return Task.CompletedTask;
    }

    /// 
    /// Executes command using System.Data.CommandType.StoredProcedure command type and
    /// returns results as collection of values of specified type
    /// 
    /// Result record type
    /// Procedure name
    /// Command parameters
    /// Returns collection of query result records
    public override Task> QueryProcAsync(string procedureName, params DataParameter[] parameters)
    {
        //stored procedure is not support by SqLite
        return Task.FromResult>(new List());
    }

    /// 
    /// Executes SQL command and returns results as collection of values of specified type
    /// 
    /// Type of result items
    /// SQL command text
    /// Parameters to execute the SQL command
    /// Collection of values of specified type
    public override Task> QueryAsync(string sql, params DataParameter[] parameters)
    {
        using (new ReaderWriteLockDisposable(_locker, ReaderWriteLockType.Read))
            return Task.FromResult>(DataContext.Query(sql, parameters).ToList());
    }

    /// 
    /// Executes command asynchronously and returns number of affected records
    /// 
    /// Command text
    /// Command parameters
    /// Number of records, affected by command execution.
    public override Task ExecuteNonQueryAsync(string sql, params DataParameter[] dataParameters)
    {
        using (new ReaderWriteLockDisposable(_locker, ReaderWriteLockType.Read))
        {
            using var dataConnection = CreateDataConnection(LinqToDbDataProvider);
            var command = new CommandInfo(dataConnection, sql, dataParameters);

            return command.ExecuteAsync();
        }
    }

    /// 
    /// Creates a new temporary storage and populate it using data from provided query
    /// 
    /// Name of temporary storage
    /// Query to get records to populate created storage with initial data
    /// Storage record mapping class
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the iQueryable instance of temporary storage
    /// 
    public override Task> CreateTempDataStorageAsync(string storeKey, IQueryable query)
    {
        return Task.FromResult>(new TempSqlDataStorage(storeKey, query, DataContext));
    }

    public Task DatabaseExistsAsync()
    {
        return Task.FromResult(DatabaseExists());
    }

    /// 
    /// Truncates database table
    /// 
    /// Performs reset identity column
    /// Entity type
    public override Task TruncateAsync(bool resetIdentity = false)
    {
        using (new ReaderWriteLockDisposable(_locker))
            DataContext.GetTable().Truncate(resetIdentity);

        return Task.CompletedTask;
    }

    #endregion

    #region Properties

    protected DataConnection DataContext => _dataContext ??= CreateDataConnection();

    /// 
    /// Linq2Db data provider
    /// 
    protected override IDataProvider LinqToDbDataProvider { get; } = SQLiteTools.GetDataProvider(ProviderName.SQLiteMS);

    /// 
    /// Gets allowed a limit input value of the data for hashing functions, returns 0 if not limited
    /// 
    public int SupportedLengthOfBinaryHash { get; } = 0;

    /// 
    /// Gets a value indicating whether this data provider supports backup
    /// 
    public bool BackupSupported { get; } = false;

    #endregion
}