So I have a method that looks something like the following:
private static DataSet GetData()
{
DataSet returnValue = new DataSet();
try
{
//get all relevant tables here, add them to returnValue
}
catch (ArgumentException e)
{
//if a table isn't whitelisted, trying to grab it will throw an ArugmentException.
}
return returnValue;
}
Now, I want to pass along the caught exceptions. However, if for example 2 tables are whitelisted, but 1 isn't, I still want those two tables to be returned in the DataSet. I've been thinking I should do something like:
DataSet returnValue = new DataSet();
//TablesToFetch == string list or something containing tablenames you want to fetch
foreach (string tableName in tablesToFetch)
{
try
{
//get table tableName and add it to returnValue
}
catch (ArgumentException e)
{
//handle exception
}
}
return returnValue;
However, the problem here is that I can't just throw the exceptions I find, because then the DataSet won't be returned. The first solution I can think of is to "bundle" the exceptions and throw them one by one later, outside of the method*, but it kind of strikes me as a bit of a messy solution. Anyone have any tips on how to handle this, or should I just go ahead with the solution I just proposed?
*I could just wrap the method in another method, which calls a "handle all exceptions"-method after calling GetData()
This depends very much on the circumstances... I like an approach like this:
public returnType MyMethod([... parameters ...], out string ErrorMessage){
ErrorMessage=null;
try{
doSomething();
return something;
}
catch(Exception exp){
ErrorMessage=exp.Message;
return null; //
}
}
Instead of the out string you could create your own supi-dupi-ErrorInformation class. You just call your routine and check, wheter the ErrorMessage is null. If not, you can react on the out passed values. Maybe you want just to pass the exception out directly...
If you can't solve 'the problem' within the method, you should throw an Exception. You shouldn't return a DataSet when an exception occurred (you didn't handled). So either you return an Exception or you return a DataSet and handle the exception within the method.
It is possible to Aggregate the exceptions like:
private DataSet GetData(IEnumerable<string> tablesToFetch)
{
var exceptions = new List<Exception>();
DataSet returnValue = new DataSet();
//TablesToFetch == string list or something containing tablenames you want to fetch
foreach (string tableName in tablesToFetch)
{
try
{
//get table tableName and add it to returnValue
}
catch (ArgumentException e)
{
//handle exception
exceptions.Add(e);
}
}
if (exceptions.Count > 0)
throw new AggregateException(exceptions);
return returnValue;
}
Another appoach is return a class with results:
public class GetDataResult
{
public GetDataResult(DataSet dataSet, string[] missingTables)
{
DataSet = dataSet;
MissingTables = missingTables;
}
public string[] MissingTables { get; private set; }
public DataSet DataSet { get; private set; }
}
private GetDataResult GetData(IEnumerable<string> tablesToFetch)
{
List<string> missingTables = new List<string>();
DataSet returnValue = new DataSet();
//TablesToFetch == string list or something containing tablenames you want to fetch
foreach (string tableName in tablesToFetch)
{
try
{
//get table tableName and add it to returnValue
}
catch (ArgumentException e)
{
//handle exception
missingTables.Add(tableName);
}
}
return new GetDataResult(returnValue, missingTables.ToArray());
}
usage:
var result = GetData(new[] { "MyTable1", "MyTable2" });
if(result.MissingTables.Count > 0)
{
Trace.WriteLine("Missing tables: " + string.Join(", ", result.MissingTables));
}
// do something with result.DataSet
update from comments
I don't know much about the structure you're using, so this is pseudo code
// PSEUDO!
private DataTable GetTable(string tableName)
{
// if table isn't found return null
if(<table is found>)
return table;
else
return null;
}
private GetDataResult GetData(IEnumerable<string> tablesToFetch)
{
List<string> missingTables = new List<string>();
DataSet returnValue = new DataSet();
//TablesToFetch == string list or something containing tablenames you want to fetch
foreach (string tableName in tablesToFetch)
{
var table = GetTable(tableName);
if(table == null)
{
missingTables.Add(tableName);
continue;
}
// do something with the table.
}
return new GetDataResult(returnValue, missingTables.ToArray());
}
Related
I need to read a List that has two properties, one is an ID int, the other is string.
After I get the values into the list I don't know how to break them down to the the IDs and name string one by one.
This is what I've got:
private async void UpdatePisterosLocal(List<Pisteros> PisterosLista)
{
try
{
PisterosDBController pistDB = new PisterosDBController();
Pisteros_Local pistLocal = new Pisteros_Local();
//this is my code trying to read the list PisterosLista
foreach (string element in PisterosLista)
{
pistLocal.IDPistero = //don't know what to write here
pistLocal.PisteroN = //and here
}
}
catch (Exception ex)
{
throw ex;
}
}
This how I finally did it
foreach (var item in PisterosLista)
{
var DatosRegistro = new T_Pisteros
{
PisteroID = item.PisteroID,
PisteroN = item.PisteroN
};
var num = db.Insert(DatosRegistro); //insert into new DB
}
Searching, I found the PRAGMA as a possible solution for my problem, but it only returns the index of each column. There's any other method to return all columns names?
I thought using a For to go through my column indexes returning their names would works fine, but I dont exactly know how the syntax of this would be, either the stop condition.
void FillColumnList()
{
try
{
string check = "SELECT * FROM PRAGMA table_info(Produtos)";
sqlCon.Open();
SQLiteCommand tst2 = new SQLiteCommand(check, sqlCon);
SQLiteDataReader rdr2 = tst2.ExecuteReader();
if (rdr2.HasRows)
{
while (rdr2.Read())
{
string columns = rdr2[0].ToString();
Columns.Add(columns);
}
sqlCon.Close();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
This code should return and fill the Global variable list Columns with the name of each column of "Produtos" table. Instead of it, my DataReader 'rdr2' return false in the HasRows, even when there's columns and Datas in my table "Produtos"
You can use the connection's GetSchema method to retrieve the column information. I'm using the following code to insert information my own class TableColumn not shown here:
string[] restrictions = new string[] { null, null, tableName };
using (DataTable columns = conn.GetSchema("Columns", restrictions)) {
int nameIndex = columns.Columns.IndexOf("COLUMN_NAME");
int ordinalPosIndex = columns.Columns.IndexOf("ORDINAL_POSITION");
int isNullableIndex = columns.Columns.IndexOf("IS_NULLABLE");
int maxLengthIndex = columns.Columns.IndexOf("CHARACTER_MAXIMUM_LENGTH");
int dataTypeIndex = columns.Columns.IndexOf("DATA_TYPE");
int isPrimaryKeyIndex = columns.Columns.IndexOf("PRIMARY_KEY");
int hasDefaultIndex = columns.Columns.IndexOf("COLUMN_HASDEFAULT");
int defaultValueIndex = columns.Columns.IndexOf("COLUMN_DEFAULT");
foreach (DataRow row in columns.Rows) {
var col = new TableColumn {
ColumnName = (string)row[nameIndex]
};
try {
col.ColumnNameForMapping = prepareColumnNameForMapping(col.ColumnName);
} catch (Exception ex) {
throw new UnimatrixExecutionException("Error in delegate 'prepareColumnNameForMapping'", ex);
}
col.ColumnOrdinalPosition = (int)row[ordinalPosIndex];
col.ColumnAllowsDBNull = (bool)row[isNullableIndex];
col.ColumnMaxLength = (int)row[maxLengthIndex];
string explicitDataType = ((string)row[dataTypeIndex]).ToLowerInvariant();
col.ColumnDbType = GetColumnDbType(explicitDataType);
col.ColumnIsPrimaryKey = (bool)row[isPrimaryKeyIndex];
col.ColumnIsIdentity = explicitDataType == "integer" && col.ColumnIsPrimaryKey;
col.ColumnIsReadOnly = col.ColumnIsIdentity;
if ((bool)row[hasDefaultIndex]) {
col.ColumnDefaultValue = GetDefaultValue(col.ColumnDbType, (string)row[defaultValueIndex]);
if (col.ColumnDefaultValue == null) { // Default value could not be determined. Probably expression.
col.AutoAction = ColumnAction.RetrieveAfterInsert;
}
}
tableSchema.ColumnSchema.Add(col);
}
}
You can simplify this code considerably if you only need the column names.
I have a c# function that reads file locations from a Datatable, and returns a List with all the file lcoations to the calling method.
In the Catch block, I want to return an empty list with a false so teh calling method can cancel it's operation.
But I can't get my return statement to compile.
Would it be better to pass in a list as a refernce, and have the function return a boolean true/false?
here is the code I am trying:
public static List<string> getEmailAttachments(string emailID, System.Data.DataTable emails)
{
List<string> allAttachments;
//System.Data.DataTable oTbl = new DataTable();
try
{
System.Diagnostics.Debugger.Break();
var results = from myRow in emails.AsEnumerable()
where myRow.Field<string>("itemID") == emailID
select myRow;
System.Diagnostics.Debug.Print("attachments");
foreach (DataRow myRow in results)
{
System.Diagnostics.Debug.Print(myRow.Field<string>("attachmentsPath"));
allAttachments.Add(myRow.Field<string>("attachmentsPath"));
//DataTable dt = (DataTable)myRow["attachmentsPath"];
//DataTable oTbl = dt.Clone();
//DataRow[] orderRows = dt.Select("CustomerID = 2");
//foreach (DataRow dr in orderRows)
//{
// oTbl.ImportRow(dr);
//}
// myTable.ImportRow(dr);
//oTbl.Rows.Add(myRow);
//oTbl.ImportRow(myRow);
}
return allAttachments;
}
catch (Exception ex)
{
logBuilder("common.getEmailAttachments", "Exception", "", ex.Message, "");
return new List<string>emptyList(); // cannot compile
}
}
If someone still looking...
Use IEnumerable<string> as return type and:
return Enumerable.Empty<string>();
Change this line:
return new List<string>emptyList(); // cannot compile
to:
return new List<string>();
Passing a list as a refernce, and returning a boolean value from the function, it is a bad idea. Your method called getEmailAttachments, it's load attachments, and it should return attachments. If you want to check the result of loading attachments, i can suggest you return null and check the returned value.
Use
return new List<string>();
I would take a slightly different approach.
I would return an empty list but ALSO set the initial capacity to ZERO!
Like this:
return new List<string>(0);//notice the initial capacity to zero.
The reason is for memory consumption and optimization... I know it is a micro optimization but it won't hurt anything. It might actually benefit the overall application.
A better way has been added since around .Net Framework 4.6 and in the Core .Net versions,
return Array.Empty<T>();
This allocates a single array (which is an IList) and reuses it for all subsequent requests for an empty array of that type. Its fast, and clean.
try this..
public static List<string> getEmailAttachments(string emailID, System.Data.DataTable emails)
{
List<string> allAttachments;
//System.Data.DataTable oTbl = new DataTable();
try
{
System.Diagnostics.Debugger.Break();
var results = from myRow in emails.AsEnumerable()
where myRow.Field<string>("itemID") == emailID
select myRow;
System.Diagnostics.Debug.Print("attachments");
foreach (DataRow myRow in results)
{
System.Diagnostics.Debug.Print(myRow.Field<string>("attachmentsPath"));
allAttachments.Add(myRow.Field<string>("attachmentsPath"));
//DataTable dt = (DataTable)myRow["attachmentsPath"];
//DataTable oTbl = dt.Clone();
//DataRow[] orderRows = dt.Select("CustomerID = 2");
//foreach (DataRow dr in orderRows)
//{
// oTbl.ImportRow(dr);
//}
// myTable.ImportRow(dr);
//oTbl.Rows.Add(myRow);
//oTbl.ImportRow(myRow);
}
//return allAttachments;
}
catch (Exception ex)
{
logBuilder("common.getEmailAttachments", "Exception", "", ex.Message, "");
allAttachments= new List<string>();
}
return allAttachments;
}
what about
allAttachments.Clear();
return allAttachments;
private void SaveButton_Click(object sender, EventArgs e)
{
try
{
if (CashPaymentGridView.Rows.Count > 1)
{
CashPaymentandReceivedE cashpament =new CashPaymentandReceivedE();
List<CashPaymentandReceivedE> cashpaymentList = new List<CashPaymentandReceivedE>();
foreach ( DataGridViewRow rows in CashPaymentGridView.Rows)
{
if (rows.IsNewRow )
{
break ;
}
cashpament.VR_NO= Convert.ToInt16(VoucherNoTextBox.Text);
cashpament.VR_DATE = VrDate.Value ;
cashpament.ETYPE = "CPV";
cashpament.USER_ID = "1";
cashpament.PARTY_ID= Convert.ToString (rows.Cells[2].Value) ;
cashpament.DESCRIPTION = Convert.ToString ( rows.Cells[3].Value);
cashpament.INVOICE = Convert.ToString(rows.Cells[4].Value);
cashpament.DEBIT = Convert.ToInt32(rows.Cells[5].Value);
cashpament.CREDIT = 0;
cashpaymentList.Add(cashpament);
cashpament = new CashPaymentandReceivedE();
cashpament.VR_NO =Convert.ToInt16(VoucherNoTextBox.Text);
cashpament.VR_DATE = VrDate.Value;
cashpament.ETYPE = "CPV";
cashpament.USER_ID = "1";
cashpament.PARTY_ID = NewAccountsDAL.Get_Id_Name ("CASH");
cashpament.DESCRIPTION = Convert.ToString(rows.Cells[3].Value);
cashpament.INVOICE = Convert.ToString(rows.Cells[4].Value);
cashpament.CREDIT = Convert.ToInt32(rows.Cells[5].Value);
cashpament.DEBIT = 0;
cashpaymentList.Add(cashpament);
}
if (CashPaymentandReceivedDAL.Save(cashpaymentList))
{
MessageBox.Show("SAVE SUCCESSFULLY...............");
ResetForm();
}
}
else
{
MessageBox.Show ("Please select atleast one record.....");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message );
}
}
Stored Procedure for saving data is given as.
public static bool Save(List <CashPaymentandReceivedE> cashreceivedpayment)
{
bool blnResult = false;
SqlConnection objSqlConnection = new SqlConnection(ConnectionString.Connection);
//SqlTransaction objSqlTransaction = null;
try
{
objSqlConnection.Open();
//objSqlTransaction = objSqlConnection.BeginTransaction();
int R = 0;
while (R < cashreceivedpayment.Count )
{
SqlCommand objSqlCommand = new SqlCommand("CASHRECEIVED_Save", objSqlConnection);
objSqlCommand.CommandType = CommandType.StoredProcedure;
//SqlParameter objIdentityParameter = objSqlCommand.Parameters.Add("#PLED_ID", SqlDbType.BigInt);
//objIdentityParameter.Direction = ParameterDirection.Output;
//objSqlCommand.Parameters.AddWithValue("#PLED_ID", cashreceivedpayment[R].PLED_ID);
objSqlCommand.Parameters.AddWithValue("#COMPANY_ID", "1");
objSqlCommand.Parameters.AddWithValue("#PARTY_ID", cashreceivedpayment[R].PARTY_ID);
objSqlCommand.Parameters.AddWithValue("#VR_NO", cashreceivedpayment[R].VR_NO);
objSqlCommand.Parameters.AddWithValue("#ETYPE", cashreceivedpayment[R].ETYPE);
objSqlCommand.Parameters.AddWithValue("#VR_DATE", cashreceivedpayment[R].VR_DATE);
objSqlCommand.Parameters.AddWithValue("#DESCRIPTION", cashreceivedpayment[R].DESCRIPTION);
objSqlCommand.Parameters.AddWithValue("#DEBIT", cashreceivedpayment[R].DEBIT);
objSqlCommand.Parameters.AddWithValue("#CREDIT", cashreceivedpayment[R].CREDIT);
objSqlCommand.Parameters.AddWithValue("#USER_ID", cashreceivedpayment[R].USER_ID);
//objSqlCommand.Parameters.AddWithValue("#COMPNAY_ID", cashreceivedpayment[R].COMPANY_ID);
objSqlCommand.Parameters.AddWithValue("#DESCRIPTION2", "DESCRIPTION2");
objSqlCommand.Parameters.AddWithValue("#INVOICE", cashreceivedpayment[R].INVOICE);
objSqlCommand.ExecuteNonQuery();
R++;
blnResult = true;
}
}
catch (Exception ex)
{
//objSqlTransaction.Rollback();
MessageBox.Show(ex.Message);
}
finally
{
//objSqlTransaction.Commit();
objSqlConnection.Close();
}
return blnResult;
}
When i save the record, one record should be of Party_id and one should be of cash.
but when i select more than one record just one entry saving to cash. when when i load the record jst one record is loaded.plz help if u understand.....
You're creating one CashPaymentandReceivedE object, and adding the reference to it on each iteration of the list. You're then also changing all the data within that single object on each iteration. Just move this line:
CashPaymentandReceivedE cashpament =new CashPaymentandReceivedE();
... inside your foreach statement and the problem should be resolved.
Before you do so though, make sure you understand why your code is behaving like this. It's really important to understand that the list doesn't contain objects - it contains references to objects. In your case, it would contain several references to a single object, until you fix it.
I'd also strongly suggest using a foreach in your Save method - or if you really need the index for some reason, use a for loop instead of a while.
I have several strongly typed datasets throughout my application. Writing methods to update the data is getting tedious as each has several tables. I want to create one generic function that I can update all of the tables easily. I don't mind if I have to create one of these for each DataSet but if one function could handle all of them, that would be amazing!
There will be any number of new, updated, or deleted records and each row should be flagged properly. This function should just be handling the actual saving. Here is what I have so far:
private bool SaveData(object oTableAdaptor, object ds)
{
try
{
Type oType = oTableAdaptor.GetType();
MethodInfo[] oMethodInfoArray = oType.GetMethods();
foreach (MethodInfo oMI in oMethodInfoArray)
{
if (oMI.Name == "Update")
{
ParameterInfo[] oParamaterInfoArray = oMI.GetParameters();
foreach (ParameterInfo oPI in oParamaterInfoArray)
{
Type DsType = null;
if (oPI.ParameterType.Name == "NameOfDataSet")
{
DsType = typeof(MyDataSet);
// get a list of the changed tables???
}
if (((DataSet)ds).HasChanges() == true)
{
if (oPI.ParameterType == DsType)
{
object[] values = { ds };
try
{
oMI.Invoke(oTableAdaptor, values);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(oTableAdaptor.GetType().Name + Environment.NewLine + ex.Message);
}
}
}
}
}
}
}
catch (Exception Exp)
{
System.Diagnostics.Debug.WriteLine(Exp.Message);
if (Exp.InnerException != null) System.Diagnostics.Debug.WriteLine(Exp.InnerException.Message);
return false;
}
return true;
I have adapted this from another bit of code another developer has in a different application. The main difference thus far is he is passing in an array (of type object) of dataadaptors and has each of the three DataSets (globally instantiated) set up as individual if blocks inside the foreach (ParameterInfo oPI in oParamaterInfoArray) block (where my 'NameOfDataSet' would be one of the datasets)
Can anybody give me a little push (or a shove?) in the direction of finishing this function up? I know I am right there but it feels like I am over looking something. This code does compile without error.
I've been using this. It would need some optimizations though. This also takes care of updating the tables in correct order depending on the relations in dataset (in case there are no self-references, which can be handled by sorting the rows, but for simplicity I'm not posting it here).
public static void Save(DataSet data, SqlConnection connection)
{
/// Dictionary for associating adapters to tables.
Dictionary<DataTable, SqlDataAdapter> adapters = new Dictionary<DataTable, SqlDataAdapter>();
foreach (DataTable table in data.Tables)
{
/// Find the table adapter using Reflection.
Type adapterType = GetTableAdapterType(table);
SqlDataAdapter adapter = SetupTableAdapter(adapterType, connection, validityEnd);
adapters.Add(table, adapter);
}
/// Save the data.
Save(data, adapters);
}
static Type GetTableAdapterType(DataTable table)
{
/// Find the adapter type for the table using the namespace conventions generated by dataset code generator.
string nameSpace = table.GetType().Namespace;
string adapterTypeName = nameSpace + "." + table.DataSet.DataSetName + "TableAdapters." + table.TableName + "TableAdapter";
Type adapterType = Type.GetType(adapterTypeName);
return adapterType;
}
static SqlDataAdapter SetupTableAdapter(Type adapterType, SqlConnection connection)
{
/// Set connection to TableAdapter and extract SqlDataAdapter (which is private anyway).
object adapterObj = Activator.CreateInstance(adapterType);
SqlDataAdapter sqlAdapter = (SqlDataAdapter)GetPropertyValue(adapterType, adapterObj, "Adapter");
SetPropertyValue(adapterType, adapterObj, "Connection", connection);
return sqlAdapter;
}
static object GetPropertyValue(Type type, object instance, string propertyName)
{
return type.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance).GetValue(instance, null);
}
static void SetPropertyValue(Type type, object instance, string propertyName, object propertyValue)
{
type.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Instance).SetValue(instance, propertyValue, null);
}
static void Save(DataSet data, Dictionary<DataTable, SqlDataAdapter> adapters)
{
if (data == null)
throw new ArgumentNullException("data");
if (adapters == null)
throw new ArgumentNullException("adapters");
Dictionary<DataTable, bool> procesedTables = new Dictionary<DataTable, bool>();
List<DataTable> sortedTables = new List<DataTable>();
while (true)
{
DataTable rootTable = GetRootTable(data, procesedTables);
if (rootTable == null)
break;
sortedTables.Add(rootTable);
}
/// Updating Deleted rows in Child -> Parent order.
for (int i = sortedTables.Count - 1; i >= 0; i--)
{
Update(adapters, sortedTables[i], DataViewRowState.Deleted);
}
/// Updating Added / Modified rows in Parent -> Child order.
for (int i = 0; i < sortedTables.Count; i++)
{
Update(adapters, sortedTables[i], DataViewRowState.Added | DataViewRowState.ModifiedCurrent);
}
}
static void Update(Dictionary<DataTable, SqlDataAdapter> adapters, DataTable table, DataViewRowState states)
{
SqlDataAdapter adapter = null;
if (adapters.ContainsKey(table))
adapter = adapters[table];
if (adapter != null)
{
DataRow[] rowsToUpdate = table.Select("", "", states);
if (rowsToUpdate.Length > 0)
adapter.Update(rowsToUpdate);
}
}
static DataTable GetRootTable(DataSet data, Dictionary<DataTable, bool> procesedTables)
{
foreach (DataTable table in data.Tables)
{
if (!procesedTables.ContainsKey(table))
{
if (IsRootTable(table, procesedTables))
{
procesedTables.Add(table, false);
return table;
}
}
}
return null;
}
static bool IsRootTable(DataTable table, Dictionary<DataTable, bool> procesedTables)
{
foreach (DataRelation relation in table.ParentRelations)
{
DataTable parentTable = relation.ParentTable;
if (parentTable != table && !procesedTables.ContainsKey(parentTable))
return false;
}
return true;
}
Can't you just treat them as their base classes, DbDataAdapter, DataSet and DataTable?
You can access the table by name by doing DataSet.Tables["name"]. This returns a DataTable object that you can pass to the DbDataAdapters update method.
Or if your TableAdapter updates all the tables in your DataSet then you can pass the entire DataSet to the update method directly.
With that said I would suggest you rethink the use of typed data sets if you have the chance to do so. In my experience they end up being a hassle to maintain and use and have found the general DataTable, DataSet and DbDataAdapter classes to be much easier to use directly.
Do you really want reflection to be used that much in your DAL? Perhaps an ORM such as LINQ to SQL or NHibernate would be a good alternative?