c# linq create dynamic query with - c#

I have 5 Dropdown lists which are filled with the columns of my dataset.
That works well.
var query = ds.Tables["Input"].AsEnumerable().Select
(a => new
{ID = a.Field<string>("ID"),
Element1 = a.Field<string>(comboBoxElement1.SelectedItem.ToString()),
Element2 = a.Field<string>(comboBoxElement2.SelectedItem.ToString()),
Element3 = a.Field<string>(comboBoxElement3.SelectedItem.ToString()),
Element4 = a.Field<string>(comboBoxElement4.SelectedItem.ToString()),
Element5 = a.Field<string>(comboBoxElement5.SelectedItem.ToString())});
But that only works if all ComboBoxes are not empty.
How can I build the query with only 2 selected Boxes dynamically?
I tried it with a StingBuilder and if (comboBoxName.SelectedIndex >= 0) statements, but I'm wondering if there is another method to do that within LINQ.

Why not add a where clause asking for only selectedIndex != 0
like this (in a WinForm):
result = Controls.OfType<ComboBox>().Where(cb => cb.SelectedIndex != -1).Aggregate(result, (current, cb) => current + (cb.SelectedItem + ","));
Have you try this:
var query = ds.Tables["Input"].AsEnumerable().Where(cb => cb.SelectedIndex != -1).Select
(a => new
{ID = a.Field<string>("ID"),
Element1 = a.Field<string>(comboBoxElement1.SelectedItem.ToString()),
Element2 = a.Field<string>(comboBoxElement2.SelectedItem.ToString()),
Element3 = a.Field<string>(comboBoxElement3.SelectedItem.ToString()),
Element4 = a.Field<string>(comboBoxElement4.SelectedItem.ToString()),
Element5 = a.Field<string>(comboBoxElement5.SelectedItem.ToString())});

I found a easy solution.
Create a List from the (not empty) ComboBoxes:
List<string> myCollection = new List<string>();
if (comboBoxElement1.SelectedIndex >= 0)
{
myCollection.Add(comboBoxElement1.SelectedItem.ToString());
}
if (comboBoxElement2.SelectedIndex >= 0)
{
myCollection.Add(comboBoxElement2.SelectedItem.ToString());
}
Create a DataView from the InputTable:
DataView dv = new DataView(dtInput);
Write to the selected DataColumns from comboBoxes to the ouputTable:
dtOutput = dv.ToTable(true, myCollection.ToArray());
You can refactor the above by creating a method such as:
public AddSelected(IList list, params ComboBox[] comboBoxes)
{
foreach (var comboBox in comboBoxes)
{
if (comboBox.SelectedIndex >= 0)
{
list.Add(comboBox.SelectedItem.ToString());
}
}
}
Then calling with code such as:
AddSelected(myCollection, comboBoxElement1, comboBoxElement2 /* , ... */ );

Related

Can I merge these two lists in 1 go

What I am trying to do,
// get members from SharePoint list (can be null)
// get members from database (can be null)
// merge database members with sharepoint list members BUT only database members should have property VIP = true
// by merge I mean if they are not in list then add them to list, if they are in list then just change there property VIP = true
// by default VIP property is false
What I have developed so far,
List<Member> Members = new List<Member>();
foreach (SPListItem mItem in GetList(Url).Items)
{
Member m = new Member();
m.ID = mItem.ID;
m.Name = mItem.Title;
m.Company = Utilities.ObjectToStringOrEmpty(mItem[companyCol]);
m.eMail = Utilities.ObjectToStringOrEmpty(mItem[emailCol]);
m.Comment = Utilities.ObjectToStringOrEmpty(mItem[commentCol]);
m.Membership = Utilities.ObjectToStringOrEmpty(mItem[msCol]);
Members.Add(m);
}
var cd = new MemberManager().GetMoreMembers(Url + "/");
var activeMembers = cd.Where(am => am.MembershipStatus == "Active" || am.MembershipStatus == "Pending").ToList();
if (activeMembers != null || activeMembers.Count() > 0)
{
foreach (var am in activeMembers)
{
if (!Members.Any(a => a.eMail.ToLowerInvariant() == am.Email.ToLowerInvariant()))
{
Member m = new Member();
m.Name = am.FirstName + " " + am.LastName;
m.eMail = am.Email;
m.IsVip = true;
Members.Add(m);
}
}
}
md.Members = Members.ToArray();
Problem
Can I use Linq and merge these lists in a single go ? Maybe something like this, pseudo would be
var dbMembers = //GetDBMembers that are active or pending
var spMembers =
Select all members using `.Cast<SPListItem>()`
If spMembers has any dbMember (compared by email)
Then change that spMembers VIP property to true (which is by default false)
For rest dbMembers that doesn't exists in spMembers, add them with VIP property = true
Not sure how can i efficiently put above pseudo code into linq
Try this:
var allSpMembers = GetSpList(); // get your members as you mentioned before by `.Cast<SPListItem>()`
List<SPListItem> spMembers =
dbMembers.GroupJoin(allSpMembers, dbM => dbM.Email, spM => spM.Email,
(dbMember, spMember) => new { dbMember, spMember })
.SelectMany(x => x.spMember.DefaultIfEmpty(), (x, spMember) =>
{
SPListItem yourSpListItem;
if (spMember != null)
{
yourSpListItem = spMember;
}
else
{
yourSpListItem = x.dbMember; //make some mapping here to SPListItem model
}
yourSpListItem.VIP = true;
return yourSpListItem;
}).ToList();
Yes you can. Use the Enumerable.Concat method to concatenate two lists into one.
If you split out the code to create Member objects into two methods, one MemberFromSpListItem and one MemberFromActiveMember then you will get the following relatively nice code:
var cd = new MemberManager().GetMoreMembers(Url + "/");
var activeMembers = cd.Where(am => am.MembershipStatus == "Active" || am.MembershipStatus == "Pending").ToList();
var members = GetList(Url)
.Items
.Select(MemberFromSpListItem)
.Concat(activeMembers.Select(MemberFromActiveMember))
.ToList();

Use linq to find DataTable(Name) in a DataSet using unique list of Column Names

I got roped into some old code, that uses loose (untyped) datasets all over the place.
I'm trying to write a helper method to find the DataTable.Name using the names of some columns.....(because the original code has checks for "sometimes we have 2 datatables in a dataset, sometimes 3, sometimes 4)..and its hard to know the order. Basically, the TSQL Select statements conditionally run. (Gaaaaaaaaaaaaaahhh).
Anyway. I wrote the below, and if I give it 2 column names, its matching on "any" columnname, not "all column names".
Its probably my linq skillz (again), and probably a simple fix.
But I've tried to get the syntax sugar down..below is one of the things I wrote, that compiles.
private static void DataTableFindStuff()
{
DataSet ds = new DataSet();
DataTable dt1 = new DataTable("TableOne");
dt1.Columns.Add("Table1Column11");
dt1.Columns.Add("Name");
dt1.Columns.Add("Age");
dt1.Columns.Add("Height");
DataRow row1a = dt1.NewRow();
row1a["Table1Column11"] = "Table1Column11_ValueA";
row1a["Name"] = "Table1_Name_NameA";
row1a["Age"] = "AgeA";
row1a["Height"] = "HeightA";
dt1.Rows.Add(row1a);
DataRow row1b = dt1.NewRow();
row1b["Table1Column11"] = "Table1Column11_ValueB";
row1b["Name"] = "Table1_Name_NameB";
row1b["Age"] = "AgeB";
row1b["Height"] = "HeightB";
dt1.Rows.Add(row1b);
ds.Tables.Add(dt1);
DataTable dt2 = new DataTable("TableTwo");
dt2.Columns.Add("Table2Column21");
dt2.Columns.Add("Name");
dt2.Columns.Add("BirthCity");
dt2.Columns.Add("BirthState");
DataRow row2a = dt2.NewRow();
row2a["Table2Column21"] = "Table2Column1_ValueG";
row2a["Name"] = "Table2_Name_NameG";
row2a["BirthCity"] = "BirthCityA";
row2a["BirthState"] = "BirthStateA";
dt2.Rows.Add(row2a);
DataRow row2b = dt2.NewRow();
row2b["Table2Column21"] = "Table2Column1_ValueH";
row2b["Name"] = "Table2_Name_NameH";
row2b["BirthCity"] = "BirthCityB";
row2b["BirthState"] = "BirthStateB";
dt2.Rows.Add(row2b);
ds.Tables.Add(dt2);
DataTable dt3 = new DataTable("TableThree");
dt3.Columns.Add("Table3Column31");
dt3.Columns.Add("Name");
dt3.Columns.Add("Price");
dt3.Columns.Add("QuantityOnHand");
DataRow row3a = dt3.NewRow();
row3a["Table3Column31"] = "Table3Column31_ValueM";
row3a["Name"] = "Table3_Name_Name00M";
row3a["Price"] = "PriceA";
row3a["QuantityOnHand"] = "QuantityOnHandA";
dt3.Rows.Add(row3a);
DataRow row3b = dt3.NewRow();
row3b["Table3Column31"] = "Table3Column31_ValueN";
row3b["Name"] = "Table3_Name_Name00N";
row3b["Price"] = "PriceB";
row3b["QuantityOnHand"] = "QuantityOnHandB";
dt3.Rows.Add(row3b);
ds.Tables.Add(dt3);
string foundDataTable1Name = FindDataTableName(ds, new List<string> { "Table1Column11", "Name" });
/* foundDataTable1Name should be 'TableOne' */
string foundDataTable2Name = FindDataTableName(ds, new List<string> { "Table2Column21", "Name" });
/* foundDataTable1Name should be 'TableTwo' */
string foundDataTable3Name = FindDataTableName(ds, new List<string> { "Table3Column31", "Name" });
/* foundDataTable1Name should be 'TableThree' */
string foundDataTableThrowsExceptionName = FindDataTableName(ds, new List<string> { "Name" });
/* show throw exception as 'Name' is in multiple (distinct) tables */
}
public static string FindDataTableName(DataSet ds, List<string> columnNames)
{
string returnValue = string.Empty;
DataTable foundDataTable = FindDataTable(ds, columnNames);
if (null != foundDataTable)
{
returnValue = foundDataTable.TableName;
}
return returnValue;
}
public static DataTable FindDataTable(DataSet ds, List<string> columnNames)
{
DataTable returnItem = null;
if (null == ds || null == columnNames)
{
return null;
}
List<DataTable> tables =
ds.Tables
.Cast<DataTable>()
.SelectMany
(t => t.Columns.Cast<DataColumn>()
.Where(c => columnNames.Contains(c.ColumnName))
)
.Select(c => c.Table).Distinct().ToList();
if (null != tables)
{
if (tables.Count <= 1)
{
returnItem = tables.FirstOrDefault();
}
else
{
throw new IndexOutOfRangeException(string.Format("FindDataTable found more than one matching Table based on the input column names. ({0})", String.Join(", ", columnNames.ToArray())));
}
}
return returnItem;
}
I tried this too (to no avail) (always has 0 matches)
List<DataTable> tables =
ds.Tables
.Cast<DataTable>()
.Where
(t => t.Columns.Cast<DataColumn>()
.All(c => columnNames.Contains(c.ColumnName))
)
.Distinct().ToList();
To me sounds like you're trying to see if columnNames passed to the method are contained within Column's name collection of Table. If that's the case, this should do the work.
List<DataTable> tables =
ds.Tables
.Cast<DataTable>()
.Where(dt => !columnNames.Except(dt.Columns.Select(c => c.Name)).Any())
.ToList();
(Below is an append by the asker of the question)
Well, I had to tweak it to make it compile, but you got me there..
Thanks.
Final Answer:
List<DataTable> tables =
ds.Tables.Cast<DataTable>()
.Where
(dt => !columnNames.Except(dt.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName))
.Any()
)
.ToList();
Final Answer (which is not case sensitive):
List<DataTable> tables =
ds.Tables.Cast<DataTable>()
.Where
(dt => !columnNames.Except(dt.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName), StringComparer.OrdinalIgnoreCase)
.Any()
)
.ToList();

querying existing ListView items with LINQ

The ListView I have populates through these loops resulting in four columns being filled
// Create a ResXResourceReader
ResXResourceReader rdr0 = new ResXResourceReader(textPath1.Text + ".resx");
ResXResourceReader rdr1 = new ResXResourceReader(textPath1.Text + ".es.resx");
ResXResourceReader rdr2 = new ResXResourceReader(textPath1.Text + ".fr.resx");
foreach (DictionaryEntry d in rdr0)
{
TransResource x = new TransResource();
x.id = d.Key.ToString();
x.en = d.Value.ToString();
resources.Add(x.id, x);
}
foreach (DictionaryEntry d in rdr1)
{
TransResource x = resources[d.Key.ToString()];
x.fr = d.Value.ToString();
}
foreach (DictionaryEntry d in rdr2)
{
TransResource x = resources[d.Key.ToString()];
x.es = d.Value.ToString();
}
foreach (TransResource x in resources.Values)
{
string[] row = { x.id, x.en, x.fr, x.es };
var listViewItem = new ListViewItem(row);
listResx.Items.Add(listViewItem);
}
What I want to do is query all of the results in this ListView against what is entered in textboxQuery. If any field in the entire listview contains the string from textboxQuery I want it to be displayed in a new listview (lets say listviewQueryresult). I've had many failed attempts at this but I know it is possible through LINQ.
Because ListView.Items implements IEnumerable, but does not implement IEnumerable<T> you have to cast it to IEnumerable<ListViewItem> first, to query it using LINQ to Objects:
var results = listResx.Items.Cast<ListViewItem>()
.Where(x => YourPredicate(x));
If any field in the entire listview contains the string from
textboxQuery I want it to then be displayed in a new listview (lets
say listviewQueryresult)
For that, the predicate would be just:
var results = listResx.Items.Cast<ListViewItem>()
.Where(x => x.Text.Contains(textboxQuery));

take out foreach using linq

Is there a way to take out the foreach in the following code in linq yet produce the same output?
DropDownList ddl = new DropDownList();
foreach (DataRow row in ds.Tables[0].Rows)
{
if ((byte)row["ListTypeID"] == 0)
{
item = new ListItem(row["ListText"].ToString(), string.Format("{0}:{1}", row["ListTypeID"].ToString(), row["ListID"].ToString()));
ddl.Items.Add(item);
}
else
{
item = new ListItem(row["ListText"].ToString(), string.Format("{0}", row["ListID"].ToString()));
ddl.Items.Add(item);
}
}
Instead of cramming all into a one-line lambda, I prefer readability over saving line space:
ddl.Items.AddRange(ds.Tables[0].Rows.Select( row =>
{
var listText = row["ListText"];
var listTypeId = (byte)row["ListTypeID"];
var listId = row["ListID"];
var format = listTypeId == 0 ? "{0}:{1}" : "{1}";
var itemText = String.Format(format, listTypeID, listId);
return new ListItem(listText, itemText);
}
));
ddl.Items.AddRange(ds.Tables[0].Rows.Select( row =>
(byte)row["ListTypeID"] == 0
? new ListItem(row["ListText"].ToString(), string.Format("{0}:{1}", row["ListTypeID"].ToString(), row["ListID"].ToString()))
: new ListItem(row["ListText"].ToString(), string.Format("{0}", row["ListID"].ToString()))
));
a bit of cleaning
ddl.Items.AddRange(ds.Tables[0].Rows.Select( row =>
(byte)row["ListTypeID"] == 0
? new ListItem(row["ListText"].ToString(), string.Format("{0}:{1}", row["ListTypeID"], row["ListID"]))
: new ListItem(row["ListText"].ToString(), string.Format("{0}", row["ListID"]))
));
leave foreach :)
After a bit of reordering you can achieve the following - adding the items excluded.
var items = ds.Tables[0].Rows.Select(row =>
new ListItem(row["ListText"].ToString(),
String.Format(
(Byte)row["ListTypeID"] == 0 ? "{0}:{1}" : "{1}",
row["ListTypeID"], row["ListID"])));
You can use something like this
var Items= (from DataRow dr in ds.Tables[0].AsEnumerable()
select new {
Text=dr.Field<string>("ListText"),
Value =dr.Field<byte>("ListTypeID") == 0?string.Format("{0}:{1}", dr.Field<string>("ListTypeID"), dr.Field<string>("ListID")):string.Format("{0}", dr.Field<string>("ListID"))
}).ToList();
now you can use Items as data-source for the dropdown.

how convert DataTable to List<String> in C#

I am using C# Linq now I am converting DataTable to List
and I am getting stuck...
give me right direction thanks..
private void treeview1_Expanded(object sender, RoutedEventArgs e)
{
coa = new List<string>();
//coa = (List<string>)Application.Current.Properties["CoAFull"];
HMDAC.Hmclientdb db = new HMDAC.Hmclientdb(HMBL.Helper.GetDBPath());
var data = (from a in db.CoA
where a.ParentId == 0 && a.Asset == true
select new { a.Asset, a.Category, a.CoAName, a.Hide, a.Recurring, a.TaxApplicable });
DataTable dtTable = new DataTable();
dtTable.Columns.Add("Asset", typeof(bool));
dtTable.Columns.Add("Category", typeof(string));
dtTable.Columns.Add("CoAName", typeof(string));
dtTable.Columns.Add("Hide", typeof(bool));
dtTable.Columns.Add("Recurring", typeof(bool));
dtTable.Columns.Add("TaxApplicable", typeof(bool));
if (data.Count() > 0)
{
foreach (var item in data)
{
DataRow dr = dtTable.NewRow();
dr["Asset"] = item.Asset;
dr["Category"] = item.Category;
dr["CoAName"] = item.CoAName;
dr["Hide"] = item.Hide;
dr["Recurring"] = item.Recurring;
dr["TaxApplicable"] = item.TaxApplicable;
dtTable.Rows.Add(dr);
}
}
coa = dtTable;
}
It seems that you already have a strongly typed list. Why converting this to a weakly typed DataTable and then back to a list?????
var data =
from a in db.CoA
where a.ParentId == 0 && a.Asset == true
select new
{
a.Asset,
a.Category,
a.CoAName,
a.Hide,
a.Recurring,
a.TaxApplicable
};
var list = data.ToList();
If you want to be able to use this list outside the scope of the method, define a type that will hold the different properties and in your select statement use this type instead of the anonymous type like:
var data =
from a in db.CoA
where a.ParentId == 0 && a.Asset == true
select new MyType
{
Asset = a.Asset,
Category = a.Category,
CoAName = a.CoAName,
Hide = a.Hide,
Recurring = a.Recurring,
TaxApplicable = a.TaxApplicable
};
List<MyType> list = data.ToList();
You don't need the data table according to the code you have displayed:
var data = (from a in db.CoA
where a.ParentId == 0 && a.Asset == true
select new { a.Asset.ToString() + a.Category.ToString()
+ a.CoAName.ToString()... }).ToList();
If you really want to convert your datatable to a 1D list, you can do it like this
foreach (DataRow row in dtTable.Rows)
{
foreach (DataColumn col in dtTable.Columns)
{
coa.Add(row[col]);
}
}
As you are using Select new in you linq query It will find object. What you can do is
var data = (from a in db.CoA
where a.ParentId == 0 && a.Asset == true
select new { a.Asset, a.Category, a.CoAName, a.Hide, a.Recurring, a.TaxApplicable });
this is your query and you select multiple columns in your query. So you can't convert your data to a single List of string. What you can do is concatenate all the column in a single string and then add them in a list of string.
To do that modify your query like 'CK' said
var data = (from a in db.CoA
where a.ParentId == 0 && a.Asset == true
select new { a.Asset.ToString() + a.Category.ToString()
+ a.CoAName.ToString()... }).ToList();
And then do
List<string> name = new List<string>(data.ToList());

Categories

Resources