moves db context to injection
This commit is contained in:
parent
f705babdc3
commit
647a1e44bd
@ -4,22 +4,11 @@ namespace PiwigoDirectorySync.Persistence;
|
|||||||
|
|
||||||
public class PersistenceContext : DbContext
|
public class PersistenceContext : DbContext
|
||||||
{
|
{
|
||||||
|
public PersistenceContext(DbContextOptions<PersistenceContext> options) : base(options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public DbSet<ServerEntity> PiwigoServers { get; set; } = null!;
|
public DbSet<ServerEntity> PiwigoServers { get; set; } = null!;
|
||||||
public DbSet<AlbumEntity> PiwigoAlbums { get; set; } = null!;
|
public DbSet<AlbumEntity> PiwigoAlbums { get; set; } = null!;
|
||||||
public DbSet<ImageEntity> PiwigoImages { get; set; } = null!;
|
public DbSet<ImageEntity> PiwigoImages { get; set; } = null!;
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
|
||||||
{
|
|
||||||
switch (AppSettings.Settings.DbProvider)
|
|
||||||
{
|
|
||||||
case "Sqlite":
|
|
||||||
options.UseSqlite(AppSettings.ConnectionString);
|
|
||||||
break;
|
|
||||||
case "InMemory":
|
|
||||||
options.UseInMemoryDatabase(AppSettings.ConnectionString);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new InvalidOperationException($"DbProvider {AppSettings.Settings.DbProvider} is not supported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,7 +1,10 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using PiwigoDirectorySync;
|
||||||
using PiwigoDirectorySync.Commands;
|
using PiwigoDirectorySync.Commands;
|
||||||
using PiwigoDirectorySync.Infrastructure;
|
using PiwigoDirectorySync.Infrastructure;
|
||||||
|
using PiwigoDirectorySync.Persistence;
|
||||||
using PiwigoDirectorySync.Services;
|
using PiwigoDirectorySync.Services;
|
||||||
using Spectre.Console.Cli;
|
using Spectre.Console.Cli;
|
||||||
|
|
||||||
@ -18,6 +21,15 @@ registrations.AddTransient<IFileIndexer, FileIndexer>();
|
|||||||
registrations.AddTransient<IFileSystemScanner, FileSystemScanner>();
|
registrations.AddTransient<IFileSystemScanner, FileSystemScanner>();
|
||||||
registrations.AddTransient<IAlbumSynchronizer, AlbumSynchronizer>();
|
registrations.AddTransient<IAlbumSynchronizer, AlbumSynchronizer>();
|
||||||
registrations.AddTransient<IImageSynchronizer, ImageSynchronizer>();
|
registrations.AddTransient<IImageSynchronizer, ImageSynchronizer>();
|
||||||
|
registrations.AddDbContext<PersistenceContext>(options =>
|
||||||
|
{
|
||||||
|
_ = AppSettings.Settings.DbProvider switch
|
||||||
|
{
|
||||||
|
"Sqlite" => options.UseSqlite(AppSettings.ConnectionString),
|
||||||
|
"InMemory" => options.UseInMemoryDatabase(AppSettings.ConnectionString),
|
||||||
|
_ => throw new Exception($"Unsupported dbType: {AppSettings.Settings.DbProvider}")
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
var registrar = new DependencyInjectionTypeRegistrar(registrations);
|
var registrar = new DependencyInjectionTypeRegistrar(registrations);
|
||||||
|
|
||||||
|
@ -10,18 +10,19 @@ namespace PiwigoDirectorySync.Services;
|
|||||||
public class AlbumSynchronizer : IAlbumSynchronizer
|
public class AlbumSynchronizer : IAlbumSynchronizer
|
||||||
{
|
{
|
||||||
private readonly ILogger<AlbumSynchronizer> _logger;
|
private readonly ILogger<AlbumSynchronizer> _logger;
|
||||||
|
private readonly PersistenceContext _persistenceContext;
|
||||||
private readonly IPiwigoClientFactory _piwigoClientFactory;
|
private readonly IPiwigoClientFactory _piwigoClientFactory;
|
||||||
|
|
||||||
public AlbumSynchronizer(ILogger<AlbumSynchronizer> logger, IPiwigoClientFactory piwigoClientFactory)
|
public AlbumSynchronizer(ILogger<AlbumSynchronizer> logger, IPiwigoClientFactory piwigoClientFactory, PersistenceContext persistenceContext)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_piwigoClientFactory = piwigoClientFactory;
|
_piwigoClientFactory = piwigoClientFactory;
|
||||||
|
_persistenceContext = persistenceContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SynchronizeAlbums(int piwigoServerId, CancellationToken ct)
|
public async Task SynchronizeAlbums(int piwigoServerId, CancellationToken ct)
|
||||||
{
|
{
|
||||||
await using var dbContext = new PersistenceContext();
|
var piwigoServer = await _persistenceContext.PiwigoServers.FindAsync(new object?[] { piwigoServerId }, ct);
|
||||||
var piwigoServer = await dbContext.PiwigoServers.FindAsync(new object?[] { piwigoServerId }, ct);
|
|
||||||
if (piwigoServer is null)
|
if (piwigoServer is null)
|
||||||
{
|
{
|
||||||
_logger.LogError("Could not sync albums with piwigo server {PiwigoServerId}", piwigoServerId);
|
_logger.LogError("Could not sync albums with piwigo server {PiwigoServerId}", piwigoServerId);
|
||||||
@ -30,30 +31,30 @@ public class AlbumSynchronizer : IAlbumSynchronizer
|
|||||||
|
|
||||||
var piwigoClient = await _piwigoClientFactory.GetPiwigoClientAsync(piwigoServer, ct);
|
var piwigoClient = await _piwigoClientFactory.GetPiwigoClientAsync(piwigoServer, ct);
|
||||||
|
|
||||||
await UpdatePiwigoAlbumsFromServerAsync(dbContext, piwigoClient, piwigoServer, ct);
|
await UpdatePiwigoAlbumsFromServerAsync(piwigoClient, piwigoServer, ct);
|
||||||
await AddMissingAlbumsToServerAsync(dbContext, piwigoClient, piwigoServer, ct);
|
await AddMissingAlbumsToServerAsync(piwigoClient, piwigoServer, ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task AddMissingAlbumsToServerAsync(PersistenceContext dbContext, IPiwigoClient piwigoClient, ServerEntity piwigoServer, CancellationToken ct)
|
private async Task AddMissingAlbumsToServerAsync(IPiwigoClient piwigoClient, ServerEntity piwigoServer, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var albumsToCreate = await dbContext.PiwigoAlbums.Where(a => a.ServerAlbumId == null && a.ServerId == piwigoServer.Id)
|
var albumsToCreate = await _persistenceContext.PiwigoAlbums.Where(a => a.ServerAlbumId == null && a.ServerId == piwigoServer.Id)
|
||||||
.OrderBy(a => a.Path)
|
.OrderBy(a => a.Path)
|
||||||
.Select(a => a.Id)
|
.Select(a => a.Id)
|
||||||
.ToListAsync(ct);
|
.ToListAsync(ct);
|
||||||
|
|
||||||
foreach (var albumId in albumsToCreate)
|
foreach (var albumId in albumsToCreate)
|
||||||
{
|
{
|
||||||
var albumEntity = await dbContext.PiwigoAlbums.GetByIdAsync(albumId, ct);
|
var albumEntity = await _persistenceContext.PiwigoAlbums.GetByIdAsync(albumId, ct);
|
||||||
var piwigoParentId = albumEntity.ParentId.HasValue ? (await dbContext.PiwigoAlbums.GetByIdAsync(albumEntity.ParentId.Value, ct)).ServerAlbumId : null;
|
var piwigoParentId = albumEntity.ParentId.HasValue ? (await _persistenceContext.PiwigoAlbums.GetByIdAsync(albumEntity.ParentId.Value, ct)).ServerAlbumId : null;
|
||||||
|
|
||||||
albumEntity.ServerAlbumId = await piwigoClient.Album.AddAsync(albumEntity.Name, piwigoParentId, visible: true, position: AlbumPosition.First,
|
albumEntity.ServerAlbumId = await piwigoClient.Album.AddAsync(albumEntity.Name, piwigoParentId, visible: true, position: AlbumPosition.First,
|
||||||
status: AlbumStatus.Public, cancellationToken: ct);
|
status: AlbumStatus.Public, cancellationToken: ct);
|
||||||
|
|
||||||
await dbContext.SaveChangesAsync(ct);
|
await _persistenceContext.SaveChangesAsync(ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdatePiwigoAlbumsFromServerAsync(PersistenceContext dbContext, IPiwigoClient piwigoClient, ServerEntity piwigoServer, CancellationToken ct)
|
private async Task UpdatePiwigoAlbumsFromServerAsync(IPiwigoClient piwigoClient, ServerEntity piwigoServer, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var serverAlbums = await piwigoClient.Album.GetListAsync(null, true, false, ThumbnailSize.Thumb, ct);
|
var serverAlbums = await piwigoClient.Album.GetListAsync(null, true, false, ThumbnailSize.Thumb, ct);
|
||||||
var serverAlbumDictionary = serverAlbums.ToDictionary(a => a.Id, a => a);
|
var serverAlbumDictionary = serverAlbums.ToDictionary(a => a.Id, a => a);
|
||||||
@ -62,21 +63,21 @@ public class AlbumSynchronizer : IAlbumSynchronizer
|
|||||||
{
|
{
|
||||||
_logger.LogInformation("Updating piwigo server album {ServerAlbumName} with piwigo id {ServerAlbumId}", serverAlbum.Name, serverAlbum.Id);
|
_logger.LogInformation("Updating piwigo server album {ServerAlbumName} with piwigo id {ServerAlbumId}", serverAlbum.Name, serverAlbum.Id);
|
||||||
|
|
||||||
var albumEntity = await GetOrAddPiwigoAlbumEntityFromServerAsync(dbContext, piwigoServer, serverAlbum, serverAlbumDictionary, ct);
|
var albumEntity = await GetOrAddPiwigoAlbumEntityFromServerAsync(piwigoServer, serverAlbum, serverAlbumDictionary, ct);
|
||||||
if (serverAlbum.IdUpperCat.HasValue)
|
if (serverAlbum.IdUpperCat.HasValue)
|
||||||
{
|
{
|
||||||
albumEntity.ParentId = (await dbContext.PiwigoAlbums.FindByServerIdAsync(piwigoServer.Id, serverAlbum.IdUpperCat.Value, ct))?.Id;
|
albumEntity.ParentId = (await _persistenceContext.PiwigoAlbums.FindByServerIdAsync(piwigoServer.Id, serverAlbum.IdUpperCat.Value, ct))?.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
await dbContext.SaveChangesAsync(ct);
|
await _persistenceContext.SaveChangesAsync(ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<AlbumEntity> GetOrAddPiwigoAlbumEntityFromServerAsync(PersistenceContext dbContext, ServerEntity piwigoServer, Album serverAlbum,
|
private async Task<AlbumEntity> GetOrAddPiwigoAlbumEntityFromServerAsync(ServerEntity piwigoServer, Album serverAlbum, IDictionary<int, Album> serverAlbumDictionary,
|
||||||
IDictionary<int, Album> serverAlbumDictionary, CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
// Already synchronized so it is easy to return
|
// Already synchronized so it is easy to return
|
||||||
var albumEntity = await dbContext.PiwigoAlbums.FindByServerIdAsync(piwigoServer.Id, serverAlbum.Id, ct);
|
var albumEntity = await _persistenceContext.PiwigoAlbums.FindByServerIdAsync(piwigoServer.Id, serverAlbum.Id, ct);
|
||||||
if (albumEntity != null)
|
if (albumEntity != null)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Found existing album {AlbumEntityName} with local id {AlbumEntityId} and piwigo server id {ServerAlbumId}", albumEntity.Name, albumEntity.Id,
|
_logger.LogDebug("Found existing album {AlbumEntityName} with local id {AlbumEntityId} and piwigo server id {ServerAlbumId}", albumEntity.Name, albumEntity.Id,
|
||||||
@ -87,7 +88,7 @@ public class AlbumSynchronizer : IAlbumSynchronizer
|
|||||||
// might exist already as the file system got scanned and created the local entries
|
// might exist already as the file system got scanned and created the local entries
|
||||||
// In this case we save the server id in our local album and link them.
|
// In this case we save the server id in our local album and link them.
|
||||||
var path = GeneratePath(serverAlbum, serverAlbumDictionary);
|
var path = GeneratePath(serverAlbum, serverAlbumDictionary);
|
||||||
albumEntity = await dbContext.PiwigoAlbums.FindByServerAndPathAsync(piwigoServer.Id, path, ct);
|
albumEntity = await _persistenceContext.PiwigoAlbums.FindByServerAndPathAsync(piwigoServer.Id, path, ct);
|
||||||
if (albumEntity != null)
|
if (albumEntity != null)
|
||||||
{
|
{
|
||||||
albumEntity.ServerAlbumId = serverAlbum.Id;
|
albumEntity.ServerAlbumId = serverAlbum.Id;
|
||||||
@ -106,7 +107,7 @@ public class AlbumSynchronizer : IAlbumSynchronizer
|
|||||||
};
|
};
|
||||||
_logger.LogInformation("Adding piwigo album {AlbumEntityName} with local id {AlbumEntityId} and piwigo server id {ServerAlbumId}", albumEntity.Name, albumEntity.Id,
|
_logger.LogInformation("Adding piwigo album {AlbumEntityName} with local id {AlbumEntityId} and piwigo server id {ServerAlbumId}", albumEntity.Name, albumEntity.Id,
|
||||||
albumEntity.ServerAlbumId);
|
albumEntity.ServerAlbumId);
|
||||||
dbContext.PiwigoAlbums.Add(albumEntity);
|
_persistenceContext.PiwigoAlbums.Add(albumEntity);
|
||||||
|
|
||||||
return albumEntity;
|
return albumEntity;
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,12 @@ public class FileIndexer : IFileIndexer
|
|||||||
{
|
{
|
||||||
private readonly IList<string> _failedFiles = new List<string>();
|
private readonly IList<string> _failedFiles = new List<string>();
|
||||||
private readonly ILogger<FileIndexer> _logger;
|
private readonly ILogger<FileIndexer> _logger;
|
||||||
|
private readonly PersistenceContext _persistenceContext;
|
||||||
|
|
||||||
public FileIndexer(ILogger<FileIndexer> logger)
|
public FileIndexer(ILogger<FileIndexer> logger, PersistenceContext persistenceContext)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_persistenceContext = persistenceContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int TotalFilesScanned { get; private set; }
|
public int TotalFilesScanned { get; private set; }
|
||||||
@ -21,8 +23,7 @@ public class FileIndexer : IFileIndexer
|
|||||||
|
|
||||||
public async Task StartProcessingAsync(Channel<string> fileQueue, int piwigoServerId, CancellationToken ct)
|
public async Task StartProcessingAsync(Channel<string> fileQueue, int piwigoServerId, CancellationToken ct)
|
||||||
{
|
{
|
||||||
await using var db = new PersistenceContext();
|
var piwigoServer = await _persistenceContext.PiwigoServers.GetByIdAsync(piwigoServerId, ct);
|
||||||
var piwigoServer = await db.PiwigoServers.GetByIdAsync(piwigoServerId, ct);
|
|
||||||
|
|
||||||
await foreach (var fullFilePath in fileQueue.Reader.ReadAllAsync(ct))
|
await foreach (var fullFilePath in fileQueue.Reader.ReadAllAsync(ct))
|
||||||
{
|
{
|
||||||
@ -46,9 +47,9 @@ public class FileIndexer : IFileIndexer
|
|||||||
|
|
||||||
var relativePath = Path.GetRelativePath(piwigoServer.RootDirectory, fullFilePath);
|
var relativePath = Path.GetRelativePath(piwigoServer.RootDirectory, fullFilePath);
|
||||||
|
|
||||||
var album = await GetOrAddAlbumAsync(db, piwigoServer, fileInfo.Directory!, ct);
|
var album = await GetOrAddAlbumAsync(piwigoServer, fileInfo.Directory!, ct);
|
||||||
|
|
||||||
var image = await GetOrAddImageAsync(db, album, relativePath, ct);
|
var image = await GetOrAddImageAsync(album, relativePath, ct);
|
||||||
|
|
||||||
if (image.LastChange != fileInfo.LastWriteTimeUtc)
|
if (image.LastChange != fileInfo.LastWriteTimeUtc)
|
||||||
{
|
{
|
||||||
@ -59,7 +60,7 @@ public class FileIndexer : IFileIndexer
|
|||||||
image.DeleteRequired = false;
|
image.DeleteRequired = false;
|
||||||
image.LastChange = fileInfo.LastWriteTimeUtc;
|
image.LastChange = fileInfo.LastWriteTimeUtc;
|
||||||
|
|
||||||
await db.SaveChangesAsync(ct);
|
await _persistenceContext.SaveChangesAsync(ct);
|
||||||
|
|
||||||
TotalFilesScanned++;
|
TotalFilesScanned++;
|
||||||
}
|
}
|
||||||
@ -71,9 +72,9 @@ public class FileIndexer : IFileIndexer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<ImageEntity> GetOrAddImageAsync(PersistenceContext db, AlbumEntity album, string relativePath, CancellationToken ct)
|
private async Task<ImageEntity> GetOrAddImageAsync(AlbumEntity album, string relativePath, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var imageEntity = await db.PiwigoImages.Where(i => i.AlbumId == album.Id && i.FilePath == relativePath).FirstOrDefaultAsync(ct);
|
var imageEntity = await _persistenceContext.PiwigoImages.Where(i => i.AlbumId == album.Id && i.FilePath == relativePath).FirstOrDefaultAsync(ct);
|
||||||
if (imageEntity is null)
|
if (imageEntity is null)
|
||||||
{
|
{
|
||||||
imageEntity = new ImageEntity
|
imageEntity = new ImageEntity
|
||||||
@ -84,16 +85,16 @@ public class FileIndexer : IFileIndexer
|
|||||||
UploadRequired = true,
|
UploadRequired = true,
|
||||||
DeleteRequired = false
|
DeleteRequired = false
|
||||||
};
|
};
|
||||||
db.PiwigoImages.Add(imageEntity);
|
_persistenceContext.PiwigoImages.Add(imageEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
return imageEntity;
|
return imageEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<AlbumEntity> GetOrAddAlbumAsync(PersistenceContext db, ServerEntity server, DirectoryInfo directory, CancellationToken ct)
|
private async Task<AlbumEntity> GetOrAddAlbumAsync(ServerEntity server, DirectoryInfo directory, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var albumPath = Path.GetRelativePath(server.RootDirectory, directory.FullName);
|
var albumPath = Path.GetRelativePath(server.RootDirectory, directory.FullName);
|
||||||
var album = await db.PiwigoAlbums.FindByServerAndPathAsync(server.Id, albumPath, ct);
|
var album = await _persistenceContext.PiwigoAlbums.FindByServerAndPathAsync(server.Id, albumPath, ct);
|
||||||
if (album != null)
|
if (album != null)
|
||||||
{
|
{
|
||||||
return album;
|
return album;
|
||||||
@ -106,7 +107,7 @@ public class FileIndexer : IFileIndexer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
parentAlbum = await GetOrAddAlbumAsync(db, server, directory.Parent!, ct);
|
parentAlbum = await GetOrAddAlbumAsync(server, directory.Parent!, ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
album = new AlbumEntity
|
album = new AlbumEntity
|
||||||
@ -118,9 +119,9 @@ public class FileIndexer : IFileIndexer
|
|||||||
ParentId = parentAlbum?.Id,
|
ParentId = parentAlbum?.Id,
|
||||||
Parent = parentAlbum
|
Parent = parentAlbum
|
||||||
};
|
};
|
||||||
db.PiwigoAlbums.Add(album);
|
_persistenceContext.PiwigoAlbums.Add(album);
|
||||||
|
|
||||||
await db.SaveChangesAsync(ct);
|
await _persistenceContext.SaveChangesAsync(ct);
|
||||||
|
|
||||||
return album;
|
return album;
|
||||||
}
|
}
|
||||||
|
@ -7,18 +7,18 @@ namespace PiwigoDirectorySync.Services;
|
|||||||
public class FileSystemScanner : IFileSystemScanner
|
public class FileSystemScanner : IFileSystemScanner
|
||||||
{
|
{
|
||||||
private readonly ILogger<FileSystemScanner> _logger;
|
private readonly ILogger<FileSystemScanner> _logger;
|
||||||
|
private readonly PersistenceContext _persistenceContext;
|
||||||
|
|
||||||
|
|
||||||
public FileSystemScanner(ILogger<FileSystemScanner> logger)
|
public FileSystemScanner(ILogger<FileSystemScanner> logger, PersistenceContext persistenceContext)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_persistenceContext = persistenceContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ScanAsync(Channel<string> fileQueue, int piwigoServerId, CancellationToken ct)
|
public async Task ScanAsync(Channel<string> fileQueue, int piwigoServerId, CancellationToken ct)
|
||||||
{
|
{
|
||||||
await using var db = new PersistenceContext();
|
var piwigoServer = await _persistenceContext.PiwigoServers.GetByIdAsync(piwigoServerId, ct);
|
||||||
|
|
||||||
var piwigoServer = await db.PiwigoServers.GetByIdAsync(piwigoServerId, ct);
|
|
||||||
_logger.LogInformation("Scanning files for piwigo server {PiwigoServerName} in directory {PiwigoServerRootDirectory}", piwigoServer.Name, piwigoServer.RootDirectory);
|
_logger.LogInformation("Scanning files for piwigo server {PiwigoServerName} in directory {PiwigoServerRootDirectory}", piwigoServer.Name, piwigoServer.RootDirectory);
|
||||||
|
|
||||||
await ScanRootDirectory(fileQueue, new DirectoryInfo(piwigoServer.RootDirectory), ct);
|
await ScanRootDirectory(fileQueue, new DirectoryInfo(piwigoServer.RootDirectory), ct);
|
||||||
|
@ -2,5 +2,10 @@
|
|||||||
|
|
||||||
public class ImageSynchronizer : IImageSynchronizer
|
public class ImageSynchronizer : IImageSynchronizer
|
||||||
{
|
{
|
||||||
public Task SynchronizeImages(int piwigoServerId, CancellationToken ct) => throw new NotImplementedException();
|
public Task SynchronizeImages(int piwigoServerId, CancellationToken ct)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user