I have the following IRoleClient and RoleClient but it varied with different routePrefix.
How to inject IRoleClient into AdminRoleController and UserRoleController with different routePrefix using dependency injection "unity". Or other approaches are able to achieve it?
public interface IRoleClient
{
Task<PagedResponse<RoleModel>> GetRolesAsync(GetRolesRequest request);
Task<CreateRoleResponse> CreateRoleAsync(CreateRoleRequest request);
Task<UpdateRoleResponse> UpdateRoleAsync(int roleId, UpdateRoleRequest request);
}
public sealed class RoleClient : IRoleClient
{
private readonly string _routePrefix;
public RoleClient(string serverBaseUrl, string routePrefix) : base(serverBaseUrl)
{
_routePrefix = routePrefix;
}
Task<PagedResponse<RoleModel>> IBackOfficeRoleClient.GetRolesAsync([FromUri] GetRolesRequest request)
{
return GetAsync<PagedResponse<RoleModel>>(_routePrefix, request);
}
async Task<CreateRoleResponse> IBackOfficeRoleClient.CreateRoleAsync(CreateRoleRequest request)
{
var res = await PostJsonAsync(_routePrefix, request);
return await ReadJsonContentAsync<CreateRoleResponse>(res.Content);
}
Task<UpdateRoleResponse> IBackOfficeRoleClient.UpdateRoleAsync(int roleId, UpdateRoleRequest request)
{
return PutAsync<UpdateRoleResponse>($"{_routePrefix}/{roleId}", request);
}
}
public class AdminRoleController()
{
private readonly IRoleClient _roleClient;
public AdminRoleController(IRoleClient roleClient)
{
_roleClient = roleClient;
}
}
public class UserRoleController()
{
private readonly IRoleClient _roleClient;
public UserRoleController(IRoleClient roleClient)
{
_roleClient = roleClient;
}
}
And here are my register in unity
container.RegisterType<IRoleClient, RoleClient>(ReuseWithinResolve, new InjectionConstructor(Config.ApiUrl,"/api/adminRoles"));
container.RegisterType<IRoleClient, RoleClient>(ReuseWithinResolve, new InjectionConstructor(Config.ApiUrl,"/api/userRoles"));
container.RegisterType<Func<string, IRoleClient>>(
new InjectionFactory(c =>
new Func<string, IRoleClient>(name => c.Resolve<IRoleClient>(name))));
You can inject object into constructor that was been register with some name.
container.RegisterType<IRoleClient, RoleClient>("SomeRegisterName", ReuseWithinResolve, new InjectionConstructor(Config.ApiUrl, "/api/adminRoles"));
....
public class AdminRoleController()
{
private readonly IRoleClient _roleClient;
public AdminRoleController([Dependency("SomeRegisterName")]IRoleClient roleClient)
{
_roleClient = roleClient;
}
}
Related
I'm very new to ASP.NET Web API and I'm trying to use Entity Framework Core's Dependency Injection to POST data to the API Controller using MediatR pattern. But every time I run my code and it opens Swagger UI, I get an error 500 response saying
Unable to cast object of type 'AsyncStateMachineBox1[System.Threading.Tasks.VoidTaskResult,S3E1.Repository.CartItemRepository+<Createitem>d__5]' to type 'System.Threading.Tasks.Task1[S3E1.Entities.CartItemEntity]'.
First, I added Dependency Injections to Program.cs
//Dependency Injection
builder.Services.AddDbContext<AppDataContext>(contextOptions => contextOptions.UseSqlServer(
builder.Configuration.GetConnectionString("DefaultConnection")
));
//Connection
builder.Services.AddSingleton<DataConnectionContext>();
These are the classes.
AppDataContext.cs
public class AppDataContext : DbContext
{
public AppDataContext(DbContextOptions<AppDataContext> contextOptions) : base(contextOptions) { }
public DbSet<CartItemEntity> CartItems { get; set; }
public DbSet<OrderEntity> Orders { get; set; }
public DbSet<UserEntity> Users{ get; set; }
}
DataConnectionContext.cs
public class DataConnectionContext
{
private readonly IConfiguration _configuration;
private readonly string _connectionString;
public DataConnectionContext(IConfiguration configuration)
{
_configuration = configuration;
_connectionString = _configuration.GetConnectionString("DefaultConnection");
}
public IDbConnection CreateConnection() => new SqlConnection(_connectionString);
}
Next is making a repository which holds the interface that has the create method.
public interface ICartItemRepository
{
//public Task<IEnumerable<CartItemEntity>> GetCartItems();
//public Task<CartItemEntity> GetCartItemEntity(Guid id);
public Task Createitem(CartItemEntity itemEntity);
}
Then a class that inherits the interface and calls the dependency constructors
public class CartItemRepository : ICartItemRepository
{
private readonly DataConnectionContext _connectionContext;
private readonly AppDataContext _appDataContext;
public CartItemRepository(DataConnectionContext connectionContext, AppDataContext appDataContext)
{
_connectionContext = connectionContext;
_appDataContext = appDataContext;
}
public async Task Createitem(CartItemEntity itemEntity)
{
_appDataContext.CartItems.Add(itemEntity);
await _appDataContext.SaveChangesAsync();
await _appDataContext.CartItems.ToListAsync();
}
}
Next is a command for POST request MediatR pattern
public record AddCartItemCommand(CartItemEntity cartItem) : IRequest<CartItemEntity>;
and a Handler which manages and returns the method createitem
public class AddItemsHandler : IRequestHandler<AddCartItemCommand, CartItemEntity>
{
private readonly ICartItemRepository _cartItemRepository;
public AddItemsHandler(ICartItemRepository cartItemRepository) => _cartItemRepository = cartItemRepository;
public async Task<CartItemEntity> Handle(AddCartItemCommand request, CancellationToken cancellationToken)
{
return await (Task<CartItemEntity>) _cartItemRepository.Createitem(request.cartItem);
}
}
and lastly, in the controller
[Route("api/cart-items")]
[ApiController]
public class CartItemsController : ControllerBase
{
private ISender _sender;
public CartItemsController(ISender sender) => _sender = sender;
[HttpPost]
public async Task<CartItemEntity> Post(CartItemEntity cartItemEntity)
{
return await _sender.Send(new AddCartItemCommand(cartItemEntity));
}
}
I tried modifying the return object in the handler but every time I change anything it always get the error squiggly line, so I just casted the (Task) after the await. Is this where I went wrong? Thank you for any answers.
The exception is clear. You can't cast a VoidTaskResult to Task<CartItemEntity>.
To solve the problem:
In ICartItemRepository, modify the return type for Createitem as Task<CartItemEntity>.
In CartItemRepository, implement Createitem method from the ICartItemRepository interface. Return the inserted itemEntity in the method.
Since you have implemented Task<CartItemEntity> Createitem(CartItemEntity itemEntity) in the ICartItemRepository interface, the casting to (Task<CartItemEntity>) is no longer needed, and suggested to be removed.
public interface ICartItemRepository
{
...
public Task<CartItemEntity> Createitem(CartItemEntity itemEntity);
}
public class CartItemRepository : ICartItemRepository
{
...
public async Task<CartItemEntity> Createitem(CartItemEntity itemEntity)
{
_appDataContext.CartItems.Add(itemEntity);
await _appDataContext.SaveChangesAsync();
return itemEntity;
}
}
public class AddItemsHandler : IRequestHandler<AddCartItemCommand, CartItemEntity>
{
...
public async Task<CartItemEntity> Handle(AddCartItemCommand request, CancellationToken cancellationToken)
{
return await _cartItemRepository.Createitem(request.cartItem);
}
}
Using SqlSugar ORM, based on blazor, dependency injection business service, an error is reported when calling, and it is empty。
SqlSugarService:
public static class SqlSugarService
{
private static readonly ILog log = LogManager.GetLogger(typeof(SqlSugarService));
public static void AddSqlSugarSevice(this IServiceCollection services)
{
if (services == null) throw new ArgumentNullException(nameof(services));
services.AddScoped<ISqlSugarClient>(o =>
{
var listConfig = new List<ConnectionConfig>();
listConfig.Add(new ConnectionConfig
{
DbType = DbType.SqlServer,
ConnectionString = "Server=.\\SQLEXPRESS;DataBase=Test;Uid=sa;Pwd=123456",
IsAutoCloseConnection = true,
InitKeyType = InitKeyType.Attribute
});
var dbContext = new SqlSugarClient(listConfig);
return dbContext;
});
}
}
The interface:
public interface IReportRepository
{
public DataTable GetTest(string sql);
}
Interface implementation:
public class ReportRepository : IReportRepository
{
private ISqlSugarClient _dbBase;
public ReportRepository(ISqlSugarClient sqlSugar)
{
_dbBase = sqlSugar;
}
public DataTable GetTest(string sql)
{
return _dbBase.Ado.GetDataTable(sql);
}
}
Injection:
services.AddSqlSugarSevice();
services.TryAddTransient<IReportRepository, ReportRepository>();
Used in component code:
public partial class Report
{
[Inject]
public IReportRepository ReportService { get; set; }
public Report()
{
ReportService.GetTest("select * from test");
}
}
ERROR :
System.NullReferenceException,HResult=0x80004003,Message=Object reference not set to an instance of an object, Source=MyReport
I want to build in my .net core application an MVC scenario when I can inject in my controller 2 different implementations of an abstract class. These implementations call their external relative API. Maybe the architecture is wrong and therefore I ask you suggestions but first follow me in my thoughts please.
I create a general abstract class. Why abstract? Because the basic way/properties for calling an API is the same for everyone. In my case so far I only have an HttpClient.
public abstract class ApiCaller
{
protected static HttpClient client;
protected static string ApiUrl;
public ApiCaller(string apiUrl)
{
client = new HttpClient();
ApiUrl = apiUrl;
}
public abstract string GetApiResultAsync();
}
Afterwards I will have my two different classes Api1Service and Api2Service that extend ApiCaller and will have their own different ways of calling their relative APIs.
public class Api1Service : ApiCaller
{
public Api1Service(string apiUrl) : base(apiUrl)
{
}
public override string GetApiResultAsync()
{
...
}
}
public class Api2Service : ApiCaller
{
public Api2Service(string apiUrl) : base(apiUrl)
{
}
public override string GetApiResultAsync()
{
...
}
}
Now, in my controller I want to inject both istances since I want to use both business services.. but I don't know if this is possible.
[Route("api/[controller]")]
[ApiController]
public class MyController : ControllerBase
{
private readonly ApiCaller _apiCaller;
public BooksAndAlbumsController(ApiCaller apiCaller)
{
_apiCaller = apiCaller;
}
[HttpPost]
public void Post([FromBody] string value)
{
_apiCaller.GetApiResultAsync() //but I want to use both my apiCallers
}
}
So, somehow, in my container I would need to register both implementations of my abstract class. How can I achieve this? If you see flaws in my architecture please let me know!
You can inject an IEnumerable<ApiCaller> and then use them both.
Register both ApiCallers in the container and then inject the IEnumerable<ApiCaller> in your controller.
Something like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ApiCaller, Api1Service>();
services.AddSingleton<ApiCaller, Api2Service>();
}
MyController
[Route("api/[controller]")]
[ApiController]
public class MyController : ControllerBase
{
private readonly IEnumerable<ApiCaller> _apiCallers;
public MyController(IEnumerable<ApiCaller> apiCallers)
{
_apiCallers = apiCallers;
}
[HttpPost]
public async Task Post([FromBody] string value)
{
// Loop through one by one or call them in parallel, up to you.
foreach(var apiCaller in _apiCallers)
{
var result = await apiCaller.GetApiResultAsync();
}
}
}
Another possibility is to register the Api1Service and Api2Service and then inject them both like this. It will not be as dynamic/flexible as the first solution though.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<Api1Service>();
services.AddSingleton<Api2Service>();
}
MyController
[Route("api/[controller]")]
[ApiController]
public class MyController : ControllerBase
{
private readonly Api1Service _api1Service;
private readonly Api2Service _api2Service;
public MyController(Api1Service api1Service, Api2Service api2Service)
{
_api1Service = api1Service;
_api2Service = api2Service;
}
[HttpPost]
public async Task Post([FromBody] string value)
{
var result1 = await apiService1.GetApiResultAsync();
var result2 = await apiService2.GetApiResultAsync();
}
}
You can use NamedHttpClients and a factory
public static class NamedHttpClients {
public const string StarTrekApi = "StarTrekApi";
public const string StarWarsApi = "StarWarsApi";
}
services.AddHttpClient(NamedHttpClients.StarTrekApi, client => {
client.BaseAddress = new Uri("http://stapi.co/api/v1/rest");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("apiClientTest", "1.0"));
});
services.AddHttpClient(NamedHttpClients.StarWarsApi, client => {
client.BaseAddress = new Uri("https://swapi.co/api/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("apiClientTest", "1.0"));
});
and then create a factory which will be injected in the controller
public interface IFanApiClientFactory {
IFanApiClient CreateStarWarsApiClient();
IFanApiClient CreateStarTrekApiClient();
}
public class FanApiClientFactory : IFanApiClientFactory {
private readonly IHttpClientFactory _httpClientFactory;
public FanApiClientFactory(IHttpClientFactory httpClientFactory) {
_httpClientFactory = httpClientFactory;
}
public IFanApiClient CreateStarWarsApiClient() {
var client = _httpClientFactory.CreateClient(NamedHttpClients.StarWarsApi);
return new StarWarsApiClient(client);
}
public IFanApiClient CreateStarTrekApiClient() {
var client = _httpClientFactory.CreateClient(NamedHttpClients.StarTrekApi);
return new StarTrekApiClient(client);
}
}
register the factory
services.AddSingleton<IFanApiClientFactory, FanApiClientFactory>();
at least implement the concrete api clients
public class StarWarsApiClient : IFanApiClient {
private readonly HttpClient _client;
public StarWarsApiClient(HttpClient client) {
_client = client;
}
public async Task<string> GetMostImportantPerson() {
var response = await _client.GetAsync("people/1");
return await response.Content.ReadAsStringAsync();
}
}
public class StarTrekApiClient : IFanApiClient {
private readonly HttpClient _client;
public StarTrekApiClient(HttpClient client) {
_client = client;
}
public async Task<string> GetMostImportantPerson() {
var response = await _client.GetAsync("character/CHMA0000126904");
return await response.Content.ReadAsStringAsync();
}
}
and finally the controller
public class HomeController : Controller {
private readonly IFanApiClientFactory _fanApiClientFactory;
public HomeController(IFanApiClientFactory fanApiClientFactory) {
_fanApiClientFactory = fanApiClientFactory;
}
public async Task<IActionResult> Index() {
var starWarsApiClient = _fanApiClientFactory.CreateStarWarsApiClient();
var starTrekApiClient = _fanApiClientFactory.CreateStarTrekApiClient();
var person1 = await starTrekApiClient.GetMostImportantPerson();
var person2 = await starWarsApiClient.GetMostImportantPerson();
return View();
}
}
Check about Composite Pattern.
public sealed class CompositeApiCaller : ApiCaller
{
private const string SEPARATION_STRING = Environnement.NewLine;
private ApiCaller[] _apiCallers;
public CompositeApiCaller(params ApiCaller[] apiCallers)
{
_apiCallers = apiCallers;
}
public override string GetApiResultAsync()
{
var builder = new StringBuilder();
for (int i = 0; i < _apiCallers.Length; i++)
{
if (i > 0)
builder.Append(SEPARATION_STRING);
builder.Append(apiCaller.GetApiResultAsync());
}
return builder.ToString();
}
}
I have the next problem, i dont understand why this code dont work i think is because i dont injectate the class of constructor by autofac but i dont know how do that, can us help me to do that the better way?
Before I add the generator this work if i comment the generator code in service work.
This is my code:
I have a class Controller that invoke a serv:
public class ZonesController : Controller
{
private IZoneService zoneService;
public ZonesController(IZoneService zoneService)
{
this.zoneService = zoneService;
}
[HttpGet]
//Do work
}
This is the service and interface:
public class ZoneService : IZoneService
{
private readonly IZoneRepository zoneRepository;
private readonly IDtoFactory dtoFactory;
private readonly ZoneGenerator zoneGenerator;
public ZoneService(IZoneRepository zoneRepository,
IDtoFactory dtoFactory,
ZoneGenerator zoneGenerator)
{
this.zoneRepository = zoneRepository;
this.dtoFactory = dtoFactory;
this.zoneGenerator = zoneGenerator;
}
public void Add(ZoneDetailDTO zone)
{
zoneGenerator.Generate(zone);
}
//Do Work
}
public interface IZoneService
{
void Add(ZoneDetailDTO zone);
//Do Methods
}
The generator invoke ohter class, factories:
public class ZoneGenerator
{
private readonly ZoneFactory zoneFactory;
private readonly IZoneRepository zoneRepository;
public ZoneGenerator(ZoneFactory zoneFactory, IZoneRepository zoneRepository)
{
this.zoneFactory = zoneFactory;
this.zoneRepository = zoneRepository;
}
public void Generate(ZoneDetailDTO zoneModel)
{
var zone = zoneFactory.Create(zoneModel);
zoneRepository.Add(zone);
}
}
The Factory:
public class ZoneFactory
{
private readonly ZoneMapFactory zoneMapFactory;
private readonly ZoneScheduleFactory zoneScheduleFactory;
public ZoneFactory(ZoneMapFactory zoneMapFactory,
ZoneScheduleFactory zoneScheduleFactory)
{
this.zoneMapFactory = zoneMapFactory;
this.zoneScheduleFactory = zoneScheduleFactory;
}
public Zone Create(zoneDetailDTO zone)
{
var map = zoneMapFactory.Create(zone.Map.Address, zone.Map.Latitude, zone.Map.Longitude);
var schedule = zoneScheduleFactory.Create(zone.Schedule.StartHour, zone.Schedule.EndHour);
return new Zone(zone.Name,
zone.ProvinceId,
map,
schedule,
zone.Tags);
}
}
And finally my container:
//method in Startup class Asp.Net Core
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddSingleton(_ => Configuration);
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterModule<DefaultModule>();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
}
public class DefaultModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ZoneService>().As<IZoneService>();
builder.RegisterType<ZoneRepository>().As<IZoneRepository>();
builder.RegisterType<ProvinceService>().As<IProvinceService>();
builder.RegisterType<ProvinceRepository>().As<IProvinceRepository>();
builder.RegisterType<DtoFactory>().As<IDtoFactory>();
}
}
You have missed to add to your Load method the following:
builder.RegisterType<ZoneGenerator>().AsSelf();
builder.RegisterType<ZoneFactory>().AsSelf();
builder.RegisterType<ZoneMapFactory>().AsSelf();
builder.RegisterType<ZoneScheduleFactory>().AsSelf();
public class FooController : ApiController
{
private IDb db;
public FooController (IDb context)
{
db = context;
}
public void DoSomething(string value)
{
var output = new DoSomethingElse(value);
}
}
DoSomethingElse object is used by couple of methods in this class but it's not a requirement for all the methods. How do I register and resolve DoSomethingElse?
The problem as I understand it:
public class FooController : ApiController
{
private IDb db;
public FooController (IDb context)
{
db = context;
}
public void DoSomething(string value)
{
var output = new DoSomethingElse(value);
}
}
You don't want to instantiate the DoSomethingElse type everytime you instantiate the FooController. You also want to provide it with a value at run time.
So this calls for the Factory Pattern:
public interface IDoSomethingElseFactory
{
IDoSomethingElse Create(string value);
}
public class DoSomethingElseFactory : IDoSomethingElseFactory
{
public IDoSomethingElse Create(string value)
{
// Every call to this method will create a new instance
return new DoSomethingElse(value);
}
}
public class FooController : ApiController
{
private IDb db;
private readonly IDoSomethingElseFactory doSomethingElseFactory;
public FooController (
IDb context,
IDoSomethingElseFactory doSomethingElseFactory)
{
db = context;
this.doSomethingElseFactory = doSomethingElseFactory;
}
public void DoSomething(string value)
{
// this will be the point at which a brand new
// instance of `DoSomethingElse` will be created.
var output = doSomethingElseFactory.Create(value);
}
}
Then to register this:
builder.RegisterType<DoSomethingElseFactory>()
.As<IDoSomethingElseFactory>()