I have tables that use BulkManager to save data to database.
I moved common functionality to BulkTableBase
. Because creation of BulkManager
depends on params of each Table I create the BulkManager
in .ctor of the Table. The Add
method of each Table is also different.
But I feel that calling CreateBulkManager
method in .ctor is wrong! What if someone who derive from BulkTableBase
would forget to call CreateBulkManager
.
Is my BulkTableBase
class constructed well?
public class Table1 : BulkTableBase
{
public Table1(int param1, int param2)
{
_dt = new DataTable();
_dt.Columns.Add(new DataColumn("Id", typeof(int)));
_dt.Columns.Add(new DataColumn("Name", typeof(int)));
_config = ConfigurationManager.GetSection("Table1_Config");
CreateBulkManager(_dt, new Dictionary<string, object> { { "param1", param1 }, { "param2", param2 } }, _config);
}
public void Add(string name, int id)
{
var row = _dt.NewRow();
row["Name"] = name;
row["Name"] = id;
BulkManager.Add(row);
}
}
public class Table2 : BulkTableBase
{
public Table2(int param3, string param4, byte param5)
{
_dt = new DataTable();
_dt.Columns.Add(new DataColumn("CustomerId", typeof(int)));
_dt.Columns.Add(new DataColumn("Address", typeof(string)));
_dt.Columns.Add(new DataColumn("Tel", typeof(string)));
_config = ConfigurationManager.GetSection("Table2_Config");
CreateBulkManager(_dt, new Dictionary<string, object> { { "param3", param3 }, { "param4", param4 }, { "param5", param5 } }, _config);
}
public void Add(int customerId, string address, string tel)
{
var row = _dt.NewRow();
row["CustomerId"] = customerId;
row["Address"] = address;
row["Tel"] = tel;
BulkManager.Add(row);
}
}
public abstract class BulkTableBase : IDisposable
{
private SqlBulkManager _bulkManager;
protected SqlBulkManager BulkManager { get { return _bulkManager; } }
protected SqlBulkManagerBase CreateBulkManager(DataTable tableScheme, IDictionary<string, object> parameters, SqlBulkManagerConfig config)
{
var sqlParams = new List<SqlParameter>();
foreach (var param in parameters)
{
sqlParams.Add(new SqlParameter(param.Key, param.Value));
}
_bulkManager = new SPSqlBulkManager(tableScheme, config, sqlParams);
_bulkManager.Start();
}
// wait until all data is saved
public void SaveAll()
{
BulkManager.Flush();
}
public void Dispose()
{
BulkManager.Stop();
}
}
public class SqlBulkManager
{
//...
public SqlBulkManager(DataTable scheme, BulkManagerConfig config, IEnumerable<SqlParameter> extraParams)
{
}
public void Add(DataRow item)
{
// add item to table and when number of rows reaches N the data is saved to DB
}
//...
}
UPDATED
I understand that I violate the Liskov substitution principle, but I wanted that eventually all bulks to be saved either exception occurred or not. Therefore, I needed BulkTableBase
. So, I moved the code to FileParser->HandleWork
method, creates BulkTable
table there and pass to Parse
method of the implemented parser.
public abstract class FileParser : MyThreadPool
{
protected sealed override void HandleWork()
{
// some common checking
//...
BulkTableBase bulkTable = CreateBulkTable();
try
{
OnBeforeParse();
Parse(bulkTable);
OnAfterParse();
}
catch(Exception ex)
{ ... }
finally
{
//...
if (bulkTable != null)
{
// The BulkManager may have many bulks that hasn't saved yet, so by
// calling "SaveAll()" it waits untill all threads accomplished the saving
bulkTable.SaveAll();
bulkTable.Dispose();
}
}
}
protected abstract void Parse(BulkTableBase bulkTable, ...);
protected abstract BulkTableBase CreateBulkTable();
}
public class Parser1 : FileParser
{
protected override void Parse(BulkTableBase bulkTable, ...)
{
var table = (Table1)bulkTable;
//...
}
protected override BulkTableBase CreateBulkTable()
{
return new Table1(...);
}
}