Search code examples
c#delegatescopy-paste

C#: How to rewrite it without "copy/paste" code


I have 2 similar methods:

    /// <summary>
    /// 
    /// </summary>
    /// <param name="domain"></param>
    /// <exception cref="DomainRecordNotFoundException">Throw when the dns record is not found in Office365</exception>
    /// <exception cref="DomainNotFoundException">Throw when domain is not added to Office365</exception>
    /// <exception cref="UnknownException">Unknown exception from Microsoft Graph</exception>
    /// <returns></returns>
    public async Task<string> GetMxRecordForDomainAsync(string domain)
    {
        try
        {
            var records = await _graphClient.Domains[domain].ServiceConfigurationRecords.Request().GetAsync();
            string mxRecord = String.Empty;

            foreach (var record in records)
            {
                if (record.RecordType == "Mx")
                {
                    mxRecord = ((Microsoft.Graph.DomainDnsMxRecord)record).MailExchange;
                    break;
                }
            }

            if (String.IsNullOrWhiteSpace(mxRecord))
                throw new DomainRecordNotFoundException(DomainRegistrationCore.Models.DomainRecordType.MX);

            return mxRecord;
        }
        catch (ServiceException graphEx)
        {
            if (graphEx.StatusCode == System.Net.HttpStatusCode.NotFound)
            {
                throw new DomainNotFoundException();
            }

            throw new UnknownException(graphEx.StatusCode, graphEx.Error.Message);
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="domain"></param>
    /// <exception cref="DomainRecordNotFoundException">Throw when the dns record is not found in Office365</exception>
    /// <exception cref="DomainNotFoundException">Throw when domain is not added to Office365</exception>
    /// <exception cref="UnknownException">Unknown exception from Microsoft Graph</exception>
    /// <returns></returns>
    public async Task<string> GetVerificationRecordForDomainAsync(string domain)
    {
        try
        {
            var records = (await _graphClient.Domains[domain].VerificationDnsRecords.Request().GetAsync());
            string verificationText = String.Empty;

            foreach (var record in records)
            {
                if (record.RecordType == "Txt")
                {
                    verificationText = ((Microsoft.Graph.DomainDnsTxtRecord)record).Text;
                    break;
                }
            }

            if (String.IsNullOrWhiteSpace(verificationText))
                throw new DomainRecordNotFoundException(DomainRegistrationCore.Models.DomainRecordType.TXT);

            return verificationText;
        }
        catch (ServiceException graphEx)
        {
            if (graphEx.StatusCode == System.Net.HttpStatusCode.NotFound)
            {
                throw new DomainNotFoundException();
            }

            throw new UnknownException(graphEx.StatusCode, graphEx.Error.Message);
        }
    }

as we can see, these 2 methods differ only this part:

        foreach (var record in records)
        {
            if (record.RecordType == **RECORD**)
            {
                mxRecord = ((**TYPE_OF_RECORD**)record).MailExchange;
                break;
            }
        }

        if (String.IsNullOrWhiteSpace(mxRecord))
            throw new DomainRecordNotFoundException(**RECORD**);

other parts are the same. I want to rewrite it for one common method, but don't understand how. I assume, that I can do it with Func<> or Action<>


Solution

  • First define common interface

    public interface IExtractor
    {
        string RecordType { get; }
        string ErrorMessage { get; }
        string GetValue(object record);
    }
    

    Next create implementations

    class MxRecordExtractor : IExtractor
    {
        public string RecordType => "Mx";
    
        public string ErrorMessage => DomainRegistrationCore.Models.DomainRecordType.MX;
    
        public string GetValue(object record)
        {
            return ((Microsoft.Graph.DomainDnsMxRecord)record).MailExchange;
        }
    }
    
    class VerificationRecordExtractor : IExtractor
    {
        public string RecordType => "Txt";
    
        public string ErrorMessage => DomainRegistrationCore.Models.DomainRecordType.TXT;
    
        public string GetValue(object record)
        {
            return ((Microsoft.Graph.DomainDnsTxtRecord)record).Text;
        }
    }
    

    Later create private abstracted version of method:

    private async Task<string> ExtractForDomainAsync(string domain, IExtractor extractor)
    {
        try
        {
            var records = (await _graphClient.Domains[domain].VerificationDnsRecords.Request().GetAsync());
            string extractedValue = String.Empty;
    
            foreach (var record in records)
            {
                if (record.RecordType == extractor.RecordType)
                {
                    extractedValue = extractor.GetValue(record);
                    break;
                }
            }
    
            if (String.IsNullOrWhiteSpace(extractedValue))
                throw new DomainRecordNotFoundException(extractor.ErrorMessage);
    
            return extractedValue;
        }
        catch (ServiceException graphEx)
        {
            if (graphEx.StatusCode == System.Net.HttpStatusCode.NotFound)
            {
                throw new DomainNotFoundException();
            }
    
            throw new UnknownException(graphEx.StatusCode, graphEx.Error.Message);
        }
    }
    

    Finally modify existing methods to use our common method:

    public Task<string> GetMxRecordForDomainAsync(string domain)
    {
        return ExtractForDomainAsync(domain,  new MxRecordExtractor());
    }
    
    public Task<string> GetVerificationRecordForDomainAsync(string domain)
    {
        return ExtractForDomainAsync(domain, new VerificationRecordExtractor());
    }