using unite.radimaging.source.n2m2.Repositories;
using unite.radimaging.source.n2m2.Entities;
using unite.radimaging.source.n2m2.Data;
using Serilog;
using Microsoft.Extensions.Configuration;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace unite.radimaging.source.n2m2.HostedServices.FileSearchHostedService {
public class FileSearchHostedService : BackgroundService {
private readonly IConfiguration _configuration;
//private IFoundFileRepository _repository;
public FileSearchHostedService(
//IFoundFileRepository repository,
IConfiguration configuration
) {
//_repository = repository ?? throw new ArgumentNullException(nameof(repository));
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
}
protected override async Task ExecuteAsync(CancellationToken cancellationToken) {
Log.Information("FileSearchHostedService: File searching started.");
cancellationToken.Register(() => Log.Information("Processing service stopped"));
//string checksum;
string _msg;
string _current_path;
FileInfo _current_file; _current_fileinfo;
FoundFile _foundFile;
FoundFile _existingFile;
FoundFile// _addFoundFile;Convert these two lines to Dependency Injections if possible
FoundFileContext FoundfileContext = new FoundFileContext(_configuration); // Normal instantiation, in leiu of injection
FoundFileRepository _repository = new FoundFileRepository(FoundfileContext); // Normal instantiation, in leiu of injection
string dir = _configuration.GetValue<string>("FileSearchSettings:SearchDir");
while (!cancellationToken.IsCancellationRequested) {
Log.Information("Searching files...");
string dir = _configuration.GetValue<string>("FileSearchSettings:SearchDir");
string[] files = Directory.GetFiles(dir);
foreach (string filename in files) {
Log.Debug($"Parsing '{filename}'");
foreach (string filename in _current_filefiles) {
_msg = $"'{filename}' ";
_current_fileinfo = new FileInfo(filename);
_current_path = Path.GetFullPath(Path.Combine(_current_file_current_fileinfo.Directory.ToString(), _current_file_current_fileinfo.Name));
_existingFile = await _repository.GetFileByPath (_current_path);
_foundFile = new FoundFile() {
Path = _current_path,
Size = _current_file_current_fileinfo.Length,
Mtime = _current_file_current_fileinfo.LastWriteTime,
Checksum = FileChecksum.getChecksum(filename)
};
Console.WriteLine("==========================");
_existingFile = await _repository.GetFileByPath (_current_path);
Console.WriteLine("past _repository.GetFileByPath");
tryif (_existingFile == null) {
Console.WriteLine($"{_existingFile_msg += $"does not exist in MongoDB.Path} exists!!!");Creating new entry ";
Console.WriteLine($"// Creating a new entry _existingFile.Checksum:in MongoDB should prolly be it's own class
// but until I figure out dependency injection for the repo, it creates more
// processing than its worth
try {_existingFile
await _repository.Checksum}"CreateFile(_foundFile);
Console.WriteLine _msg += "($"OK)";
_existingFile.Length = {_existingFile.Size }");
Console.WriteLinecatch ($"Exception e) {
_existingFile.LastWriteTime = _msg += $"(FAILED with '{_existingFilee.MtimeMessage}");'";
}
}
catch {
else if Console.WriteLine($"Creating MongoDB for file {_foundFile.Path} ");
_addFoundFile = new FoundFileEquals(_existingFile)) {
Path = _foundFile.Path,
Size = _foundFile.Size,
Mtime = _foundFile.Mtime,
_msg += "is already Checksumin =the _foundFiledatabase.Checksum
};
No further awaitprocessing _repositoryneeded.CreateFile(_addFoundFile);";
}
}
else {
_msg += $"has changed since being added to MongoDB. Updating entry ";
try {
var _result = await Task_repository.DelayUpdateFile(_foundFile);
if (!_result == true) {
_configuration throw new ApplicationException("FoundFileRepository.GetValue<int>UpdateFile did not return 'true'. Unknown MongoDB error updating the document.");
}
_msg += "("FileSearchSettings:Delay"OK),";
cancellationToken }
catch (Exception e) {
_msg += $"(FAILED with '{e.Message}')";
}
};
Log.Debug(_msg);
}
await Task.Delay(_configuration.GetValue<int>("FileSearchSettings:Delay"),cancellationToken);
}
}
}
}
using unite.radimaging.source.n2m2.Repositories;
using unite.radimaging.source.n2m2.Entities;
using unite.radimaging.source.n2m2.Data;
using Serilog;
using Microsoft.Extensions.Configuration;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace unite.radimaging.source.n2m2.HostedServices {
public class FileSearchHostedService : BackgroundService {
private readonly IConfiguration _configuration;
//private IFoundFileRepository _repository;
public FileSearchHostedService(
//IFoundFileRepository repository,
IConfiguration configuration
) {
//_repository = repository ?? throw new ArgumentNullException(nameof(repository));
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
}
protected override async Task ExecuteAsync(CancellationToken cancellationToken) {
Log.Information("FileSearchHostedService: File searching started.");
cancellationToken.Register(() => Log.Information("Processing service stopped"));
//string checksum;
string _current_path;
FileInfo _current_file;
FoundFile _foundFile;
FoundFile _existingFile;
FoundFile _addFoundFile;
FoundFileContext FoundfileContext = new FoundFileContext(_configuration); // Normal instantiation, in leiu of injection
FoundFileRepository _repository = new FoundFileRepository(FoundfileContext); // Normal instantiation, in leiu of injection
while (!cancellationToken.IsCancellationRequested) {
Log.Information("Searching files...");
string dir = _configuration.GetValue<string>("FileSearchSettings:SearchDir");
string[] files = Directory.GetFiles(dir);
foreach (string filename in files) {
Log.Debug($"Parsing '{filename}'");
_current_file = new FileInfo(filename);
_current_path = Path.GetFullPath(Path.Combine(_current_file.Directory.ToString(), _current_file.Name));
_foundFile = new FoundFile() {
Path = _current_path,
Size = _current_file.Length,
Mtime = _current_file.LastWriteTime,
Checksum = FileChecksum.getChecksum(filename)
};
Console.WriteLine("==========================");
_existingFile = await _repository.GetFileByPath (_current_path);
Console.WriteLine("past _repository.GetFileByPath");
try {
Console.WriteLine($"{_existingFile.Path} exists!!!");
Console.WriteLine($" _existingFile.Checksum: {_existingFile.Checksum}");
Console.WriteLine($" _existingFile.Length = {_existingFile.Size}");
Console.WriteLine($" _existingFile.LastWriteTime = ({_existingFile.Mtime}");
}
catch {
Console.WriteLine($"Creating MongoDB for file {_foundFile.Path} ");
_addFoundFile = new FoundFile() {
Path = _foundFile.Path,
Size = _foundFile.Size,
Mtime = _foundFile.Mtime,
Checksum = _foundFile.Checksum
};
await _repository.CreateFile(_addFoundFile);
}
}
await Task.Delay(
_configuration.GetValue<int>("FileSearchSettings:Delay"),
cancellationToken
);
}
}
}
}
using unite.radimaging.source.n2m2.Repositories;
using unite.radimaging.source.n2m2.Entities;
using unite.radimaging.source.n2m2.Data;
using Serilog;
using Microsoft.Extensions.Configuration;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
namespace unite.radimaging.source.n2m2.HostedServices.FileSearchHostedService {
public class FileSearchHostedService : BackgroundService {
private readonly IConfiguration _configuration;
//private IFoundFileRepository _repository;
public FileSearchHostedService(
//IFoundFileRepository repository,
IConfiguration configuration
) {
//_repository = repository ?? throw new ArgumentNullException(nameof(repository));
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
}
protected override async Task ExecuteAsync(CancellationToken cancellationToken) {
Log.Information("FileSearchHostedService: File searching started.");
cancellationToken.Register(() => Log.Information("Processing service stopped"));
//string checksum;
string _msg;
string _current_path;
FileInfo _current_fileinfo;
FoundFile _foundFile;
FoundFile _existingFile;
// Convert these two lines to Dependency Injections if possible
FoundFileContext FoundfileContext = new FoundFileContext(_configuration); // Normal instantiation, in leiu of injection
FoundFileRepository _repository = new FoundFileRepository(FoundfileContext); // Normal instantiation, in leiu of injection
string dir = _configuration.GetValue<string>("FileSearchSettings:SearchDir");
while (!cancellationToken.IsCancellationRequested) {
string[] files = Directory.GetFiles(dir);
foreach (string filename in files) {
_msg = $"'{filename}' ";
_current_fileinfo = new FileInfo(filename);
_current_path = Path.GetFullPath(Path.Combine(_current_fileinfo.Directory.ToString(), _current_fileinfo.Name));
_existingFile = await _repository.GetFileByPath (_current_path);
_foundFile = new FoundFile() {
Path = _current_path,
Size = _current_fileinfo.Length,
Mtime = _current_fileinfo.LastWriteTime,
Checksum = FileChecksum.getChecksum(filename)
};
if (_existingFile == null) {
_msg += $"does not exist in MongoDB. Creating new entry ";
// Creating a new entry in MongoDB should prolly be it's own class
// but until I figure out dependency injection for the repo, it creates more
// processing than its worth
try {
await _repository.CreateFile(_foundFile);
_msg += "(OK)";
}
catch (Exception e) {
_msg += $"(FAILED with '{e.Message}'";
}
}
else if (_foundFile.Equals(_existingFile)) {
_msg += "is already in the database. No further processing needed.";
}
else {
_msg += $"has changed since being added to MongoDB. Updating entry ";
try {
var _result = await _repository.UpdateFile(_foundFile);
if (!_result == true) {
throw new ApplicationException("FoundFileRepository.UpdateFile did not return 'true'. Unknown MongoDB error updating the document.");
}
_msg += "(OK)";
}
catch (Exception e) {
_msg += $"(FAILED with '{e.Message}')";
}
};
Log.Debug(_msg);
}
await Task.Delay(_configuration.GetValue<int>("FileSearchSettings:Delay"),cancellationToken);
}
}
}
}
Asp.Net overall design pattern integrating REST, dependency injection, and Hosted Services (See full project at Github)
I'm new to C#, and want to conform as much as possible to good design patterns. This code is the beginning of a Microservice, running as Asp.Net. It is based on a Microsoft tutorial doing similar work.
It has three functional components currently:
- Converts a CSV file to JSON, for return via REST
- A REST controller to test the conversion of the CSV to JSON return
- A background service which monitors a directory looking for changed files. The file attributes (Path, Date, Checksum) are stored in a MongoDB database via a Repository.
Opinions/Recommendations regarding the design patterns welcomed.
The full code can be found at: https://github.com/BioComSoftware/unite-radimaging-source-n2m2.git
BONUS: Notice in FileSearchHostedService.cs
, I instantiate the Repository objects explicitly. I think it would be more appropriate to have them as Dependency Injection - but I couldn't get it to work. Notice the commented-out lines that would have done this. My understanding is; I would need to do this with IScopedProcessingService
- but I have no idea how to do this with this specific code.
[Startup.cs]
using unite.radimaging.source.n2m2.Data;
using unite.radimaging.source.n2m2.Repositories;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
//using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace unite.radimaging.source.n2m2 {
public class Startup {
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime.
// Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) {
services.AddControllers();
services.AddScoped<IFoundFileContext, FoundFileContext>();
services.AddScoped<IFoundFileRepository, FoundFileRepository>();
}
// This method gets called by the runtime.
// Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
app.UseExceptionHandler("/error");
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
});
}
}
}
[FoundFileRepository.cs]
using unite.radimaging.source.n2m2.Data;
using unite.radimaging.source.n2m2.Entities;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace unite.radimaging.source.n2m2.Repositories {
public class FoundFileRepository : IFoundFileRepository {
private readonly IFoundFileContext _context;
public FoundFileRepository(IFoundFileContext context) {
_context = context ?? throw new ArgumentNullException(nameof(context));
}
public async Task<IEnumerable<FoundFile>> GetFiles() {
return await _context
.FoundFiles
.Find(p => true)
.ToListAsync();
}
public async Task<FoundFile> GetFile(string id) {
return await _context
.FoundFiles
.Find(p => p.Id == id)
.FirstOrDefaultAsync();
}
public async Task<FoundFile> GetFileByPath(string path) {
return await _context
.FoundFiles
.Find(p => p.Path == path)
.FirstOrDefaultAsync();
}
public async Task<FoundFile> GetFileByChecksum(string checksum) {
return await _context
.FoundFiles
.Find(p => p.Checksum == checksum)
.FirstOrDefaultAsync();
}
//public async Task<IEnumerable<FoundFile>> GetFileByMtime(string mtime) {
// FilterDefinition<FoundFile> filter = Builders<FoundFile>.Filter.ElemMatch<DateTime>(p => p.Mtime, mtime);
// return await _context
// .FoundFiles
// .Find(filter)
// .ToListAsync();
//}
//public async Task<IEnumerable<FoundFile>> GetFileBySize(long size) {
// FilterDefinition<FoundFile> filter = Builders<FoundFile>.Filter.ElemMatch<long>(p => p.Size, size);
// return await _context
// .FoundFiles
// .Find(filter)
// .ToListAsync();
//}
public async Task CreateFile(FoundFile foundFile) {
await _context.FoundFiles.InsertOneAsync(foundFile);
}
public async Task<bool> UpdateFile(FoundFile foundFile) {
var updateResult = await _context.FoundFiles.ReplaceOneAsync(
filter: g => g.Path == foundFile.Path, replacement: foundFile);
return updateResult.IsAcknowledged && updateResult.ModifiedCount > 0;
}
public async Task<bool> DeleteFile(string path) {
FilterDefinition<FoundFile> filter = Builders<FoundFile>.Filter.Eq(p => p.Path, path);
DeleteResult deleteResult = await _context
.FoundFiles
.DeleteOneAsync(filter);
return deleteResult.IsAcknowledged
&& deleteResult.DeletedCount > 0;
}
}
}
[FileSearchHostedService.cs]
using unite.radimaging.source.n2m2.Repositories;
using unite.radimaging.source.n2m2.Entities;
using unite.radimaging.source.n2m2.Data;
using Serilog;
using Microsoft.Extensions.Configuration;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace unite.radimaging.source.n2m2.HostedServices {
public class FileSearchHostedService : BackgroundService {
private readonly IConfiguration _configuration;
//private IFoundFileRepository _repository;
public FileSearchHostedService(
//IFoundFileRepository repository,
IConfiguration configuration
) {
//_repository = repository ?? throw new ArgumentNullException(nameof(repository));
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
}
protected override async Task ExecuteAsync(CancellationToken cancellationToken) {
Log.Information("FileSearchHostedService: File searching started.");
cancellationToken.Register(() => Log.Information("Processing service stopped"));
//string checksum;
string _current_path;
FileInfo _current_file;
FoundFile _foundFile;
FoundFile _existingFile;
FoundFile _addFoundFile;
FoundFileContext FoundfileContext = new FoundFileContext(_configuration); // Normal instantiation, in leiu of injection
FoundFileRepository _repository = new FoundFileRepository(FoundfileContext); // Normal instantiation, in leiu of injection
while (!cancellationToken.IsCancellationRequested) {
Log.Information("Searching files...");
string dir = _configuration.GetValue<string>("FileSearchSettings:SearchDir");
string[] files = Directory.GetFiles(dir);
foreach (string filename in files) {
Log.Debug($"Parsing '{filename}'");
_current_file = new FileInfo(filename);
_current_path = Path.GetFullPath(Path.Combine(_current_file.Directory.ToString(), _current_file.Name));
_foundFile = new FoundFile() {
Path = _current_path,
Size = _current_file.Length,
Mtime = _current_file.LastWriteTime,
Checksum = FileChecksum.getChecksum(filename)
};
Console.WriteLine("==========================");
_existingFile = await _repository.GetFileByPath (_current_path);
Console.WriteLine("past _repository.GetFileByPath");
try {
Console.WriteLine($"{_existingFile.Path} exists!!!");
Console.WriteLine($" _existingFile.Checksum: {_existingFile.Checksum}");
Console.WriteLine($" _existingFile.Length = {_existingFile.Size}");
Console.WriteLine($" _existingFile.LastWriteTime = ({_existingFile.Mtime}");
}
catch {
Console.WriteLine($"Creating MongoDB for file {_foundFile.Path} ");
_addFoundFile = new FoundFile() {
Path = _foundFile.Path,
Size = _foundFile.Size,
Mtime = _foundFile.Mtime,
Checksum = _foundFile.Checksum
};
await _repository.CreateFile(_addFoundFile);
}
}
await Task.Delay(
_configuration.GetValue<int>("FileSearchSettings:Delay"),
cancellationToken
);
}
}
}
}