Linq grouping query to DataTable - c#

Trying to return a Linq group by query into a DataTable. Getting the error
Cannot implicitly convert type
'System.Collections.Generic.IEnumerable<AnonymousType#1>' to
'System.Collections.Generic.IEnumerable<System.Data.DataRow>'. An
explicit conversion exists (are you missing a cast?)
The I am querying a DataTable named Vendors where the data would be as follows:
Vendor Name
654797 Lowes
897913 Home Depot
800654 Waffle House
The Vendor is stored as char(6) in the DB and name as char as well... don't ask me why, I just work here :)
DataTable VendorsDT = New DataTable();
DataColumn VenName = VendorsDT.Columns.Add("Name", typeof (string));
DataColumn VenCode = VendorsDT.Columns.Add("Vendor", typeof(string));
IEnumerable<DataRow> Vendors = from row in alertsDT.AsEnumerable()
group row by new { Name = row.Field<string>("Name"), Vendor = row.Field<string>("Vendor") } into z
select new
{
Name = z.Key.Name,
Vendor = z.Key.Vendor,
};
VendorsDT = Vendors.CopyToDataTable();

This is not going to work, as you are projecting your original query of alertsDT into an anonymous type, which would not be able to be referenced by your IEnumerable<DataRow> Vendor variable, because your query is not a sequence of DataRows.
Given that you are performing a grouping, and that you have also already set up your VendorsDT table with the desired columns, the path of least resistance is to fix your Vendor variable type (use var for type inference) and then loop over the result to populate your second table.
var Vendors = /* your unchanged query omitted */
foreach (var item in Vendors)
{
VendorsDT.Rows.Add(item.Name, item.Vendor);
}
As a note, I've used your variable names, although it is convention in C# to use lower case letters to start local variable names, with upper case typically left for method names, properties, etc. So you would favor vendors over Vendors and vendorsDT (or vendorsTable) over VendorsDT, for example.

This is a more linq way of doing
var query = from row in alertsDT.AsEnumerable()
group row by new { Name = row.Field<string>("Name"), Vendor = row.Field<string>("Vendor") } into z
select new
{
Name= z.Key.Name,
Vendor= z.Key.Vendor,
};
VendorsDT = query.CopyToDataTable();
Here define the extension method 'CopyToDataTable()' as specified in the following MSDN article.
The CopyToDataTable method takes the results of a query and copies the data into a DataTable, which can then be used for data binding. The CopyToDataTable methods, however, only operate on an IEnumerable source where the generic parameter T is of type DataRow. Although this is useful, it does not allow tables to be created from a sequence of scalar types, from queries that project anonymous types, or from queries that perform table joins.

Related

Get columns name from a table in database

in my database i have many of tables one of Tables is nambed Company". and its columns names are as follows ("ComName","ComAddress","ComType" ..etc)
I have a function which get the columns name from Company table :
public List<string> GetColumnNames(string tablename)
{
var names = typeof(Company).GetProperties().Select(p => p.Name).ToArray();
return names.ToList();
}
Until now everything is good but what if i want to get the columns of other tables ? so i have to put the name of the new table (that is given in the function as "tablename") instead of "Company" in this sentence :
var names = typeof(Company).GetProperties().Select(p => p.Name).ToArray();
but it is string so its not working. any idea ?
typeof(Company).GetProperties() isn't fetching the names of the columns of your table at all though - it's fetching the names of the properties of the C# class/entity that you're using to represent that table. Do you already have a process to guarantee you have C# entities with property names that exactly match the column names of your tables? If so, you can do the same principle (but instead of using typeof(Company) you'd need to find the type by name, which there are various ways of doing). If not, you'll need to to actually query the database to find the column names.
As far as i know, you can use the Type parameter.
public List<string> GetColumnNames(Type tablename)
{
var names = tablename.GetProperties().Select(p => p.Name).ToArray();
return names.ToList();
}

Column does not belong to table c#

I am getting an error when trying to create a list of objects. I am calling a web service which returns a different number of columns for certain products.
I have a class to store these values which hold all columns available, but when receiving data for products that are missing 1 column it fails with the error:
Column 'x' does not belong to table y.
Since I know column does not exist I need a replacement value if it does not exist. I have about 50 different columns so putting each one of them in an (if column exist) is something I would like to avoid!
Sample code, dprow is the Object template which holds all available columns
dprow.currency = row.Field<string>("currency");
dprow.categoryCode = row.Field<string>("categoryCode");
This is under the assumption that your row is a DataRow, but the concept applies regardless. You can write an extension method that will check if the column exists first:
public static class DataRowExtensions
{
public static T TryGetField<T>(this DataRow row, string fieldName)
{
return row.Table.Columns.Contains(fieldName) ? row.Field<T>(fieldName) : default(T);
}
}
Usage example: dprow.currency = row.TryGetField<string>("currency");
Take the table you get back from the web service (in my case I'm just saying create a brand new one) and then create a dictionary with all the columns that you want to have present, and what the default value should be. If that column isn't already in the table, then add it to the table with the default value.
DataTable dt = new DataTable();
Dictionary<string, object> columnDefaultValues = new Dictionary<string, object>(){
{"currency", "some default"},
{"categoryCode", "some other default"},
{"another", 43}
};
foreach (var keyPair in columnDefaultValues) {
if (!dt.Columns.Contains(keyPair.Key)) {
dt.Columns.Add(new DataColumn(keyPair.Key) { DefaultValue = keyPair.Value });
}
}

How can I get data type of each column in a SQL Server table/view etc. using C# Entity Framework?

How can I get the type of each column in a SQL Server table or view using Entity Framework?
I need to do this BEFORE I get all the data from the table, because I want to allow users to filter, for example, from a date before the data is actually retrieved from the SQL Server table/view.
So if I had a table of Books with a publish date, I would like to return each column (Name, Publisher, Publish Date) type in order to allow filtering beforehand. I need this code to do this because I will not necessarily know the columns, since the user may use several different tables.
The .NET Framework type is fine, I don't need the SQL Server type...
Here's some example code:
using (var ArgoEntities = new ARGOEntities())
{
//find the types here before the user performs the query so i can build the below code
var query = from b in ArgoEntities.tbl_Books
where b.PublishDate>[user specified date] //some date here the user enters
select b;
var book = query.First();
}
EDIT: I can do this so far only by getting the first record in the table, like this...
using (ARGOEntities ArgoEntities = new ARGOEntities())
{
//var fred = typeof(ARGOEntities).GetProperties();
//var fred = ArgoEntities.GetType().GetProperties();
var a=ArgoEntities.tbl_Books.FirstOrDefault();
var b = ObjectContext.GetObjectType(a.GetType());
var c=b.GetProperties();
}
but i repeat, I DON'T want to get any records first.
you can use the GetProperty and then PropertyType:
using (var ArgoEntities = new ARGOEntities())
{
//find the types here before the user performs the query so i can build the below code
//Like this you can retrieve the types:
foreach (string propertyName in ArgoEntities.CurrentValues.PropertyNames)
{
var propertyInfo = ArgoEntities.Entity.GetType().GetProperty(propertyName);
var propertyType = propertyInfo.PropertyType;
}
//
var query = from b in ArgoEntities.tbl_Books
where b.PublishDate>[user specified date] //some date here the user enters
select b;
var book = query.First();
}
Ryios' comment lead me to the extremely simple answer I knew it had to be, which will give me an array of PropertyInfo for each field in the table.
var columns=typeof(tbl_Books).GetProperties();

Working with DataGridViewRow.DataBoundItem - transform to Enumerable Object?

I have a DataGridView with
myGridView.DataSource = GetSomeData()
// method called
public IQueryable GetSomeData()
{
var source = from r in records
select r;
return source; // made correction from 'r' to 'source'
}
GetSomeData() fills the DataGridView as expected. The user will select a row to edit which then passes the row data to a form. Since the DataGridViewRow.DataBoundItem is an anonymous type, how can I pass DataBoundItem?
I half expected that my DataBoundItem would be IQueryable - incorrect. Here is the info from the debugger on the DataBoundItem property:
DataBoundItem { CustomerID = "3133",
Last_Name = "Smith", First_Name =
"John", AccountNumber = "JS3133",
ActiveYN = True } < Anonymous Type >
Once the data is passed to the new form, I would like to do something like:
txtFName.Text = SomeEnumeratedObject["First_Name"];
txtLName.Text = SomeEnumeratedObject["Last_Name"];
Any ideas on how I can do this? It would be even better, IMO, if the controls on the new form could some how be bound to SomeEnumeratedObject.
Would it possible to query the DataBoundItem with LINQ?
Edit:
Changed method:
public DataView GetSomeData()
{
var source = from r in records
select r;
DataTable table = ToDataTable(myContext, source);
return new DataView(table);
}
See complete solution here.
You could pass it as dynamic and access the properties that way:
public void Test(dynamic item)
{
MessageBox.Show(string.Format("{0} : {1}", item.First_Name, item.Last_Name));
textBox1.DataBindings.Add("Text", _item, "First_Name");
textBox2.DataBindings.Add("Text", _item, "Last_Name");
}
One thing to consider is that properties on anonymous types are read only, so your user will not be able to edit the values that way. From the C# Language Spec:
The members of an anonymous type are a sequence of read-only properties inferred from the anonymous object initializer used to create an instance of the type.
You should consider declaring a type for this instead of using an anonymous type. That way you'll also get the benefit of intellisense and be able to rename/refactor your properties without any problems.
What type of objects are in records? You should be able to cast DataGridViewRow.DataBoundItem to whatever type of object records holds.
Say records is a list of Customer objects. You should be able to go:
txtFName.Text = ((Customer)DataGridViewRow.DataBoundItem).First_Name;
txtLName.Text = ((Customer)DataGridViewRow.DataBoundItem).Last_Name;
If that isn't possible, then I think you will have to use reflection:
Type type = DataGridViewRow.DataBoundItem.GetType();
String firstName = (String)type.GetProperty("First_Name")
.GetValue(DataGridViewRow.DataBoundItem, null);
Both kevev22 & adrift had the right idea: create a class to hold the data. Since I can transform an IQueryable resultset to a DataTable, it only made sense to do that:
public DataView GetSomeData()
{
var source = from r in records
select r;
DataTable table = ToDataTable(myContext, source);
return new DataView(table);
}
Now:
myDGView.DataSource = GetSomeData();
And, when a datagrid row is selected, you can cast the DataBoundItem to DataRow.
FYI - to transform an IQueryable resultset to a DataTable:
public DataTable ToDataTable(DataContext context, IQueryable source)
{
DataTable table = new DataTable();
{
adapter.SelectCommand = context.GetCommand(source);
sqlCommand.Connection.Open();
adapter.FillSchema(table, SchemaType.Source);
adapter.Fill(table);
}
return table;
}

LINQ select items from set A which are not in set B

I would like to perform an Except operation on set of items.
Code is like this:
IEnumerable<DataGridViewColumn> dgvColumns = dataGridView.Columns.OfType<DataGridViewColumn>();
IEnumerable<DataColumn> dsColumns = dataSet.Tables[0].Columns.OfType<DataColumn>();
Now, how to select Columns from dataSet.Tables[0] which are not in dgvColumns?
I know that Columns from DataGridView are different type than Columns in DataSet. I want to pick up only a subset of common values. Like this:
var ColumnsInDGV = from c1 in dgvColumns
join c2 in dsColumns on c1.DataPropertyName equals c2.ColumnName
select new { c1.HeaderText, c1.DataPropertyName, c2.DataType, c1.Visible };
Above code selects me "columns" that are in both sets. So I tought I will create another set of "columns" that are in DataSet:
var ColumnsInDS = from c2 in dsColumns select new { HeaderText = c2.ColumnName, DataPropertyName = c2.ColumnName, c2.DataType, Visible = false };
and now that I will be able to perfrom Except like this:
var ColumnsOnlyInDS = ColumnsInDS.Except<ColumnsInDGV>;
But I am getting two errors:
The type or namespace name 'ColumnsInDGV' could not be found (are you missing a using directive or an assembly reference?)
Cannot assign method group to an implicitly-typed local variable
So the solution would be to build a class and then use it instead of implictly - typed local variable. But I think that developing a class only for this reason is a not necessery overhead.
Is there any other solution for this problem?
You've almost got it. You just need to write:
// use () to pass a parameter
// type (should) be inferred
var ColumnsOnlyInDS = ColumnsInDS.Except(ColumnsInDGV);
instead of:
// do not use <> - that passes a type parameter;
// ColumnsInDGV is not a type
var ColumnsOnlyInDS = ColumnsInDS.Except<ColumnsInDGV>;
Update: So, the above actually doesn't work because Except depends on comparing items in two sequences for equality; obviously, your anonymous type has not overriden object.Equals and so each object that you create of this type is treated as a distinct value. Try this* instead:
var dgvColumns = dataGridView.Columns.Cast<DataGridViewColumn>();
var dsColumns = dataSet.Tables[0].Columns;
// This will give you an IEnumerable<DataColumn>
var dsDgvColumns = dgvColumns
.Where(c => dsColumns.Contains(c.DataPropertyName))
.Select(c => dsColumns[c.DataPropertyName]);
// Then you can do this
var columnsOnlyInDs = dsColumns.Cast<DataColumn>().Except(dsDgvColumn);
*Note: Where in the above expression for dsDgvColumns makes more sense than SkipWhile because it will apply the specified filter over all results. SkipWhile would only apply the filter as long as it was true and would then stop applying it. In other words it would work if your DataGridViewColumn not bound to your DataSet were at the beginning of the DataGridView; but not if it were in the middle or at the end.

Categories

Resources