Custom DataSet.Table.FindBy method - c#

I have a strongly-typed DataSet that was created using visual studio 2010's Configuration Wizard. I can find a DataRow easily as long as I know the primary key (see How to: Edit Rows in a DataTable).
The problem occurs when I don't know the PK. Is there a way to create a custom method that returns a DataRow if you have a combination of columns that could also be a composite primary key (unique constraint). Using the example from the link, I would like to do something like this:
NorthwindDataSet.CustomersRow customersRow = northwindDataSet1.Customers.FindByCustomerNameAndType("TestCustomerName", "TestCustomerType");
This assumes their Northwind DB Customers Table has two columns (name and type) that could also be a composite key. And the the FindBYCustomerNameAndType method would map to
SELECT *
FROM Customers
WHERE name = "TestCustomerName" AND type = "TestCustomerType"

string whereClause = "name = 'TestCustomerName' and type = 'TestCustomerType'";
DataRow[] x = northwindDataSet1.Customers.Select(whereClause);
if (x.Length > 0){
CustomersRow customersRow = x[0] as CustomersRow;
//other code here
}

Related

Create column from another table dynamically

I'm working with TSQL and C#. I have two queries that return strings:
string[] allSubcategories = dt.AsEnumerable().Select(x => x.Field<string>("SubcategoryName")).Distinct().ToArray();
var redMark = db.GetTableBySQL("SELECT * FROM RedMarkItems");
string[] redMarkColumns = redMark.Columns.Cast<DataColumn>().Select(x => x.ColumnName).ToArray();
So, as you can see I have two different arrays, first I get subcategoriesNames:
and all columns of table RedMarkItems:
That I want to do is to create column dynamically, I mean, if subcategorieName does not exist as column in RedMarkItems do an Update and create it someting like:
var createColumn = db.ExeSQL($"ALTER TABLE RedMarkItems ADD {ColumnName} BIT");
How can I compare if subcategorieName does not exist as column in RedMarkItems table? Then create column as my query? Regards
If you want to know if a particular column exists in an already filled DataTable using the Linq approach then it is just:
bool exists = redMark.Columns.Cast<DataColumn>().Any(x => x.ColumnName == "SubCategoryName");
Instead, if you want to ask this info directly to the database then use the INFORMATION_SCHEMA views The Columns view is the one to use with a query like this.
string query = #"IF EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.Column
WHERE Column_Name = #colName)
SELECT 1 ELSE SELECT 0";
SqlCommand cmd = new SqlCommand(query, connection);
cmd.Parameters.Add("#colName", SqlDbType.NVarChar).Value = "SubCategoryName";
bool exists = (cmd.ExecuteScalar() == 1);
Now, the part about creating the column is pretty simple as code per se. It is just an appropriate ALTER TABLE. But there are a lot of things to be cleared before. What will be the datatype of the new column? What will be its length and precision? What will be the constraints applied to it (Null/Not Null defaults etc)? As you can see all these info are very important and require to be defined somewhere in your code.

DataTables: restrictionValues and table names

I have got a single function using OleDb:
I think maybe the use of a list to find out if a specific table exist could be made better, like this:
Check for MS Access database table if not exist create it
But I also want to understand the old code given, to learn something.
Questions:
1) What does the exact restrictionValues mean in the example code below? ( not solved)
2) Why row.ItemArray[2] of all cells is containing the table names? (solved)
3) Is there a better way to get table names out of a database? (solved)
This is the code I have got:
public List<string> GetTableNames(string tableName, string[] field_names)
{
List<string> retTableNames = new List<string>();
if (dbConnection != null)
{
dbConnection.Open();
string strSQL = "SELECT * ";
string[] restrictionValues = new string[4] { null, null, null, "TABLE" };
OleDbCommand cmd = new OleDbCommand(strSQL, dbConnection);
try
{
/*search after all possible tables in dataset*/
DataTable schemaInformation = dbConnection.GetSchema("Tables", restrictionValues);
foreach (DataRow row in schemaInformation.Rows)
{
retTableNames.Add(row.ItemArray[2].ToString());
}
}
catch
{
retTableNames = null;
}
}
return retTableNames;
}
(I just noticed that you said you already get this first part, but I'll leave it in anyway. There's a little more below about "TABLE".)
Some of the explanation of the restrictions is in the Main program in the link that #jdweng gave.
// You can specify the Catalog, Schema, Table Name, Table Type to get
// the specified table(s).
// You can use four restrictions for Table, so you should create a 4 members array.
String[] tableRestrictions = new String[4];
// For the array, 0-member represents Catalog; 1-member represents Schema;
// 2-member represents Table Name; 3-member represents Table Type.
// Now we specify the Table Name of the table what we want to get schema information.
tableRestrictions[2] = "Course";
DataTable courseTableSchemaTable = conn.GetSchema("Tables", tableRestrictions);
The rest of the explanation is in the overload of GetSchema: GetSchema Method (String, String[]), which is what you're using.
Verbatim:
In order to set values on a given restriction, and not set the values of other restrictions, you need to set the preceding restrictions to null and then put the appropriate value in for the restriction that you would like to specify a value for.
An example of this is the "Tables" collection. If the "Tables" collection has three restrictions -- database, owner, and table name--and you want to get back only the tables associated with the owner "Carl", you need to pass in the following values: null, "Carl". If a restriction value is not passed in, the default values are used for that restriction. This is the same mapping as passing in null, which is different from passing in an empty string for the parameter value. In that case, the empty string ("") is considered to be the value for the specified parameter.
A more complete article on schema restrictions.
It looks like you can omit some parameters, which is why the example above only lists 3 restrictions.
I've been looking for an explanations of the "TABLE" parameter but it's hard to find. It's either a default that gets all tables, or it's ignored, or something else. The easiest way to get all table types might be to do a basic DataTable table = connection.GetSchema("Tables"); then get the types of each table to see what the options are. Otherwise, sticking to "TABLE" will no doubt get the commonly used tables, not system tables or anything like that.
Hope your ears are ok.

Avoid duplication in DataTable query and build

What would be the right way to avoid duplication when querying datatable and then saving it to DataTable. I'm using the pattern below, which gets very error-prone once tables grow. I looked at below hints. With first one copyToDataTable() looks not really applicable and second is for me much too complex for the task. I would like to split the below code into 2 separate methods (first to build the query and second to retrieve the DataTable). Perhaps if I avoid the anonymous type in the query this should be easier to avoid hardcoding all the column names - but I'm somehow lost with this.
Filling a DataSet or DataTable from a LINQ query result set
or
https://msdn.microsoft.com/en-us/library/bb669096%28v=vs.110%29.aspx
public DataTable retrieveReadyReadingDataTable()
{
DataTable dtblReadyToSaveToDb = RetrieveDataTableExConstraints();
var query = from scr in scrTable.AsEnumerable()
from products in productsTable.AsEnumerable()
where(scr.Field<string>("EAN") == products.Field<string>("EAN"))
select
new
{
Date = DateTime.Today.Date,
ProductId = products.Field<string>("SkuCode"),
Distributor = scr.Field<string>("Distributor"),
Price = float.Parse(scr.Field<string>("Price")),
Url = scr.Field<string>("Url")
};
foreach (var q in query)
{
DataRow newRow = dtblReadyToSaveToDb.Rows.Add();
newRow.SetField("Date", q.Date);
newRow.SetField("ProductId", q.ProductId);
newRow.SetField("Distributor", q.Distributor);
newRow.SetField("Price", q.Price);
newRow.SetField("Url", q.Url);
}
return dtblReadyToSaveToDb;
}
Firstly, you have to decide what "duplicate" means in your case. According to your code i would say a duplicate is a row with the same value in column Date, ProductId and Distributor. So add a multi column primary key for those columns first.
Secondly, you should add some sort of code that first queries existing rows and then compares these existing rows to the rows you want to create. If a match is found, then simply just don't insert a new row.

Column [key] throws MissingPrimaryKeyException in dataTable

var sdr = db.ExecuteReader("SELECT [key],translation FROM language WHERE lang=#lang");
DataTable someTable = new DataTable();
someTable.Load(sdr);
return ((string)(someTable.Rows.Find("[key] = CUSTOMDATEFORMAT").ItemArray[1]));
The last line now throws the error System.Data.MissingPrimaryKeyException: "The table has no primary key."
So I guess it finds the keyword "key" and now expects me to query on the primary key column, not the column named "key". How do I have to escape the column name?
If you want to use DataRowCollection.Find you have to specify the DataTable's PrimaryKey(s).
I would use Linq-To-DataTable:
DataRow row = someTable.AsEnumerable()
.FirstOrDefault(row => row.Field<string>("key") == "CUSTOMDATEFORMAT");
if(row != null)
return row.Field<string>(1); // or by name but with correct type
else
return "something else";
Then you can use all methods you want in the query.
The Find method is used to find a single rows by the primary key value. Use Select to find rows by arbitrary column values:
DataRow[] foundRows;
foundRows = someTable.Select("[key] = CUSTOMDATEFORMAT");
return foundRows[0].ItemArray[1]; // adding proper bounds checking, of course.
Find method is using the column that is specified in the DataTable object as primary key and return a row that contains the value it gets inside the primary key cell.
From MSDN:
Gets the row specified by the primary key value.
Since your table doesn't have a column that is specified as a primary key, you get this error.
If your key column is guaranteed to not contain duplicates, you can specify it as the primary key using the DataTable's PrimaryKey property.
If not, then use Linq's Select method instead of find:
return ((string)(someTable.Rows.Select("[key] = CUSTOMDATEFORMAT").ItemArray[1]));

Get all tables in an Access database by ordinal position

I am coding in C# against an Access database...
I am able to get all of the tables in my access database with the following:
DataTable dataTbl;
DataView dvCols;
DataRowView drvCols;
...
dataTbl = m_connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables,
new Object[] { null, null, null, "TABLE" });
dvCols = new DataView(dataTbl);
for (int j = 0; j < dvCols.Count; j++)
{
drvCols = dvCols[j];
tableName = drvCols.Row["TABLE_NAME"].ToString();
// ...
}
The problem is that this code gets the table names in alphabetical order. Now if I were reading the columns from particular table I could sort by ordinal position using
dvCols.Sort = "ORDINAL_POSITION";
However this is not possible with tables, there really is no ordinal position I guess. My tables have relationships, e.g. Table A has a one to many relationship to Table B which has a one to many relationship to Table C, etc... So I need to get the table names in that order, e.g. A then B then C.
Is there any way to do this?
Do I need to somehow examine the primary and foreign keys and figure out this information myself?
Ok..figured it out.
What I ended up doing was the following:
dataTable_ForeignKeys =
m_connection.GetOleDbSchemaTable(OleDbSchemaGuid.Foreign_Keys, null);
//Check if a DataTable object is initialized
if (dataTable_ForeignKeys != null)
{
// iterate and get the names
foreach (DataRow row in dataTable_ForeignKeys.Rows)
{
tableName = row["PK_TABLE_NAME"].ToString();
tblNames.Add(tableName);
To get a list of the foreign key relationships. This returned the tables in the order I needed, i.e. 'parent' tables first. If a table is not the 'parent' of another table, i.e. it does not have a field that is the foreign key in another table then it will not be returned. I reconciled this by also getting all database table names as I did in my original post.
If you want the tables ordered according to their relationships, then, yes, you will have to implement that yourself (or find a third-party tool; I'm not aware of any).
Note that you will need some strategy for handling cycles in the relationships.

Categories

Resources