System.AggregateException: Some services are not able to be constructed (Error while validating the service ..'ServiceType: MediatR.IRequestHandler - asp.net-web-api

My CQRS file layout is as in the picture. Whenever I enable the handler inside the GetAllBooks folder, I get an error.
Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: MediatR.IRequestHandler2[BookAPI.Application.Features.Queries.Book.GetAllBooks.GetAllBookQueryRequest,System.Collections.Generic.List1[BookAPI.Application.Features.Queries.Book.GetAllBooks.GetAllBookQueryResponse]] Lifetime: Transient ImplementationType: BookAPI.Application.Features.Queries.Book.GetAllBooks.GetAllBookQueryHandler': Unable to resolve service for type 'BookAPI.Application.Repositories.IBookReadRepository' while attempting to activate 'BookAPI.Application.Features.Queries.Book.GetAllBooks.GetAllBookQueryHandler'.)
GetAllBookQueryHandler
public class GetAllBookQueryHandler : IRequestHandler<GetAllBookQueryRequest, List<GetAllBookQueryResponse>>
{
private IBookReadRepository bookReadRepository;
public GetAllBookQueryHandler(IBookReadRepository bookReadRepository)
{
this.bookReadRepository = bookReadRepository;
}
public async Task<List<GetAllBookQueryResponse>> Handle(GetAllBookQueryRequest request, CancellationToken cancellationToken)
{
List<B.Book> books = bookReadRepository.GetAll().Include(x=>x.Authors).Include(x=>x.Category).Include(x=>x.BookImages).ToList();
List<GetAllBookQueryResponse> responses = new();
foreach (B.Book book in books)
{
responses.Add(
new GetAllBookQueryResponse
{
Name= book.Name,
CategoryName=book.Category.Name,
AuthorName=book.Authors.First().Name,
Img=book.BookImages.First().Path,
UnitPrice=book.UnitPrice,
}
);
}
return responses;
}
}
GetAllBookQueryRequest
public class GetAllBookQueryRequest : IRequest<List<GetAllBookQueryResponse>>
{
//This place is empty as all books are requested
}
GetAllBookQueryResponse
public class GetAllBookQueryResponse
{
public int Id { get; set; }
public string Name { get; set; }
public string CategoryName { get; set; }
public string AuthorName { get; set; }
public string Img { get; set; }
public ushort UnitPrice { get; set; }
}
ServiceRegistration for IoC
using MediatR;
using Microsoft.Extensions.DependencyInjection;
namespace BookAPI.Application
{
public static class ServiceRegistration
{
public static void AddApplicationServices(this IServiceCollection services)
{
//find all handler, request and response and add IoC
services.AddMediatR(typeof(ServiceRegistration));
services.AddHttpClient();
}
}
}
Program.cs
I add services
builder.Services.AddApplicationServices();
Book Controller
.
.
.
readonly IMediator mediator;
public BookController(IBookWriteRepository bookWriteRepository, IWebHostEnvironment webHostEnvironment, IFileService fileService, IMediator mediator)
{
bookWriteRepository = bookWriteRepository;
_fileService = fileService;
this.mediator = mediator;
}
[HttpGet]
public async Task<IActionResult> GetAllBooks([FromQuery] GetAllBookQueryRequest getAllBookQueryRequest)
{
return Ok(await mediator.Send(getAllBookQueryRequest));
}
.
.
.
I guess it doesn't see the service I introduced, but I don't understand why GetAllBookHandler is throwing an error in the operation and not the others. For example, my handlers that list and create customers are working.

Related

Options are not passed to the class

I'm trying to pass options to the class via the constructor from the appsettings.json file.
The file itself looks like this:
"IDP": {
"UrlCbr": "https://www.cbr-xml-daily.ru/daily_json.js",
"UrlDadata": "https://suggestions.dadata.ru/suggestions/api/4_1/rs/suggest/currency",
"DadataToken": "94dabe1e8342c21fdad9622be29514d4f0f99bbd8",
"BotToken": "1549046386:AAHJsdsaMVaCT-8O3D_P8VLxw6EKAr4P9JfSU",
"BotName": "Hop_hipBot",
"BotUrl": "https://111295d46c69.ngrok.io/{0}"
}
To do this, I created the IDP.cs class:
public class IDP
{
public string UrlCbr { get; set; }
public string UrlDadata { get; set; }
public string DadataToken { get; set; }
public string BotToken { get; set; }
public string BotName { get; set; }
public string BotUrl { get; set; }
}
Then in the Startup.cs I'm making a connection:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews().AddNewtonsoftJson();
services.Configure<IDP>(Configuration.GetSection("IDP"));
}
I need this data in the implementation of the bot:
class Bot
{
private static TelegramBotClient botClient;
private static IOptions<IDP> _IDPs;
public void Temp(IOptions<IDP> IDPs)
{
_IDPs = IDPs;
}
public static async Task<TelegramBotClient> GetBotClientAsync()
{
if (botClient != null)
{
return botClient;
}
botClient = new TelegramBotClient(_IDPs.Value.BotToken);
var hook = string.Format(_IDPs.Value.BotUrl, #"api/bot");
await botClient.SetWebhookAsync(hook);
return botClient;
}
}
Well, the bot itself is launched in Startup.cs:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
Bot.GetBotClientAsync().Wait();
}
And when you run the entire application, the _IDPs field in the bot turns out to be null. Why is this happening and how do I need to properly deliver the data to the bot? In other classes, everything works correctly and options are passed
You have a bug in the Bot class. Change:
public void Temp(IOptions<IDP> IDPs)
{
_IDPs = IDPs;
}
To :
public class Bot
{
.....
private static IOptions<IDP> _IDPs;
public Bot(IOptions<IDP> IDPs)
{
_IDPs = IDPs;
}
......
}
Change your code like below:
public class Bot
{
private static IOptions<IDP> _IDPs;
public static void Temp(IOptions<IDP> IDPs)
{
_IDPs = IDPs;
}
public static async Task GetBotClientAsync()
{
var data = _IDPs.Value.BotToken;
}
}
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
//more services...
services.Configure<IDP>(Configuration.GetSection("IDP"));
var serviceProvider = services.BuildServiceProvider();
Bot.Temp(serviceProvider.GetService<IOptions<IDP>>());
Bot.GetBotClientAsync().Wait();
}
Result:

Mediator Api call failing

I'm trying to make a simple request using mediator and .net core. I'm getting an error that I am not understanding. All I'm trying to do is a simple call to get back a guid.
BaseController:
[Route("api/[controller]/[action]")]
[ApiController]
public class BaseController : Controller
{
private IMediator _mediator;
protected IMediator Mediator => _mediator ?? (_mediator = HttpContext.RequestServices.GetService<IMediator>());
}
Controller:
// GET: api/Customer/username/password
[HttpGet("{username}/{password}", Name = "Get")]
public async Task<ActionResult<CustomerViewModel>> Login(string username, string password)
{
return Ok(await Mediator.Send(new LoginCustomerQuery { Username = username,Password = password }));
}
Query:
public class LoginCustomerQuery : IRequest<CustomerViewModel>
{
public string Username { get; set; }
public string Password { get; set; }
}
View Model:
public class CustomerViewModel
{
public Guid ExternalId { get; set; }
}
Handler:
public async Task<CustomerViewModel> Handle(LoginCustomerQuery request, CancellationToken cancellationToken)
{
var entity = await _context.Customers
.Where(e =>
e.Username == request.Username
&& e.Password == Encypt.EncryptString(request.Password))
.FirstOrDefaultAsync(cancellationToken);
if (entity.Equals(null))
{
throw new NotFoundException(nameof(entity), request.Username);
}
return new CustomerViewModel
{
ExternalId = entity.ExternalId
};
}
This is the exception I am getting:
Please let me know what else you need to determine what could be the issue. Also, be kind I have been away from c# for a while.
Thanks for the info it was the missing DI. I added this
// Add MediatR
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPreProcessorBehavior<,>));
services.AddMediatR(typeof(LoginCustomerQueryHandler).GetTypeInfo().Assembly);
and we are good to go.

Alternative way for Throwing UserFriendlyException and Exception Handling for Business Rule Validation

Considering the cost of throwing exception, an alternative way is something like this:
public interface IValidationDictionary
{
void AddError(string key, string message);
bool IsValid { get; }
}
public class ModelStateWrapper : IValidationDictionary
{
private ModelStateDictionary _modelState;
public ModelStateWrapper(ModelStateDictionary modelState)
{
_modelState = modelState;
}
public void AddError(string key, string errorMessage)
{
_modelState.AddModelError(key, errorMessage);
}
public bool IsValid
{
get { return _modelState.IsValid; }
}
}
public interface IApplicationService
{
void Initialize(IValidationDictionary validationDictionary);
}
public interface IUserService : IApplicationService
{
Task CreateAsync(UserCreateModel model);
}
public class UserService : IUserService
{
private readonly IUnitOfWork _uow;
private IValidationDictionary _validationDictionary;
public UserService(IUnitOfWork uow)
{
_uow = uow ?? throw new ArgumentNullException(nameof(uow));
}
public void Initialize(IValidationDictionary validationDictionary)
{
_validationDictionary = validationDictionary ?? throw new ArgumentNullException(nameof(validationDictionary));
}
public Task CreateAsync(UserCreateModel model)
{
//todo: logic for create new user
if (condition)
//alternative: throw new UserFriendlyException("UserFriendlyMessage");
_validationDictionary.AddError(string.Empty, "UserFriendlyMessage");
if (other condition)
//alternative: throw new UserFriendlyException("UserFriendlyMessage");
_validationDictionary.AddError(string.Empty, "UserFriendlyMessage");
}
}
public class UsersController : Controller
{
private readonly IUserService _service;
public UsersController(IUserService service)
{
_service = service ?? throw new ArgumentNullException(nameof(service));
_service.Initialize(new ModelStateWrapper(ModelState));
}
[HttpPost]
public async Task<IActionResult> Create([FromForm]UserCreateModel model)
{
if (!ModelState.IsValid) return View(model);
await _service.CreateAsync(model);
//todo: Display ModelState's Errors
}
}
considering there is a difference between input validation like validate a DTO and business rule validation
https://ayende.com/blog/2278/input-validation-vs-business-rules-validation
Input Validation for me is about validating the user input. Some
people call "Name must not be empty" a business rule, I think about it
as input validation. Business Rules validation is more complex,
because a business rule for me is not "Name must not be empty", it is
a definition of a state in the system that requires an action. Here is
a definition of a business rule:
An order should be payed within 30 days, this duration can be
extended, to a maximum of three times.
Is there any idea for send some error message of business rule validation that appear in between application service method's logic
Another approach
public class Result
{
public bool Success { get; private set; }
public string Error { get; private set; }
public bool Failure { /* … */ }
protected Result(bool success, string error) { /* … */ }
public static Result Fail(string message) { /* … */ }
public static Result<T> Ok<T>(T value) { /* … */ }
}
public class Result<T> : Result
{
public T Value { get; set; }
protected internal Result(T value, bool success, string error)
: base(success, error)
{
/* … */
}
}
The method is a command and it can’t fail:
public void Save(Customer customer)
The method is a query and it can’t fail:
public Customer GetById(long id)
The method is a command and it can fail:
public Result Save(Customer customer)
The method is a query and it can fail
public Result<Customer> GetById(long id)

How use Bounded Context Pattern in AspnetBoilerplate

How use Bounded Context Pattern in AspnetBoilerplate, Is there any way to use to use Bounded Context Pattern in AspnetBoilerplate?
Connect with multiple database in ASP.NET ZERO/ASP.NET BOILERPLATE.
Note - Use seperate DB Context to use multiple Databases.
Step 1. Create modal class in "MultipleDbContextEfCoreDemo.Core" Project for your tables.
[Table ("tblStudent")] //Is in First Database
public class Student : Entity<long> {
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
protected Student () { }
}
[Table ("tblCourses")] //Is in Second Database
public class Courses : Entity<long> {
public int ID { get; set; }
public string CourseName { get; set; }
public string Standard { get; set; }
protected Courses () { }
}
Step 2. In same project("MultipleDbContextEfCoreDemo.Core" Project) create/use "MultipleDbContextEfCoreDemoConsts.cs" file to add Database Connection names.
public class MultipleDbContextEfCoreDemoConsts
{
public const string LocalizationSourceName = "MultipleDbContextEfCoreDemo";
public const string ConnectionStringName = "Default";
public const string SecondDbConnectionStringName = "Second";
}
Step 3. In "MultipleDbContextEfCoreDemo.EntityFrameworkCore" Project goto "EntityFrameworkCore" Folder and create individual "DBContext" and "DbContextConfigurer" file for each database connection to which you want to connect.
FirstDatabase Setting -
required files to connect to first db -
1. FirstDbContext.cs
public class FirstDbContext : AbpDbContext, IAbpPersistedGrantDbContext {
/* Define an IDbSet for each entity of the application */
public DbSet<PersistedGrantEntity> PersistedGrants { get; set; }
public virtual DbSet<Student> Student { get; set; }
public FirstDbContext (DbContextOptions<FirstDbContext> options) : base (options) {
}
protected override void OnModelCreating (ModelBuilder modelBuilder) { }
}
2. FirstDbContextConfigurer
public static class FirstDbContextConfigurer {
public static void Configure (DbContextOptionsBuilder<FirstDbContext> builder, string connectionString) {
builder.UseSqlServer (connectionString);
}
public static void Configure (DbContextOptionsBuilder<FirstDbContext> builder, DbConnection connection) {
builder.UseSqlServer (connection);
}
}
SecondDatabase Setting -
required files to connect to second db -
1. SecondDbContext.cs
public class SecondDbContext : AbpDbContext, IAbpPersistedGrantDbContext {
/* Define an IDbSet for each entity of the application */
public DbSet<PersistedGrantEntity> PersistedGrants { get; set; }
public virtual DbSet<Student> Student { get; set; }
public SecondDbContext (DbContextOptions<SecondDbContext> options) : base (options) {
}
protected override void OnModelCreating (ModelBuilder modelBuilder) { }
}
2. SecondDbContextConfigurer
public static class SecondDbContextConfigurer {
public static void Configure (DbContextOptionsBuilder<SecondDbContext> builder, string connectionString) {
builder.UseSqlServer (connectionString);
}
public static void Configure (DbContextOptionsBuilder<SecondDbContext> builder, DbConnection connection) {
builder.UseSqlServer (connection);
}
}
Step 4. Then in same project("MultipleDbContextEfCoreDemo.EntityFrameworkCore") add "MyConnectionStringResolver.cs"
public class MyConnectionStringResolver : DefaultConnectionStringResolver
{
public MyConnectionStringResolver(IAbpStartupConfiguration configuration)
: base(configuration)
{
}
public override string GetNameOrConnectionString(ConnectionStringResolveArgs args)
{
if (args["DbContextConcreteType"] as Type == typeof(SecondDbContext))
{
var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder());
return configuration.GetConnectionString(MultipleDbContextEfCoreDemoConsts.SecondDbConnectionStringName);
}
return base.GetNameOrConnectionString(args);
}
}
Step 5. Then in same project("MultipleDbContextEfCoreDemo.EntityFrameworkCore"), Update "MultipleDbContextEfCoreDemoEntityFrameworkCoreModule.cs" file to replace the "IConnectionStringResolver" with our custom implementation MyConnectionStringResolver.
[DependsOn(typeof(MultipleDbContextEfCoreDemoCoreModule), typeof(AbpEntityFrameworkCoreModule))]
public class MultipleDbContextEfCoreDemoEntityFrameworkCoreModule : AbpModule
{
public override void PreInitialize()
{
Configuration.ReplaceService<IConnectionStringResolver, MyConnectionStringResolver>();
// Configure first DbContext
Configuration.Modules.AbpEfCore().AddDbContext<FirstDbContext>(options =>
{
if (options.ExistingConnection != null)
{
FirstDbContextConfigurer.Configure(options.DbContextOptions, options.ExistingConnection);
}
else
{
FirstDbContextConfigurer.Configure(options.DbContextOptions, options.ConnectionString);
}
});
// Configure second DbContext
Configuration.Modules.AbpEfCore().AddDbContext<SecondDbContext>(options =>
{
if (options.ExistingConnection != null)
{
SecondDbContextConfigurer.Configure(options.DbContextOptions, options.ExistingConnection);
}
else
{
SecondDbContextConfigurer.Configure(options.DbContextOptions, options.ConnectionString);
}
});
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(typeof(MultipleDbContextEfCoreDemoEntityFrameworkCoreModule).GetAssembly());
}
}
Step 6. Create the Service in "MultipleDbContextEfCoreDemo.Application" project with Dto, Interface and Service Class.
ITestAppService.cs-
public interface ITestAppService : IApplicationService
{
List<string> GetStudentAndCourses();
}
TestAppService.cs
public class TestAppService : MultipleDbContextEfCoreDemoAppServiceBase, ITestAppService
{
private readonly IRepository<Student> _studentRepository; //in the first db
private readonly IRepository<Courses> _coursesRepository; //in the second db
public TestAppService(
IRepository<Student> studentRepository,
IRepository<Courses> coursesRepository
)
{
_studentRepository = studentRepository;
_coursesRepository = coursesRepository;
}
//a sample method uses both databases concurrently
public List<string> GetStudentAndCourses()
{
List<string> names = new List<string>();
var studentNames = _studentRepository.GetAllList().Select(p => "Student: " + p.FirstName).ToList();
names.AddRange(peopleNames);
var courseNames = _coursesRepository.GetAllList().Select(p => "Course: " + p.CourseName).ToList();
names.AddRange(courseNames);
return names;
}
}
Step 7. Add Database connectionStrings to your MultipleDbContextEfCoreDemo.Web/MultipleDbContextEfCoreDemo.Web.Host project's
"appsettings.json" file.
{
"ConnectionStrings": {
"Default":
"Server=XXX.XXX.XX.XX;Database=firstDB;Integrated Security=False;TrustServerCertificate=True;User ID=XX;Password=XXX;",
"Second":
"Server=XXX.XXX.XX.XX;Database=secondDB;Integrated Security=False;TrustServerCertificate=True;User ID=XX;Password=XXX;"
}
}
Step 8. Use Service in your angular/MVC project.
With EF you need one complete dbcontext for migrations.
Create other "bounded" dbcontext, with the entities to be ignored in modelbuilder, then use this in appservice.
Very simple answer
HTH

Confusion over MVC3 Code First / Repositories

Please can someone help me because I am getting confused.
I have an Entity like this:
public class Code
{
public int ID { get; set; }
public int UserID { get; set; }
public string CodeText { get; set; }
}
and an Interface like this:
public interface ICodeRepository
{
IQueryable<Code> Codes { get; }
void AddCode(Code code);
void RemoveCode(Code code);
Code GetCodeById(int id);
}
and a Repository like this:
public class SQLCodeRepository : ICodeRepository
{
private EFSQLContext context;
public SQLCodeRepository()
{
context = new EFSQLContext();
}
public IQueryable<Code> Codes
{
get { return context.Codes; }
}
public void AddCode(Code code)
{
context.Codes.Add(code);
context.SaveChanges();
}
public void RemoveCode(Code code)
{
context.Codes.Remove(code);
context.SaveChanges();
}
public Code GetCodeById(int id)
{
return context.Codes.Where(x => x.ID == id).FirstOrDefault();
}
}
and a Context like this:
public class EFSQLContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Code> Codes { get; set; }
public DbSet<PortfolioUser> PortfolioUsers { get; set; }
}
If I declare my controller like this:
public class SearchController : Controller
{
private ICodeRepository cRepo;
public SearchController(ICodeRepository codeRepository)
{
cRepo = codeRepository;
}
}
and then try to do cRepo.GetCodeById(1) nothing happens. But if I declare private ICodeRepository rep = new SQLCodeRepository and then call rep.GetCodeById(1) I can see the method in the Repository being called.
What am I doing wrong?
It looks like from the constructor signature, you are going to be doing some dependency injection. The step you are missing is to set up a DI container using a tool like Castle Windsor. You then configure the MVC resolver to use the DI container to give you the correct implementation of ICodeRepository.
See this
You'll need to create a resolver that implements IDependencyResolver and IDependencyScope and a controller factory that inheritsDefaultControllerFactory
Once you have those you can do something like the following:
MyContainer container; // this needs to be a class level member of the asax
var configuration = GlobalConfiguration.Configuration;
container = new MyContainer() // may need additional stuff here depending on DI tool used
configuration.DependencyResolver = new MyDependancyResolver(container);
var mvcControllerFactory = new MyFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(mvcControllerFactory);
You would call the above code from the asax Application_Start()
See this answer for more specifics on using Ninject and MVC3

Resources