pull/1/head
Ogoun 5 years ago
commit 6e0ee9ae3d

@ -1,6 +1,7 @@
using System;
using System.Data;
using System.Data.Common;
using System.Linq;
namespace ZeroLevel.SqlServer
{
@ -33,8 +34,7 @@ namespace ZeroLevel.SqlServer
{
public static T Read<T>(this DbDataReader reader, int index)
{
if (reader == null) return default;
if (reader[index] == DBNull.Value) return default;
if (reader == null || reader.FieldCount <= index || reader[index] == DBNull.Value) return default;
Type t;
if ((t = Nullable.GetUnderlyingType(typeof(T))) != null)
{
@ -44,8 +44,7 @@ namespace ZeroLevel.SqlServer
}
public static T Read<T>(this DbDataReader reader, string name)
{
if (reader == null) return default;
if (reader[name] == DBNull.Value) return default;
if (reader == null || false == reader.GetColumnSchema().Any(c => c.ColumnName.Equals(name, StringComparison.OrdinalIgnoreCase)) || reader[name] == DBNull.Value) return default;
Type t;
if ((t = Nullable.GetUnderlyingType(typeof(T))) != null)
{
@ -55,8 +54,7 @@ namespace ZeroLevel.SqlServer
}
public static T Read<T>(this IDataReader reader, int index)
{
if (reader == null) return default;
if (reader[index] == DBNull.Value) return default;
if (reader == null || reader.FieldCount <= index || reader[index] == DBNull.Value) return default;
Type t;
if ((t = Nullable.GetUnderlyingType(typeof(T))) != null)
{
@ -66,8 +64,7 @@ namespace ZeroLevel.SqlServer
}
public static T Read<T>(this IDataReader reader, string name)
{
if (reader == null) return default;
if (reader[name] == DBNull.Value) return default;
if (reader == null || false == reader.HasColumn(name) || reader[name] == DBNull.Value) return default;
Type t;
if ((t = Nullable.GetUnderlyingType(typeof(T))) != null)
{
@ -75,11 +72,9 @@ namespace ZeroLevel.SqlServer
}
return (T)Convert.ChangeType(reader[name], typeof(T));
}
public static T Read<T>(this DataRow reader, int index)
{
if (reader == null) return default;
if (reader.ItemArray[index] == DBNull.Value) return default;
if (reader == null || reader.ItemArray.Length <= index || reader.ItemArray[index] == DBNull.Value) return default;
Type t;
if ((t = Nullable.GetUnderlyingType(typeof(T))) != null)
{
@ -87,11 +82,9 @@ namespace ZeroLevel.SqlServer
}
return (T)Convert.ChangeType(reader.ItemArray[index], typeof(T));
}
public static T Read<T>(this DataRow reader, string name)
{
if (reader == null) return default;
if (reader[name] == DBNull.Value) return default;
if (reader == null || false == reader.Table.Columns.Contains(name) || reader[name] == DBNull.Value) return default;
Type t;
if ((t = Nullable.GetUnderlyingType(typeof(T))) != null)
{
@ -99,5 +92,14 @@ namespace ZeroLevel.SqlServer
}
return (T)Convert.ChangeType(reader[name], typeof(T));
}
public static bool HasColumn(this IDataRecord dr, string columnName)
{
for (int i = 0; i < dr.FieldCount; i++)
{
if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
return true;
}
return false;
}
}
}

@ -6,10 +6,15 @@
<Description>Light wrapper over ado.net</Description>
<Authors>ogoun</Authors>
<Copyright>Copyright Ogoun 2019</Copyright>
<PackageProjectUrl>https://github.com/ogoun/Zero</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ogoun/Zero/tree/master/ZeroLevel.SQL</PackageProjectUrl>
<RepositoryUrl>https://github.com/ogoun/Zero</RepositoryUrl>
<PackageIconUrl>https://raw.githubusercontent.com/ogoun/Zero/master/zero.png</PackageIconUrl>
<PackageIconUrl></PackageIconUrl>
<RepositoryType>GitHub</RepositoryType>
<PackageIcon>zero.png</PackageIcon>
<AssemblyVersion>1.0.0.1</AssemblyVersion>
<Version>1.0.1</Version>
<FileVersion>1.0.1.1</FileVersion>
<PackageReleaseNotes>Fix read values from DbDataReader, DataRow and IDataReader</PackageReleaseNotes>
</PropertyGroup>
<ItemGroup>
@ -22,4 +27,11 @@
<ProjectReference Include="..\ZeroLevel\ZeroLevel.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="E:\Desktop\Developing\NUGET\zero.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
</Project>

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using ZeroLevel.Models;
namespace ZeroLevel.SqLite
{
public class AuthRepository
{
private static byte[] DEFAULT_ADMIN_PWD_HASH = null;
private readonly SqLiteUserRepository _userRepository = new SqLiteUserRepository();
public UserInfo GetUserInfo(string username, string password)
{
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
{
return UserInfo.GetAnonimus();
}
// Check built-in admin
if (DEFAULT_ADMIN_PWD_HASH != null && DEFAULT_ADMIN_PWD_HASH.Length > 0 && (username.Equals("root", System.StringComparison.Ordinal) || username.Equals("admin", System.StringComparison.Ordinal))
&& DEFAULT_ADMIN_PWD_HASH.SequenceEqual(ComputeHash(password)))
{
return new UserInfo
{
Role = UserRole.SysAdmin,
UserId = -1,
UserName = "sysadmin",
DisplayName = "System Administrator",
Created = DateTime.Now
};
}
else
{
var user = _userRepository.Get(username, ComputeHash(password));
if (user != null)
{
return new UserInfo
{
Created = new DateTime(user.Timestamp, DateTimeKind.Utc),
DisplayName = user.DisplayName,
Role = user.Role,
UserId = user.Id,
UserName = user.UserName
};
}
}
return null;
}
public InvokeResult<long> CreateUser(string username, string pwd, string displayName, UserRole role, long currentUserId)
{
return _userRepository.SaveUser(new User
{
Creator = currentUserId,
DisplayName = displayName,
PasswordHash = ComputeHash(pwd),
Role = role,
Timestamp = DateTime.UtcNow.Ticks,
UserName = username
});
}
public InvokeResult<IEnumerable<User>> GetUsers()
{
try
{
return InvokeResult<IEnumerable<User>>.Succeeding(_userRepository.GetAll());
}
catch (Exception ex)
{
return InvokeResult<IEnumerable<User>>.Fault<IEnumerable<User>>(ex.Message);
}
}
public InvokeResult RemoveUser(string login)
{
return _userRepository.RemoveUser(login);
}
public void SetAdminPassword(string rootPwd) => DEFAULT_ADMIN_PWD_HASH = ComputeHash(rootPwd);
private byte[] ComputeHash(string pwd)
{
using (SHA256 shaM = new SHA256Managed())
{
return shaM.ComputeHash(Encoding.UTF8.GetBytes(pwd));
}
}
}
}

@ -0,0 +1,92 @@
using System;
using System.Data.SQLite;
using System.IO;
using ZeroLevel.Services.FileSystem;
namespace ZeroLevel.SqLite
{
public abstract class BaseSqLiteDB
{
#region Helpers
protected static bool HasColumn(SQLiteDataReader dr, string columnName)
{
for (int i = 0; i < dr.FieldCount; i++)
{
if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
return true;
}
return false;
}
protected static Tr Read<Tr>(SQLiteDataReader reader, int index)
{
if (reader == null || reader.FieldCount <= index || reader[index] == DBNull.Value) return default;
Type t;
if ((t = Nullable.GetUnderlyingType(typeof(Tr))) != null)
{
return (Tr)Convert.ChangeType(reader[index], t);
}
return (Tr)Convert.ChangeType(reader[index], typeof(Tr));
}
protected static Tr Read<Tr>(SQLiteDataReader reader, string name)
{
if (reader == null || HasColumn(reader, name) || reader[name] == DBNull.Value) return default;
Type t;
if ((t = Nullable.GetUnderlyingType(typeof(Tr))) != null)
{
return (Tr)Convert.ChangeType(reader[name], t);
}
return (Tr)Convert.ChangeType(reader[name], typeof(Tr));
}
protected static void Execute(string query, SQLiteConnection connection, SQLiteParameter[] parameters = null)
{
using (var cmd = new SQLiteCommand(query, connection))
{
if (parameters != null && parameters.Length > 0)
{
cmd.Parameters.AddRange(parameters);
}
cmd.ExecuteNonQuery();
}
}
protected static object ExecuteScalar(string query, SQLiteConnection connection, SQLiteParameter[] parameters = null)
{
using (var cmd = new SQLiteCommand(query, connection))
{
if (parameters != null && parameters.Length > 0)
{
cmd.Parameters.AddRange(parameters);
}
return cmd.ExecuteScalar();
}
}
protected static SQLiteDataReader Read(string query, SQLiteConnection connection, SQLiteParameter[] parameters = null)
{
using (var cmd = new SQLiteCommand(query, connection))
{
if (parameters != null && parameters.Length > 0)
{
cmd.Parameters.AddRange(parameters);
}
return cmd.ExecuteReader();
}
}
protected static string PrepareDb(string path)
{
if (Path.IsPathRooted(path) == false)
{
path = Path.Combine(FSUtils.GetAppLocalDbDirectory(), path);
}
if (!File.Exists(path))
{
SQLiteConnection.CreateFile(path);
}
return Path.GetFullPath(path);
}
#endregion Helpers
}
}

@ -0,0 +1,183 @@
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
namespace ZeroLevel.SqLite.Model
{
/// <summary>
/// Хранит данные указанное число дней, и позволяет выполнить проверку наличия данных, для отбрасывания дубликатов
/// </summary>
public sealed class SqLiteDupStorage
: BaseSqLiteDB, IDisposable
{
#region Fields
private const string DEFAUL_TABLE_NAME = "History";
private readonly SQLiteConnection _db;
private readonly long _removeOldRecordsTaskKey;
private readonly int _countDays;
private readonly string _table_name;
private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
#endregion Fields
#region Private members
private sealed class DuplicationStorageRecord
{
public string Hash { get; set; }
public byte[] Body { get; set; }
public long Timestamp { get; set; }
}
private void RemoveOldRecordsTask(long key)
{
_rwLock.EnterWriteLock();
try
{
Execute($"DELETE FROM {_table_name} WHERE timestamp < @limit", _db,
new SQLiteParameter[] { new SQLiteParameter("limit", DateTime.Now.AddDays(-_countDays).Ticks) });
}
catch (Exception ex)
{
Log.Error(ex, "[SQLiteDupStorage] Fault remove old records from db");
}
finally
{
_rwLock.ExitWriteLock();
}
}
#endregion Private members
#region Ctor
public SqLiteDupStorage(string database_file_path, string tableName, int period)
{
var path = PrepareDb(database_file_path);
if (string.IsNullOrWhiteSpace(tableName))
{
_table_name = DEFAUL_TABLE_NAME;
}
else
{
_table_name = tableName;
}
_db = new SQLiteConnection($"Data Source={path};Version=3;");
_db.Open();
Execute($"CREATE TABLE IF NOT EXISTS {_table_name} (id INTEGER PRIMARY KEY AUTOINCREMENT, hash TEXT, body BLOB, timestamp INTEGER)", _db);
Execute($"CREATE INDEX IF NOT EXISTS hash_index ON {_table_name} (hash)", _db);
_countDays = period > 0 ? period : 1;
_removeOldRecordsTaskKey = Sheduller.RemindEvery(TimeSpan.FromMinutes(1), RemoveOldRecordsTask);
}
#endregion Ctor
#region API
/// <summary>
/// true в случае обнаружения дубликата
/// </summary>
public bool TestAndInsert(byte[] body)
{
var hash = GenerateSHA256String(body);
var timestamp = DateTime.Now.Ticks;
SQLiteDataReader reader;
_rwLock.EnterReadLock();
var exists = new List<byte[]>();
try
{
reader = Read($"SELECT body FROM {_table_name} WHERE hash=@hash", _db, new SQLiteParameter[] { new SQLiteParameter("hash", hash) });
while (reader.Read())
{
exists.Add((byte[])reader.GetValue(0));
}
reader.Close();
}
catch (Exception ex)
{
Log.Error(ex, $"[SQLiteDupStorage] Fault search existing records by hash ({hash})");
// no return!
}
finally
{
_rwLock.ExitReadLock();
}
reader = null;
if (exists.Any())
{
foreach (var candidate in exists)
{
if (ArrayExtensions.UnsafeEquals(candidate, body))
return true;
}
}
_rwLock.EnterWriteLock();
try
{
Execute($"INSERT INTO {_table_name} ('hash', 'body', 'timestamp') values (@hash, @body, @timestamp)", _db,
new SQLiteParameter[]
{
new SQLiteParameter("hash", hash),
new SQLiteParameter("body", body),
new SQLiteParameter("timestamp", timestamp)
});
}
catch (Exception ex)
{
Log.Error(ex, $"[SQLiteDupStorage] Fault insert record in duplications storage. Hash '{hash}'. Timestamp '{timestamp}'.");
}
finally
{
_rwLock.ExitWriteLock();
}
return false;
}
#endregion API
#region IDisposable
public void Dispose()
{
Sheduller.Remove(_removeOldRecordsTaskKey);
try
{
_db?.Dispose();
}
catch (Exception ex)
{
Log.Error(ex, "[SQLiteDupStorage] Fault close db connection");
}
}
#endregion IDisposable
#region Helpers
private static string GenerateSHA256String(byte[] bytes)
{
using (SHA256 sha256 = SHA256Managed.Create())
{
byte[] hash = sha256.ComputeHash(bytes);
return ByteArrayToString(hash);
}
}
private static string ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
#endregion Helpers
}
}

@ -0,0 +1,13 @@
namespace ZeroLevel.SqLite
{
public class User
{
public long Id { get; set; }
public string UserName { get; set; }
public string DisplayName { get; set; }
public byte[] PasswordHash { get; set; }
public long Timestamp { get; set; }
public long Creator { get; set; }
public UserRole Role { get; set; }
}
}

@ -0,0 +1,24 @@
using System;
namespace ZeroLevel.SqLite
{
public class UserInfo
{
private readonly static UserInfo _anon = new UserInfo
{
Created = DateTime.MinValue,
DisplayName = "Anonimus",
Role = UserRole.Anonimus,
UserId = -2,
UserName = "anonimus"
};
public static UserInfo GetAnonimus() => _anon;
public long UserId { get; set; }
public string UserName { get; set; }
public string DisplayName { get; set; }
public DateTime Created { get; set; }
public UserRole Role { get; set; }
}
}

@ -0,0 +1,14 @@
using System;
namespace ZeroLevel.SqLite
{
public enum UserRole
: Int32
{
Anonimus = 0,
Operator = 1,
Editor = 512,
Administrator = 1024,
SysAdmin = 4096
}
}

@ -0,0 +1,197 @@
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.IO;
using System.Text;
using System.Threading;
using ZeroLevel.Services.Serialization;
using ZeroLevel.Services.Shedulling;
namespace ZeroLevel.SqLite
{
public sealed class SqLiteDelayDataStorage<T>
: BaseSqLiteDB, IDisposable
where T : IBinarySerializable
{
#region Fields
private readonly IExpirationSheduller _sheduller;
private readonly Func<T, DateTime> _expire_date_calc_func;
private readonly SQLiteConnection _db;
private readonly string _table_name;
private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
#endregion Fields
#region Ctor
public SqLiteDelayDataStorage(string database_file_path,
Func<T, bool> expire_callback,
Func<T, DateTime> expire_date_calc_func)
{
this._expire_date_calc_func = expire_date_calc_func;
var path = PrepareDb(database_file_path);
_table_name = "expiration";
_db = new SQLiteConnection($"Data Source={path};Version=3;");
_db.Open();
Execute($"CREATE TABLE IF NOT EXISTS {_table_name} (id INTEGER PRIMARY KEY AUTOINCREMENT, body BLOB, expirationtime INTEGER)", _db);
Execute($"CREATE INDEX IF NOT EXISTS expirationtime_index ON {_table_name} (expirationtime)", _db);
_sheduller = Sheduller.CreateExpirationSheduller();
OnExpire += expire_callback;
Preload();
}
#endregion Ctor
#region API
public event Func<T, bool> OnExpire;
public bool Push(T packet)
{
DateTime expirationDate;
try
{
expirationDate = _expire_date_calc_func(packet);
}
catch (Exception ex)
{
Log.Error(ex, "[SqLiteDelayDataStorage] Fault append data to storage");
return false;
}
var expirationTime = expirationDate.Ticks;
_rwLock.EnterWriteLock();
long id = -1;
try
{
Execute($"INSERT INTO {_table_name} ('body', 'expirationtime') values (@body, @expirationtime)", _db,
new SQLiteParameter[]
{
new SQLiteParameter("body", MessageSerializer.Serialize(packet)),
new SQLiteParameter("expirationtime", expirationTime)
});
id = (long)ExecuteScalar("select last_insert_rowid();", _db);
}
catch (Exception ex)
{
Log.Error(ex, $"[SqLiteDelayDataStorage] Fault insert record in delay storage. Expiration time: '{expirationTime}'.");
return false;
}
finally
{
_rwLock.ExitWriteLock();
}
_sheduller.Push(expirationDate, (k) => Pop(id));
return true;
}
#endregion API
#region Private members
private void Preload()
{
SQLiteDataReader reader;
_rwLock.EnterReadLock();
try
{
reader = Read($"SELECT id, expirationtime FROM {_table_name}", _db);
while (reader.Read())
{
var id = reader.GetInt64(0);
_sheduller.Push(new DateTime(reader.GetInt64(1), DateTimeKind.Local), (k) => Pop(id));
}
reader.Close();
}
catch (Exception ex)
{
Log.Error(ex, "[SqLiteDelayDataStorage] Fault preload datafrom db");
}
finally
{
_rwLock.ExitReadLock();
}
reader = null;
}
private void Pop(long id)
{
try
{
byte[] body;
_rwLock.EnterReadLock();
try
{
body = (byte[])ExecuteScalar($"SELECT body FROM {_table_name} WHERE id=@id", _db, new SQLiteParameter[] { new SQLiteParameter("id", id) });
}
catch (Exception ex)
{
Log.Error(ex, $"[SqLiteDelayDataStorage] Fault get body by id '{id}'");
RemoveRecordById(id);
return;
}
finally
{
_rwLock.ExitReadLock();
}
T packet;
try
{
packet = MessageSerializer.Deserialize<T>(body);
}
catch (Exception ex)
{
Log.Error(ex, $"[SqLiteDelayDataStorage] Fault deserialize body. Id '{id}'");
RemoveRecordById(id);
return;
}
if (OnExpire?.Invoke(packet) ?? false)
{
RemoveRecordById(id);
}
}
catch (Exception ex)
{
Log.Error(ex, "[SqLiteDelayDataStorage] Сбой обработки отложенной записи из DB");
}
}
private void RemoveRecordById(long id)
{
_rwLock.EnterWriteLock();
try
{
Execute($"DELETE FROM {_table_name} WHERE id = @id", _db,
new SQLiteParameter[] { new SQLiteParameter("id", id) });
}
catch (Exception ex)
{
Log.Error(ex, $"[SqLiteDelayDataStorage] Fault remove record by id '{id}'");
}
finally
{
_rwLock.ExitWriteLock();
}
}
#endregion Private members
#region IDisposable
public void Dispose()
{
try
{
_db?.Close();
_db?.Dispose();
}
catch (Exception ex)
{
Log.Error(ex, "[SqLiteDelayDataStorage] Fault close db connection");
}
_sheduller.Dispose();
}
#endregion IDisposable
}
}

@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.IO;
using System.Text;
using System.Threading;
using ZeroLevel.Services.Serialization;
namespace ZeroLevel.SqLite
{
/// <summary>
/// Промежуточное/временное хранилище пакетов данных, для случаев сбоя доставок через шину данных
/// </summary>
public sealed class SqLitePacketBuffer<T>
: BaseSqLiteDB, IDisposable
where T : IBinarySerializable
{
private sealed class PacketBufferRecord
{
public int Id { get; set; }
public byte[] Body { get; set; }
}
#region Fields
private readonly SQLiteConnection _db;
private readonly string _table_name;
private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
#endregion Fields
public SqLitePacketBuffer(string database_file_path)
{
var path = PrepareDb(database_file_path);
_table_name = "packets";
_db = new SQLiteConnection($"Data Source={path};Version=3;");
_db.Open();
Execute($"CREATE TABLE IF NOT EXISTS {_table_name} (id INTEGER PRIMARY KEY AUTOINCREMENT, body BLOB, created INTEGER)", _db);
Execute($"CREATE INDEX IF NOT EXISTS created_index ON {_table_name} (created)", _db);
}
public void Push(T frame)
{
long id = -1;
_rwLock.EnterWriteLock();
var creationTime = DateTime.Now.Ticks;
try
{
Execute($"INSERT INTO {_table_name} ('body', 'created') values (@body, @created)", _db,
new SQLiteParameter[]
{
new SQLiteParameter("body", MessageSerializer.Serialize(frame)),
new SQLiteParameter("created", creationTime)
});
id = (long)ExecuteScalar("select last_insert_rowid();", _db);
}
catch (Exception ex)
{
Log.Error(ex, $"[SqLitePacketBuffer] Fault insert record in buffer storage.");
}
finally
{
_rwLock.ExitWriteLock();
}
}
public bool Pop(Func<T, bool> pop_callback)
{
bool success = false;
long id = -1;
SQLiteDataReader reader;
_rwLock.EnterReadLock();
try
{
reader = Read($"SELECT id, body FROM {_table_name} ORDER BY created ASC LIMIT 1", _db);
if (reader.Read())
{
id = reader.GetInt64(0);
var body = (byte[])reader.GetValue(1);
try
{
success = pop_callback(MessageSerializer.Deserialize<T>(body));
}
catch (Exception ex)
{
Log.Error(ex, "Fault handle buffered data");
}
}
reader.Close();
}
catch (Exception ex)
{
Log.Error(ex, "[SqLitePacketBuffer] Fault preload datafrom db");
}
finally
{
_rwLock.ExitReadLock();
}
if (success)
{
RemoveRecordById(id);
}
reader = null;
return success;
}
private void RemoveRecordById(long id)
{
_rwLock.EnterWriteLock();
try
{
Execute($"DELETE FROM {_table_name} WHERE id = @id", _db,
new SQLiteParameter[] { new SQLiteParameter("id", id) });
}
catch (Exception ex)
{
Log.Error(ex, $"[SqLitePacketBuffer] Fault remove record by id '{id}'");
}
finally
{
_rwLock.ExitWriteLock();
}
}
public void Dispose()
{
try
{
_db?.Close();
_db?.Dispose();
}
catch (Exception ex)
{
Log.Error(ex, "[SqLitePacketBuffer] Fault close db connection");
}
}
}
}

@ -0,0 +1,204 @@
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.IO;
using System.Threading;
using ZeroLevel.Models;
using ZeroLevel.Services.FileSystem;
namespace ZeroLevel.SqLite
{
public class SqLiteUserRepository
: BaseSqLiteDB
{
#region Fields
private readonly SQLiteConnection _db;
private readonly string _table_name = "users";
private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
#endregion Fields
#region Ctor
public SqLiteUserRepository()
{
var path =PrepareDb("users.db");
_db = new SQLiteConnection($"Data Source={path};Version=3;");
_db.Open();
Execute($"CREATE TABLE IF NOT EXISTS {_table_name} (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, displayname TEXT, hash BLOB, timestamp INTEGER, creator INTEGER, role INTEGER)", _db);
Execute($"CREATE INDEX IF NOT EXISTS username_index ON {_table_name} (username)", _db);
Execute($"CREATE INDEX IF NOT EXISTS hash_index ON {_table_name} (hash)", _db);
}
#endregion Ctor
public IEnumerable<User> GetAll()
{
var list = new List<User>();
SQLiteDataReader reader;
_rwLock.EnterReadLock();
try
{
reader = Read($"SELECT id, username, displayname, hash, timestamp, creator, role FROM {_table_name}", _db);
while (reader.Read())
{
list.Add(new User
{
Id = reader.GetInt64(0),
UserName = reader.GetString(1),
DisplayName = Read<string>(reader, 2),
PasswordHash = (byte[])reader.GetValue(3),
Timestamp = reader.GetInt64(4),
Creator = reader.GetInt64(5),
Role = (UserRole)reader.GetInt32(6)
});
}
reader.Close();
}
catch (Exception ex)
{
Log.Error(ex, "[SqLiteUserRepository] Fault get all users");
}
finally
{
_rwLock.ExitReadLock();
}
reader = null;
return list;
}
public User Get(long id)
{
User user = null;
SQLiteDataReader reader;
_rwLock.EnterReadLock();
try
{
reader = Read($"SELECT id, username, displayname, hash, timestamp, creator, role FROM {_table_name} WHERE id = @id", _db,
new SQLiteParameter[] { new SQLiteParameter("id", id) });
if (reader.Read())
{
var body = (byte[])reader.GetValue(1);
user = new User
{
Id = reader.GetInt64(0),
UserName = reader.GetString(1),
DisplayName = reader.GetString(2),
PasswordHash = (byte[])reader.GetValue(3),
Timestamp = reader.GetInt64(4),
Creator = reader.GetInt64(5),
Role = (UserRole)reader.GetInt32(6)
};
}
reader.Close();
}
catch (Exception ex)
{
Log.Error(ex, $"[SqLiteUserRepository] Fault get user by id '{id}'");
}
finally
{
_rwLock.ExitReadLock();
}
reader = null;
return user;
}
public User Get(string username, byte[] hash)
{
User user = null;
SQLiteDataReader reader;
_rwLock.EnterReadLock();
try
{
reader = Read($"SELECT id, username, displayname, hash, timestamp, creator, role FROM {_table_name} WHERE username = @username AND hash = @hash", _db,
new SQLiteParameter[]
{
new SQLiteParameter("username", username),
new SQLiteParameter("hash", hash)
});
if (reader.Read())
{
user = new User
{
Id = reader.GetInt64(0),
UserName = reader.GetString(1),
DisplayName = reader.GetString(2),
PasswordHash = (byte[])reader.GetValue(3),
Timestamp = reader.GetInt64(4),
Creator = reader.GetInt64(5),
Role = (UserRole)reader.GetInt32(6)
};
}
reader.Close();
}
catch (Exception ex)
{
Log.Error(ex, $"[SqLiteUserRepository] Fault get user by username '{username}' and pwdhash");
}
finally
{
_rwLock.ExitReadLock();
}
reader = null;
return user;
}
public InvokeResult<long> SaveUser(User user)
{
long id = -1;
_rwLock.EnterWriteLock();
var creationTime = DateTime.UtcNow.Ticks;
try
{
var count_obj = ExecuteScalar($"SELECT COUNT(*) FROM {_table_name} WHERE username=@username", _db, new SQLiteParameter[] { new SQLiteParameter("username", user.UserName) });
if (count_obj != null && (long)count_obj > 0)
{
return InvokeResult<long>.Fault<long>("Пользователь уже существует");
}
Execute($"INSERT INTO {_table_name} ('username', 'displayname', 'hash', 'timestamp', 'creator', 'role') values (@username, @displayname, @hash, @timestamp, @creator, @role)", _db,
new SQLiteParameter[]
{
new SQLiteParameter("username", user.UserName),
new SQLiteParameter("displayname", user.DisplayName),
new SQLiteParameter("hash", user.PasswordHash),
new SQLiteParameter("timestamp", creationTime),
new SQLiteParameter("creator", user.Creator),
new SQLiteParameter("role", user.Role)
});
id = (long)ExecuteScalar("select last_insert_rowid();", _db);
}
catch (Exception ex)
{
Log.Error(ex, $"[SqLiteUserRepository] Fault insert user in storage.");
InvokeResult<long>.Fault(ex.Message);
}
finally
{
_rwLock.ExitWriteLock();
}
return InvokeResult<long>.Succeeding(id);
}
public InvokeResult RemoveUser(string login)
{
_rwLock.EnterWriteLock();
try
{
Execute($"DELETE FROM {_table_name} WHERE username = @username", _db,
new SQLiteParameter[] { new SQLiteParameter("username", login) });
return InvokeResult.Succeeding();
}
catch (Exception ex)
{
Log.Error(ex, $"[SqLiteUserRepository] Fault remove user '{login}'");
return InvokeResult.Fault(ex.Message);
}
finally
{
_rwLock.ExitWriteLock();
}
}
}
}

@ -0,0 +1,197 @@
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
using ZeroLevel.Services.FileSystem;
using ZeroLevel.Services.Serialization;
namespace ZeroLevel.SqLite
{
public sealed class UserCacheRepository<T>
: BaseSqLiteDB, IDisposable
where T : IBinarySerializable
{
#region Fields
private readonly SQLiteConnection _db;
private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
private readonly string _tableName;
#endregion Fields
#region Ctor
public UserCacheRepository()
{
_tableName = typeof(T).Name;
var path = PrepareDb($"{_tableName}_user_cachee.db");
_db = new SQLiteConnection($"Data Source={path};Version=3;");
_db.Open();
Execute($"CREATE TABLE IF NOT EXISTS {_tableName} (id INTEGER PRIMARY KEY AUTOINCREMENT, key TEXT, body BLOB)", _db);
Execute($"CREATE INDEX IF NOT EXISTS key_index ON {_tableName} (key)", _db);
}
#endregion Ctor
#region API
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private string KEY(long userId, string name) => $"{userId}.{name}";
public bool AddOrUpdate(long userid, string name, T data)
{
var key = KEY(userid, name);
bool update = false;
_rwLock.EnterReadLock();
try
{
var count_obj = ExecuteScalar($"SELECT COUNT(*) FROM {_tableName} WHERE key=@key", _db, new SQLiteParameter[] { new SQLiteParameter("key", key) });
if (count_obj != null && (long)count_obj > 0)
{
update = true;
}
}
catch (Exception ex)
{
Log.Error(ex, $"[UserCacheRepository] Fault search existing records by name ({name})");
// no return!
}
finally
{
_rwLock.ExitReadLock();
}
_rwLock.EnterWriteLock();
try
{
var body = MessageSerializer.Serialize(data);
if (update)
{
Execute($"UPDATE {_tableName} SET body=@body WHERE key=@key", _db,
new SQLiteParameter[]
{
new SQLiteParameter("key", key),
new SQLiteParameter("body", body)
});
}
else
{
Execute($"INSERT INTO {_tableName} ('key', 'body') values (@key, @body)", _db,
new SQLiteParameter[]
{
new SQLiteParameter("key", key),
new SQLiteParameter("body", body)
});
}
return true;
}
catch (Exception ex)
{
Log.Error(ex, $"[UserCacheRepository] Fault insert record in storage. UserId: {userid}. Name '{name}'. Data: {typeof(T).Name}.");
}
finally
{
_rwLock.ExitWriteLock();
}
return false;
}
public void Remove(long userid, string name)
{
var key = KEY(userid, name);
_rwLock.EnterWriteLock();
try
{
Execute($"DELETE FROM {_tableName} WHERE key=@key", _db, new SQLiteParameter[]
{
new SQLiteParameter("key", key)
});
}
catch (Exception ex)
{
Log.Error(ex, $"[UserCacheRepository] Fault remove record from db by name '{name}'. UserId: {userid}. Data: {typeof(T).Name}.");
}
finally
{
_rwLock.ExitWriteLock();
}
}
public long Count(long userid, string name)
{
var key = KEY(userid, name);
_rwLock.EnterWriteLock();
try
{
return Convert.ToInt64(ExecuteScalar($"SELECT COUNT(*) FROM {_tableName} WHERE key=@key", _db, new SQLiteParameter[]
{
new SQLiteParameter("key", key)
}));
}
catch (Exception ex)
{
Log.Error(ex, $"[UserCacheRepository] Fault get count record from db by name '{name}'. UserId: {userid}. Data: {typeof(T).Name}.");
}
finally
{
_rwLock.ExitWriteLock();
}
return 0;
}
public IEnumerable<T> GetAll(long userid, string name)
{
var key = KEY(userid, name);
var result = new List<T>();
SQLiteDataReader reader;
_rwLock.EnterReadLock();
try
{
reader = Read($"SELECT [body] FROM {_tableName} WHERE key=@key", _db, new SQLiteParameter[]
{
new SQLiteParameter("key", key)
});
while (reader.Read())
{
var data = Read<byte[]>(reader, 0);
if (data != null)
{
result.Add(MessageSerializer.Deserialize<T>(data));
}
}
reader.Close();
}
catch (Exception ex)
{
Log.Error(ex, $"[UserCacheRepository] Fault read all records by name: {name}. UserId: {userid}. Data: {typeof(T).Name}.");
}
finally
{
_rwLock.ExitReadLock();
reader = null;
}
return result;
}
#endregion API
#region IDisposable
public void Dispose()
{
try
{
_db?.Dispose();
}
catch (Exception ex)
{
Log.Error(ex, "[UserCacheRepository] Fault close db connection");
}
}
#endregion IDisposable
}
}

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Authors>Ogoun</Authors>
<Description>Helpers for sqlite databases.
Based on System.Data.SQLite.Core</Description>
<Copyright>Copyright Ogoun 2020</Copyright>
<PackageProjectUrl>https://github.com/ogoun/Zero/tree/master/ZeroLevel.SqLite</PackageProjectUrl>
<RepositoryType>GitHub</RepositoryType>
<RepositoryUrl>https://github.com/ogoun/Zero</RepositoryUrl>
<PackageIcon>zero.png</PackageIcon>
<PackageIconUrl />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.112" />
<PackageReference Include="ZeroLevel" Version="3.1.9" />
</ItemGroup>
<ItemGroup>
<None Include="E:\Desktop\Developing\NUGET\zero.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
</Project>

@ -37,7 +37,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DependencyInjectionTests",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZeroLevel.Logger", "ZeroLevel.Logger\ZeroLevel.Logger.csproj", "{D1C061DB-3565-43C3-B8F3-628DE4908750}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroLevel.WPF", "ZeroLevel.WPF\ZeroLevel.WPF.csproj", "{0D70D688-1E21-4E9D-AA49-4D255DF27D8D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZeroLevel.WPF", "ZeroLevel.WPF\ZeroLevel.WPF.csproj", "{0D70D688-1E21-4E9D-AA49-4D255DF27D8D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroLevel.SqLite", "ZeroLevel.SqLite\ZeroLevel.SqLite.csproj", "{5B545DD6-8573-4CDD-B32D-9B0AA2AC2F9A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -241,6 +243,18 @@ Global
{0D70D688-1E21-4E9D-AA49-4D255DF27D8D}.Release|x64.Build.0 = Release|Any CPU
{0D70D688-1E21-4E9D-AA49-4D255DF27D8D}.Release|x86.ActiveCfg = Release|Any CPU
{0D70D688-1E21-4E9D-AA49-4D255DF27D8D}.Release|x86.Build.0 = Release|Any CPU
{5B545DD6-8573-4CDD-B32D-9B0AA2AC2F9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5B545DD6-8573-4CDD-B32D-9B0AA2AC2F9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5B545DD6-8573-4CDD-B32D-9B0AA2AC2F9A}.Debug|x64.ActiveCfg = Debug|Any CPU
{5B545DD6-8573-4CDD-B32D-9B0AA2AC2F9A}.Debug|x64.Build.0 = Debug|Any CPU
{5B545DD6-8573-4CDD-B32D-9B0AA2AC2F9A}.Debug|x86.ActiveCfg = Debug|Any CPU
{5B545DD6-8573-4CDD-B32D-9B0AA2AC2F9A}.Debug|x86.Build.0 = Debug|Any CPU
{5B545DD6-8573-4CDD-B32D-9B0AA2AC2F9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5B545DD6-8573-4CDD-B32D-9B0AA2AC2F9A}.Release|Any CPU.Build.0 = Release|Any CPU
{5B545DD6-8573-4CDD-B32D-9B0AA2AC2F9A}.Release|x64.ActiveCfg = Release|Any CPU
{5B545DD6-8573-4CDD-B32D-9B0AA2AC2F9A}.Release|x64.Build.0 = Release|Any CPU
{5B545DD6-8573-4CDD-B32D-9B0AA2AC2F9A}.Release|x86.ActiveCfg = Release|Any CPU
{5B545DD6-8573-4CDD-B32D-9B0AA2AC2F9A}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

@ -6,17 +6,18 @@
</Description>
<Authors>ogoun</Authors>
<Company>ogoun</Company>
<AssemblyVersion>3.1.9.0</AssemblyVersion>
<AssemblyVersion>3.2.0.0</AssemblyVersion>
<PackageReleaseNotes>Fix bugs, append Multiprocessor class</PackageReleaseNotes>
<PackageProjectUrl>https://github.com/ogoun/Zero/wiki</PackageProjectUrl>
<Copyright>Copyright Ogoun 2020</Copyright>
<PackageLicenseUrl></PackageLicenseUrl>
<PackageIconUrl>https://raw.githubusercontent.com/ogoun/Zero/master/zero.png</PackageIconUrl>
<PackageIconUrl></PackageIconUrl>
<RepositoryUrl>https://github.com/ogoun/Zero</RepositoryUrl>
<RepositoryType>GitHub</RepositoryType>
<Version>3.1.9</Version>
<FileVersion>3.1.9.0</FileVersion>
<Version>3.2.0</Version>
<FileVersion>3.2.9.0</FileVersion>
<Platforms>AnyCPU;x64</Platforms>
<PackageIcon>zero.png</PackageIcon>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@ -43,4 +44,11 @@
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.3" />
</ItemGroup>
<ItemGroup>
<None Include="E:\Desktop\Developing\NUGET\zero.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
</Project>

Loading…
Cancel
Save

Powered by TurnKey Linux.