Applying Dependency Injection to different implementations of an abstract class c# - c#

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();
}
}

Related

Get List of Open Generic Types in .net core

Trying to resolve dynamically a specific implementation based on name (unless there is a better way)
Having a client:
public interface IClient { }
public interface IClient<TModel> : IClient where TModel : ListingBase // ListingBase is abstract class
{
public Task<TModel> MapToObject(Stream file);
}
// base class with common funcationality
public abstract class ClientBase<TModel> : IClient<TModel> where TModel : ListingBase
{
protected ClientBase(IHttpClient httpClient)
{
HttpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
}
public IHttpClient HttpClient { get; }
public abstract Task<TModel> MapToObject(Stream file);
... // other stuff
}
Implementations:
public class Client1 : ClientBase<Model1> // Model1 inherits from ListingBase
{
{
public override Task<Model1> MapToObject(Stream file) // Model1 inherits from ListingBase
{
// do mapping from Json / Stream to object
return Task.FromResult(new Model1());
}
}
// Another client
public class Client2 : ClientBase<Model2> // Model2 inherits from ListingBase
{
{
public override Task<Model2> MapToObject(Stream file) // Model2 inherits from ListingBase
{
// do mapping from Json / Stream to object
return Task.FromResult(new Model2());
}
}
Then somewhere in some service I inject ClientFactory where I need to get the right client based on "Name" specified in configs:
public interface IClientFactory
{
IClient<ListingBase> GetClient(string clientName);
}
public class ClientFactory : IClientFactory
{
private readonly IServiceProvider _serviceProvider;
public ClientFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
}
public IClient<ListingBase> GetClient(string clientName)
{
var clients = _serviceProvider.GetServices(typeof(IClient)); // this works - I get a list of 2
// match by name
var client = clients.FirstOrDefault(); // TODO - but for now get first
return (IClient<ListingBase>) client; // failing here - invalid cast
}
}
Error:
Unable to cast object of type 'Client1' to type
'Abstract.IClient`1..
In some service want to get a specific client based on a string:
public class DefaultProcessor
{
private readonly IListingRepository _repository;
public DefaultProcessor(
IClientFactory clientFactory,
...
Client = clientFactory.GetClient("name-from-config");
}
public async Task Write(Stream file, CancellationToken cancellationToken = default)
{
var model = await Client.MapToObject(file);
...
}
}
To convert SaborClient (that I suppose implement IClient<?>) to IClient<ListingBase> you need IClient<T> to be covariant in T, but for that you need Task<T> to be covariant in T too. Which is not the case because Task<> is a class and variance declaration is only available on interfaces.
You can bypass this limitation with a well constructed interface, see this answer.
Here an implementation example (you need to import MorseCode.ITask NuGet package):
public abstract class ListingBase
{
}
public class Model1 : ListingBase
{
}
public interface IClient
{
}
// Note the *out* here
public interface IClient<out TModel> : IClient where TModel : ListingBase
{
public ITask<TModel> MapToObject(Stream file);
}
public class Client1 : IClient<Model1>
{
public async ITask<Model1> MapToObject(Stream file)
{
return new Model1();
}
}
public class ClientFactory
{
public IClient<ListingBase> GetClient()
{
var client = GetClientInternal();
return (IClient<ListingBase>) client;
}
private static IClient GetClientInternal() => new Client1();
}
class Program
{
static void Main(string[] args)
{
var factory = new ClientFactory();
var client = factory.GetClient();
Console.WriteLine("Client:" + client);
Console.ReadLine();
}
}

Get client IP and compare values with the configuration

I am trying to get client IP and compare values with the configuration. If it matches need to return true/false. How do I make this variable accessible to web application? I am new to .NET core. thanks
I have followed this article to create middleware class but not sure how to pass variable from this context.
https://learn.microsoft.com/en-us/aspnet/core/security/ip-safelist?view=aspnetcore-2.2
public class SafeListMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<SafeListMiddleware> _logger;
private readonly string _adminSafeList;
public SafeListMiddleware(
RequestDelegate next,
ILogger<SafeListMiddleware> logger,
string adminSafeList)
{
_adminSafeList = adminSafeList;
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Method != "GET")
{
var remoteIp = context.Connection.RemoteIpAddress;
string[] ip = _adminSafeList.Split(';');
var bytes = remoteIp.GetAddressBytes();
var match = false;
foreach (var address in ip)
{
var testIp = IPAddress.Parse(address);
var rangeA = IPAddressRange.Parse(address);
if(rangeA.Contains(remoteIp))
{
match = true;
break;
}
}
}
await _next.Invoke(context);
}
}
}
I would create an interface such as:
public interface IIPChecker
{
bool IsSafe(IPAddress remoteIpAddress);
}
with an implementation:
public class IPChecker : IIPChecker
{
private readonly IPAddress[] _safeList;
public IPChecker(string safeList)
{
var _safeList = safeList
.Split(';')
.Select(IPAddress.Parse)
.ToArray();
}
public bool IsSafe(IPAddress remoteIpAddress)
{
return _safeList.Contains(remoteIpAddress);
}
}
and inject it in the controllers that need it:
public class ValuesController : ControllerBase
{
private readonly IIPChecker _ipChecker;
public ValuesController(IIPChecker ipChecker)
{
_ipChecker = ipChecker;
}
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
var isValid = _ipChecker.IsSafe(HttpContext.Connection.RemoteIpAddress);
.....
}
}
If you need this information in all controllers, you can change them to inherit from something like
public class IpCheckController : ControllerBase
{
private readonly IIPChecker _ipChecker;
public IpCheckController(IIPChecker ipChecker)
{
_ipChecker = ipChecker;
}
private bool IsSafe => _ipChecker.IsSafe(HttpContext.Connection.RemoteIpAddress);
}
To get the client RemoteIp and compare with the configuration values. you have to first define Http Accessor in the Startup file like below.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
and then in the Middleware access the RemoteIp using below code and compare the value.
var remoteIp = context.Request.HttpContext.Connection.RemoteIpAddress.ToString();

Can a static helper class be used for WebApi calls (from UI)?

I have X controllers that use a API site (WebApi). I have created an ApiHelper class. Which I use in these controllers. Now my question is this. Can I make this ApiHelper a static class? I think I can because the httpClient is instanced. Or do I overlook something, and does it need to be an instanced ApiHelper. (the use of static still confuses me sometimes). Example code below.
public class HomeController : Controller
{
public async Task<string> VersionDemo()
{
var response = await ApiHelper.Call("/api/config/version");
var data = response.Content.ReadAsStringAsync();
var res = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(data.Result);
return res;
}
}
public class ConfigController : Controller
{
private async Task<List<ConfigSetting>> GetGeneralConfigurationDemo()
{
var generalConfiguration = new List<ConfigSetting>();
var response = await ApiHelper.Call("api/configuration/GetGeneralConfiguration");
var data = response.Content.ReadAsStringAsync();
generalConfiguration = JsonConvert.DeserializeObject<List<ConfigSetting>>(data.Result);
return generalConfiguration;
}
}
public static class ApiHelper
{
public static async Task<HttpResponseMessage> Call(string url)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var baseAdress = System.Configuration.ConfigurationManager.AppSettings["ApiBaseAddress"];
string apiUrl = baseAdress + url;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(apiUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync(apiUrl);
return response;
}
}
}
Make base controller and hide http client as protected thing.
public abstract class BaseController : Controller
{
protected ApiHelper Api { get; set; }
}
Then derive your controllers from BaseController
public class ConfigController : BaseController {}
public class HomeController : BaseController {}
Note : try not to use static classes cause they make your heap littered. They are allocated in "high-frequency" heap, which is never garbage collected.
There would be no problem to leave your class static as the HttpClient stays on the method scope and thus each call to your static method will use a different HttpClient. It would not be safe if you used a static member (field or property) as it would be shared by all the callers and you would need to synchronize the access (for a multi thread usage).
After reading (httpClient your are doing it wrong , singleton pattern) and subsequently testing. I ended up using the following code. Main goal is one httpClient application wide and avoid socket exhaustion.
In my controllers where I'm in need of a httpClient I use the HttpClientSingleton.Instance see below.
And here is a BaseController you can inherit from in your controllers that are going to use your API.
public class BaseController : Controller
{
public readonly string ApiBaseAdress = System.Configuration.ConfigurationManager.AppSettings["ApiBaseAddress"];
public BaseController()
{
//Set as needed Servicepoint settings
//string SecurityProtocolTypeFromConfig = System.Configuration.ConfigurationManager.AppSettings["SecurityProtocolType"];
//SecurityProtocolType fromConfig;
//Enum.TryParse(SecurityProtocolTypeFromConfig, out fromConfig);
//ServicePointManager.SecurityProtocol = fromConfig;
//possible ServicePoint setting needed in some cases.
//ServicePointManager.Expect100Continue = false;
//ServicePointManager.MaxServicePointIdleTime = 2000;
//ServicePointManager.SetTcpKeepAlive(false, 1, 1);
}
}
And here is the HttpClientSingleton class:
public sealed class HttpClientSingleton
{
private static readonly Lazy<HttpClient> lazy = new Lazy<HttpClient>(() => new HttpClient());
public static HttpClient Instance { get { return lazy.Value; } }
private HttpClientSingleton()
{
}
}
So putting it together. Here is an example of getting some loginfo from the API.
public class MyLogController : BaseController
{
[HttpPost]
public async Task<JsonResult> log(string requestId)
{
var url = ApiBaseAdress + string.Format("/api/runs/log/{0}", requestId);
List<Log> logs = new List<Log>();
var response = await HttpClientSingleton.Instance.GetAsync(url);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
logs = JsonConvert.DeserializeObject<List<Log>>(result);
return Json(logs);
}
}
You can write a static helper class. If the name is ApiHelper, then add a Microsoft.AspNet.WebApi.Client reference. When your app is initialized, call the class's InitializeClient() method, and you can call the GetAsync() method if you need. The code is below:
public static class ApiHelper
{
public static HttpClient ApiClient { get; set; }
public static void InitializeClient()
{
ApiClient = new HttpClient();
ApiClient.DefaultRequestHeaders.Accept.Clear();
ApiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public static async Task<T> GetAsync<T>(string url)
{
using (HttpResponseMessage response = await ApiHelper.ApiClient.GetAsync(url))
{
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsAsync<T>();
return result;
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
}
}

Dependency Injection with different parameter in constructor (C#)

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;
}
}

How to convert this static class method into a dependency injection? (Specific code included)

(I am sure that I formatted the question badly, I would be happy to revise and fix depending on comments)
I have a static class and I am trying to improve the design with dependency injection. I don't necessarily want this class to be static anymore because I will be using .NET Core, which promotes dependency injection over static class situations.
The simplified code in .NET (not Core):
public static class Utils
{
public static readonly string tokenUrl = ConfigurationManager.AppSettings["tokenUrl"];
public static readonly string tokenKey = ConfigurationManager.AppSettings["tokenKey"];
public async static Task<bool> SendEmail(Email email)
{
var http = new HttpClient();
http.DefaultRequestHeaders.Add("subscription-key", tokenKey);
try
{
await http.PostAsync(tokenUrl + "email", new StringContent(JsonConvert.SerializeObject(email), Encoding.UTF8, "application/json"));
}
catch (Exception e)
{
return false;
}
return true;
}
}
For ConfigurationManager.AppSettings (it does not exist in .NET Core), I am planning to use the method in this link: http://www.danylkoweb.com/Blog/no-configurationmanager-in-aspnet-core-GC
However, for converting this (SendMail) method into a dependency injection, I am quite lost. I have read many examples and articles and I understand the logic of dependency injection but I don't know how to convert this static class into a proper dependency injection. There are other methods in the same Utils class but this is the simplest one and I hope to figure out the others using this one.
An approach that I was thinking off was:
public interface ISendMail
{
FormSettings ConfigSettings { get; set; }
Task<bool> SendEmail(IOptions<FormSettings> settings, Email email);
}
and:
public class SendEmail : ISendMail
{
public async static Task<bool> SendEmail(IOptions<FormSettings> settings, Email email)
{
//do same things
}
}
but I am CLEARLY lost with this because it does not even make sense. Another approach that I was thinking of was:
public class SendEmail
{
FormSettings ConfigSettings { get; set; }
protected Email email = null;
public SendEmail(IOptions<FormSettings> settings, Email email)
{
ConfigSettings = settings.Value;
this.email = email;
}
public async static Task<bool> SendEmailAction()
{
//do same things with "email" and "ConfigSettings"
}
}
I know I am giving a lot of code here and I wasn't sure if I should ask about this in "Code Review" or something. My biggest concern is not the FormSettings part but implementing the functionality of SendEmail in a dependency injection format.
Shortly, how can I convert this "SendEmail" class into a format where I can use it with .NET Core, without having a static class? This particular method does not require change with .NET Core but my other methods do, that is why I am trying to get rid of the static class approach.
I can exclude the tokenUrl and tokenKey parts and simplify the problem if requested, I am just quite lost as to how to approach this situation.
What should do this class? Sending email, right? So interface:
public interface IEmailSender
{
Task<bool> Send(Email email);
}
How we can implement it? Like this:
public class MyEmailSenderOne : IEmailSender
{
public static readonly string tokenUrl = ConfigurationManager.AppSettings["tokenUrl"];
public static readonly string tokenKey = ConfigurationManager.AppSettings["tokenKey"];
public async Task<bool> Send(Email email)
{
var http = new HttpClient();
http.DefaultRequestHeaders.Add("subscription-key", tokenKey);
try
{
await http.PostAsync(tokenUrl + "email", new StringContent(JsonConvert.SerializeObject(email), Encoding.UTF8, "application/json"));
}
catch (Exception e)
{
return false;
}
return true;
}
}
or
public class MyAnotherAwesomeEmailSender : IEmailSender
{
public async Task<bool> Send(Email email)
{
// send with different way
return true;
}
}
How we can inject this?
public class SomeClass
{
private IEmailSender _sender;
public SomeClass(IEmailSender sender)
{
_sender = sender;
}
public void Foo()
{
// do smth useful
_sender.Send(new Email());
}
}
UPD.
Because your email settings persistant (will not change during lifetime), and because this settings related ONLY to your implementation of IEMailSender, you should to inject them in your implementation. Just think about = why caller code (Controller) should know about how your implementation works?
So
public class MyEmailSenderOne : IEmailSender
{
private FormSettings _settings;
public MyEmailSenderOne(IOptions<FormSettings> settings)
{
_settings = settings.Value;
}
public async Task<bool> Send(Email email)
{
var http = new HttpClient();
http.DefaultRequestHeaders.Add("subscription-key", _settings.tokenApiKey);
try
{
await http.PostAsync(_settings.tokenApiUrl + "email", new StringContent(JsonConvert.SerializeObject(email), Encoding.UTF8, "application/json"));
}
catch (Exception e)
{
return false;
}
return true;
}
}
And, controller now dint know about any settings for your implementation, and it looks like
public class CommunicationsController : Controller
{
private IEmailSender _sender;
public CommunicationsController(IEmailSender sender)
{
_sender = sender;
}
public async Task<ActionResult> ContactUsFormSubmit(ContactUs request)
{
...
request.EmailSent = await _sender.SendEmail(new Email() { TemplateId = 3, Body = JsonConvert.SerializeObject(request) });
...
}
}
As you can see, controller is very clean now and you can easily change your implementation of IEmailSender to any other without changing Controller code. This is one of advantages of using DI.
Based on tym32167's answer, I was able to implement the IEmailSender functionality (finally). I will still choose his answer as the correct answer but this is how I implemented dependency injection.
Please read the link I provided in the question, if you'd like to know more about the IOptions and FormSettings class that I am using.
Here is the interface and the class:
public interface IEmailSender
{
Task<bool> SendEmail(Email email, FormSettings settings);
}
public class EmailSender : IEmailSender
{
FormSettings ConfigSettings { get; set; }
public async Task<bool> SendEmail(Email email, FormSettings settings)
{
var http = new HttpClient();
http.DefaultRequestHeaders.Add("subscription-key", settings.tokenApiKey);
try
{
await http.PostAsync(settings.tokenApiUrl + "email", new StringContent(JsonConvert.SerializeObject(email), Encoding.UTF8, "application/json"));
}
catch (Exception e)
{
return false;
}
return true;
}
}
In controller injection:
public class CommunicationsController : Controller
{
private IEmailSender _sender;
private FormSettings ConfigSettings { get; set; }
public CommunicationsController(IEmailSender sender, IOptions<FormSettings> settings)
{
_sender = sender;
ConfigSettings = settings.Value;
}
public async Task<ActionResult> ContactUsFormSubmit(ContactUs request)
{
...
request.EmailSent = await _sender.SendEmail(new Email() { TemplateId = 3, Body = JsonConvert.SerializeObject(request) }, ConfigSettings);
...
}
Here is FormSettings just for easy reference in case the link dies:
public class FormSettings
{
public string tokenApiUrl { get; set; }
public string tokenApiKey { get; set; }
}
I hope I didn't miss any details, so far it didn't give me any errors but since we do not have unit testing in the project, I won't be able to test immediately. Please let me know if there is something missing with the answer.

Categories