Convert a Linq result to a datatable - vb.net

I am trying to convert a Linq result in to a datatable
I have a linq that is created from a dataset of many tables. It returns results, but I need to get the results in to a new datatable.
Examples I have seen say I sould be able to use .CopyToDataTable But for some reason this doesn't work?
I have noticed that I can to .ToArray perhaps I can then turn the array in to a datatable? Seems line an unnecessary step?
Here is my query: (it works)
Dim R2 = From Inq In DS.Tables!CNLocalInquiry.AsEnumerable()
Join Cust In DS.Tables!CustomerID.AsEnumerable() On Inq.Field(Of Integer)("CNLocalInquiry_Id") Equals Cust.Field(Of Integer)("CNLocalInquiry_Id")
Select New With {.date = Inq.Field(Of String)("date"),
.CName = Cust.Field(Of String)("CustomerNumber"),
.Name = Cust.Field(Of String)("name")}
Dim MemberInq as new datatable
MemberInq = R2.CopyToDataTable() <-- this doesn't work
This is what my query returns:

(this is the easy to code way... this will not be performant for large datasets)
public static class ToolsEx
{
public static DataTable ToDataTable<T>(this IEnumerable<T> items)
{
var t = typeof(T);
var dt = new DataTable(t.Name);
var props = t.GetProperties()
.Select(p => new { N = p.Name, Getter = p.GetGetMethod() })
.Where(p => p.Getter != null)
.ToList();
props.ForEach(p => dt.Columns.Add(p.N));
foreach (var item in items)
dt.Rows.Add(props.Select(p => p.Getter.Invoke(item, null)).ToArray());
return dt;
}
}

I've saved this as an extension method and it's always worked perfectly:
https://msdn.microsoft.com/en-us/library/bb669096.aspx
Examples here:
https://msdn.microsoft.com/en-us/library/bb386921.aspx
Hope that does the trick!

Related

Get and manipulate data from elasticsearch

I am new to elasticsearch so I will need some help. Unfortunately, I didnt found the answer in other topics here on SO.
I have some .net core application which I inherited and now there is a need to implement some changes.
I already have a method of getting data from elasticsearch, but after getting them, I am not sure how to change it and use it in application.
To be precise, I need to parse first and last name and to remove special characters, specific serbian latin letters like "šđžčć" etc... I already have a method for this parsing written but not sure how to call it...
So, my question is can I and how can I do this?
What I have now is the following:
var result = await _elasticClient.SearchAsync<CachedUserEntity>(
s =>
s.Index(_aliasName)
.Query(q => andQuery));
CachedUserEntity, among others, contains property about FirstName and LastName.
Inside results.Documents, I am getting the data about FirstName and LastName from elasticsearch, but I am not sure how to access it in order to update it via aformentioned NameParser ...
Sorry if the question is too easy, not to say stupid :)
I wont use updateByQuery here, for some reasons. I would scroll on documents (i use matchAll on my exemple, you obviously need to replace it with your query), or, if you dont know how to identify documents to update, only update usefull documents in UpdateManyWithIndex/UpdateManyPartial function.
For performance, we have to update severals documents at once, so we use bulk/updateMany function.
You can use both solution, the classic update, or the second (partial update) with an object containing the targeteds fields.
On server sides, both solutions will have the same cost / performance.
var searchResponse = Client.Search<CachedUserEntity>(s => s
.Query(q => q
MatchAll()
)
.Scroll("10s")
);
while (searchResponse.Documents.Any())
{
List<CachedUserEntity> NewSearchResponse = RemoveChar(searchResponse);
UpdateManyWithIndex<CachedUserEntity>(NewSearchResponse, _aliasName);
searchResponse = Client.Scroll<Project>("2h", searchResponse.ScrollId);
}
public void UpdateManyWithIndex<C>(List<C> obj, string index) where C : class {
var bulkResponse = Client.Bulk(b => b
.Index(index).Refresh(Elasticsearch.Net.Refresh.WaitFor) // explicitly provide index name
.UpdateMany<C>(obj, (bu, d) => bu.Doc(d)));
}
Or, using partial update object
Note: in this case Indix is already set on my client (add .index if needed)
var searchResponse = Client.Search<CachedUserEntity>(s => s
.Query(q => q
MatchAll()
)
.Scroll("2h")
);
while (searchResponse.Documents.Any())
{
List<object> listPocoPartialObj = GetPocoPartialObjList(searchResponse);
UpdateManyPartial(listPocoPartialObj);
searchResponse = Client.Scroll<Project>("2h", searchResponse.ScrollId);
}
private List<object> GetPocoPartialObjList(List<CachedUserEntity> cachedList) {
List<object> listPoco = new List<object>();
//note if you dont have cachedList.Id, take a look at result.source, comments if needed
foreach (var eltCached in cachedList) {
listPoco.Add( new object() { Id = cachedList.Id, FirstName = YOURFIELDWITHOUTSPECIALCHAR, LastName = YOURSECONDFIELDWITHOUTSPECIALCHAR});
}
return listPoco;
}
public bool UpdateManyPartial(List<object> partialObj)
{
var bulkResponse = Client.Bulk(b => b
.Refresh(Elasticsearch.Net.Refresh.WaitFor)
.UpdateMany(partialObj, (bu, d) => bu.Doc(d))
);
if (!bulkResponse.IsValid)
{
GetErrorMsgs(bulkResponse);
}
return (bulkResponse?.IsValid == true);
}

Return type of Linq on Datatable

I have a datatable with two columns ID & Role.
Same ID can have multiple roles.
I need to convert this table to a comma separated grouped table.
I am trying to use following query but unable to solve the issue.
LINQ:
From row As DataRow In dtData.Rows.Cast(Of DataRow)
Group row By id = row.Field(Of Integer)("ID") Into Group
Select ID, Role = String.Join(",", From i In Group Select i.Field(Of String)("Role"))
Issue
Any help will be appreciated.
Update 1:
Table structure
Needed table Structure
You could create a linq like in your comments just that this returns a list of arrays of string:
Here is the code:
(From row As DataRow In myDatatable
Group row By id = row.Field(Of String)("ID") Into Group
Select {id, String.Join(",", From i In Group Select i.Field(Of String)("Role"))}).ToList
If you need the result in a datatable you can build a new datatable
Make a for each of result and use the activity Add data row. In ArrayRow add the item and in DataTable the new data table
If you use the activity Output data table you can see the results
I am kind of confused by what you are wanting as the ultimate outcome. An idea that may guide you but not be exactly what you want is you can change a DataTable to an anonymous projection and then get what you want out of that. You can do a 'Select' off a DataTable which enters into an extension method of 'what' do you want to select. If I was to do a new {} without any class or container object after the 'new' I would be scoped to just a method or not. This is a good advantage when you want to mold something for a specific use in just a single method to use tailored to a specific view.
static void Main(string[] args)
{
DataTable d = new DataTable();
d.Columns.Add("ItemName", typeof(string));
d.Columns.Add("MinValue", typeof(float));
d.Columns.Add("MaxValue", typeof(float));
d.Rows.Add("Widget1", 0.1, 0.2);
d.Rows.Add("Widget2", 0.2, 0.4);
d.Rows.Add("Widget3", 0.1, 0.2);
var dataTable = d.AsEnumerable();
//What do you want to select? The new {} without an indicator means anonymous type projection. This will exist only in
// the scope listed.
var data = dataTable.Select(x => new { ItemName = x[0], MinValue = x[1], MaxValue = x[2] }).ToList();
//My 'data' type is now well typed for it's properties in the scope it's in.
var joined = String.Join(", ", data.Select(x => x.ItemName).ToList());
Console.WriteLine(joined);
Console.WriteLine($"{data.Count}");
Console.ReadLine();
}
EDIT 1-26-18
Strange I thought I updated the code yesterday. To get a reusable object you could bind your front end to, you just make a POCO like so:
public class Foo
{
public string Bar { get; set; }
public string MinAndMax { get; set; }
}
While you could make another DataTable, frankly DataTables are like WinForms. They get the job done, but they are archaic and not friendly to do Linq with as easily as just a well formed POCO. And if you get into using Linq it will play better with well formed objects and they are easy to create.
var data = dataTable.Select(x => new Foo { Bar = x[0].ToString(), MinAndMax = $"{x[1]} {x[2]}" }).ToList();
//My 'data' type is now well typed for 'Foo' class and it's properties in the scope it's in.
var joined = String.Join(", ", data.Select(x => $"{x.Bar} {x.MinAndMax}").ToList());

How to segregate data into different buckets using LINQ in VB.NET?

I am new to VB.NET and can't find a way to segregate data into different buckets using LINQ in VB.NET ?
I have a list of objects and based on a particular property, I want to divide it based on distinct values for the property.
Edit:
static void Main(string[] args)
{
List<DemoClass> ListDemoClass = new List<DemoClass>();
DemoClass demo1 = new DemoClass();
demo1.city = "Pune";
demo1.name = "Pranav";
ListDemoClass.Add(demo1);
DemoClass demo2 = new DemoClass();
demo1.city = "Pune";
demo1.name = "Brij";
ListDemoClass.Add(demo2);
DemoClass demo3 = new DemoClass();
demo1.city = "Pune";
demo1.name = "Piyush";
ListDemoClass.Add(demo3);
DemoClass demo4 = new DemoClass();
demo1.city = "NJ";
demo1.name = "Igal";
ListDemoClass.Add(demo4);
DemoClass demo5 = new DemoClass();
demo1.city = "NJ";
demo1.name = "Mithun";
ListDemoClass.Add(demo5);
List<List<DemoClass>> buckets = ListDemoClass
.GroupBy(x => x.city)
.Select(grp => grp.ToList())
.ToList();
}
Expected output is a list containing two lists of 2 and 3 objects respectively based on the city.
However this is not working. The result is I have a list of list of 1 and 4 items, which comes to be wrong. In the second list ,the objects are null.
The question is pretty unclear but maybe this helps anyway, use Enumerable.GroupBy:
VB:
Dim buckets As List(Of List(Of ClassName)) = list.
GroupBy(Function(x) x.PropertyName).
Select(Function(grp) grp.ToList()).
ToList()
C#
List<List<ClassName>> buckets = list
.GroupBy(x => x.PropertyName)
.Select(grp => grp.ToList())
.ToList();
Your demo code only initializes demo1 properties not the other objects.
You can use something like:
Dim result As List(Of List(Of SomeType)) = list.GrouBy(Function(x) x.Property).Select(Function(x) x.ToList()).ToList()
In C#, you would group the items to a list of lists in the following way:
List<List<SomeType>> result = list.GrouBy(x => x.Property).Select(x => x.ToList()).ToList();

Write SQL queries using LINQ from linq object List

I have a linq object and I want to write the query using linq.
please help me.
INPUT:
var tags = (from row in tempChildData.AsEnumerable()
join tagOrder in tupleInfoDataset.Tables["TagHierarchy"].AsEnumerable() on row.Field<Int64>("TAGID") equals tagOrder.Field<Int64>("TAGID")
join tagName in tupleInfoDataset.Tables["SequenceChoiceList"].AsEnumerable() on tagOrder.Field<Int64>("PARENTTAGID") equals tagName.Field<Int64>("TAGID")
join facet in tupleInfoDataset.Tables["FacetType"].AsEnumerable() on tagName.Field<string>("Tag_Name") equals facet.Field<string>("Facetname")
join tagIdInfo in schDataTogetTagid.AsEnumerable() on row.Field<string>("refTagName").Contains(":") ? row.Field<string>("refTagName").Split(':').Last():row.Field<string>("refTagName") equals tagIdInfo.Field<string>("TAGNAME")
where ( childList.Contains(row.Field<Int64>("TAGID")) && facet.Field<string>("FacetType").ToLower().Equals("ctype"))
select new
{
Tagid = row.Field<Int64>("TAGID"),
TagIdToInsert=tagIdInfo.Field<Int64>("TAGID"),
MaxOccur = row.Field<string>("Maxoccurs"),
MinOccur =Convert.ToInt32(Convert.ToString(row.Field<string>("Minoccur"))),
ParentTagId=tagOrder.Field<Int64>("PARENTTAGID"),
Order=tagOrder.Field<Int64>("TAG_ORDER"),
ParentTagname = tagName.Field<string>("Tag_Name"),
FacetId=facet.Field<Int64>("FacetID")
}).ToList();
var parentTagID = (from tagIdInfo in tupleInfoDataset.Tables["Tuple"].AsEnumerable()
where tagIdInfo.Field<Int64>("TAGID").Equals(key.Key)
select tagIdInfo.Field<Int64>("ConceptID")).ToList();
long parentID =Convert.ToInt64(parentTagID[0]);
Now i want the query out of the above code as:
INSERT INTO TUPLE_MAP (TagId,ParentTagId,ParentTagname,MinOccur,MaxOccur,Order)
VALUES (TagIdToInsert,ParentTagId,ParentTagname,MinOccur,MaxOccur,Order)
Please help me I don't know how to write SQL queries using linq
Maybe something like this:
using(var db=new DataContext("YourConnectionStringHERE"))
{
db.TUPLE_MAP.InsertAllOnSubmit(tags.Select (t =>
new TUPLE_MAP()
{
TagId=t.TagIdToInsert,
ParentTagId=t.ParentTagId,
ParentTagname=t.ParentTagname,
MinOccur=t.MinOccur,
MaxOccur=t.MaxOccur,
Order=t.Order
}));
db.SubmitChanges();
}
Or if you want to use the parentID then something like this:
using(var db=new DataContext("YourConnectionStringHERE"))
{
db.TUPLE_MAP.InsertAllOnSubmit(tags.Select (t =>
new TUPLE_MAP()
{
TagId=t.TagIdToInsert,
ParentTagId=parentID,
ParentTagname=t.ParentTagname,
MinOccur=t.MinOccur,
MaxOccur=t.MaxOccur,
Order=t.Order
}));
db.SubmitChanges();
}
where db is your linq data context
Useful references:
How to: Insert Rows Into the Database (LINQ to SQL)
EDIT
So if you are using the Compact database 3.5 then many something like this:
using (var conn =new SqlCeConnection("Data Source = test.sdf; Password ='pass'"))
{
foreach (var tag in tags)
{
using(var cmd = conn.CreateCommand())
{
cmd.CommandText = #"INSERT INTO TUPLE_MAP (TagId,ParentTagId,ParentTagname,MinOccur,MaxOccur,Order)
VALUES (#TagIdToInsert,#ParentTagId,#ParentTagname,#MinOccur,#MaxOccur,#Order)";
cmd.Parameters.AddWithValue("#TagIdToInsert", tag.TagIdToInsert);
cmd.Parameters.AddWithValue("#ParentTagId", tag.ParentTagId);
cmd.Parameters.AddWithValue("#ParentTagname", tag.ParentTagname);
cmd.Parameters.AddWithValue("#MinOccur", tag.MinOccur);
cmd.Parameters.AddWithValue("#MaxOccur", tag.MaxOccur);
cmd.Parameters.AddWithValue("#Order", tag.Order);
cmd.ExecuteNonQuery();
}
}
}
Useful references:
Why can't I insert a record into my SQL Compact 3.5 database?
SqlCeCommand.Parameters Property
SqlCeCommand Class
SqlParameterCollection.AddWithValue Method
Use linq Pad or sql profiler to see the generated SQL.
You can also use visual studio for that purpose. In the debug mode,hold cursor on the variable "tags", you will be able to see the SQL.
I am assuming you are using Linq to SQL, if you are doing so you would have entity called Tuple_map in you xxxDataContext. Then you would just have to create object of that entity something like this....
using (XXXDataContext context = new XXXDataContext())
{
Tuple_map obj = new Tuple_map();
//Populate obj properties like obj.tabid = from objects you got it from above query
context.Tuple_map.InsertOnSubmit(obj);
context.SubmitChanges();
}

Simplifying multiple for each loops in LINQ

in my prev question i was suggested with LINQ solution. It was great and simple. I tried to solve next similar but a bit diff problem with similar approach but i failed.
how can i make the below code better
For Each Item As ListViewItem In RoomsListView.Items
For Each Item1 As Room In myBookedRooms
If Item1.UIN = Item.SubItems(1).Text Then
Item.Checked = True
End If
Next
Next
Sorry - can read but not write VB...
In C# Linq, your query would be something like:
var query = from room in RoomsListView.Items
from bookedRoom in myBookedRooms
where ((Room)bookedRoom).UIN == room.SubItems(1).Text
select room;
foreach (var room in query)
{
room.Checked = true;
}
You can use Join for that. Here is a C# sample:
var itemsToUpdate = RoomsListView.Items
.Cast<ListViewItem>()
.Join(myBookedRooms,
Item => Item.SubItems(1).Text,
Item1 => Item1.UIN,
(Item, Item1) => Item);
foreach (var item in itemsToUpdate)
item.Checked = true;