Calibre_EntityFrameworkCore
Changes
src/Calibre.Model.Database/CalibreContext.cs 110(+36 -74)
src/Calibre.Model.Database/Entities/Book.cs 12(+12 -0)
src/Calibre.Model.Database/Entities/Data.cs 61(+61 -0)
src/Calibre.sln 7(+7 -0)
src/Calibre/Calibre.csproj 1(+1 -0)
src/Calibre/Program.cs 73(+60 -13)
src/Tools.PdfProvider/SimplePdfReader.cs 46(+46 -0)
Details
diff --git a/src/Calibre.Model.Database/BookFileProvider.cs b/src/Calibre.Model.Database/BookFileProvider.cs
new file mode 100644
index 0000000..061c021
--- /dev/null
+++ b/src/Calibre.Model.Database/BookFileProvider.cs
@@ -0,0 +1,67 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+
+using Calibre.Model.Database.Entities;
+
+namespace Calibre.Model.Database
+{
+ public class BookFileProvider
+ {
+ public async Task<string> BuildFilePathAsync(
+ string bookDirectory,
+ Book book,
+ Func<Book, Task<Data>> fileSelectorActionAsync
+ )
+ {
+ var file = await fileSelectorActionAsync(book);
+
+ var filePath = Path.Combine(
+ bookDirectory,
+ book.Path.Replace('/', '\\'),
+ $"{file.Name}.{file.Format}"
+ );
+
+ return filePath;
+ }
+
+ public async Task ReadFileAsync(
+ string bookDirectory,
+ Book book,
+ Func<Book, Task<Data>> fileSelectorActionAsync,
+ Func<Stream, Task> readActionAsync
+ )
+ {
+ var filePath = await BuildFilePathAsync(
+ bookDirectory,
+ book,
+ fileSelectorActionAsync
+ );
+
+ using (var stream = new FileStream(filePath, FileMode.Open))
+ {
+ await readActionAsync(stream);
+ }
+ }
+
+ public async Task<T> ReadFileAsync<T>(
+ string bookDirectory,
+ Book book,
+ Func<Book, Task<Data>> fileSelectorActionAsync,
+ Func<Stream, Task<T>> readActionAsync
+ )
+ {
+ var filePath = await BuildFilePathAsync(
+ bookDirectory,
+ book,
+ fileSelectorActionAsync
+ );
+
+ using (var stream = new FileStream(filePath, FileMode.Open))
+ {
+ var result = await readActionAsync(stream);
+ return result;
+ }
+ }
+ }
+}
src/Calibre.Model.Database/CalibreContext.cs 110(+36 -74)
diff --git a/src/Calibre.Model.Database/CalibreContext.cs b/src/Calibre.Model.Database/CalibreContext.cs
index 17bdaba..bbfda10 100644
--- a/src/Calibre.Model.Database/CalibreContext.cs
+++ b/src/Calibre.Model.Database/CalibreContext.cs
@@ -4,6 +4,8 @@ using Microsoft.EntityFrameworkCore.Metadata;
#nullable disable
+using System.IO;
+
using Calibre.Model.Database.Entities;
namespace Calibre.Model.Database
@@ -11,6 +13,14 @@ namespace Calibre.Model.Database
public partial class CalibreContext
: DbContext
{
+ #region
+
+ public string ConnectionString { private set; get; }
+
+ public bool AsNoTracking { set; get; }
+ public DirectoryInfo LibraryDirectory { set; get; }
+
+
#region Sets
public virtual DbSet<Annotation> Annotations { get; set; }
@@ -37,7 +47,7 @@ namespace Calibre.Model.Database
public virtual DbSet<Comment> Comments { get; set; }
public virtual DbSet<ConversionOption> ConversionOptions { get; set; }
public virtual DbSet<CustomColumn> CustomColumns { get; set; }
- public virtual DbSet<Datum> Data { get; set; }
+ public virtual DbSet<Data> FileData { get; set; }
public virtual DbSet<Feed> Feeds { get; set; }
public virtual DbSet<Identifier> Identifiers { get; set; }
public virtual DbSet<Language> Languages { get; set; }
@@ -52,11 +62,13 @@ namespace Calibre.Model.Database
#endregion
- private readonly string ConnectionString;
+ #endregion
+
[Obsolete]
public CalibreContext()
{
+ SetupContext();
}
/// <summary>
@@ -65,15 +77,16 @@ namespace Calibre.Model.Database
public CalibreContext(string connectionString)
{
ConnectionString = connectionString;
+ SetupContext();
}
- public CalibreContext(DbContextOptions<CalibreContext> options)
- : base(options)
- {
- }
+ //public CalibreContext(DbContextOptions<CalibreContext> options)
+ // : base(options)
+ //{
+ //}
- #region
+ #region BuildModel
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
@@ -346,22 +359,7 @@ namespace Calibre.Model.Database
modelBuilder.Entity<BooksAuthorsLink>(entity =>
{
- entity.ToTable("books_authors_link");
-
- entity.HasIndex(e => new { e.Book, e.Author }, "IX_books_authors_link_book_author")
- .IsUnique();
-
- entity.HasIndex(e => e.Author, "books_authors_link_aidx");
-
- entity.HasIndex(e => e.Book, "books_authors_link_bidx");
-
- entity.Property(e => e.Id)
- .ValueGeneratedNever()
- .HasColumnName("id");
-
- entity.Property(e => e.Author).HasColumnName("author");
-
- entity.Property(e => e.Book).HasColumnName("book");
+ BooksAuthorsLink.EfSetup(entity);
});
modelBuilder.Entity<BooksLanguagesLink>(entity =>
@@ -470,32 +468,7 @@ namespace Calibre.Model.Database
modelBuilder.Entity<BooksTagsLink>(entity =>
{
- entity.ToTable("books_tags_link");
-
- entity.HasIndex(e => new { e.Book, e.Tag }, "IX_books_tags_link_book_tag")
- .IsUnique();
-
- entity.HasIndex(e => e.Tag, "books_tags_link_aidx");
-
- entity.HasIndex(e => e.Book, "books_tags_link_bidx");
-
- entity.Property(e => e.Id)
- .ValueGeneratedNever()
- .HasColumnName("id");
-
- entity.Property(e => e.Book).HasColumnName("book");
-
- entity.Property(e => e.Tag).HasColumnName("tag");
-
-
- entity
- .HasOne(e => e.BookItem)
- .WithMany(e => e.Tags)
- .HasForeignKey(e => e.Book);
- entity
- .HasOne(e => e.TagItem)
- .WithMany(e => e.Books)
- .HasForeignKey(e => e.Tag);
+ BooksTagsLink.EfSetup(entity);
});
modelBuilder.Entity<Comment>(entity =>
@@ -596,32 +569,9 @@ namespace Calibre.Model.Database
.HasColumnName("normalized");
});
- modelBuilder.Entity<Datum>(entity =>
+ modelBuilder.Entity<Data>(entity =>
{
- entity.ToTable("data");
-
- entity.HasIndex(e => new { e.Book, e.Format }, "IX_data_book_format")
- .IsUnique();
-
- entity.HasIndex(e => e.Book, "data_idx");
-
- entity.HasIndex(e => e.Format, "formats_idx");
-
- entity.Property(e => e.Id)
- .ValueGeneratedNever()
- .HasColumnName("id");
-
- entity.Property(e => e.Book).HasColumnName("book");
-
- entity.Property(e => e.Format)
- .IsRequired()
- .HasColumnName("format");
-
- entity.Property(e => e.Name)
- .IsRequired()
- .HasColumnName("name");
-
- entity.Property(e => e.UncompressedSize).HasColumnName("uncompressed_size");
+ Data.EfSetup(entity);
});
modelBuilder.Entity<Feed>(entity =>
@@ -848,6 +798,18 @@ namespace Calibre.Model.Database
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
+ private void SetupContext()
+ {
+ if (AsNoTracking)
+ {
+ ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll;
+ }
+ else
+ {
+ ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
+ }
+ }
+
#endregion
}
}
diff --git a/src/Calibre.Model.Database/Entities/Authors/BooksAuthorsLink.cs b/src/Calibre.Model.Database/Entities/Authors/BooksAuthorsLink.cs
new file mode 100644
index 0000000..e6d3264
--- /dev/null
+++ b/src/Calibre.Model.Database/Entities/Authors/BooksAuthorsLink.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+
+#nullable disable
+
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+namespace Calibre.Model.Database.Entities
+{
+ public partial class BooksAuthorsLink
+ {
+ public long Id { get; set; }
+
+ public long Book { get; set; }
+ public virtual Book BookItem { get; set; }
+
+ public long Author { get; set; }
+ public virtual Author AuthorItem { get; set; }
+
+
+ public override string ToString()
+ {
+ return $"{Author}|{AuthorItem?.Name}|{Book}|{BookItem?.Title}";
+ }
+
+
+ internal static void EfSetup(
+ EntityTypeBuilder<BooksAuthorsLink> entity
+ )
+ {
+ entity.ToTable("books_authors_link");
+
+ entity.HasIndex(e => new { e.Book, e.Author }, "IX_books_authors_link_book_author")
+ .IsUnique();
+
+ entity.HasIndex(e => e.Author, "books_authors_link_aidx");
+
+ entity.HasIndex(e => e.Book, "books_authors_link_bidx");
+
+ entity.Property(e => e.Id)
+ .ValueGeneratedNever()
+ .HasColumnName("id");
+
+ entity.Property(e => e.Author).HasColumnName("author");
+
+ entity.Property(e => e.Book).HasColumnName("book");
+
+
+ entity
+ .HasOne(e => e.BookItem)
+ .WithMany(e => e.Autors)
+ .HasForeignKey(e => e.Book);
+ entity
+ .HasOne(e => e.AuthorItem)
+ .WithMany(e => e.Books)
+ .HasForeignKey(e => e.Author);
+ }
+ }
+}
src/Calibre.Model.Database/Entities/Book.cs 12(+12 -0)
diff --git a/src/Calibre.Model.Database/Entities/Book.cs b/src/Calibre.Model.Database/Entities/Book.cs
index 3d54a48..4012422 100644
--- a/src/Calibre.Model.Database/Entities/Book.cs
+++ b/src/Calibre.Model.Database/Entities/Book.cs
@@ -8,6 +8,9 @@ namespace Calibre.Model.Database.Entities
public partial class Book
{
public long Id { get; set; }
+ /// <summary>
+ /// Заголовок
+ /// </summary>
public string Title { get; set; }
public string Sort { get; set; }
public byte[] Timestamp { get; set; }
@@ -16,13 +19,22 @@ namespace Calibre.Model.Database.Entities
public string AuthorSort { get; set; }
public string Isbn { get; set; }
public string Lccn { get; set; }
+ /// <summary>
+ /// Относительный путь к папке с файлами книги
+ /// </summary>
public string Path { get; set; }
public long Flags { get; set; }
public string Uuid { get; set; }
public byte[] HasCover { get; set; }
public byte[] LastModified { get; set; }
+
public virtual List<BooksTagsLink> Tags { get; set; }
= new List<BooksTagsLink>();
+ public virtual List<BooksAuthorsLink> Autors { get; set; }
+ = new List<BooksAuthorsLink>();
+ public virtual List<Data> FileData { get; set; }
+ = new List<Data>();
+
public override string ToString()
{
src/Calibre.Model.Database/Entities/Data.cs 61(+61 -0)
diff --git a/src/Calibre.Model.Database/Entities/Data.cs b/src/Calibre.Model.Database/Entities/Data.cs
new file mode 100644
index 0000000..f7f75c8
--- /dev/null
+++ b/src/Calibre.Model.Database/Entities/Data.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+
+#nullable disable
+
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+namespace Calibre.Model.Database.Entities
+{
+ /// <summary>
+ /// Файлы книги
+ /// </summary>
+ public partial class Data
+ {
+ public long Id { get; set; }
+
+ public long Book { get; set; }
+ public Book BookItem { get; set; }
+
+ public string Format { get; set; }
+ public long UncompressedSize { get; set; }
+ public string Name { get; set; }
+
+
+ internal static void EfSetup(
+ EntityTypeBuilder<Data> entity
+ )
+ {
+ entity.ToTable("data");
+
+ entity.HasIndex(e => new { e.Book, e.Format }, "IX_data_book_format")
+ .IsUnique();
+
+ entity.HasIndex(e => e.Book, "data_idx");
+
+ entity.HasIndex(e => e.Format, "formats_idx");
+
+ entity.Property(e => e.Id)
+ .ValueGeneratedNever()
+ .HasColumnName("id");
+
+ entity.Property(e => e.Book).HasColumnName("book");
+
+ entity.Property(e => e.Format)
+ .IsRequired()
+ .HasColumnName("format");
+
+ entity.Property(e => e.Name)
+ .IsRequired()
+ .HasColumnName("name");
+
+ entity.Property(e => e.UncompressedSize).HasColumnName("uncompressed_size");
+
+
+ entity.HasOne(e => e.BookItem)
+ .WithMany(e => e.FileData)
+ .HasForeignKey(e => e.Book);
+ }
+ }
+}
src/Calibre.sln 7(+7 -0)
diff --git a/src/Calibre.sln b/src/Calibre.sln
index d65cffe..cb6c14a 100644
--- a/src/Calibre.sln
+++ b/src/Calibre.sln
@@ -11,6 +11,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Model", "Model", "{951387AC
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{5654144E-E0B3-4C6D-AFEB-799E77215B4E}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tools.PdfProvider", "Tools.PdfProvider\Tools.PdfProvider.csproj", "{057A6C6C-DC6B-4BA2-9D25-C263C6F04B68}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -25,6 +27,10 @@ Global
{4AA0EAB2-7B70-495A-9769-1665B857A91B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4AA0EAB2-7B70-495A-9769-1665B857A91B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4AA0EAB2-7B70-495A-9769-1665B857A91B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {057A6C6C-DC6B-4BA2-9D25-C263C6F04B68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {057A6C6C-DC6B-4BA2-9D25-C263C6F04B68}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {057A6C6C-DC6B-4BA2-9D25-C263C6F04B68}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {057A6C6C-DC6B-4BA2-9D25-C263C6F04B68}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -32,6 +38,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{39E1440C-9955-4744-8A22-02ED52502C11} = {5654144E-E0B3-4C6D-AFEB-799E77215B4E}
{4AA0EAB2-7B70-495A-9769-1665B857A91B} = {951387AC-1771-4B72-B885-B5DFAFB55D4D}
+ {057A6C6C-DC6B-4BA2-9D25-C263C6F04B68} = {951387AC-1771-4B72-B885-B5DFAFB55D4D}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {362F0FEB-FB13-46F0-825D-15E6F10100D4}
src/Calibre/Calibre.csproj 1(+1 -0)
diff --git a/src/Calibre/Calibre.csproj b/src/Calibre/Calibre.csproj
index d42fee7..a69768f 100644
--- a/src/Calibre/Calibre.csproj
+++ b/src/Calibre/Calibre.csproj
@@ -15,6 +15,7 @@
<ItemGroup>
<ProjectReference Include="..\Calibre.Model.Database\Calibre.Model.Database.csproj" />
+ <ProjectReference Include="..\Tools.PdfProvider\Tools.PdfProvider.csproj" />
</ItemGroup>
</Project>
src/Calibre/Program.cs 73(+60 -13)
diff --git a/src/Calibre/Program.cs b/src/Calibre/Program.cs
index 0ba59fc..f90f828 100644
--- a/src/Calibre/Program.cs
+++ b/src/Calibre/Program.cs
@@ -1,13 +1,15 @@
using System;
-
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using System.IO;
using System.Diagnostics;
+using System.IO;
using Microsoft.EntityFrameworkCore;
+using Tools.PdfProvider;
+
using Calibre.Model.Database;
using Calibre.Model.Database.Entities;
@@ -31,20 +33,29 @@ namespace Calibre
}
private static async Task<Dictionary<string, Book[]>> SearchBooksByTitle(
- string[] connectionStrings,
+ string[] dbFiles,
string containsTitle
)
{
List<CalibreContext> contexts
- = new List<CalibreContext>(connectionStrings.Length);
+ = new List<CalibreContext>(containsTitle.Length);
Dictionary<string, Task<Book[]>> data
- = new Dictionary<string, Task<Book[]>>(connectionStrings.Length);
+ = new Dictionary<string, Task<Book[]>>(containsTitle.Length);
try
{
- foreach (var elem in connectionStrings)
+ foreach (var elem in dbFiles)
{
- var context = new CalibreContext(elem);
+ var connectionString = BuildConnectionStringFromPath(elem);
+ var libraryDirectory = new DirectoryInfo(
+ Path.GetDirectoryName(elem)
+ );
+
+ var context = new CalibreContext(connectionString)
+ {
+ AsNoTracking = false,
+ LibraryDirectory = libraryDirectory
+ };
contexts.Add(context);
IQueryable<Book> selectExpr = context
@@ -52,6 +63,13 @@ namespace Calibre
.Include(e => e.Tags)
.ThenInclude(e => e.TagItem);
+ selectExpr = selectExpr
+ .Include(e => e.FileData);
+
+ selectExpr = selectExpr
+ .Include(e => e.Autors)
+ .ThenInclude(e => e.AuthorItem);
+
if (string.IsNullOrEmpty(containsTitle))
{
selectExpr = selectExpr
@@ -112,13 +130,7 @@ namespace Calibre
var watch = Stopwatch.StartNew();
var dbFiles = SelectDbFiles(directory);
- var connectionStrings = dbFiles
- .Select(
- e => BuildConnectionStringFromPath(e)
- )
- .ToArray();
-
- var searchResult = await SearchBooksByTitle(connectionStrings, "")
+ var searchResult = await SearchBooksByTitle(dbFiles, "")
.ConfigureAwait(false);
watch.Stop();
@@ -136,7 +148,9 @@ namespace Calibre
var searchIgnoreCase = allBooks
.Where(
e =>
+ //Заголовок
e.Title.Contains(containsTitle, StringComparison.OrdinalIgnoreCase)
+ //Теги
|| e.Tags.Any(
e => e.TagItem.Name.Contains(containsTitle, StringComparison.OrdinalIgnoreCase)
)
@@ -147,6 +161,39 @@ namespace Calibre
{
Console.WriteLine($"{elem.Title}|{elem.AuthorSort}");
}
+
+ var book = searchIgnoreCase.First();
+
+ var dbFile = searchResult
+ .First(
+ e => e.Value.Any(e2 => e2.Title == book.Title)
+ )
+ .Key;
+
+ BookFileProvider bookFileProvider = new BookFileProvider();
+ //var path = await bookFileProvider.BuildFilePathAsync(
+ // Path.GetDirectoryName(dbFile),
+ // book,
+ // (b) => Task.FromResult(b.FileData.First())
+ // );
+
+ var bookText = await bookFileProvider.ReadFileAsync(
+ Path.GetDirectoryName(dbFile),
+ book,
+ (b) => Task.FromResult(b.FileData.First()),
+ async (s) =>
+ {
+ SimplePdfReader simplePdfReader = new SimplePdfReader();
+ var result = await simplePdfReader.ReadPdfAsync(s);
+ return result;
+ }
+ );
+
+ var booksWithPdf = allBooks
+ .Where(
+ e => e.FileData.Any(e2 => e2.Format == "PDF")
+ )
+ .ToArray();
}
}
}
src/Tools.PdfProvider/SimplePdfReader.cs 46(+46 -0)
diff --git a/src/Tools.PdfProvider/SimplePdfReader.cs b/src/Tools.PdfProvider/SimplePdfReader.cs
new file mode 100644
index 0000000..a429748
--- /dev/null
+++ b/src/Tools.PdfProvider/SimplePdfReader.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Text;
+using System.IO;
+using System.Threading.Tasks;
+
+using iText.Kernel.Pdf;
+using iText.Kernel.Pdf.Canvas.Parser;
+using iText.Kernel.Pdf.Canvas.Parser.Listener;
+
+namespace Tools.PdfProvider
+{
+ public class SimplePdfReader
+ {
+ public Task<string> ReadPdfAsync(Stream stream)
+ {
+ StringBuilder text = new StringBuilder();
+
+ using (PdfReader iTextReader = new PdfReader(stream))
+ using (PdfDocument pdfDoc = new PdfDocument(iTextReader))
+ {
+ int numberofpages = pdfDoc.GetNumberOfPages();
+ for (int page = 1; page <= numberofpages; page++)
+ {
+ ITextExtractionStrategy strategy = new SimpleTextExtractionStrategy();
+ string currentText = PdfTextExtractor.GetTextFromPage(
+ pdfDoc.GetPage(page),
+ strategy
+ );
+
+ //currentText = Encoding.UTF8.GetString(
+ // ASCIIEncoding.Convert(
+ // Encoding.Default,
+ // Encoding.UTF8,
+ // Encoding.Default.GetBytes(currentText)
+ // )
+ // );
+ text.Append(currentText);
+ }
+ }
+
+ return Task.FromResult(
+ text.ToString()
+ );
+ }
+ }
+}
diff --git a/src/Tools.PdfProvider/Tools.PdfProvider.csproj b/src/Tools.PdfProvider/Tools.PdfProvider.csproj
new file mode 100644
index 0000000..991bc59
--- /dev/null
+++ b/src/Tools.PdfProvider/Tools.PdfProvider.csproj
@@ -0,0 +1,11 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net5.0</TargetFramework>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="itext7" Version="7.1.16" />
+ </ItemGroup>
+
+</Project>