Select only needed field with ListAsync in Ardalis Clean Architecture - ardalis-cleanarchitecture

All - I have got two questions regarding the repository pattern used in the Ardalis CleanArchitecture.
With ListAsync how can I project to only a few fields not to all of them? Example: if I have 20+ fields in the table & I need only 3 fields to fetch from it. I see in the template the repository uses "ToListAsync()" ef api, however this executes a "Select * " in the SQL Server database table. I wanted only to fetch 3 fields which is part of my DTO.
With specification pattern I see an option to select required fields. That means when using specification I can project to a DTO. Where is the ideal place to put these kinds of dtos? Under specifications folder itself?
Any help on these would be very much appreciated. Thanks a bunch!
Template Repo: https://github.com/ardalis/CleanArchitecture

The easiest way I thought of is using AutoMapper. AutoMapper has an extension method IQueryable.ProjectTo<>. You can implement RepositoryBase<> and inherit that to EfRepository<> instead of inheriting RepositoryBase<> directly.
public class EfRepository<T> : ApplicationRepository<T>, IReadRepository<T>, IRepository<T> where T : EntityBase, IAggregateRoot
{
public EfRepository(ApplicationDbContext dbContext) : base(dbContext)
{
}
public EfRepository(ApplicationDbContext dbContext, IMapper mapper)
: base(dbContext, mapper)
{}
}
ApplicationRepository class
public abstract class ApplicationRepository<T> : RepositoryBase<T> where T : EntityBase, IAggregateRoot
{
private readonly IMapper _mapper;
private readonly DbContext _dbContext;
protected ApplicationRepository(DbContext dbContext)
: base(dbContext)
{
_dbContext = dbContext;
}
protected ApplicationRepository(DbContext dbContext, ISpecificationEvaluator specificationEvaluator)
: base(dbContext, specificationEvaluator)
{
_dbContext = dbContext;
}
protected ApplicationRepository(DbContext dbContext,
IMapper mapper)
: base(dbContext)
{
_dbContext = dbContext;
_mapper = mapper;
}
public async Task<List<TProjectTo>> ListAsyncProjected<TProjectTo>(CancellationToken cancellationToken = default)
{
return await ListAsyncProjected<TProjectTo>(null, cancellationToken);
}
public async Task<List<TProjectTo>> ListAsyncProjected<TProjectTo>(ISpecification<T> specification,
CancellationToken cancellationToken = default)
{
if (specification == null)
return await _dbContext.Set<T>()
.AsNoTracking()
.ProjectTo<TProjectTo>(_mapper.ConfigurationProvider)
.ToListAsync(cancellationToken);
return await SpecificationEvaluator.Default
.GetQuery(_dbContext.Set<T>()
.AsNoTracking()
.AsQueryable(), specification)
.ProjectTo<TProjectTo>(_mapper.ConfigurationProvider)
.ToListAsync(cancellationToken);
}
}

Related

ASP.NET Registering Services with Ninject

I've taken the project I'm currently working on and I have to be deploying soon, however I'm having some issues with WCF. I have the RestService and the IRestService and when I make some test calls everything is working fine. However I want to use some of the data services I have created so I could query the database in order to perform CRUD operations. Here is what I have in the Service:
public readonly ITimesheetService timesheetService;
public readonly IProjectService projectService;
public readonly IUserService userService;
public readonly INotificationService notificationsService;
public readonly IDepartmentService departmentService;
public readonly IUserTokenService userTokenService;
public TimesheetRestService(ITimesheetService timesheetService, IProjectService projectService, IUserService userService, INotificationService notificationService, IDepartmentService departmentService, IUserTokenService userTokenService)
{
this.timesheetService = timesheetService;
this.projectService = projectService;
this.userService = userService;
this.notificationsService = notificationService;
this.departmentService = departmentService;
this.userTokenService = userTokenService;
}
public TimesheetRestService()
{
}
I had to add constructor without parameters otherwise it's not working. And then I have the following 2 methods:
public string[] NewMethod()
{
string[] data = new string[] { "grant_type", "p_username", "p_password" };
return data;
}
public IEnumerable<DepartmentServiceModel> GetDepertment()
{
string userId = GetUserId();
if (userId == null)
{
return null;
}
var deparments = this.departmentService.GetDepartments(userId).ToList().AsQueryable().To<DepartmentServiceModel>().ToList();
return deparments;
}
The NewMethod() is there just for testing purposes. My problem is with the GetDepartment() method as the departmentService and all the other services are null.
The developer before me has been using ninject, so I have tried adding the following code to the RegisterServices(IKernel kernel):
kernel.Bind(b => b.From(Assemblies.RestService).
SelectAllClasses().
BindDefaultInterface());
kernel.Bind<ITimesheetRestService>().To<TimesheetRestService>();
However when the TimeSheetRestService class is being initialized - the constructor with no parameters is being called. How can I call the constructor with the other services so I could use them to pull data from the database?

Sending Message in SignalR from the Web API

I have created a hub in my Web API. It is very simple:
public class DashboardHub : Hub
{
public async Task SendMessage(InfoSummary infoSummary)
{
await Clients.All.SendAsync("ReceiveMessage", infoSummary);
}
}
I am trying to send a message to the Hub from a controller in the same Web API when data is updated.
I have seen 100 different answers, and nothing works. Basically my hub object in my controller is null, and I can't seem to get it instantiated.
private readonly IRepository _repo;
private readonly Helpers.Convert _convert;
private readonly CoreContext _context;
private readonly IMapper _mapper;
private readonly NotifyService _service;
private readonly DashboardHub _hub;
public MyController(IRepository repo,
CoreContext context,
IMapper mapper)
{
_convert = new Helpers.Convert(repo, mapper);
_repo = repo;
_context = context;
_mapper = mapper;
_hub = new DashboardHub();
_service = new NotifyService(_hub);
}
[HttpPost("updatestatus")]
public async Task<IActionResult> UpdateStatus(Header header) {
var returnVal = await _repo.ChangeStatus(header.HeaderId, header.Status);
headerSummary = _convert.ToReturnStatusHeader( await _repo.GetHeader(header.HeaderId));
// await _service.SendNotificationAsync(headerSummary);
await _hub.SendMessage(headerSummary);
return Ok(returnVal);
}
I have the
services.AddSignalR();
services.AddScoped(typeof(DashboardHub));
and
endpoints.MapHub<DashboardHub>("/Hubs/DashboardHub");
in the proper sections in the startup.cs file
I know I am missing something very small, but I would love to know what it is.
I have also tried creating a strongly typed hub, but that introduced even more problems.
Thanks in advance.
You have done there or four mistakes.
You don't need this line to be in your ConfigureServices method of Startup.cs. services.AddScoped(typeof(DashboardHub));
Remove it. Just keep services.AddSignalR();
Why are you using new key word, since .net core provide in-built dependency
injection service. Remove below lines.
_hub = new DashboardHub();
_service = new NotifyService(_hub);
Instead create a new interface INotifyService.cs for NotifyService.cs.
Register this service in ConfigureServices method of Startup.cs.
services.AddScoped<INotifyService, NotifyService>();
Your MyController.cs should be like below
Add this line.
using Microsoft.AspNetCore.SignalR;
private readonly IRepository _repo;
private readonly Helpers.Convert _convert;
private readonly CoreContext _context;
private readonly IMapper _mapper;
private readonly INotifyService _service;
private readonly IHubContext<DashboardHub> _hubContext
public MyController(IRepository repo, CoreContext context, IMapper mapper,INotifyService service,IHubContext<DashboardHub> hubContext)
{
_convert = new Helpers.Convert(repo, mapper);
_repo = repo;
_context = context;
_mapper = mapper;
_service = service;
_hubContext = hubContext;
}
[HttpPost("updatestatus")]
public async Task<IActionResult> UpdateStatus(Header header) {
var returnVal = await _repo.ChangeStatus(header.HeaderId, header.Status);
headerSummary = _convert.ToReturnStatusHeader( await _repo.GetHeader(header.HeaderId));
// await _service.SendNotificationAsync(headerSummary);
await hubContext.Clients.All.SendAsync("ReceiveMessage", headerSummary);
return Ok(returnVal);
}
Use same concept if you are sending messages inside your NotifyService.cs.
Well, I feel like a complete and utter newb. The fix is very simple. You must add the using statement telling the controller you want to use SignalR. OMG.. I am almost too embarrassed to put this up, but hope it will help someone else.
FIX:
using Microsoft.AspNetCore.SignalR;
:facepalm
What you could do is inject your hub using dependency injection in your controller. You can't just instanciate the hub in the controller like you are doing, and I would change it to a Singleton also.
services.AddSingleton(typeof(DashboardHub));
internal DashboardHub DashboardHub
{
get
{
return this.HttpContext.RequestServices.GetRequiredService<DashboardHub>();
}
}

Get transistent/scoped Database access in singletonservice

i updating my app from asp core 1.0 to 2.0. In 1.0 i have a soulution for my longlive import-task, initialated as singleton. The singleton used the DBContext. But in core 2.0 this soulution dosn't work. Can you help me?
My soulution in aps core 1.0 was
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("LocalConnection")));
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IDataStore, DataStore>();
services.AddSingleton<IImportRepository, ImportRepository>();
with
public class ImportRepository : IImportRepository
{
Importer Importer;
private readonly ApplicationDbContext DBContext;
private readonly IDataStore store;
private ImportSet runningSet = null;
public ImportRepository(ApplicationDbContext context, IDataStore store)
{
this.DBContext = context;
this.store = store;
Importer = new Importer(DBContext, store);
}
With this soulutions i get errormessages (in german, but i try to translate). "you cannot use scoped services in singleton"
Last attempt i used this solution
services.AddSingleton<ImportService>(
provider => new ImportService((ApplicationDbContext)provider.GetService(typeof(ApplicationDbContext)))
);
But here i get the errormessage "Cannot resolve scoped service 'Portal.Data.ApplicationDbContext' from root provider."
How can i get access to my database in my Import-Service?
You may resolve dependencies manually using IServiceProvider instance.
public class ImportRepository : IImportRepository
{
private readonly IServiceProvider _provider;
public ImportRepository(IServiceProvider provider)
{
_provider = provider;
...
}
public void DoSomething()
{
var dBContext = (ApplicationDbContext) provider.GetService(typeof(ApplicationDbContext));
...
}
}
By the way, there is an extension method GetService<T>(); defined in Microsoft.Extensions.DependencyInjection namespace:
// using Microsoft.Extensions.DependencyInjection;
var dBContext = provider.GetService<ApplicationDbContext>();
Since your singleton lives longer and is shared, the only option I see is that you take it as a parameter to the functions.
public class ImportRepository : IImportRepository
{
public void DoSomething(ApplicationDbContext context, IDataStore store)
{
}
}
The other option is to make ImportRepository scoped as well.
Ok. I have a soulution, that works, but not perfektly.
Like Juunas example i build a long life funktion
public async Task RunImportAsync(string fileName, DataService data)
{
await Task.Run(() =>
{
if (!System.IO.File.Exists(internalPath + fileName))
{
throw new Exception($"Datei {fileName} nicht gefunden.");
}
[long Operations...]
data.DBContext.Add(new ImportHistory(set));
data.DBContext.SaveChanges();
});
}
the call is simple
[HttpPost]
[Route("runImport")]
public async Task<IActionResult> RunImport([FromBody]dynamic body)
{
string id = "";
try
{
id = body.filename;
_logger.LogInformation($"Import from {id}");
await ImportService.RunImportAsync(id, DB);
return StatusCode(StatusCodes.Success_2xx.OK);
}
catch (Exception e)
{
return SendError(e);
}
}
But postmen get no Response with this solution. Is there a idea, how i can fix it?

What are best practices for managing DataContext?

In an effort to make my entities persistent ignorant and make my repositories testable, I've implemented a repository pattern like so:
public interface IJobRepository : IRepository<Job>
{
Job GetJobById(int jobId); //Special case where I'm eager loading other entities
void SaveJob(Job job, Job originalJob);
}
public class JobRepository : IJobRepository
{
private readonly IContext _context;
public JobRepository()
{
_context = new CustomObjectContext();
}
public JobRepository(UnitOfWork unitOfWork)
{
_context = unitOfWork.Context;
}
//Basic GetAll, GetById, Add and Delete methods from IRepository<T> Interface here
//omitted for brevity
public Job GetJobById(int jobId)
{
var job = _context.Jobs.Include("Company").Include("Location").
Include("PlantInfo").Where(j => j.Jobid == jobId).SingleOrDefault();
_context.DisposeContext();
return job;
}
public void SaveJob(Job job, Job originalJob)
{
if (job.Jobid > 0)
{
// Update
_context.Jobs.Attach(originalJob);
_context.PlantInfoes.Attach(originalJob.PlantInfo);
_context.Jobs.ApplyCurrentValues(job);
_context.PlantInfoes.ApplyCurrentValues(job.PlantInfo);
Note: ApplyCurrentValues is an extension method I'm using on the ObjectSet
}
else
{
// Create
_context.Jobs.AddObject(job);
}
_context.Save();
}
}
public class UnitOfWork
{
private readonly IContext _context;
public UnitOfWork()
{
_context = new CustomObjectContext();
}
public UnitOfWork(IContext context)
{
_context = context;
}
public string Save()
{
return _context.Save();
}
internal IContext Context
{
get { return _context; }
}
}
public interface IContext
{
IObjectSet<Job> Jobs { get; }
IObjectSet<Company> Companies { get; }
IObjectSet<Location> Locations { get; }
IObjectSet<PlantInfo> PlantInfoes { get; }
string Save();
}
My ObjectContext inherits from IContext...So my understanding is that I will only use the overloaded constructor on the repository to facilitate unit tests or to use it in the case that I want to use the same context (not desirable based on this post I found on SO "Entity Framework and Connection Pooling" -- Is this right?
Also, assuming the context only gets disposed when the repository is garbage collected, I have to dispose the context explicitly to avoid the "An entity object cannot be referenced by multiple instances of IEntityChangeTracker." exception when attaching the entity prior to a save.
That said, what is the best practice for managing the DataContext in a manner that keeps your entities persistent ignorant and repositories testable?
Note: This is an asp.net webapplication; UnitOfWork and IContext implementation was based on examples from "Programming Entity Framework", Second Edition by Julia Lerman Ch24.
Thanks in advance!
Firstly, I would ensure that whatever my "consumable" object is (either repository or unit of work, depending on your setup) implements IDisposable. When your consumbed object is disposed of, then you would dispose your underlying context.
For instance, if you're using your UnitOfWork as the consumable object (the one that gets created and called in your application), it would look something like:
public class UnitOfWork : IDisposable
{
// All the other stuff you had before plus:
public void Dispose ()
{
if (_context != null)
{
_context.Dispose ();
}
}
}
(Note: This can also be done on your repositories if they're the ones being consumed directly)
And then, you have a few options in your application. If you are going to use the UnitOfWork directly, you can use it like:
public void SomeMethodThatAccessesYourData ()
{
using (var unitOfWork = new UnitOfWork (/*Load in the context*/))
{
// Access your data here.
}
}
Or, in your Web Forms or MVC object you can use constructor injection and dispose of it when the Web Forms or MVC object is disposed of:
// If you're using MVC:
public class MyController : Controller
{
private UnitOfWork _unitOfWork;
public MyController (UnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public override Dispose (bool Disposing)
{
if (Disposing && _unitOfWork != null)
{
_unitOfWork.Dispose ();
}
}
}
The same idea stands for a web forms Page.
The main reason for using the constructor overload is for Inversion of Control (IOC). It helps with both unit testing and with production code when used with an IoC Container. WebForms doesn't lend itself well to IoC, but it is really easy with MVC.
Edit
I don't really see the connection with your repositories and the unit of work. Usually you access the repositories from a unit of work or, in other implementations, you request a unit of work from your target repository. In your implementation (which I understand is not your own) there seems to be no need for both.
Edit 2
If the UoW is overkill for your application, and you know you can use IoC to inject your IContext, and you don't have very many repositories, you can do something like:
public IRepository<T> : IDisposable { }
public IJobRepository : IRepository<Job> { /* All the stuff you put here */ }
public JobRepository : IJobRepository
{
private IContext _context;
...
public void Dispose ()
{
if (_context != null)
{
_context.Dispose ();
}
}
public JobRepository (IContext context)
{
_context = context;
}
}
Then, how you use it depends on your specific task. I'm not a fan of this direct use of IRepository, but this answer is getting too long.

IOC with multiple databases that use same interface (StructureMap or any other DI Framework)

We've been experimenting with StructureMap, and I'm having trouble grasping how to handle situations where a single interface has multiple implementations. The code below shows an example where we have two databases that are both accessible from a single service.
public class SomeController : Controller
{
private ISomeService _service;
private IClientRepository _repository;
protected IContext _masterContext;
protected IContext _clientContext;
public SomeController(ISomeService service, ISomeRepository repository
, IContext masterCon, IContext clientCon)
{
_service = service;
_repository = repository;
_masterContext = masterCon;
_clientContext = clientCon;
}
}
public class SomeService : ISomeService
{
private IContext _masterContext;
private IContext _clientContext;
public SomeService(IContext masterContext, IContext clientContext)
{
masterContext = _masterContext;
clientContext = _clientContext;
}
}
public class ClientRepository : IClientRepository
{
private IContext _clientContext;
public ClientRepository(IContext clientContext)
{
_clientContext = clientContext;
}
}
public class MasterContext : IContext
{
public MasterContext(String connString)
//<snip, snip> implement 3rd party data context
}
public class ClientContext : IContext
{
public ClientContext(String connString)
//<snip, snip> implement 3rd party data context
}
StructureMap worked GREAT when we had a single context (database), but how do I tell it how to resolve the 2nd? Note: in most situations we wouldn't have a service handling 2 databases (but may have a controller handling 2 connections, i.e. 2 repositories accessing 2 different databases), but it still doesn't seem to make it easier.
I'm half ready to just give up on using an IoC framework and go back to poor man's DI.
Is it not possible to have an IClientContext and an IMasterContext, possibly inheriting from IContext. My feeling is that the code would be doing one of two very different things depending on whether you were talking to the 'Master' or 'Client' database.
In Unity you can have named registrations, allowing you to effectively register more than a class for a given interface. So you could do (typing by heart, check the actual Unity documentation if interested):
container.RegisterType<IContext, MasterContext>("Master");
container.RegisterType<IContext, ClientContext>("Client");
and then the constructor for SomeService would be:
public SomeService(
[Dependency("Master")]IContext masterContext,
[Dependency("Client")]IContext clientContext)
{
//...
}
The drawback is that in this way your service class is no longer independent of the DI framework used, but depending on the project that may be ok.
This can be a little difficult if you're relying on StructureMap to resolve the dependencies automatically. The first solution (and what I'd err towards) is to make use of marker interfaces like Richard mentions in his answer then just register them. You can then explicitly specify whether you want your client or master context there.
The second way is to make use of named registrations, then specify the constructor params explicitly.
ForRequestedType<IContext>().AddInstances(
i => {
i.OfConcreteType<ClientContext>().WithName("Client");
i.OfConcreteType<MasterContext>().WithName("Master");
});
ForRequestedType<SomeController>().TheDefault.Is.ConstructedBy(
i => new SomeController(i.GetInstance<ISomeService>(),
i.GetInstance<IClientRepository>(),
i.GetInstance<IContext>("Master"),
i.GetInstance<IContext>("Client")));
Not particularly nice but it does the job and ultimately if it's only in one or two places it might be OK.
If you want to resolve differently on namespace / assembly you could try something like this:-
ForRequestedType<IContext>().AddInstances(
i => {
i.OfConcreteType<ClientContext>().WithName("Client");
i.OfConcreteType<MasterContext>().WithName("Master");
}).TheDefault.Is.Conditional(c => {
c.If(con => con.ParentType.Namespace.EndsWith("Client"))
.ThenIt.Is.TheInstanceNamed("Client");
c.If(con => con.ParentType.Namespace.EndsWith("Master"))
.ThenIt.Is.TheInstanceNamed("Master");
c.TheDefault.Is.OfConcreteType<ClientContext>();
});
Where the predicate on ParentType can refer to Assembly (or whatever you want really)
In case someone stumble in this problem, you can achieve it using factory pattern.
Service extension
public static class ServiceFactoryExtensions
{
public static void RegisterSqlFactory(this IServiceCollection serviceCollection)
{
serviceCollection.Configure<MsSqlOption>(option => option.ConnectionString = "Mssql connection string");
serviceCollection.Configure<MySqlOption>(option => option.ConnectionString = "Mysql connection string");
serviceCollection.Configure<PostgreOption>(option => option.ConnectionString = "Postgrel connection string");
serviceCollection.AddSingleton<ISqlDatabase, MsSql>();
serviceCollection.AddSingleton<ISqlDatabase, Postgre>();
serviceCollection.AddSingleton<ISqlDatabase, MySql>();
serviceCollection.AddSingleton<Func<IEnumerable<ISqlDatabase>>>(serviceProvider => () => serviceProvider.GetService<IEnumerable<ISqlDatabase>>());
serviceCollection.AddSingleton<ISqlDatabaseFactory, SqlDatabaseFactory>();
}
}
Factory class
public class SqlDatabaseFactory : ISqlDatabaseFactory
{
private readonly Func<IEnumerable<ISqlDatabase>> _factory;
public SqlDatabaseFactory(Func<IEnumerable<ISqlDatabase>> factory)
{
_factory = factory;
}
public ISqlDatabase CreateSql(SqlType sqlType)
{
var databases = _factory();
var sqlDatabase = databases.FirstOrDefault(x => x.DatabaseName == sqlType);
if (sqlDatabase == null)
throw new NotImplementedException($"Sql type {nameof(sqlType)} is not implemented");
return sqlDatabase;
}
}
Sql classes
public class MsSql : ISqlDatabase
{
public SqlType DatabaseName => SqlType.MsSql;
public string Connecionstring { get; private set; }
public MsSql(IOptions<MsSqlOption> option)
{
Connecionstring = option.Value.ConnectionString;
}
}
public class Postgre : ISqlDatabase
{
public SqlType DatabaseName => SqlType.Postgre;
public string Connecionstring { get; private set; }
public Postgre(IOptions<PostgreOption> option)
{
Connecionstring = option.Value.ConnectionString;
}
}
public class MySql : ISqlDatabase
{
public SqlType DatabaseName => SqlType.MySql;
public string Connecionstring { get; private set; }
public MySql(IOptions<MySqlOption> option)
{
Connecionstring = option.Value.ConnectionString;
}
}
public interface ISqlDatabase
{
string Connecionstring { get; }
SqlType DatabaseName { get; }
}
public enum SqlType
{
MsSql,
Postgre,
MySql
}
Usage
internal class Program
{
static void Main(string[] args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.RegisterSqlFactory();
var provider = serviceCollection.BuildServiceProvider();
var sqlFactory = provider.GetService<ISqlDatabaseFactory>();
var mySql = sqlFactory.CreateSql(SqlType.MySql);
var msSql = sqlFactory.CreateSql(SqlType.MsSql);
var postgre = sqlFactory.CreateSql(SqlType.Postgre);
Console.WriteLine($"Database Type : {mySql.DatabaseName}, Connectionstring: {mySql.Connecionstring}");
Console.WriteLine($"Database Type : {msSql.DatabaseName}, Connectionstring: {msSql.Connecionstring}");
Console.WriteLine($"Database Type : {postgre.DatabaseName}, Connectionstring: {postgre.Connecionstring}");
Console.ReadKey();
}
}
Output
Dependencies:
.Net Core 3.1
Microsoft.Extensions.DependencyInjection;
Microsoft.Extensions.Options;
System
System.Collections.Generic
System.Linq;

Resources