C
C#6mo ago
Mayka

Dependency Injection - Data Exporter

I’ve been trying to wrap my head around dependency injection, but I’m struggling to bridge the gap between written explanations and putting the paradigm into practice within my own code. I need to export data from an Access database to an Excel spreadsheet using Interop (the OLE DB driver isn't an option here, unfortunately), but the goal is to eventually get the data out of Access and into a better format like perhaps a SQL Server database. To make the code more flexible for these changes in the future, I tried to implement a general IExporter interface that could take any kind of input and export the data to any kind of output. Would anyone be able to offer any feedback on how I could improve the below? It feels like my implementation is a bit off design-wise. IDataExporter.cs:
namespace MyApp
{
   public interface IDataExporter
   {
       void Export();
   }
}
namespace MyApp
{
   public interface IDataExporter
   {
       void Export();
   }
}
DataExporter.cs:
namespace MyApp
{
   public class DataExporter
   {
       private readonly IDataExporter exporter;

       public DataExporter(IDataExporter dataExporter)
       {
           exporter = dataExporter;
       }

       public void Export()
       {
           exporter.Export();
       }
   }
}
namespace MyApp
{
   public class DataExporter
   {
       private readonly IDataExporter exporter;

       public DataExporter(IDataExporter dataExporter)
       {
           exporter = dataExporter;
       }

       public void Export()
       {
           exporter.Export();
       }
   }
}
2 Replies
Mayka
Mayka6mo ago
AccessDataExporter.cs:
using System;
using System.Runtime.InteropServices;
using Access = Microsoft.Office.Interop.Access;

namespace MyApp
{
   internal class AccessDataExporter : IDataExporter
   {
       private readonly string database;
       private readonly string objectName;
       private readonly string outputPath;
       private readonly bool applicationVisible = false;

       public AccessDataExporter(
           string database,
           string objectName,
           string outputFile,
           bool visible = false)
       {
           this.database = database;
           this.objectName = objectName;
           outputPath = outputFile;
           applicationVisible = visible;
       }

       public void Export()
       {
           Access.Application access = new Access.Application
           {
               Visible = applicationVisible,
           };

           try
           {
               access.OpenCurrentDatabase(database);

               // Access may become visible after the database is opened if
               // a form is configured to display within the database file
               access.Visible = applicationVisible;

               access.DoCmd.TransferSpreadsheet(
                   Access.AcDataTransferType.acExport,
                   Access.AcSpreadSheetType.acSpreadsheetTypeExcel12Xml,
                   objectName,
                   outputPath);
           }
           finally
           {
               access.CloseCurrentDatabase();
               access.Quit(Access.AcQuitOption.acQuitSaveNone);

               Marshal.FinalReleaseComObject(access);
               GC.Collect();
               GC.WaitForPendingFinalizers();
           }
       }
   }
}
using System;
using System.Runtime.InteropServices;
using Access = Microsoft.Office.Interop.Access;

namespace MyApp
{
   internal class AccessDataExporter : IDataExporter
   {
       private readonly string database;
       private readonly string objectName;
       private readonly string outputPath;
       private readonly bool applicationVisible = false;

       public AccessDataExporter(
           string database,
           string objectName,
           string outputFile,
           bool visible = false)
       {
           this.database = database;
           this.objectName = objectName;
           outputPath = outputFile;
           applicationVisible = visible;
       }

       public void Export()
       {
           Access.Application access = new Access.Application
           {
               Visible = applicationVisible,
           };

           try
           {
               access.OpenCurrentDatabase(database);

               // Access may become visible after the database is opened if
               // a form is configured to display within the database file
               access.Visible = applicationVisible;

               access.DoCmd.TransferSpreadsheet(
                   Access.AcDataTransferType.acExport,
                   Access.AcSpreadSheetType.acSpreadsheetTypeExcel12Xml,
                   objectName,
                   outputPath);
           }
           finally
           {
               access.CloseCurrentDatabase();
               access.Quit(Access.AcQuitOption.acQuitSaveNone);

               Marshal.FinalReleaseComObject(access);
               GC.Collect();
               GC.WaitForPendingFinalizers();
           }
       }
   }
}
Implementation:
string tempPath = @"C:\Path\To\Spreadsheet.xlsx";
string accessDb = @"C:\Path\To\Database.accdb";

DataExporter exporter = new DataExporter(
new AccessDataExporter(accessDb, "My Table", tempPath));
exporter.Export();
string tempPath = @"C:\Path\To\Spreadsheet.xlsx";
string accessDb = @"C:\Path\To\Database.accdb";

DataExporter exporter = new DataExporter(
new AccessDataExporter(accessDb, "My Table", tempPath));
exporter.Export();
cap5lut
cap5lut6mo ago
to have a more loose coupling i would actually recommend a data reader, an data writer and then a service that takes both to do the actual job:
public interface IDataReader<T>
{
public T ReadData();
}
public interface IDataWriter<T>
{
public void WriteData(T data);
}
public class DataExportService<T>(IDataReader<T> reader, IDataWriter<T> writer)
{
public void ExportData()
{
T data = reader.ReadData();
writer.WriteData(data);
}
}
public interface IDataReader<T>
{
public T ReadData();
}
public interface IDataWriter<T>
{
public void WriteData(T data);
}
public class DataExportService<T>(IDataReader<T> reader, IDataWriter<T> writer)
{
public void ExportData()
{
T data = reader.ReadData();
writer.WriteData(data);
}
}
this way u could do something like
var exporter = new DataExportService(
new AccessDataReader(@"C:\Path\To\Spreadsheet.xlsx"),
new JsonDataWriter(@"C:\Path\To\dump.json"));
exporter.ExportData();
var exporter = new DataExportService(
new AccessDataReader(@"C:\Path\To\Spreadsheet.xlsx"),
new JsonDataWriter(@"C:\Path\To\dump.json"));
exporter.ExportData();
basically u have separated the reading and the writing, thus u need to write every reading/writing type just once and can wire it together. with ur example implementation u would have a tight coupling, as in if u want an JSON to MYSQL and a JSON to Access exporter, u would have to implement the JSON part twice ofc this is just a small example, i would also give the data source/data destination info on the actual method eg
public interface IDataReader<TData, TSource>
{
public TData ReadData(TSource source);
}
public interface IDataReader<TData, TSource>
{
public TData ReadData(TSource source);
}
and similar for the writer