2023-06-04 19:00:46 +02:00
using System ;
using System.Collections.Generic ;
using System.Data.Common ;
using System.IO ;
using System.Linq ;
2023-06-05 15:15:18 +02:00
using System.Security.Policy ;
2023-06-04 19:00:46 +02:00
using System.Threading.Tasks ;
using Dapper ;
using Microsoft.Data.Sqlite ;
using Serilog ;
using SharpRss.Models ;
namespace SharpRss
{
internal static class DbAccess
{
static DbAccess ( )
{
Initialize ( ) ;
}
private static readonly string ConnectionString = $"Data Source={Path.Combine(Environment.CurrentDirectory, " sharp_rss . sqlite ")};" ;
private static bool _isInitialized ;
public static async Task SetSyndicationAsync ( SyndicationContainer synContainer )
{
2023-06-05 15:15:18 +02:00
if ( synContainer . Category ! = null )
await SetCategoryAsync ( synContainer . Category ) ;
if ( synContainer . FeedModel ! = null )
await SetFeedAsync ( synContainer . FeedModel ) ;
if ( synContainer . FeedItems ! = null & & synContainer . FeedItems . Any ( ) )
await SetFeedItemsAsync ( synContainer . FeedItems ) ;
2023-06-04 19:00:46 +02:00
}
public static async Task < HashSet < CategoryModel > > GetCategoriesAsync ( )
{
HashSet < CategoryModel > categories = new HashSet < CategoryModel > ( ) ;
await using SqliteConnection dbc = new SqliteConnection ( ConnectionString ) ;
dbc . Open ( ) ;
await using DbDataReader reader = await dbc . ExecuteReaderAsync ( "SELECT * FROM category" ) ;
while ( await reader . ReadAsync ( ) )
{
CategoryModel categoryModel = new CategoryModel ( )
{
Id = reader [ "id" ] . ToString ( ) ,
Name = reader [ "name" ] . ToString ( ) ,
HexColor = reader [ "hex_color" ] . ToString ( ) ,
FeedCount = 0 , // Not implemented.
Icon = reader [ "icon" ] . ToString ( )
} ;
categories . Add ( categoryModel ) ;
}
return categories ;
}
public static async Task < bool > SetCategoryAsync ( CategoryModel category )
{
if ( category = = null ) return false ;
await using SqliteConnection dbc = new SqliteConnection ( ConnectionString ) ;
dbc . Open ( ) ;
int affected = await dbc . ExecuteAsync ( "INSERT OR REPLACE INTO category (id, hex_color, icon, name) VALUES (IFNULL((SELECT id FROM category WHERE name=@Name), @Id), @HexColor, @Icon, @Name)" ,
new { category . Id , category . HexColor , category . Icon , category . Name } ) ;
return affected > 0 ;
}
public static async Task < bool > DeleteCategory ( CategoryModel category )
{
if ( category = = null ) return false ;
await using SqliteConnection dbc = new SqliteConnection ( ConnectionString ) ;
dbc . Open ( ) ;
int affected = await dbc . ExecuteAsync ( "DELETE FROM category WHERE id=@Id; UPDATE feed SET category_id=NULL WHERE category_id=@Id" , new { category . Id } ) ;
return affected > 0 ;
}
public static async Task SetFeedAsync ( FeedModel feed )
{
await using SqliteConnection dbc = new SqliteConnection ( ConnectionString ) ;
dbc . Open ( ) ;
2023-06-05 18:11:43 +02:00
await using SqliteCommand cmd = new SqliteCommand ( "INSERT OR REPLACE INTO feed (url, title, category_id, feed_type, feed_version, description, language, copyright, publication_date, last_updated, categories, image_url) VALUES (@Url, @Title, @CategoryId, @FeedType, @FeedVersion, @Description, @Language, @Copyright, @PublicationDate, @LastUpdated, @Categories, @ImageUrl)" , dbc )
{
Parameters =
{
new SqliteParameter ( "Url" , feed . OriginalUrl ? ? string . Empty ) ,
new SqliteParameter ( "Title" , feed . Title ? ? string . Empty ) ,
new SqliteParameter ( "CategoryId" , feed . CategoryId ? ? string . Empty ) ,
new SqliteParameter ( "FeedType" , feed . FeedType ? ? string . Empty ) ,
new SqliteParameter ( "Description" , feed . Description ? ? string . Empty ) ,
new SqliteParameter ( "Language" , feed . Language ? ? string . Empty ) ,
new SqliteParameter ( "Copyright" , feed . Copyright ? ? string . Empty ) ,
new SqliteParameter ( "PublicationDate" , feed . PublicationDate ? . ToUnixTimeMilliseconds ( ) ? ? 0 ) ,
new SqliteParameter ( "LastUpdated" , feed . LastUpdated ? . ToUnixTimeMilliseconds ( ) ? ? 0 ) ,
new SqliteParameter ( "Categories" , feed . Categories ! = null & & feed . Categories . Any ( ) ? string . Join ( ',' , feed . Categories ) : string . Empty ) ,
new SqliteParameter ( "ImageUrl" , feed . ImageUrl ? ? string . Empty )
}
} ;
int exec = await cmd . ExecuteNonQueryAsync ( ) ;
2023-06-05 15:15:18 +02:00
/ * int affected = await dbc . ExecuteAsync ( "INSERT OR REPLACE INTO feed (url, title, category_id, feed_type, feed_version, description, language, copyright, publication_date, last_updated, categories, image_url) VALUES (@Url, @Title, @CategoryId, @FeedType, @FeedVersion, @Description, @language, @Copyright, @PublicationDate, @LastUpdated, @Categories, @ImageUrl)" ,
new
{
Url = feed . OriginalUrl ? ? string . Empty ,
Title = feed . Title ? ? string . Empty ,
CategoryId = feed . CategoryId ? ? string . Empty ,
FeedType = feed . FeedType ? ? string . Empty ,
Description = feed . Description ? ? string . Empty ,
Language = feed . Language ? ? string . Empty ,
Copyright = feed . Copyright ? ? string . Empty ,
PublicationDate = feed . PublicationDate ? . ToUnixTimeMilliseconds ( ) ? ? 0 ,
LastUpdated = feed . LastUpdated ? . ToUnixTimeMilliseconds ( ) ? ? 0 ,
Categories = feed . Categories . Any ( ) ? string . Join ( ',' , feed . Categories ) : string . Empty ,
ImageUrl = feed . ImageUrl ? ? string . Empty
} ) ; * /
2023-06-05 18:11:43 +02:00
/ * if ( affected = = 0 )
Log . Warning ( "Failed to add feed: {FeedUrl}" , feed . OriginalUrl ) ; * /
2023-06-04 19:00:46 +02:00
}
public static async Task < HashSet < FeedModel > > GetFeedsAsync ( string [ ] ? categoryIds = null )
{
await using SqliteConnection dbc = new SqliteConnection ( ConnectionString ) ;
dbc . Open ( ) ;
HashSet < FeedModel > feeds = new HashSet < FeedModel > ( ) ;
await using DbDataReader reader = await dbc . ExecuteReaderAsync ( categoryIds = = null ? "SELECT * FROM feed" : "SELECT * FROM feed WHERE category_id IN(@CatIds)" , new { CatIds = categoryIds } ) ;
while ( await reader . ReadAsync ( ) )
{
FeedModel feedModel = new FeedModel ( )
{
OriginalUrl = reader [ "url" ] . ToString ( ) ,
Title = reader [ "title" ] . ToString ( ) ,
CategoryId = reader [ "category_id" ] . ToString ( ) ,
FeedType = reader [ "feed_type" ] . ToString ( ) ,
FeedVersion = reader [ "feed_version" ] . ToString ( ) ,
Description = reader [ "description" ] . ToString ( ) ,
Language = reader [ "language" ] . ToString ( ) ,
Copyright = reader [ "copyright" ] . ToString ( ) ,
PublicationDate = DateTimeOffset . FromUnixTimeMilliseconds ( long . Parse ( reader [ "publication_date" ] . ToString ( ) ) ) ,
LastUpdated = DateTimeOffset . FromUnixTimeMilliseconds ( long . Parse ( reader [ "last_updated" ] . ToString ( ) ) ) ,
Categories = reader [ "categories" ] . ToString ( ) . Split ( ',' ) ,
ImageUrl = reader [ "image_url" ] . ToString ( )
} ;
feeds . Add ( feedModel ) ;
}
return feeds ;
}
public static async Task SetFeedItemsAsync ( HashSet < FeedItemModel > items )
{
2023-06-05 15:15:18 +02:00
await using SqliteConnection dbc = new SqliteConnection ( ConnectionString ) ;
dbc . Open ( ) ;
int totalAffected = 0 ;
await using SqliteTransaction dbTransaction = dbc . BeginTransaction ( ) ;
foreach ( FeedItemModel item in items )
{
2023-06-05 18:11:43 +02:00
await using SqliteCommand cmd = new SqliteCommand ( "INSERT OR REPLACE INTO feed (url, title, category_id, feed_type, feed_version, description, language, copyright, publication_date, last_updated, categories, image_url) VALUES (@Url, @Title, @CategoryId, @FeedType, @FeedVersion, @Description, @language, @Copyright, @PublicationDate, @LastUpdated, @Categories, @ImageUrl)" , dbc , dbTransaction )
{
Parameters =
{
new SqliteParameter ( "Id" , item . Id ? ? string . Empty ) ,
new SqliteParameter ( "FeedUrl" , item . FeedUrl ? ? string . Empty ) ,
new SqliteParameter ( "Read" , item . Read . ToString ( ) ) ,
new SqliteParameter ( "Description" , item . Description ? ? string . Empty ) ,
new SqliteParameter ( "Link" , item . Link ? ? string . Empty ) ,
new SqliteParameter ( "LastUpdated" , item . LastUpdated ? . ToUnixTimeMilliseconds ( ) ? ? 0 ) ,
new SqliteParameter ( "PublishingDate" , item . PublishingDate ? . ToUnixTimeMilliseconds ( ) ? ? 0 ) ,
new SqliteParameter ( "Authors" , item . Authors . Any ( ) ? string . Join ( ',' , item . Authors ) : string . Empty ) ,
new SqliteParameter ( "Categories" , item . Categories . Any ( ) ? string . Join ( ',' , item . Categories ) : string . Empty ) ,
new SqliteParameter ( "Content" , item . Content ? ? string . Empty )
}
} ;
int exec = await cmd . ExecuteNonQueryAsync ( ) ;
2023-06-05 15:15:18 +02:00
/ * int affected = await dbc . ExecuteAsync ( "INSERT OR REPLACE INTO feed_item (id, feed_url, read, title, description, link, last_updated, publishing_date, authors, categories, content) VALUES (@Id, @FeedUrl, @Read, @Title, Description, @Link, @LastUpdated, @PublishingDate, @Authors, @Categories, @Content)" ,
transaction : dbTransaction ,
param : new
{
Id = item . Id ? ? string . Empty ,
FeedUrl = item . FeedUrl ? ? string . Empty ,
Read = item . Read . ToString ( ) ,
Description = item . Description ? ? string . Empty ,
Link = item . Link ? ? string . Empty ,
LastUpdated = item . LastUpdated ? . ToUnixTimeMilliseconds ( ) ? ? 0 ,
PublishingDate = item . PublishingDate ? . ToUnixTimeMilliseconds ( ) ? ? 0 ,
Authors = item . Authors . Any ( ) ? string . Join ( ',' , item . Authors ) : string . Empty ,
Categories = item . Categories . Any ( ) ? string . Join ( ',' , item . Categories ) : string . Empty ,
Content = item . Content ? ? string . Empty
} ) ; * /
2023-06-05 18:11:43 +02:00
totalAffected + = exec ;
2023-06-05 15:15:18 +02:00
}
dbTransaction . Commit ( ) ;
2023-06-04 19:00:46 +02:00
}
public static async Task < HashSet < FeedItemModel > > GetFeedItemsAsync ( string [ ] ? feedUrls )
{
await using SqliteConnection dbc = new SqliteConnection ( ConnectionString ) ;
dbc . Open ( ) ;
HashSet < FeedItemModel > items = new HashSet < FeedItemModel > ( ) ;
await using DbDataReader reader = await dbc . ExecuteReaderAsync ( feedUrls = = null ? "SELECT * FROM feed_item" : "SELECT * FROM feed_item WHERE feed_item.feed_url IN(@Urls)" , new { Urls = feedUrls } ) ;
while ( await reader . ReadAsync ( ) )
{
FeedItemModel feedItemModel = new FeedItemModel ( )
{
Id = reader [ "id" ] . ToString ( ) ,
FeedUrl = reader [ "feed_url" ] . ToString ( ) ,
2023-06-05 15:15:18 +02:00
Read = bool . Parse ( reader [ "read" ] . ToString ( ) ) ,
2023-06-04 19:00:46 +02:00
Title = reader [ "title" ] . ToString ( ) ,
Description = reader [ "description" ] . ToString ( ) ,
Link = reader [ "link" ] . ToString ( ) ,
LastUpdated = DateTimeOffset . FromUnixTimeMilliseconds ( long . Parse ( reader [ "last_updated" ] . ToString ( ) ) ) ,
PublishingDate = DateTimeOffset . FromUnixTimeMilliseconds ( long . Parse ( reader [ "publishing_date" ] . ToString ( ) ) ) ,
Authors = reader [ "authors" ] . ToString ( ) . ToString ( ) . Split ( ',' ) ,
Categories = reader [ "categories" ] . ToString ( ) . Split ( ',' ) ,
Content = reader [ "content" ] . ToString ( )
} ;
items . Add ( feedItemModel ) ;
}
return items ;
}
private static async void Initialize ( )
{
if ( _isInitialized ) return ;
Log . Verbose ( "Checking database..." ) ;
await using SqliteConnection dbc = new SqliteConnection ( ConnectionString ) ;
dbc . Open ( ) ;
Log . Verbose ( "Checking table: {Table}" , "category" ) ;
await dbc . ExecuteAsync ( "CREATE TABLE IF NOT EXISTS category (id STRING PRIMARY KEY, name STRING NOT NULL, hex_color STRING NOT NULL, icon STRING)" ) ;
Log . Verbose ( "Checking table: {Table}" , "feed" ) ;
await dbc . ExecuteAsync ( "CREATE TABLE IF NOT EXISTS feed (url STRING PRIMARY KEY, title STRING, category_id STRING, feed_type STRING, feed_version STRING, description STRING, language STRING, copyright STRING, publication_date INT, last_updated INT, categories STRING, image_url STRING)" ) ;
Log . Verbose ( "Checking table: {Table}" , "feed_item" ) ;
2023-06-05 15:15:18 +02:00
await dbc . ExecuteAsync ( "CREATE TABLE IF NOT EXISTS feed_item (id STRING PRIMARY KEY, feed_url STRING, read STRING, title STRING, description STRING, link STRING, last_updated INT, publishing_date INT, authors STRING, categories STRING, content STRING)" ) ;
2023-06-04 19:00:46 +02:00
Log . Verbose ( "Checking database done!" ) ;
_isInitialized = true ;
}
}
}