Is filling different DataTables of the same DataSet a thread-safe operation? - c#

I have many methods filling different DataTables in the same DataSet:
public override void FillMethod1(MyDataSet ds)
{
try
{
using (SqlDataAdapter adapter = new SqlDataAdapter(query, connectionObj))
{
using (SqlCommandBuilder adapterSCB = new SqlCommandBuilder(adapter))
{
adapter.Fill(ds.MyTable);
}
}
}
catch (Exception e)
{
//Exception handling
}
}
public override void FillMethod2(MyDataSet ds)
{
try
{
using (SqlDataAdapter adapter = new SqlDataAdapter(query, connectionObj))
{
using (SqlCommandBuilder adapterSCB = new SqlCommandBuilder(adapter))
{
adapter.Fill(ds.MyTable2);
}
}
}
catch (Exception e)
{
//Exception handling
}
}
[....]
using(MyDataSet ds = new MyDataSet())
{
FillMethod1(ds);
FillMethod2(ds);
[...]
}
And I'd like to parallelize these operation using Task:
using(MyDataSet ds = new MyDataSet())
{
Task fill1 = Task.Run(() => FillMethod1(ds));
Task fill2 = Task.Run(() => FillMethod2(ds));
[...]
Task.WaitAll(fill1, fill2, [...]);
}
After some research I found that DataSet is not thread-safe, but is it safe when working on different DataTable?

I created a class handling parallel fills on the same DataSet. Each fill has its own DataSet and it will be merged into the original one at the end of the execution.
To handle data table relations, it is possible to create a sequential list of fills that will be run in parallel with other fills. The merge method will wait for all operations to finish and merge results into the original DataSet (passed in the constructor)
public class FillParallelizer<TDataSet> : IDisposable
where TDataSet : DataSet, new()
{
private bool _seqFillStarted = false;
private IList<QueryObject> _queryObjList = new List<QueryObject>();
private Queue<FillOperation> _seqOperationBufferQueue = new Queue<FillOperation>();
public DataSet MasterDS { get; }
public FillParallelizer(DataSet masterDS)
{
MasterDS = masterDS ?? throw new ArgumentNullException(nameof(masterDS));
}
public FillParallelizer<TDataSet> ParallelFill(Action<TDataSet> methodAction)
{
if (methodAction == null) throw new ArgumentNullException(nameof(methodAction));
QueryObject queryObj = new QueryObject(methodAction);
_queryObjList.Add(queryObj);
return this;
}
public void Merge()
{
var taskList = _queryObjList.Select(x => x.Task).ToArray();
Task.WaitAll(taskList);
foreach (var item in _queryObjList)
{
MasterDS.Merge(item.TempDS);
}
}
public FillParallelizer<TDataSet> SequentialFill(Action<TDataSet> action)
{
if(!_seqFillStarted)
{
_seqFillStarted = true;
}
if (!_seqFillStarted) throw new ArgumentException("Sequential fill not started");
_seqOperationBufferQueue.Enqueue(new FillOperation(action));
return this;
}
public FillParallelizer<TDataSet> RunSequentialFill()
{
_seqFillStarted = false;
QueryObject queryObj = new QueryObject(new Queue<FillOperation>(_seqOperationBufferQueue));
_seqOperationBufferQueue.Clear();
_queryObjList.Add(queryObj);
return this;
}
public void Dispose()
{
if (_queryObjList == null || !_queryObjList.Any())
{
return;
}
foreach (var item in _queryObjList) item.Dispose();
}
class FillOperation
{
public Action<TDataSet> Action { get; }
public FillOperation(Action<TDataSet> action)
{
Action = action ?? throw new ArgumentNullException(nameof(action));
}
public void Run(TDataSet tempDS)
{
if (tempDS == null) throw new ArgumentNullException(nameof(tempDS));
Action(tempDS);
}
}
class QueryObject : IDisposable
{
public TDataSet TempDS { get; }
public Queue<FillOperation> FillOperationList { get; }
public Task Task { get; }
public QueryObject(Queue<FillOperation> fillOperationList)
{
TempDS = new TDataSet();
FillOperationList = fillOperationList ?? throw new ArgumentNullException(nameof(fillOperationList));
Task =
Task
.Run
(() =>
{
foreach (FillOperation op in FillOperationList) op.Run(TempDS);
}
);
}
public QueryObject(Action<TDataSet> action)
: this(new Queue<FillOperation>(new FillOperation[] { new FillOperation(action) }))
{
}
public void Dispose()
{
Task.Dispose();
}
}
}
An example of usage:
using(MyDataSet myDS = new MyDataSet())
using (FillParallelizer<MyDataSet> parallelizer = new FillParallelizer<MyDataSet>(myDS))
{
parallelizer
.ParallelFill(ds => MyDAO.FillTable1(ds))
.ParallelFill(ds => MyDAO.FillTable2(ds))
.SequentialFill(ds => MyDAO.FillTable3(ds))
.SequentialFill(ds => MyDAO.FillTable4(ds))
.SequentialFill(ds => MyDAO.FillTable5(ds))
.RunSequentialFill()
.ParallelFill(ds => MyDAO.FillTable6(ds))
.ParallelFill(ds => MyDAO.FillTable7(ds))
.Merge();
//All fills completed
}
Fill methods 3, 4, 5 are done sequentially but in parallel with other fills, to handle relations between tables

Related

Xamarin unable get instance from async

I've written a Xamarin.Forms Application that connects to an ESP32 via Bluetooth. Now I'd like to get a value from a CustomControl.JoystickControl from the MainPage.xaml Page.
I've tried it like that:
MainPage.xaml.cs:
public partial class MainPage : ContentPage
{
public static Bluetooth.IBth bth = new Bluetooth.Bth();
public MainPage()
{
InitializeComponent();
Task.Run(async () => bth.Start("mecanumWheelRobot", 200, false));
}
public CustomControls.JoystickControl JoystickControlElement
{
get { return JoystickControl; }
}
public CustomControls.JoystickControl JoystickControlElement1
{
get { return JoystickControl1; }
}
}
Now I'd like to get the JoystickControlElement from the following Async Function:
var mainpage = new Views.MainPage();
string test = mainpage.test;
(these two lines are the problem)
class Bth : IBth
{
private CancellationTokenSource _ct { get; set; }
const int RequestResolveError = 1000;
public Bth()
{
}
public void Start(string name, int sleepTime = 200, bool readAsCharArray = false)
{
Task.Run(async () => loop(name, sleepTime, readAsCharArray));
}
private async Task loop(string name, int sleepTime, bool readAsCharArray)
{
BluetoothDevice device = null;
BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
BluetoothSocket BthSocket = null;
_ct = new CancellationTokenSource();
while (_ct.IsCancellationRequested == false)
{
try
{
Thread.Sleep(sleepTime);
adapter = BluetoothAdapter.DefaultAdapter;
if (adapter == null)
System.Diagnostics.Debug.WriteLine("No Bluetooth adapter found.");
else
System.Diagnostics.Debug.WriteLine("Adapter found!!");
if (!adapter.IsEnabled)
System.Diagnostics.Debug.WriteLine("Bluetooth adapter is not enabled.");
else
System.Diagnostics.Debug.WriteLine("Adapter enabled!");
System.Diagnostics.Debug.WriteLine("Try to connect to " + name);
foreach (var bd in adapter.BondedDevices)
{
System.Diagnostics.Debug.WriteLine("Paired devices found: " + bd.Name.ToUpper());
if (bd.Name.ToUpper().IndexOf(name.ToUpper()) >= 0)
{
System.Diagnostics.Debug.WriteLine("Found " + bd.Name + ". Try to connect with it!");
device = bd;
break;
}
}
if (device == null)
{
System.Diagnostics.Debug.WriteLine("Named device not found.");
MainThread.BeginInvokeOnMainThread(() =>
{
// Code to run on the main thread
});
}
else
{
UUID uuid = UUID.FromString("00001101-0000-1000-8000-00805f9b34fb");
if ((int)Android.OS.Build.VERSION.SdkInt >= 10) // Gingerbread 2.3.3 2.3.4
BthSocket = device.CreateInsecureRfcommSocketToServiceRecord(uuid);
else
BthSocket = device.CreateRfcommSocketToServiceRecord(uuid);
if (BthSocket != null)
{
//Task.Run ((Func<Task>)loop); /*) => {
await BthSocket.ConnectAsync();
if (BthSocket.IsConnected)
{
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastSuccess("Connected!");
// Code to run on the main thread
//pages.bluetoothBarcodeScan.connectedScanner();
});
System.Diagnostics.Debug.WriteLine("Connected!");
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastSuccess("Writing!");
// Code to run on the main thread
//
});
while (_ct.IsCancellationRequested == false)
{
var mainpage = new Views.MainPage();
string test = mainpage.test;
var bytes = Encoding.ASCII.GetBytes("{test}-");
BthSocket.OutputStream.Write(bytes, 0, bytes.Length);
Thread.Sleep(100);
}
System.Diagnostics.Debug.WriteLine("Exit the inner loop");
}
}
else
System.Diagnostics.Debug.WriteLine("BthSocket = null");
}
}
catch(Exception ex)
{
service.toast.toastSuccess(ex.Message);
}
finally
{
if (BthSocket != null)
BthSocket.Close();
device = null;
adapter = null;
}
}
System.Diagnostics.Debug.WriteLine("Exit the external loop");
}
public void Cancel()
{
if (_ct != null)
{
System.Diagnostics.Debug.WriteLine("Send a cancel to task!");
_ct.Cancel();
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastError("Disconnected");
// Code to run on the main thread
});
}
}
public async Task<string> GetData()
{
var mainpage = new Views.MainPage();
string test = mainpage.test;
return "1";
}
public ObservableCollection<string> PairedDevices()
{
BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
ObservableCollection<string> devices = new ObservableCollection<string>();
foreach (var bd in adapter.BondedDevices)
devices.Add(bd.Name);
return devices;
}
#endregion
}
The async function always starts new when it comes to this line.
How should I get the variable?
You can change your constructor method like:
public Bth(string txt)
{}
And then pass the mainpage.test like:
public static Bluetooth.IBth bth = new Bluetooth.Bth(txt);

C# call function from async false result [duplicate]

I've written a Xamarin.Forms Application that connects to an ESP32 via Bluetooth. Now I'd like to get a value from a CustomControl.JoystickControl from the MainPage.xaml Page.
I've tried it like that:
MainPage.xaml.cs:
public partial class MainPage : ContentPage
{
public static Bluetooth.IBth bth = new Bluetooth.Bth();
public MainPage()
{
InitializeComponent();
Task.Run(async () => bth.Start("mecanumWheelRobot", 200, false));
}
public CustomControls.JoystickControl JoystickControlElement
{
get { return JoystickControl; }
}
public CustomControls.JoystickControl JoystickControlElement1
{
get { return JoystickControl1; }
}
}
Now I'd like to get the JoystickControlElement from the following Async Function:
var mainpage = new Views.MainPage();
string test = mainpage.test;
(these two lines are the problem)
class Bth : IBth
{
private CancellationTokenSource _ct { get; set; }
const int RequestResolveError = 1000;
public Bth()
{
}
public void Start(string name, int sleepTime = 200, bool readAsCharArray = false)
{
Task.Run(async () => loop(name, sleepTime, readAsCharArray));
}
private async Task loop(string name, int sleepTime, bool readAsCharArray)
{
BluetoothDevice device = null;
BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
BluetoothSocket BthSocket = null;
_ct = new CancellationTokenSource();
while (_ct.IsCancellationRequested == false)
{
try
{
Thread.Sleep(sleepTime);
adapter = BluetoothAdapter.DefaultAdapter;
if (adapter == null)
System.Diagnostics.Debug.WriteLine("No Bluetooth adapter found.");
else
System.Diagnostics.Debug.WriteLine("Adapter found!!");
if (!adapter.IsEnabled)
System.Diagnostics.Debug.WriteLine("Bluetooth adapter is not enabled.");
else
System.Diagnostics.Debug.WriteLine("Adapter enabled!");
System.Diagnostics.Debug.WriteLine("Try to connect to " + name);
foreach (var bd in adapter.BondedDevices)
{
System.Diagnostics.Debug.WriteLine("Paired devices found: " + bd.Name.ToUpper());
if (bd.Name.ToUpper().IndexOf(name.ToUpper()) >= 0)
{
System.Diagnostics.Debug.WriteLine("Found " + bd.Name + ". Try to connect with it!");
device = bd;
break;
}
}
if (device == null)
{
System.Diagnostics.Debug.WriteLine("Named device not found.");
MainThread.BeginInvokeOnMainThread(() =>
{
// Code to run on the main thread
});
}
else
{
UUID uuid = UUID.FromString("00001101-0000-1000-8000-00805f9b34fb");
if ((int)Android.OS.Build.VERSION.SdkInt >= 10) // Gingerbread 2.3.3 2.3.4
BthSocket = device.CreateInsecureRfcommSocketToServiceRecord(uuid);
else
BthSocket = device.CreateRfcommSocketToServiceRecord(uuid);
if (BthSocket != null)
{
//Task.Run ((Func<Task>)loop); /*) => {
await BthSocket.ConnectAsync();
if (BthSocket.IsConnected)
{
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastSuccess("Connected!");
// Code to run on the main thread
//pages.bluetoothBarcodeScan.connectedScanner();
});
System.Diagnostics.Debug.WriteLine("Connected!");
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastSuccess("Writing!");
// Code to run on the main thread
//
});
while (_ct.IsCancellationRequested == false)
{
var mainpage = new Views.MainPage();
string test = mainpage.test;
var bytes = Encoding.ASCII.GetBytes("{test}-");
BthSocket.OutputStream.Write(bytes, 0, bytes.Length);
Thread.Sleep(100);
}
System.Diagnostics.Debug.WriteLine("Exit the inner loop");
}
}
else
System.Diagnostics.Debug.WriteLine("BthSocket = null");
}
}
catch(Exception ex)
{
service.toast.toastSuccess(ex.Message);
}
finally
{
if (BthSocket != null)
BthSocket.Close();
device = null;
adapter = null;
}
}
System.Diagnostics.Debug.WriteLine("Exit the external loop");
}
public void Cancel()
{
if (_ct != null)
{
System.Diagnostics.Debug.WriteLine("Send a cancel to task!");
_ct.Cancel();
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastError("Disconnected");
// Code to run on the main thread
});
}
}
public async Task<string> GetData()
{
var mainpage = new Views.MainPage();
string test = mainpage.test;
return "1";
}
public ObservableCollection<string> PairedDevices()
{
BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
ObservableCollection<string> devices = new ObservableCollection<string>();
foreach (var bd in adapter.BondedDevices)
devices.Add(bd.Name);
return devices;
}
#endregion
}
The async function always starts new when it comes to this line.
How should I get the variable?
You can change your constructor method like:
public Bth(string txt)
{}
And then pass the mainpage.test like:
public static Bluetooth.IBth bth = new Bluetooth.Bth(txt);

C# WebAPI Thread Aborting when Sharing Data Access Logic

I'm getting the following error on my C# Web API: "Exception thrown: 'System.Threading.ThreadAbortException' in System.Data.dll
Thread was being aborted". I have a long running process on one thread using my data access logic class to get and update records being process. Meanwhile a user submits another group to process which has need of the same data access logic class, thus resulting in the error. Here is a rough sketch of what I'm doing.
WebAPI Class:
public IHttpActionResult OkToProcess(string groupNameToProcess)
{
var logic = GetLogic();
//Gets All Unprocessed Records and Adds them to Blocking Queue
Task.Factory.StartNew(() => dataAccessLogic.LoadAndProcess(groupNameToProcess);
}
public IHttpActionResult AddToProcess(int recordIdToProcess)
{
StaticProcessingFactory.AddToQueue(recordIdToProcess);
}
StaticProcessingFactory
internal static ConcurrentDictionary<ApplicationEnvironment, Logic> correctors = new ConcurrentDictionary<ApplicationEnvironment, Logic>();
internal static BlockingCollection<CorrectionMessage> MessageQueue = new BlockingCollection<Message>(2000);
public void StartService(){
Task.Factory.StartNew(() => LoadService());
}
public void LoadService(){
var logic = GetLogic();
if(isFirstGroupOkToProcessAsPerTextFileLog())
logic.LoadAndProcess("FirstGroup");
if(isSeconddGroupOkToProcessAsPerTextFileLog())
logic.LoadAndProcess("SecondGroup");
}
public static GetLogic(){
var sqlConnectionFactory = Tools.GetSqlConnectionFactory();
string environment = ConfigurationManager.AppSettings["DefaultApplicationEnvironment"];
ApplicationEnvironment applicationEnvironment =
ApplicationEnvironmentExtensions.ToApplicationEnvironment(environment);
return correctors.GetOrAdd(applicationEnvironment, new Logic(sqlConnectionFactory ));
}
public static void AddToQueue(Message message, bool completeAdding = true)
{
if (MessageQueue.IsAddingCompleted)
MessageQueue = new BlockingCollection<Message>();
if (completeAdding && message.ProcessImmediately)
StartQueue(message);
else
MessageQueue.Add(message);
}
public static void StartQueue(Message message = null)
{
if (message != null)
{
if(!string.IsNullOrEmpty(message.ID))
MessageQueue.Add(message);
Logic logic = GetLogic(message.Environment);
try
{
var messages = MessageQueue.TakeWhile(x => logic.IsPartOfGroup(x.GroupName, message.GroupName));
if (messages.Count() > 0)
MessageQueue.CompleteAdding();
int i = 0;
foreach (var msg in messages)
{
i++;
Process(msg);
}
}
catch (InvalidOperationException) { MessageQueue.CompleteAdding(); }
}
}
public static void Process(Message message)
{
Var logic = GetLogic(message.Environment);
var record = logic.GetRecord(message.ID);
record.Status = Status.Processed;
logic.Save(record);
}
Logic Class
private readonly DataAccess DataAccess;
public Logic(SqlConnectionFactory factory)
{
DataAccess = new DataAcess(factory);
}
public void LoadAndProcess(string groupName)
{
var groups = DataAccess.GetGroups();
var records = DataAccess.GetRecordsReadyToProcess(groups);
for(int i = 0; i < records.Count; i++)
{
Message message = new Message();
message.Enviornment = environment.ToString();
message.ID = records[i].ID;
message.User = user;
message.Group = groupName;
message.ProcessImmediately = true;
StaticProcessingFactory.AddToQueue(message, i + 1 == records.Count);
}
}
Any ideas how I might ensure that all traffic from all threads have access to the Data Access Logic without threads being systematically aborted?

Generic method for database update

I use following method to update orgnrs in a database:
UpdateService service = new UpdateService()
service.UpdateDocsA(orgnrs);
public void UpdateDocsA(List<string> orgnrs)
{
using (var context = new Data.InexchangeEntitiesA())
{
foreach (string orgnr in orgnrs)
{
try
{
Data.Customers customer = new Data.Customers();
customer.CustNo = orgnr.Trim();
context.Customers.Add(customer);
}
catch (Exception ex)
{
}
}
context.SaveChanges();
}
}
Problem is that I have multiple databases with similar update.
service.UpdateDocsA(orgnrs);
service.UpdateDocsB(orgnrs);
service.UpdateDocsC(orgnrs);
service.UpdateDocsC(orgnrs);
The only difference is in following line:
using (var context = new Data.InexchangeEntitiesA())
using (var context = new Data.InexchangeEntitiesB())
using (var context = new Data.InexchangeEntitiesC())
using (var context = new Data.InexchangeEntitiesD())
I want to create a generic update method. Any ideas how can I achieve it? or how to pass Data.InexchangeEntities to a method?
Assuming that the method signature for InexchangeEntitiesA() and InexchangeEntitiesB() etc. is common why not pass that in to your UpdateDocs method?
If we assume those methods all return an IDataContext object which implements a Customers() method;
UpdateService service = new UpdateService()
service.UpdateDocs(Data.InexchangeEntitiesA, orgnrs);
service.UpdateDocs(Data.InexchangeEntitiesB, orgnrs);
service.UpdateDocs(Data.InexchangeEntitiesC, orgnrs);
service.UpdateDocs(Data.InexchangeEntitiesD, orgnrs);
public void UpdateDocs<T>(Func<T> getContext, List<string> orgnrs) where T : IDataContext
{
using (var context = getContext())
{
foreach (string orgnr in orgnrs)
{
try
{
Data.Customers customer = context.Customers();
customer.CustNo = orgnr.Trim();
context.Customers.Add(customer);
}
catch (Exception ex)
{
}
}
context.SaveChanges();
}
}
Or something like this
public class Customer { }
public interface ISomeFunkyInterface
{
DbSet<Customer> Customers { get; set; }
}
public class DbContextA : DbContext, ISomeFunkyInterface
{
public DbSet<Customer> Customers { get; set; }
}
public void UpdateDocs<T>(List<string> orgnrs)
where T : DbContext, ISomeFunkyInterface ,new()
{
using var context = new T();
foreach (string orgnr in orgnrs)
{
context.Customers.Add(...);
}
context.SaveChanges();
}
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesA());
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesB());
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesC());
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesD());
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesE());
...
public void UpdateDocs(List<string> orgnrs, YOURCONTEXTTYPE context)
{
using (context)
{
foreach (string orgnr in orgnrs)
{
try
{
Data.Customers customer = new Data.Customers();
customer.CustNo = orgnr.Trim();
context.Customers.Add(customer);
}
catch (Exception ex)
{
}
}
context.SaveChanges();
}
}
There are multiple ways to achieve that. The most obvious to me is extract what is common and create multiple methods for that:
public void UpdateDocsA(List<string> orgnrs)
{
using (var context = new Data.InexchangeEntitiesA())
{
Perform(context, orgnrs);
}
}
void Perform(DbContext context, List<string> orgnrs)
{
foreach (string orgnr in orgnrs)
{
try
{
Data.Customers customer = new Data.Customers();
customer.CustNo = orgnr.Trim();
context.Customers.Add(customer);
}
catch (Exception ex)
{ }
}
context.SaveChanges();
}
This will reduce the duplicated code massivly. However it still feels ugly, because you still have four Update-methods.
You could also extract what is uncommon and inject a paremeter to switch on:
public void UpdateDocs(string type, List<string> orgnrs)
{
using (var context = GetContext(type))
{
Perform(context, orgnrs);
}
}
DbContext GetContext(string type)
{
return type == "A" ? new Data.InexchangeEntitiesA() :
type == "B" ? new Data.InexchangeEntitiesB() :
type == "C" ? new Data.InexchangeEntitiesC() :
new Data.InexchangeEntitiesD();
}
This does not seem much better, does it? We only have a single method, however it still uses some weird type-switching. Let´s try something more generic - assuming all your entities have some common base-interface or -class:
public void UpdateDocs<T>(List<string> orgnrs) where T: IMyInterface, new()
{
using (var context = new T)
{
Perform(context, orgnrs);
}
}

Reduce Multiple Calls To Database

I have a code a base that iterates through a list of records and does the follwing
'select * from Table to see if fields exist
then later in the interation
'select * from Table to retreive some data
then farther down in the interation
select field1, field2 from Table to get certain pieces of information
I need to do all of these functions for each record. Would the process speed up if I called the query once for each record and hold the data in a datatable and retreive what I need from there? Or is there another more efficient way to not have to make 3 db calls to the same table which will speed up the process?
You can cache query data to System.Data.DataTable. To simplify things I has written CMyDynaset class, which populates DataTable with data from DB. Here how to use it for example with MySQL:
using System;
using System.Data.Common;
using MySql.Data.MySqlClient;
namesapce MyProg
{
class Program
{
private const string strMyConnection = "Host=localhost;Database=mydb;User Id=myuser;Password=mypsw";
static void Main(string[] args)
{
using (MySqlConnection myConnection = new MySqlConnection(strMyConnection))
{
using (MyDb.CMyDynaset dyn = new MyDb.CMyDynaset(myConnection, MySqlClientFactory.Instance))
{
// populate dyn.Table (System.Data.DataTable)
dyn.Init("select * from Table");
dyn.Load();
// access fields
foreach (DataColumn column in dyn.Table.Columns)
{
// ...
}
// get data
long nCountAll = dyn.Table.Rows.Count; // rows count
foreach (DataRow row in dyn.Table.Rows)
{
Object val1 = row[1]; // acess by index
Object val2 = row["id"]; // acess by name
// ...
}
// update data
dyn.Table.Rows[0]["name"] = "ABC";
dyn.Update();
}
}
}
}
}
CMyDynaset class (CMyDynaset.cs):
// CMyDynaset.cs
using System;
using System.Data.Common;
namespace MyDb
{
/// <summary>
/// Summary description for CMyDynaset.
/// </summary>
public class CMyDynaset : IDisposable
{
public System.Data.DataTable Table = null;
// private
private DbConnection myConnection = null;
private DbProviderFactory myFactory = null;
private DbDataAdapter dataAdap = null;
private DbCommandBuilder cmdBld = null;
private bool bIsSchema = false;
public CMyDynaset(DbConnection conn, DbProviderFactory factory)
{
this.myConnection = conn;
this.myFactory = factory;
}
#region IDisposable Members
public void Dispose()
{
if (this.Table != null)
{
this.Table.Dispose();
this.Table = null;
}
if (this.cmdBld != null)
{
this.cmdBld.Dispose();
this.cmdBld = null;
}
if (this.dataAdap != null)
{
this.dataAdap.Dispose();
this.dataAdap = null;
}
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
#endregion
// Init
public void Init(string strSelect)
{
DbCommand cmdSel = this.myConnection.CreateCommand();
cmdSel.CommandText = strSelect;
this.dataAdap = this.myFactory.CreateDataAdapter();
this.dataAdap.SelectCommand = cmdSel;
this.cmdBld = this.myFactory.CreateCommandBuilder();
this.cmdBld.DataAdapter = this.dataAdap;
this.Table = new System.Data.DataTable();
// schema
this.bIsSchema = false;
}
public void AddParameter(string name, object value)
{
DbParameter param = this.dataAdap.SelectCommand.CreateParameter();
param.ParameterName = name;
param.Value = value;
this.dataAdap.SelectCommand.Parameters.Add(param);
}
public void AddParameter(DbParameter param)
{
this.dataAdap.SelectCommand.Parameters.Add(param);
}
// Open, Close
private void Open(ref bool bClose)
{
if (this.myConnection.State == System.Data.ConnectionState.Closed)
{
this.myConnection.Open();
bClose = true;
}
if (!this.bIsSchema)
{ // schema
this.dataAdap.FillSchema(this.Table, System.Data.SchemaType.Mapped);
this.bIsSchema = true;
}
}
private void Close(bool bClose)
{
if (bClose)
this.myConnection.Close();
}
// Load, Update
public void Load()
{
bool bClose = false;
try
{
this.Table.Clear();
this.Open(ref bClose);
this.dataAdap.Fill(this.Table);
}
catch (System.Exception ex)
{
throw ex;
}
finally
{
this.Close(bClose);
}
}
public void Update()
{
bool bClose = false;
try
{
this.Open(ref bClose);
this.dataAdap.Update(this.Table);
}
catch (System.Exception ex)
{
throw ex;
}
finally
{
this.Close(bClose);
}
}
}
}

Categories