Abstract
As the title specifies, we will be replacing the conditional If and Switch statement with the help of polymorphism using DI container and will talk about the benefits of doing the same in this article. OOPs provides us with pretty great features which we are aware of theoretically but never implemented practically. Polymorphism is one of the main features provided by Object-Oriented Languages which, in turn, implies that a parent class can have more than one behavior and can point toward its child classes at runtime. If you want to learn more about OOPS features, you can go through the below blog which talks about oops and other features.
There are tons and tons of articles you can read.
Problem statement
I have been using dynamic Polymorphism where parent class can point towards child class and call the child method at runtime and solving the problem that way. But I found I can also use it to replace our Switch and if statement with the help of Polymorphism. I was aware of this concept when more technically sound people talk about this topic but the implementation is not so concrete and nor are practical demos easy to find onthe internet. So let’s try to solve this problem with a practical use case and how we can avoid using Switch and if statement via Polymorphism.
“Let’s get started”
I wish I was aware of the concept before or that someone would have told me to do it this way. Fellas, it’s still in production but I have not yet replaced it as Client requirements have been frozen. So in one of our user stories, there was a requirement of saving FTP details of the customer and using these ftp details for File Processing etc. I am talking about this requirement on the overall focus on the FTP details part because there is where actual if else problem exists. So the requirement says Client wants support for FTP and SFTP which logs in to the application and will save the FTP details and if there is no permission of reading and writing we should tell the user at that time only.
As a passionate developer, I started working on this story and created a simple use case and short diagram without using polymorphism allowing him to save the FTP or SFTP details.
So I created a ENUM to handle the problem as shown below,
- public enum EFtpTypes
- {
- FTP=1,
- SFTP=2
- }
Here I have both the two supported FTP protocols.
I have my services function ready for these two protocols separately with the two separate interfaces.
So my IFTP interface contains the following method and other methods which I am assuming are not required for this article.
- public interface IFTP
- {
- bool IsSFTPLoginFileCreationSuccessful(string host, string ftpPort, string ftpUsername, string ftpPassword, string folder);
- }
Now the implementation Class FTPManager to manage FTP things:
- public class FtpManager : IFTP
- {
- public bool IsFtpLoginFileCreationSuccessful(string host, string ftpUsername, string ftpPassword, string folder)
- {
-
- var folders = string.IsNullOrEmpty(folder) ? new string[] { "" } : folder.Split('/');
- StringBuilder directoryMaker = new StringBuilder("/");
- for (int i = 0; i < folders.Length; i++)
- {
-
- if (i >= 1) directoryMaker.Append(folders[i - 1] + "/");
- host = host + directoryMaker.ToString();
- FtpWebRequest request;
- if (host.StartsWith("ftp") && !host.StartsWith("ftps"))
- {
- request = (FtpWebRequest)WebRequest.Create(new Uri(host));
- }
- else
- {
- host = "ftp://" + host;
- request = (FtpWebRequest)WebRequest.Create(new Uri(host));
- }
- request.Method = WebRequestMethods.Ftp.ListDirectory;
- request.Credentials = new NetworkCredential(ftpUsername, ftpPassword);
- request.KeepAlive = false;
- var response = (FtpWebResponse)request.GetResponse();
- if (!(response.StatusCode == FtpStatusCode.CommandOK || response.StatusCode == FtpStatusCode.FileActionOK || response.StatusCode == FtpStatusCode.DataAlreadyOpen || response.StatusCode == FtpStatusCode.OpeningData)) return false;
- Stream responseStream = response.GetResponseStream();
- StreamReader reader = new StreamReader(responseStream);
- string names = reader.ReadToEnd();
- var Directories = names.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries).ToList();
- if (Directories.Where(x => x.ToString().ToLower() == folders[i].Trim().ToLower()).Any())
- {
- if (i == folders.Length - 1)
- {
- CreateFile(request, folders[i], ftpUsername, ftpPassword);
- directoryMaker.Append(folders[i]);
- host = host + folders[i];
- Delete(request, folder, host, ftpUsername, ftpPassword);
- }
- }
- else
- {
- if (!string.IsNullOrEmpty(folder)) CreateDirectory(request, folders[i], host, ftpUsername, ftpPassword);
- if (i == folders.Length - 1)
- {
- CreateFile(request, folders[i], ftpUsername, ftpPassword);
- if (!string.IsNullOrEmpty(folder)) directoryMaker.Append(folders[i]);
- if (!string.IsNullOrEmpty(folder)) host = host + folders[i];
- Delete(request, folder, host, ftpUsername, ftpPassword);
- }
- }
- }
-
- return true;
-
- }
-
-
- public bool CreateFile(WebRequest request, string folder, string userName, string password)
- {
- try
- {
- int buffLength = 2048;
- byte[] buff = new byte[buffLength];
- byte[] bytes = Encoding.UTF8.GetBytes("Test Export");
- request = (FtpWebRequest)FtpWebRequest.Create(new Uri(request.RequestUri + "/" + folder + "/" + "Sample.txt"));
- request.Credentials = new NetworkCredential(userName, password);
- request.Method = WebRequestMethods.Ftp.UploadFile;
- request.ContentLength = bytes.Length;
- Stream requestStream = request.GetRequestStream();
- requestStream.Write(bytes, 0, bytes.Length);
- requestStream.Close();
- FtpWebResponse response = (FtpWebResponse)request.GetResponse();
- return true;
- }
- catch (Exception)
- {
- throw;
- }
-
- }
-
- private bool Delete(WebRequest request, string folder, string host, string userName, string password)
- {
- try
- {
- if (host.StartsWith("ftp") && !host.StartsWith("ftps"))
- {
- request = (FtpWebRequest)WebRequest.Create(new Uri(host + "//" + "Sample.txt"));
- }
- else
- {
- host = "ftp://" + host;
- request = (FtpWebRequest)WebRequest.Create(new Uri(host + "//" + "Sample.txt"));
- }
- request.Method = WebRequestMethods.Ftp.DeleteFile;
- request.Credentials = new NetworkCredential(userName, password);
- FtpWebResponse response = (FtpWebResponse)request.GetResponse();
- return true;
- }
- catch (Exception ex)
- {
- throw;
- }
-
- }
- private bool CreateDirectory(WebRequest request, string folder, string Host, string userName, string password)
- {
- try
- {
- var getFolders = folder.Split('/');
- StringBuilder directoriesToBeCreated = new StringBuilder("/");
- for (int i = 0; i < getFolders.Length; i++)
- {
- directoriesToBeCreated.Append(getFolders[i] + "/");
- if (Host.StartsWith("ftp") && !Host.StartsWith("ftps"))
- {
- request = (FtpWebRequest)WebRequest.Create(new Uri(Host + directoriesToBeCreated.ToString()));
- }
- else
- {
- Host = "ftp://" + Host;
- request = (FtpWebRequest)WebRequest.Create(new Uri(Host + directoriesToBeCreated.ToString()));
- }
-
- request.Credentials = new NetworkCredential(userName, password);
- request.Method = WebRequestMethods.Ftp.MakeDirectory;
- FtpWebResponse responses = (FtpWebResponse)request.GetResponse();
-
- }
- return true;
- }
- catch (Exception ex)
- {
- throw ex;
- }
- }
So this API is totally responsible for creating and checking whether the user has rights to the FTP server or not.
Now my SFTP service comes into the picture.
- public class SftpManager: ISFTP
- {
- public bool IsSFTPLoginFileCreationSuccessful(string host, string ftpPort, string ftpUsername, string ftpPassword, string folder)
- {
- try
- {
- var folders = string.IsNullOrEmpty(folder) ? new string[] { "" } : folder.Split('/');
- StringBuilder directoryMaker = new StringBuilder("");
- int defaultSFtpPort = 115;
- int port = (Convert.ToInt32(ftpPort) == 0) ? defaultSFtpPort : Convert.ToInt32(ftpPort);
- using (var ftpClient = new SftpClient(host, port, ftpUsername, ftpPassword))
- {
- ftpClient.Connect();
- for (int i = 0; i < folders.Length; i++)
- {
- var directories = ftpClient.ListDirectory(directoryMaker.ToString());
- List<string> lstDirectory = new List<string>();
- foreach (var directory in directories)
- {
- lstDirectory.Add(directory.Name);
- }
- if (lstDirectory.Where(x => x.ToString().ToLower() == folders[i].ToLower()).Any())
- {
- directoryMaker.Append(folders[i] + "/");
-
- if (i == folders.Length - 1)
- {
- UploadFileAndDeleteFromSFTPServer(directoryMaker, ftpClient);
- }
-
- }
- else
- {
- if (!string.IsNullOrEmpty(folder)) directoryMaker.Append(folders[i] + "/");
- if (!string.IsNullOrEmpty(folder)) ftpClient.CreateDirectory(directoryMaker.ToString());
- if (i == folders.Length - 1)
- {
- UploadFileAndDeleteFromSFTPServer(directoryMaker, ftpClient);
- }
- }
- }
- ftpClient.Disconnect();
- }
- return true;
-
- }
-
- catch (Exception EX)
- {
- throw;
- }
- }
-
- private void UploadFileAndDeleteFromSFTPServer(StringBuilder directoryMaker, SftpClient ftpClient)
- {
- if (!string.IsNullOrEmpty(directoryMaker.ToString())) ftpClient.ChangeDirectory(directoryMaker.ToString());
- using (Stream s = GenerateStreamFromString("TestFile"))
- {
- ftpClient.BufferSize = 4 * 1024;
- ftpClient.UploadFile(s, "Sample.txt");
- }
- ftpClient.Delete("Sample.txt");
- }
-
- private static Stream GenerateStreamFromString(string s)
- {
- MemoryStream stream = new MemoryStream();
- StreamWriter writer = new StreamWriter(stream);
- writer.Write(s);
- writer.Flush();
- stream.Position = 0;
- return stream;
-
-
- }
- }
I have the below dummy UI which shows User Interface to enter FTP details,
It’s a simple UI which I have created for demonstration purposes. FTP Type 1 here means normal FTP and 2 stands for SFTP. Just for demonstration purposes, I have left this like 1,2 andthis should be in radio button in the appropriate UI.
Controller code to save the FTP Details entered by User.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc;
- using WebApplication1.Models;
- using WebInterfaces;
-
- namespace WebApplication1.Controllers
- {
- public class FileProcessingController : Controller
- {
- private readonly IFTP _ftpservice;
- private readonly ISFTP _sftpservice;
- public FileProcessingController(IFTP ftpservice, ISFTP sftpservice)
- {
- _ftpservice = ftpservice;
- _sftpservice = sftpservice;
-
- }
-
-
-
- public ActionResult Index()
- {
- return View("FTPDetails",new FtpDetailsViewModels());
- }
-
- [HttpPost]
- public ActionResult UpdateSetting(FtpDetailsViewModels ftpDetails)
- {
-
- if (ModelState.IsValid)
- {
- try
- {
-
- if (ftpDetails.FtpType.Equals((int)EFtpTypes.FTP))
- {
- if (!_ftpservice.IsFtpLoginFileCreationSuccessful(ftpDetails.FtpHost.Trim(), ftpDetails.FtpUserName, ftpDetails.FtpPassword.Trim(), ftpDetails.Directory)) ModelState.AddModelError("FTPDetail.FtpHost", "Invalid FTP Details");
- }
- else if (ftpDetails.FtpType.Equals((int)EFtpTypes.SFTP))
- {
- _sftpservice.IsSFTPLoginFileCreationSuccessful(ftpDetails.FtpHost.Trim(), ftpDetails.FtpPort.Trim(), ftpDetails.FtpUserName.Trim(), ftpDetails.FtpPassword.Trim(), ftpDetails.Directory.Trim());
-
- }
-
- }
- catch (Exception ex)
- {
-
- ModelState.AddModelError("FTPDetail.FtpHost", ex.Message);
-
- }
-
- return View("FTPDetails", ftpDetails);
- }
- else
- {
- return View("FTPDetails", ftpDetails);
- }
-
-
-
- }
-
- }
- }
I have used Constructor Injection DI to get all the services instance like FTPManager, SFTPManager which otherwise I would have to create based on selected ftp type by User.
So this design when I started was working fine and I was happy that I was done with this user story. But as we all know that,
““The Only Thing That Is Constant Is Change -”
So the client tested the User Story, it worked great and does all the functionality and then they came up and said we want support for FTPS as FTP over SSL. One of our clients uses the FTPS. So I started it again and created one more enum with 3 which stands for FTPS and then there was one more else if condition in Controller for FTPS.
Problems
- The Old code has to be changed again in order to handle new ftp protocol.
- if (ftpDetails.FtpType.Equals((int)EFtpTypes.FTP))
- {
- if (!_ftpservice.IsFtpLoginFileCreationSuccessful(ftpDetails.FtpHost.Trim(), ftpDetails.FtpUserName, ftpDetails.FtpPassword.Trim(), ftpDetails.Directory)) ModelState.AddModelError("FTPDetail.FtpHost", "Invalid FTP Details");
- }
- else if (ftpDetails.FtpType.Equals((int)EFtpTypes.SFTP))
- {
- _sftpservice.IsSFTPLoginFileCreationSuccessful(ftpDetails.FtpHost.Trim(), ftpDetails.FtpPort.Trim(), ftpDetails.FtpUserName.Trim(), ftpDetails.FtpPassword.Trim(), ftpDetails.Directory.Trim());
-
- }
If a new network protocol is added, we have to add another else if statement to satisfy the requirement.
- The services and web project has to be deployed again for the new change.
- Prone to error because this change will force me to test FTP and SFTP which are totally independent of FTPS,
Source: Google
Solution - Replace If with Polymorphism Refactoring
So when I think of Polymorphism Dynamic Polymorphism comes into my mind; i.e. parent class can call its child classes at run time; i.e. parent class has more than one form. So with respect to our User story the Dynamic Polymorphism will look like as shown below:
So now, I have an Abstract Class named NetworkProtocol which has abstract method IsFtpLoginFileCreationSuccessful or other methods you want to other protocol to implement. Now let’s get started with the new design and learn how it helps us in removing unnecessary if else conditions.
Abstract class
- public abstract class FileTransferProtocol:IProtocol
- {
- public abstract bool IsFtpLoginFileCreationSuccessful(string host, string ftpPort, string ftpUsername, string ftpPassword, string folder);
- }
Child Class which implement Network Protocol
New class FTPS that is to be supported now as per the new change.
- public class FTPS : FileTransferProtocol
- {
- private FtpClient _client;
- private int _ftpReadTimeout = 0;
-
- public object ConfigurationManager { get; private set; }
-
- private void Connect(string Username, string Password, string HostName)
- {
- if (string.IsNullOrEmpty(Username)) throw new ArgumentNullException();
- if (string.IsNullOrEmpty(Password)) throw new ArgumentNullException();
- if (string.IsNullOrEmpty(HostName)) throw new ArgumentNullException();
-
- _client = new FtpClient(HostName);
- _client.Credentials = new NetworkCredential(Username, Password);
- _client.Connect();
- }
-
- private void CreateSetDirectory(string DirectoryName)
- {
- if (string.IsNullOrEmpty(DirectoryName)) throw new ArgumentNullException();
- _client.CreateDirectory(DirectoryName);
- _client.SetWorkingDirectory(DirectoryName);
- }
-
- private void Upload(byte[] data, string FileName)
- {
- _client.Upload(data, FileName);
- }
-
-
- public void CreateFile(string UserName, string Password, string HostName, string FileName, byte[] data, string Directory)
- {
- Connect(UserName, Password, HostName);
- if (!string.IsNullOrEmpty(Directory)) CreateSetDirectory(Directory);
- Upload(data, FileName);
- Disconnect();
- }
-
- public void DeleteFile(string FileName)
- {
- _client.DeleteFile(FileName);
- }
-
- public string ReadFile(string UserName, string Password, string HostName, string FileName, string Directory)
- {
- Connect(UserName, Password, HostName);
- string filePath = Directory + "/" + FileName;
-
- var _ftpStream = _client.OpenRead(filePath);
- using (var reader = new StreamReader(_ftpStream))
- {
- var Output = reader.ReadToEnd();
- Disconnect();
- return Output;
-
- }
-
- }
-
- private void Disconnect()
- {
- _client.Disconnect();
- _client.Dispose();
- }
-
- public override bool IsFtpLoginFileCreationSuccessful(string host, string ftpPort, string ftpUsername, string ftpPassword, string folder)
- {
- this.Connect(ftpUsername, ftpPassword, host);
- if (!string.IsNullOrEmpty(folder)) this.CreateSetDirectory(folder);
- byte[] bytes = Encoding.UTF8.GetBytes("Test");
- this.Upload(bytes, "sample.txt");
- this.DeleteFile("sample.txt");
- this.Disconnect();
- return true;
- }
- }
-
-
- blic class SFTP : FileTransferProtocol
- {
- public override bool IsFtpLoginFileCreationSuccessful( string host, string ftpPort, string ftpUsername, string ftpPassword, string folder)
- {
- try
- {
- var folders = string.IsNullOrEmpty(folder) ? new string[] { "" } : folder.Split('/');
- StringBuilder directoryMaker = new StringBuilder("");
- int defaultSFtpPort = 115;
- int port = (Convert.ToInt32(ftpPort) == 0) ? defaultSFtpPort : Convert.ToInt32(ftpPort);
- using (var ftpClient = new SftpClient(host, port, ftpUsername, ftpPassword))
- {
- ftpClient.Connect();
- for (int i = 0; i < folders.Length; i++)
- {
- var directories = ftpClient.ListDirectory(directoryMaker.ToString());
- List<string> lstDirectory = new List<string>();
- foreach (var directory in directories)
- {
- lstDirectory.Add(directory.Name);
- }
- if (lstDirectory.Where(x => x.ToString().ToLower() == folders[i].ToLower()).Any())
- {
- directoryMaker.Append(folders[i] + "/");
-
- if (i == folders.Length - 1)
- {
- UploadFileAndDeleteFromSFTPServer(directoryMaker, ftpClient);
- }
-
- }
- else
- {
- if (!string.IsNullOrEmpty(folder)) directoryMaker.Append(folders[i] + "/");
- if (!string.IsNullOrEmpty(folder)) ftpClient.CreateDirectory(directoryMaker.ToString());
- if (i == folders.Length - 1)
- {
- UploadFileAndDeleteFromSFTPServer(directoryMaker, ftpClient);
- }
- }
- }
- ftpClient.Disconnect();
- }
- return true;
-
- }
-
- catch (Exception EX)
- {
- throw;
- }
- }
- private void UploadFileAndDeleteFromSFTPServer(StringBuilder directoryMaker, SftpClient ftpClient)
- {
- if (!string.IsNullOrEmpty(directoryMaker.ToString())) ftpClient.ChangeDirectory(directoryMaker.ToString());
- using (Stream s = GenerateStreamFromString("TestFile"))
- {
- ftpClient.BufferSize = 4 * 1024;
- ftpClient.UploadFile(s, "Sample.txt");
- }
- ftpClient.Delete("Sample.txt");
- }
-
- private static Stream GenerateStreamFromString(string s)
- {
- MemoryStream stream = new MemoryStream();
- StreamWriter writer = new StreamWriter(stream);
- writer.Write(s);
- writer.Flush();
- stream.Position = 0;
- return stream;
-
-
- }
- }
-
- blic class FTP : FileTransferProtocol
- {
- public override bool IsFtpLoginFileCreationSuccessful(string host,string ftpPort, string ftpUsername, string ftpPassword, string folder)
- {
- var folders = string.IsNullOrEmpty(folder) ? new string[] { "" } : folder.Split('/');
- StringBuilder directoryMaker = new StringBuilder("/");
- for (int i = 0; i < folders.Length; i++)
- {
-
- if (i >= 1) directoryMaker.Append(folders[i - 1] + "/");
- host = host + directoryMaker.ToString();
- FtpWebRequest request;
- if (host.StartsWith("ftp") && !host.StartsWith("ftps"))
- {
- request = (FtpWebRequest)WebRequest.Create(new Uri(host));
- }
- else
- {
- host = "ftp://" + host;
- request = (FtpWebRequest)WebRequest.Create(new Uri(host));
- }
- request.Method = WebRequestMethods.Ftp.ListDirectory;
- request.Credentials = new NetworkCredential(ftpUsername, ftpPassword);
- request.KeepAlive = false;
- var response = (FtpWebResponse)request.GetResponse();
- if (!(response.StatusCode == FtpStatusCode.CommandOK || response.StatusCode == FtpStatusCode.FileActionOK || response.StatusCode == FtpStatusCode.DataAlreadyOpen || response.StatusCode == FtpStatusCode.OpeningData)) return false;
- Stream responseStream = response.GetResponseStream();
- StreamReader reader = new StreamReader(responseStream);
- string names = reader.ReadToEnd();
- var Directories = names.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries).ToList();
- if (Directories.Where(x => x.ToString().ToLower() == folders[i].Trim().ToLower()).Any())
- {
- if (i == folders.Length - 1)
- {
- CreateFile(request, folders[i], ftpUsername, ftpPassword);
- directoryMaker.Append(folders[i]);
- host = host + folders[i];
- Delete(request, folder, host, ftpUsername, ftpPassword);
-
- }
-
- }
- else
- {
- if (!string.IsNullOrEmpty(folder)) CreateDirectory(request, folders[i], host, ftpUsername, ftpPassword);
- if (i == folders.Length - 1)
- {
- CreateFile(request, folders[i], ftpUsername, ftpPassword);
- if (!string.IsNullOrEmpty(folder)) directoryMaker.Append(folders[i]);
- if (!string.IsNullOrEmpty(folder)) host = host + folders[i];
- Delete(request, folder, host, ftpUsername, ftpPassword);
- }
- }
- }
-
- return true;
- }
-
- private bool CreateFile(WebRequest request, string folder, string userName, string password)
- {
- try
- {
- int buffLength = 2048;
- byte[] buff = new byte[buffLength];
- byte[] bytes = Encoding.UTF8.GetBytes("Test Export");
- request = (FtpWebRequest)FtpWebRequest.Create(new Uri(request.RequestUri + "/" + folder + "/" + "Sample.txt"));
- request.Credentials = new NetworkCredential(userName, password);
- request.Method = WebRequestMethods.Ftp.UploadFile;
- request.ContentLength = bytes.Length;
- Stream requestStream = request.GetRequestStream();
- requestStream.Write(bytes, 0, bytes.Length);
- requestStream.Close();
- FtpWebResponse response = (FtpWebResponse)request.GetResponse();
- return true;
- }
- catch (Exception)
- {
- throw;
- }
-
- }
- private bool CreateDirectory(WebRequest request, string folder, string Host, string userName, string password)
- {
- try
- {
- var getFolders = folder.Split('/');
- StringBuilder directoriesToBeCreated = new StringBuilder("/");
- for (int i = 0; i < getFolders.Length; i++)
- {
- directoriesToBeCreated.Append(getFolders[i] + "/");
- if (Host.StartsWith("ftp") && !Host.StartsWith("ftps"))
- {
- request = (FtpWebRequest)WebRequest.Create(new Uri(Host + directoriesToBeCreated.ToString()));
- }
- else
- {
- Host = "ftp://" + Host;
- request = (FtpWebRequest)WebRequest.Create(new Uri(Host + directoriesToBeCreated.ToString()));
- }
-
- request.Credentials = new NetworkCredential(userName, password);
- request.Method = WebRequestMethods.Ftp.MakeDirectory;
- FtpWebResponse responses = (FtpWebResponse)request.GetResponse();
-
- }
- return true;
- }
- catch (Exception ex)
- {
- throw ex;
- }
- }
- private bool Delete(WebRequest request, string folder, string host, string userName, string password)
- {
- try
- {
- if (host.StartsWith("ftp") && !host.StartsWith("ftps"))
- {
- request = (FtpWebRequest)WebRequest.Create(new Uri(host + "//" + "Sample.txt"));
- }
- else
- {
- host = "ftp://" + host;
- request = (FtpWebRequest)WebRequest.Create(new Uri(host + "//" + "Sample.txt"));
- }
- request.Method = WebRequestMethods.Ftp.DeleteFile;
- request.Credentials = new NetworkCredential(userName, password);
- FtpWebResponse response = (FtpWebResponse)request.GetResponse();
- return true;
- }
- catch (Exception ex)
- {
- throw;
- }
-
- }
- }
So now in the future when the client comes and says we want another ftp to be supported we will create a new class and inherit our base class NetworkProtocol and implement the same to use in our application.
This is just the first phase of design. Now, the important thing is pending, that is, getting the instance of a class based on selected Network protocol in UI by which we will be able to call correct Protocol and check if username and password are correct or not.
As I mentioned I will be using Dependency injection container to get the appropriate instance based on type of Protocol; i.e. in DB we have set protocol details as shown below,
ID |
Protocol |
1 |
FTP |
2 |
SFTP |
3 |
FTPS |
I will not be explaining Dependency injection thoroughly here. But fwhenever we create an instance of a class in our controller our controller is dependent on that class if anything on that class changes, the compiler has to go through all desired changes and also has to recompile the controller due to change in Class itself. So in order to remove the use of new in our classes, we inject the required instance via Interface constructor.
For IOC (inversion of control) container which is responsible for giving me the instance, I am using AutoFac. I will configure the above dependency as shown below in di configuration.
- public static IContainer Build()
- {
- var builder = new ContainerBuilder();
- builder.RegisterControllers(Assembly.GetExecutingAssembly());
-
-
- builder.RegisterType<FileTransferProtocol>().As<IProtocol>().InstancePerLifetimeScope();
- builder.RegisterType<FTP>().Named<NetworkProtocol>("1").InstancePerLifetimeScope();
- builder.RegisterType<SFTP>().Named<NetworkProtocol>("2").InstancePerLifetimeScope();
- builder.RegisterType<FTPS>().Named<NetworkProtocol>("3").InstancePerLifetimeScope();
- Container = builder.Build();
- DependencyResolver.SetResolver(new AutofacDependencyResolver(Container));
- Registry.Container = Container;
- return Container;
- }
So I have created named dependency with 1,2,3 which I will get in controller action method while saving these details and based on received ftp type I will get the required instance of Protocol by resolving the same with the help of DI Container. Let’s get started.
Now, in my action method, I will just resolve the instance by FTP type -- it's as simple as that.
- [HttpPost]
- public ActionResult UpdateSetting(FtpDetailsViewModels ftpDetails)
- {
- if (ModelState.IsValid)
- {
- try
- {
- var reportSubject = _container.ResolveOptionalNamed<IProtocol>(ftpDetails.FtpType.ToString());
- var status= reportSubject.IsFtpLoginFileCreationSuccessful(ftpDetails.FtpHost.Trim(),ftpDetails.FtpPort, ftpDetails.FtpUserName, ftpDetails.FtpPassword.Trim(), ftpDetails.Directory);
- if(status)
- {
- saveDetails();
- }else
- {
- ModelState.AddModelError("FTPDetail.FtpHost", "Invalid FTP Details"); ;
- }
- }
- catch (Exception ex)
- {
- ModelState.AddModelError("FTPDetail.FtpHost", ex.Message);
- }
-
- return View("FTPDetails", ftpDetails);
- }
- else
- {
- return View("FTPDetails", ftpDetails);
- }
-
- }
Now when I save the detail in UI:
Now, in my controller, I can see the user entered ftp details in my Model.
Now we can see the FTPtype is 1 i.e. FTP which means the user has selected FTP protocol. Now, when we resolve the dependency, let’s see which instance we get.
We can clearly see the instance is pointing to FTP service which will call FTP service IsFtpLoginFileCreationSuccessful method to check ftp detail.
We are able to successfully check the rights and username and password for ftp protocol and the same can be done for other protocol.
Now you can clearly see there is no if else statement it’s purely a one liner to resolve the dependency. We have seen the power of Polymorphism to “Remove if with Polymorphism”
Conclusion
Here we learned how easily we can get rid of our common if else condition and make it cleaner with the help of Polymorphism. With the help of DI, we are able to decouple the controller from the service layer.
In future, if we need to support any more protocols we have to just create a new respective protocol service and add this in DI container to register it and it will be ready to go.
I hope this article was helpful to understand RIP.