Mediatr - Generic Requests and Request Handlers - c#

I'm trying to use generics to create an abstract query request handler for aggregate roots in a domain-driven design architecture.
I've got the following classes to setup a Mediatr request.
However, I get a compiler error when using ISender.Send because it thinks my response object is just an object instead a QueryResult<T>.
Do you know what I'm doing wrong?
public interface IQuery<T> : IRequest<T>
where T : class
{
Guid CorrelationId { get; }
}
public abstract class BaseResult
{
protected BaseResult(ResultStatus status, Dictionary<string, List<string>>? errors)
{
this.Status = status;
this.Errors = errors ?? new Dictionary<string, List<string>>();
this.IsSuccess = status == ResultStatus.Success;
}
public bool IsSuccess { get; private set; }
public ResultStatus Status { get; private set; }
public Dictionary<string, List<string>> Errors { get; private set; }
...
}
public class QueryResult<T> : BaseResult
where T : class
{
private QueryResult(T? data, ResultStatus status, Dictionary<string, List<string>>? errors)
: base(status, errors)
{
this.Data = data;
}
public T? Data { get; }
...
}
public class AggregateRootQuery<TAggregate, TDto>
: IRequest<QueryResult<IEnumerable<TDto>>>, IQuery<IEnumerable<TDto>>
where TAggregate : class, IAggregateRoot // IAggregate root is a marker interface to only allow queries to start at the aggregate root when using my EF core DB context wrapper
where TDto : class
{
public AggregateRootQuery(Guid correlationId)
{
this.CorrelationId = correlationId;
}
public Guid CorrelationId { get; }
}
public abstract class BaseAggregateRootQueryHandler<TAggregate, TDto> : IRequestHandler<AggregateRootQuery<TAggregate, TDto>, QueryResult<IEnumerable<TDto>>>
where TAggregate : class, IAggregateRoot
where TDto : class
{
protected BaseAggregateRootQueryHandler(IDbContext dbContext, IMapper mapper, ILogger logger)
{
this.DbContext = dbContext;
this.Mapper = mapper;
this.Logger = logger;
}
protected IDbContext DbContext { get; }
protected IMapper Mapper { get; }
protected ILogger Logger { get; }
public async Task<QueryResult<IEnumerable<TDto>>> Handle(AggregateRootQuery<TAggregate, TDto> request, CancellationToken cancellationToken)
{
var entities = await this.ApplyFilter(this.DbContext.AggregateRoot<TAggregate>())
.ToArrayAsync(cancellationToken);
var dtos = this.MapToDataTransferObjects(entities);
this.Logger.Debug("{Count} {EntityType} read from database", entities.Length, nameof(TAggregate));
return QueryResult<IEnumerable<TDto>>.Success(dtos);
}
protected abstract IQueryable<TAggregate> ApplyFilter(IQueryable<TAggregate> source);
protected virtual IEnumerable<TDto> MapToDataTransferObjects(IEnumerable<TAggregate> source)
{
return this.Mapper.Map<IEnumerable<TDto>>(source);
}
}
Usage
// Domain.Order implements IAggregateRoot
public class OrderQueryHandler : BaseAggregateRootQueryHandler<Domain.Order, OrderDto>
{
public OrderQueryHandler(IDbContext dbContext, IMapper mapper, ILogger logger)
:base(dbContext, mapper, logger)
{
}
protected override IQueryable<Domain.Order> ApplyFilter(IQueryable<Domain.Order> source)
{
return source.OrderBy(x => x.Name);
{
}
var result = await this.Mediator.Send(new AggregateRootQuery<Domain.Order, OrdrDto>(Guid.NewGuid()));
// `IsSuccess` and `Data` have compiler errors because it thinks `result` is an object and not a `QueryResult<IEnumerable<OrderDto>>`
if (!result.IsSuccess || result.Data == null)
{
// Error handing
}

Related

How to implement a GenericDbContext in GenericRepository?

I have more DbContext's. I want to use just one GenericRepository.
I try to create a GenericDbContextFactory. But how can I create TContext? What do I have to do, so the context is not null?
public class GenericRepository<TTable, TContext> : IGenericRepository<TTable, TContext>
where TTable : class
where TContext : DbContext
{
private TContext _context { get; set; } = default!;
private IGenericDbContextFactory _contextFactory;
private DbSet<TTable> _table { get; set; } = default!;
private readonly string _connectionString;
public GenericRepository(IGenericDbContextFactory contextFactory, string connectionString)
{
_connectionString = connectionString;
_contextFactory = contextFactory;
_context = GetNew();
}
public virtual void Reset()
{
_context.Dispose();
_context = GetNew();
}
public TContext GetNew()
{
var context = _contextFactory.Create(_connectionString) as TContext;
_table = context.Set<TTable>();
return context;
}
public async Task Save()
{
try
{
await _context.SaveChangesAsync();
}
catch (Exception ex)
{
Reset();
throw new Exception(ex.Message);
}
_context.ChangeTracker.Clear();
}
public class GenericDbContextFactory : IGenericDbContextFactory
{
public DbContext Create(string connectionString)
{
if (!string.IsNullOrEmpty(connectionString))
{
var optionsBuilder = new DbContextOptionsBuilder<DbContext>();
optionsBuilder.UseSqlServer(connectionString);
var context = new DbContext(optionsBuilder.Options);
return context;
}
else
{
throw new ArgumentNullException("ConnectionId");
}
}
}
GetNew return null (throw NullReferenceExceptioninstead?) because :
public TContext GetNew()
{
// GenericDbContextFactory.Create return GenericDbContext
GenericDbContext genericDbContext = _contextFactory.Create(_connectionString);
// GenericDbContext isn't TContext, then as operator return null
// context is set with null
var context = genericDbContext as TContext;
// throw NullReference Exception
_table = context.Set<TTable>();
return context;
}
To resolve this, you need GenericDbContextFactory.Create return TContext instance. But it isn't possible to have generic constrain with constructor parameters. One solution :
public class GenericRepository<TTable, TContext> : IGenericRepository<TTable, TContext>
where TTable : class
where TContext : GenericDbContext, new()
{
public TContext GetNew()
{
var context = _contextFactory.Create<TContext>(_connectionString);
_table = context.Set<TTable>();
return context;
}
...
}
public class GenericDbContextFactory : IGenericDbContextFactory
{
public TContext Create<TContext>(string connectionString) where TContext : GenericDbContext, new()
{
if (!string.IsNullOrEmpty(connectionString))
{
var context = new TContext();
context.Initialize(connectionString);
return context;
}
...
}
}
public abstract class GenericDbContext : DbContext
{
private string _connectionString;
public GenericDbContext()
{ }
public abstract void Initialize(string connectionString)
=> _connectionString = connectionString;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (string.IsNullOrEmpty(_connectionString))
throw new InvalidOperationException();
optionsBuilder.UseSqlServer(_connectionString);
}
}
You can found other possibilities in this other question :
Is there a generic constructor with parameter constraint in C#?
But please read carefully the remark of #PanagiotisKanavos. It's a really bad implementation of the repository pattern, because EF Core isn't hidden.
To use GenericRepository, it need to specify the real DbContext type and DbSet is raw exposed. With a good repository, you only manipulate the repository, without know what is under the hood.
In the majority of cases, EF Core can be use directly like a repository.

Get record by ID using Entity Framework

How can I change the following action to get a single result by ID?
I have the following code to get All records from the database, I want to modifiy it so that I can get a single record.
namespace PDS.Core.App.Data.Action.Product
{
public class GetProducts_Action : BaseEFAction<GetProducts_Action_Request, GetProducts_Action_Response>
{
private CRMSContext Context { get; }
private IMapper Mapper { get; }
public GetProducts_Action(ILogger<GetProducts_Action> logger, CRMSContext context, ITransactionManager scope, IMapper mapper) : base(logger, context, scope)
{
Context = context.ValidateAndConsumeNonNullableArgument(nameof(context));
Mapper = mapper.ValidateAndConsumeNonNullableArgument(nameof(mapper));
}
protected override async Task<GetProducts_Action_Response> PerformActionAsync(GetProducts_Action_Request request)
{
var tb_Products = await Context.TB_Products
.ToListAsync();
var tb_ProductsDTOs = Mapper.Map<IList<TB_ProductDTO>>(tb_Products);
return new GetProducts_Action_Response { TB_Products = tb_ProductsDTOs };
}
}
public class GetProducts_Action_Request : BaseActionRequest
{
}
public class GetProducts_Action_Response : BaseActionResponse
{
public IList<TB_ProductDTO> TB_Products { get; set; }
}
}
You need to write the code a bit if you extend the code instead of modifying it.
First, you can create a new request class similar to GetProducts_Action_Request that has ProductId in order to use it for getting a single product item. For example, let's say GetProduct_Action_Request:
public class GetProduct_Action_Request : BaseActionRequest
{
public int ProductId {get; set;}
}
And response object that has single product:
public class GetProduct_Action_Response : BaseActionResponse
{
public TB_ProductDTO TB_Product { get; set; }
}
Then you need to create new action GetProduct_Action similar to GetProducts_Action. And you can use SingleOrDefaultAsync to return the only Product element that satisfies a Id condition:
public class GetProduct_Action : BaseEFAction<GetProduct_Action_Request, GetProduct_Action_Response>
{
private CRMSContext Context { get; }
private IMapper Mapper { get; }
public GetProduct_Action(ILogger<GetProduct_Action> logger, CRMSContext context, ITransactionManager scope, IMapper mapper) : base(logger, context, scope)
{
Context = context.ValidateAndConsumeNonNullableArgument(nameof(context));
Mapper = mapper.ValidateAndConsumeNonNullableArgument(nameof(mapper));
}
protected override async Task<GetProduct_Action_Response> PerformActionAsync(GetProduct_Action_Request request)
{
var tb_Product = await Context.TB_Products.SingleOrDefaultAsync(i=> i.Id == request.ProductId);
var tb_ProductDTO = Mapper.Map<IList<TB_ProductDTO>>(tb_Product);
return new GetProduct_Action_Response { TB_Product = tb_ProductDTO };
}
}
public class GetProduct_Action_Request : BaseActionRequest
{
public int ProductId {get; set;}
}
public class GetProduct_Action_Response : BaseActionResponse
{
public TB_ProductDTO TB_Product { get; set; }
}
Therefore you have 2 actions GetProduct_Action for a single product based on Id and GetProducts_Action for a list of all products.

Object Reference Not Set to an Instance of an Object at Repository Layer

I am receiving null exception error on my framework. I have tried to apply Repository and Unit of Work design patterns in my application. What I am trying to do is simply retreiving user titles from my data base with GetAll() method.
Here is my repository class:
public class Repository<T> : IRepository<T> where T : class
{
protected readonly DbContext Context;
public Repository(DbContext context)
{
this.Context = context;
}
public T Get(int id)
{
return Context.Set<T>().Find(id);
}
public IEnumerable<T> GetAll()
{
return Context.Set<T>().ToList();
}
public IEnumerable<T> Find(Expression<Func<T, bool>> predicate)
{
return Context.Set<T>().Where(predicate);
}
public void Add(T entity)
{
Context.Set<T>().Add(entity);
}
public void AddRange(IEnumerable<T> entityList)
{
Context.Set<T>().AddRange(entityList);
}
public void Remove(T entity)
{
Context.Set<T>().Remove(entity);
}
public void RemoveRange(IEnumerable<T> entityList)
{
Context.Set<T>().RemoveRange(entityList);
}
}
This is IUserTitlesRepository:
public interface IUserTitlesRepository : IRepository<UserTitles>
{
}
And, the class where above interface implemented:
public UserTitlesRepository(XaPaDataContext context) : base(context)
{
}
public XaPaDataContext XaPaDataContext
{
get { return Context as XaPaDataContext; }
}
Before coming to Controller layer, I have two more layers, which are Operation and Manager layers. And, I think I have messed up on that part (on Base Manager class as shown below).
This is operation layer:
public class UserTitlesOperations
{
private readonly IUnitOfWork _uow;
public UserTitlesOperations(IUnitOfWork uow)
{
_uow = uow;
}
public List<UserTitles> GetAllUserTitles()
{
try
{
List<UserTitles> userTitleList = _uow.UserTitles.GetAll().ToList();
_uow.Complete();
return userTitleList;
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
}
}
Below is the BaseManager class which gives inheritance to all manager classes.
public abstract class BaseManager
{
private IUnitOfWork _iUow;
private readonly XaPaDataContext _context;
public IUnitOfWork IUOW
{
get
{
if (_iUow == null)
{
_iUow = new XaPaUnitOfWork(_context);
}
return _iUow;
}
}
}
This is the manager class:
public class UserTitlesManager : BaseManager
{
private readonly UserTitlesOperations _userTitlesOperations;
public UserTitlesManager()
{
_userTitlesOperations = new UserTitlesOperations(base.IUOW);
}
public List<UserTitlesWM> GetAllUserTitles()
{
try
{
return UserTitlesMapping.MaptoWM(_userTitlesOperations.GetAllUserTitles());
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
}
}
Finally, this is my API Controller:
[Route("api/LoginRequest")]
public class TitlesController : BaseController
{
UserTitlesManager _userTitlesManager;
public LoginController()
{
_userTitlesManager = new UserTitlesManager();
}
[Route("RetreiveTitles")]
public HttpResponseMessage GetTitles()
{
try
{
return Request.CreateResponse(HttpStatusCode.OK, _userTitlesManager.GetAllUserTitles());
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex.ToString());
}
}
}
By the way BaseController is just another API controller which gives inheritance to all other API controllers, and houses a method which is used by all the other controllers.
So, I'm still trying to sharpen my self on this design patterns and would be glad if anyone could show my mistake on BaseManager class. As I said, I suppose the problem is caused by that private readonly XaPaDataContext _context; line. On the other hand,I can't figure out how to corrrect it as my operation classes' constructors are asking for IUnitOfWork.
Thank you in advance!
EDIT:
Just realized that I forgot to share my Unit of Work class:
public class XaPaUnitOfWork : IUnitOfWork
{
private readonly XaPaDataContext _context;
public XaPaUnitOfWork(XaPaDataContext context)
{
_context = context;
Categories = new CategoriesRepository(_context);
OrderDetails = new OrderDetailsRepository(_context);
Orders = new OrdersRepository(_context);
ProductImages = new ProductImagesRepository(_context);
Products = new ProductsRepository(_context);
Users = new UsersRepository(_context);
UserTitles = new UserTitlesRepository(_context);
UserTokens = new UserTokensRepository(_context);
}
public ICategoriesRepository Categories { get; private set; }
public IOrderDetailsRepository OrderDetails { get; private set; }
public IOrdersRepository Orders { get; private set; }
public IProductImagesRepository ProductImages { get; private set; }
public IProductsRepository Products { get; private set; }
public IUsersRepository Users { get; private set; }
public IUserTitlesRepository UserTitles { get; private set; }
public IUserTokensRepository UserTokens { get; private set; }
public int Complete()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
After I have changed my BaseManager class as below:
public abstract class BaseManager
{
private IUnitOfWork _iUow;
public IUnitOfWork IUOW
{
get
{
if (_iUow == null)
{
_iUow = new XaPaUnitOfWork(new XaPaDataContext());
}
return _iUow;
}
}
}
I have achived to receive HttpStatusCode.OK
But, honestly, I'm still unsure about the real reason. I make this correction mostly by heart.

ASP .NET Core disposing UnitOfWork before getting all data

I'm implementing a generic repository + Unit of work pattern along with a WebApi project. I'm having problems with getting one entity and including the collection of another enttity that refers to it.
I have the following entities mapped through code first:
public class Ingredient : BaseEntity
{
public string Name { get; set; }
public string Amount { get; set; }
public Guid RecipeId { get; set; }
public virtual Recipe Recipe { get; set; }
}
public class Recipe : BaseEntity
{
public string Name { get; set; }
public string Description { get; set; }
public string ImagePath { get; set; }
public virtual ICollection<Ingredient> Ingredients { get; set; }
}
This is my unit of work:
public class UnitOfWork<TContext> : IRepositoryFactory, IUnitOfWork<TContext>, IUnitOfWork where TContext : DbContext
{
private readonly TContext context;
private Dictionary<Type, object> repositories;
public UnitOfWork(TContext context)
{
this.context = context ?? throw new ArgumentNullException(nameof(context));
}
public TContext Context => context;
public IRepository<TEntity> GetRepository<TEntity>() where TEntity : BaseEntity
{
if (repositories == null)
{
repositories = new Dictionary<Type, object>();
}
var type = typeof(TEntity);
if (!repositories.ContainsKey(type))
{
repositories.Add(type, new Repository<TEntity>(context));
}
return (IRepository<TEntity>)repositories[type];
}
public int Commit()
{
return context.SaveChanges();
}
public void Dispose()
{
context?.Dispose();
}
}
And my generic repository:
public class Repository<T> : IRepository<T> where T : BaseEntity
{
protected readonly DbContext dbContext;
protected readonly DbSet<T> dbSet;
public Repository(DbContext context)
{
dbContext = context;
dbSet = dbContext.Set<T>();
}
public T GetEntity(Guid id)
{
return dbSet.Find(id);
}
public T GetEntity(Guid id, params Expression<Func<T, object>>[] includeProperties)
{
IEnumerable<string> properties = GetProperties(includeProperties);
IQueryable<T> queryable = dbSet;
foreach (var property in includeProperties)
{
queryable = dbSet.Include(property);
}
return queryable.FirstOrDefault(x => x.Id == id);
}
[...]
private static IEnumerable<string> GetProperties(Expression<Func<T, object>>[] includeProperties)
{
List<string> includelist = new List<string>();
foreach (var item in includeProperties)
{
MemberExpression body = item.Body as MemberExpression;
if (body == null)
throw new ArgumentException("The body must be a member expression");
includelist.Add(body.Member.Name);
}
return includelist.AsEnumerable();
}
}
The controller is injecting the RecipeService. In the controller I have this method:
[HttpGet("{id}", Name = "Get")]
public IActionResult Get(Guid id)
{
var recipe = recipeService.GetRecipe(id);
if (recipe == null)
{
return NotFound();
}
return Ok(recipe);
}
The recipe service injects the IUnitOfWork and has the following method:
public Recipe GetRecipe(Guid id)
{
return repository.GetEntity(id, r => r.Ingredients);
}
Also I have the services registered as follows:
services.AddScoped<IRepositoryFactory, UnitOfWork<TContext>>();
services.AddScoped<IUnitOfWork, UnitOfWork<TContext>>();
services.AddScoped<IUnitOfWork<TContext>, UnitOfWork<TContext>>();
services.AddScoped<IRecipeService, RecipeService>();
My problem is when I'm getting a specified recipe(along with its ingredients) i got a "The connection has been restarted" error (in firefox). While debugging I can see that I have the recipe with its ingredients. But when returning Ok(recipe) and mapping the result to the entities, the IUnitOfWork is disposed after getting the first ingredient.
Anyone can help me out? Thanks
The problem was I was having a circular reference i wasn't getting any exception.
I fixed by adding the following in the ConfigureServices method of the Startup class:
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});

How to inject a parameter constructor into Repository constructor using Ninject?

I am trying to create DBContect object by passing connection string at run time.
Following is the structure of my NiNject Repository implementation.
public class HomeController : ApiController
{
MyService _service{ get; set; }
public HomeController(MyService service)
{
_service= service;
}
}
public class MyService
{
IRepository _repo { get; set; }
public MyService(IRepository repo)
{
_repo = repo;
}
}
Repository implementation is as follows :
public interface IRepository
{
TenantDbContext _db { get; set; }
void Add<T>(T entity) where T : class;
void Delete<T>(int id) where T : class;
T Find<T>(int id) where T : class;
IQueryable<T> Query<T>() where T : class;
void SaveChanges();
MasterDbContext _db_master { get; set; }
void Add_Master<T>(T entity) where T : class;
void Delete_Master<T>(int id) where T : class;
T Find_Master<T>(int id) where T : class;
IQueryable<T> Query_Master<T>() where T : class;
void SaveChanges_Master();
}
public class Repository : IRepository
{
public TenantDbContext _db { get; set; }
public MasterDbContext _db_master { get; set; }
public Repository(TenantDbContext db)
{
_db = db;
}
public Repository(MasterDbContext db_master)
{
_db_master = db_master;
}
public IQueryable<T> Query<T>() where T : class
{
return _db.Set<T>().AsQueryable();
}
public IQueryable<T> Query_Master<T>() where T : class
{
return _db_master.Set<T>().AsQueryable();
}
//.....Rest of the implemetation
}
Here goes my TenantDBContext class which takes an argument as a database string. No default constructor
public class TenantDbContext : DbContext
{
public TenantDbContext(string connString)
: base(connString)
{
//Configuration.AutoDetectChangesEnabled = true;
//Configuration.LazyLoadingEnabled = false;
//Configuration.ProxyCreationEnabled = false; //change tracking
}
public static TenantDbContext Create(string DbString)
{
// Some logic to get the tenant database string.
// Presently i am just passing it hard coded as follows.
return new TenantDbContext(DbString);
}
}
public class MasterDbContext : IdentityDbContext<ApplicationUser>
{
public MasterDbContext() : base("MasterDBConnection", throwIfV1Schema: false)
{
// dbmigration.AutomaticMigrationsEnabled = true;
Configuration.ProxyCreationEnabled = false;
Configuration.LazyLoadingEnabled = false;
}
public static MasterDbContext Create()
{
return new MasterDbContext();
}
//public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public DbSet<Tenant> Tenants { get; set; }
public DbSet<TenantUserMap> TenantUserMaps { get; set; } }
Finally, RegisterServices which i have in my NinjectWebCommons.cs looks as follows :
Each Tenant have its different database. We are fetching out the Tenant name from the access token on every request and caching that requested Tenant object so we can pass the correct Tenant Database string in order to do the operations on the requested Tenant Database.
Below snippet, We are fetching the Tenant object from the current request cache which will provide us the Tenant Database string of the requested client.
public Tenant Tenant
{
get
{
object multiTenant;
if (!HttpContext.Current.GetOwinContext().Environment.TryGetValue("MultiTenant", out multiTenant))
{
throw new ApplicationException("Could Not Find Tenant");
}
return (Tenant)multiTenant;
}
}
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IRepository>().To<Repository>();
kernel.Bind<TenantDbContext>().ToMethod(_ =>
TenantDbContext.Create(Tenant.DBString));
kernel.Bind<MasterDbContext>().ToMethod(__ => MasterDbContext.Create());
}
Problem : When i add second binding in my NinjectWebCommons.cs "kernel.Bind()" , i start getting exception saying that "No default constructor found". It is simply not taking two bindings with the kernal. Request you to please have a look at the code above and point me out where i am going wrong.
I will appreciate your help. Thanks in Advance.
You may add a binding for the database context and point Ninject to use your factory method:
kernel.Bind<TenantDbContext>().ToMethod(_ => TenantDbContext.Create());

Categories