1

I have 1 long running process wrapped inside a method and it is for 2 different types like below:

  • Type1
  • Type2

Code:

public interface IBaseType
    {
        MyResult LongRunningProcess(int jobId,int noOfTimes); //doenst save long running process data in database.just returns the results to consumer
        void LongRunningProcess(int noOfTimes); //Save results of long running process in database.Background job with on-demand as well as scheduled
    }

public class Type1 : IBaseType
{
    public void LongRunningProcess(int jobId,int noOfTimes)
    {
        try
        {
           //Step1 : 
           var type1Manager =  new Type1Manager(params);
           for (int i = 0; i < noOfTimes; i++)
               {
                 var con = ConnectionFactory.OpenConnection();
                 type1Manager.Start(con);
                //Save results of those processing
            }

          //Step2:
          IVersioning versioning = new Versioning();
          string version = versioning.GetVersion();
          using (connection = new SqlConnection(connectionString))
          {
               connection.Open();
               using (var transaction = connection.BeginTransaction())
               {
                  try
                  {
                     Repository.UpdateVariantVersioning(connection, transaction,jobId, version);
                     Repository.UpdateCategoryWithVersion(connection, transaction,versioning.Category,version);
                     transaction.Commit();
                  }
                  catch(Exception ex)
                  {
                       transaction.Rollback();
                       //code to delete everything that has been performed in step1
                       throw ex;
                  }
                }
            }
        }
        catch (Exception ex)
        {
            Repository.UpdateErrorDetails(connectionString,jobId,ex.Message);
        }

        //Step3 : if step1 and step2 successfull than mark this job as succeeded else failed
        // Updating time of whole process in table
    }
 }





public class Type2 : IBaseType
{
    public void LongRunningProcess(int jobId,int noOfTimes)
    {
        try
        {
           //Step1 : 
            var type2Manager =  new Type2Manager(params);
            for (int i = 0; i < noOfTimes; i++)
               {
                 var con = ConnectionFactory.OpenConnection();
                 type2Manager.Start(con);
                //Save results of those processing
            }

          //Step2:
          IVersioning versioning = new Versioning();
          string version = versioning.GetVersion();
          using (connection = new SqlConnection(connectionString))
          {
               connection.Open();
               using (var transaction = connection.BeginTransaction())
               {
                  try
                  {
                     Repository.UpdateVariantVersioning(connection, transaction,jobId, version);
                     Repository.UpdateCategoryWithVersion(connection, transaction,versioning.Category,version);
                     transaction.Commit();
                  }
                  catch(Exception ex)
                  {
                       transaction.Rollback();
                       //code to delete everything that has been performed in step1
                       throw ex;
                  }
                }
            }
        }
        catch (Exception ex)
        {
            Repository.UpdateErrorDetails(connectionString,jobId,ex.Message);
        }

        //Step3 : if step1 and step2 successfull than mark this job as succeeded else failed
        // Updating time of whole process in table
    }
 }

So as you can see here that step2 and step3 code are getting repeated for both types so I want to this code repetition.

Secondly I want to keep step1 and step2 in sync so that when step2 fails, then rollback whatever has been done inside the entire step1 process.

I am a bit confused with moving versioning in base abstract class because that would probably be tightly coupled with this long running process. I want to design it in a way that tomorrow if I think of removing versioning then it should not hamper my current design and code.

Can anybody please help me with this?

Update : Added versioning code

interface IVersion
{
    string CreateVersion();
}

Public class Version : IVersion
{
     public string Category { get; private set; }
}
0

So you want to share the code of step 2 and step 3 without them both inheriting from the same class with this code mentioned. This is plausible, and even good.

You can create a class that implements and exposes the versioning you mentioned (step 2 and step 3). Either have it as a member in your Type1/2 or invoke it as a static function of this class from within your long running process function. And according to the error code/exception you receive, you can invoke the specific roll-back to the type.

    public interface IBaseType
{
    MyResult LongRunningProcess(int jobId,int noOfTimes); //doenst save long running process data in database.just returns the results to consumer
    void LongRunningProcess(int noOfTimes); //Save results of long running process in database.Background job with on-demand as well as scheduled
}

public class Step2 
{
    public static HandleFunction(int jobId) 
    {
        IVersioning versioning = new Versioning();
        string version = versioning.GetVersion();
        using (connection = new SqlConnection(connectionString))
        {
           connection.Open();
           using (var transaction = connection.BeginTransaction())
           {
              try
              {
                 Repository.UpdateVariantVersioning(connection, transaction,jobId, version);
                 Repository.UpdateCategoryWithVersion(connection, transaction,versioning.Category,version);
                 transaction.Commit();
              }
              catch(Exception ex)
              {
                   transaction.Rollback();
                   throw new Step2SpecificException(...);
              }
            }
        }
    }
}

public class Type1 : IBaseType
{
    public void LongRunningProcess(int jobId,int noOfTimes)
    {
        try
        {
           //Step1 : 
           var type1Manager =  new Type1Manager(params);
           for (int i = 0; i < noOfTimes; i++)
               {
                 var con = ConnectionFactory.OpenConnection();
                 type1Manager.Start(con);
                //Save results of those processing
            }

          //Step2:
          Step2::HandleFunction(jobId);
        }
        catch (Step2Exception) 
        {
            //code to delete everything that has been performed in step1
            return?
        }
        catch (Exception ex)
        {
            Repository.UpdateErrorDetails(connectionString,jobId,ex.Message);
        }

        //Step3 : if step1 and step2 successfull than mark this job as succeeded else failed
        // Updating time of whole process in table
    }
 }

public class Type2 : IBaseType
{
    public void LongRunningProcess(int jobId,int noOfTimes)
    {
        try
        {
           //Step1 : 
            var type2Manager =  new Type2Manager(params);
            for (int i = 0; i < noOfTimes; i++)
               {
                 var con = ConnectionFactory.OpenConnection();
                 type2Manager.Start(con);
                //Save results of those processing
            }

          //Step2:
          Step2::HandleFunction(jobId);
        }
        catch (Step2Exception) 
        {
            //code to delete everything that has been performed in step1
            return?
        }
        catch (Exception ex)
        {
            Repository.UpdateErrorDetails(connectionString,jobId,ex.Message);
        }

        //Step3 : if step1 and step2 successfull than mark this job as succeeded else failed
        // Updating time of whole process in table
    }
 }

Couple of points:

  • find a better name than "Step2" and "HandleFunction" that I gave.
  • I don't like the creation of "Step2Exception", I think that just returning a 'false' in case of failure from "HandleFunction" and act accordingly would be better.
  • But how do i share the common code of step2 and Step3 between type1 and type 2?Can you share any sample design it would be very helpful – ILoveStackoverflow Feb 5 at 7:10
  • You said that step 2 and step 3 of the long_running_process are mostly identical and you want to share. Any other parameters being used by this steps specific to the type can be moved via the function call etc. You should clarify your code (what being shared/not in steps2/3) if you want me to suggest a more specific answer. – nadir Feb 5 at 9:01
  • I have updated my question with code and more details related to versioning – ILoveStackoverflow Feb 6 at 7:45
  • Edited my question – nadir Feb 6 at 16:09
  • Concerning "I don't like the creation of "Step2Exception"", you're absolutely correct. That's a boneheaded exception. There are absolutely no exceptional circumstances here, just 2 very much expected results. – R. Schmitz Feb 12 at 10:02

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.