rss feed by linq - linq

I am trying to extract the rss feed using linq. Thought it would be simple, but its is not returning any nodes. probably i have to go the channel/item node, but don't know how.
Dim rssUrl As String = "http://webclip.in/rss.aspx?u=mostliked"
Dim rssDoc As XDocument = XDocument.Load(rssUrl)
Dim rssResultSet = From node In rssDoc.Descendants("item") _
Select New With { _
.title = node.Element("title").Value, _
.link = node.Element("link").Value, _
.description = node.Element("description").Value, _
.pubDate = Date.Parse(node.Element("pubdate").Value) _
}
DataGridView1.DataSource = rssResultSet

Two issues here... First, you should correct this line:
.pubDate = Date.Parse(node.Element("pubDate").Value)
The pubDate is a case-sensitive node in XML. Secondly, your dataSource will never work because LINQ is lazy computation. You have to use ToList() or a similar method that enumerate your collection. If you debug within Visual Studio 2010, you'll see that rssResultSet does not have a value because it is only enumerated when your code calls for it. Replace with this:
DataGridView1.DataSource = rssResultSet.ToList()
My last piece of advice is to set your DataGrid to AutoGenerate it's columns.

the casing on pubdate is wrong. It should be "pubDate". otherwise, works fine.

Related

LINQ query through Azure blobs of IEnumerable(Of IListBlobItem)

I am trying to find specific Uri.AbsolutePath for the Block blob by its name. Azure Storage container contains only Block blobs. List of blobs returned from storage is IEnumerable(Of IListBlobItem).
I use FirstOrDefault to find specific blob by its name. Compiler says there is no Name property for CloudBlockBlob. This is probably related to single blob item type. Even if I use CloudBlockBlob within FirstOrDefault it is still IListBlobItem, thus Name property is missing. How to tackle this in an efficient way?
Dim storageAccount As CloudStorageAccount = CloudStorageAccount.Parse("Storage connection string")
Dim blobClient As CloudBlobClient = storageAccount.CreateCloudBlobClient()
Dim BlobList As IEnumerable(Of IListBlobItem) = blobClient.GetContainerReference("ContainerName").ListBlobs
Path= If(BlobList.FirstOrDefault(Function(CloudBlockBlob) CloudBlockBlob.Name = "ABC.pdf")?.Uri.AbsolutePath, "")
Sure, FirstOrDefault will result in an IListBlobItem according to intellisense but it has of course an actual implementation. Any debugger will tell you what actual type is returned.
In your case you are only interested in the results of ListBlobs that are actual of type CloudBlockBlob. To do that you can use the OfType method:
Dim storageAccount As CloudStorageAccount = CloudStorageAccount.Parse("Storage connection string")
Dim blobClient As CloudBlobClient = storageAccount.CreateCloudBlobClient()
Dim BlobList As IEnumerable(Of CloudBlockBlob) = blobClient.GetContainerReference("ContainerName").ListBlobs.OfType(Of CloudBlockBlob)
Path = If(BlobList.FirstOrDefault(Function(CloudBlockBlob) CloudBlockBlob.Name = "ABC.pdf")?.Uri.AbsolutePath, "")
You can probably improve you search by filtering items out on the server side using the prefix option of ListBlobs
Dim BlobList As IEnumerable(Of CloudBlockBlob) = blobClient.GetContainerReference("ContainerName").ListBlobs(prefix := "ABC.pdf").OfType(Of CloudBlockBlob)
This line will only list blobs of which the name starts with / equals to "ABC.pdf".
Disclaimer: I am not a VB.Net developer so there might me some small mistakes and some room for readability improvements.

How to reference the same table twice?

First of all I have to admit I'm very much a novice in Linq and Lambda expressions.
I'm trying to get the following SQL statement into a Linq statement (using lamda expressions):
select *
from dbo.tblStockTransfers t1,
dbo.tblSuppliers t2
where t1.SupplierID = t2.SupplierID
and t2.WarehouseID in (1,2,3)
and t1.GoodsPickedUp = 1
and Not exists
(select 1 from dbo.tblStockTransfers t3
where t3.TransferOutID = t1.TransferID and t3.TransferConfirm = 1)
My class StockTransfer is an aggregate root and has it's own repository.
Now so far I got the following (the variable allowedWarehouses contains the list of warehouse IDs):
Return GetObjectSet().Where(Function(st) allowedWarehouses.Contains(st.Supplier.WarehouseID) And st.GoodsPickedUp = True)
This works fine, but obviously is missing the " and not exists ..." part (the last 3 lines of the SQL code at the top of this posting).
I know that Linq doesn't have a "not exists", but you can use the "Any" method for this.
Here's a working example of this elsewhere in my code:
Return GetObjectSet().Where(Function(sw) sw.Active = True And Not sw.Suppliers.Any(Function(sp) sp.WarehouseID = sw.Id))
This works fine and will give me any warehouses which are not linked to a supplier yet.
As you can see in the above example this is fine as I'm referring to the related table "Suppliers".
However, in the SQL code I'm now trying to convert into Linq, the "not exists" is not on a linked table but on itself. Is there a way I can create a 2nd reference on the main table and use that in a ".. not ..any" part. Maybe something like:
Return GetObjectSet().Where(Function(st) allowedWarehouses.Contains(st.Supplier.WarehouseID) And st.GoodsPickedUp = True And Not st2.Any(st2.TransferOutID = st.TransferId and st2.TransferConfirm = true)
But I don't know how to define st2 (i.e. in this case st2 would be a 2nd alias to StockTransfer).
Any Help would be greatly appreciated.
This is not the answer to the question, but it is a work-around which does get me the result I need:
Dim st1 As List(Of StockTransfer) = GetObjectSet.Where(Function(st) allowedWarehouses.Contains(st.Supplier.WarehouseID) And st.GoodsPickedUp = True).ToList
Dim st2 As List(Of StockTransfer) = GetObjectSet.Where(Function(st) st.TransferConfirm = True).ToList
For Each st As StockTransfer In st2
st1.RemoveAll(Function(x) x.Id = st.TransferOutID)
Next
Return st1
I'm obviously cheating by splitting out the query in 2 parts, where each part ends up in a list and then I remove from list 1 any items which I've got in list 2 (removing the ones which would normally be ignored by the "not exists" part).
However, I would love to hear it if anyone can come up with a working solution using Linq and lambda expressions (as this does feel a bit like a cheat).
I would do it something like this:
Dim lsWareHouseIds As New List(Of Integer)() From {1,2,3}
dim obj= ( _
From t1 in db.tblStockTransfers _
join t2 in db.tblSuppliers _
on t1.SupplierID equals company.SupplierID _
where lsWareHouseIds.Contains(t2.WarehouseID) _
andalso t1.GoodsPickedUp =1 _
andalso Not _
(
from t3 in db.tblStockTransfers _
where t3.TransferConfirm=1 _
select t3.TransferOutID _
).Contains(t1.TransferID) _
select t1 _
)
I did see you comment and you answer. Can't you do like this:
GetObjectSet.Where(Function(st) _
allowedWarehouses.Contains(st.Supplier.WarehouseID) And st.GoodsPickedUp = True _
Andalso Not _
GetObjectSet.Where(Function(st) _
st.TransferConfirm = True).Any(Function(x) x.Id = st.TransferOutID)).ToList

Linq does not group in VB.Net

For educational purposes I tried to convert the following Linq expression from the book "Linq in action" into VB.net
Original C#
var list =
from book in SampleData.Books
group book by new { book.Publisher.Name, book.Subject }
into grouping
select new {
Publisher = grouping.Key.Publisher,
Subject = grouping.Key.Subject,
Book = grouping
};
My attempt:
Dim list = _books.GroupBy(Function(book) New With {.Publisher = book.Publisher.Name,
book.Subject}).
Select(Function(grouping) New With {.Publisher = grouping.Key.Publisher,
.Subject = grouping.Key.Subject,
.Books = grouping})
For Each item In list
Console.WriteLine("Publisher:" & item.Publisher & ", Subject:" & item.Subject)
For Each Book In item.Books
Console.WriteLine(" " & Book.Title)
Next
Next
This leads to the following output:
Publisher:FunBooks, Subject:Fun
Funny Stories
Publisher:Joe Publishing, Subject:Work
LINQ rules
Publisher:Joe Publishing, Subject:Work
C# on rails
Publisher:Joe Publishing, Subject:Fun
All your base are belong to us
Publisher:FunBooks, Subject:Fun
Bonjour mon Amour
I expected, that the books "LINQ rules" and "C# on rails" are grouped as well as the books "Funny Stories" and "Bonjour mon Amour" because they have the same Publisher and Subject.
My anonymous key consists a new object of two simple strings.
I already tried to search in SO, but other (or) answers do not solve my problem. Even some code translators like telerik or carlosag are no help in this case.
This is the problem:
GroupBy(Function(book) New With {.Publisher = book.Publisher.Name,
book.Subject})
That's not equivalent to the C# version, because unfortunately VB uses mutable properties in anonymous types by default, and mutable properties aren't considered as part of the hash code or equality operations. You need to make both properties "Key" properties:
GroupBy(Function(book) New With {Key .Publisher = book.Publisher.Name,
Key book.Subject})
Then it should work fine. You can read more about anonymous types in VB on MSDN.
While I applaud your efforts to translate the samples, we actually have all of the samples for LINQ in Action in C# and VB available for download from the Manning Site: http://www.manning.com/marguerie/. Also, we have added samples to the LinqPad samples to make it easy to try the samples and save your changes. See http://www.thinqlinq.com/Default/LINQ-In-Action-Samples-available-in-LINQPad.aspx for instructions on how to access that.
It appears that you are working on example 5.06b. Updating it slightly, our VB translation is:
Dim query = _
From book In SampleData.Books _
Group book.Title By book.Publisher, book.Subject Into grouping = Group _
Select _
Publisher = Publisher.Name, _
Subject = Subject.Name, _
Titles = grouping
If you want to use the Lambda syntax, you do need to specify the Key as #johnskeet indicated:
Dim list = SampleData.Books.GroupBy(Function(book) New With {
Key .Publisher = book.Publisher.Name,
Key .Subject = book.Subject}).
Select(Function(grouping) New With {
.Publisher = grouping.Key.Publisher,
.Subject = grouping.Key.Subject,
.Books = grouping})

Accessing anonymous types returned via a dynamic link query

I have been trying to access the data returned from a dynamic linq query as an anonymous type. The advice I have found suggests that I should create a custom type and use it in the Select clause with the new keyword. I was directed to the followed Question for a code example:
System.LINQ.Dynamic: Select(" new (...)") into a List<T> (or any other enumerable collection of <T>)
This was indeed an excellent example which I incorporated into my code (which is VB so I had to do some translation).
My code compiles fine but When I run it , I get the error following error:
"Value cannot be null. Parameter name: member" at the following line from the example:
bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]);
This seems to be linked to expressions(i), which correctly contains two items as I am returning two fields from the database table. properties(i) holds the name of those two fields correctly. Any ideas as to what the value for member is supposed to be and where it should be found? Is it something from the database?
The where clause of this query works and when I run it as an anonymous type it brings back records (or rather two fields from records). The returned fields contain data not nulls.
Here is my VB version of the code from the example provided in the earlier Question. I have bolded or ** the line where the error occurs. Any ideas as to what is causing this?
Much appreciated.
Function ParseNew() As Expression
NextToken()
ValidateToken(TokenId.OpenParen, Res.OpenParenExpected)
NextToken()
Dim properties As New List(Of DynamicProperty)()
Dim expressions As New List(Of Expression)()
Do
Dim exprPos = tokenVal.pos
Dim expr = ParseExpression()
Dim propName As String
If TokenIdentifierIs("as") Then
NextToken()
propName = GetIdentifier()
NextToken()
Else
Dim [me] As MemberExpression = TryCast(expr, MemberExpression)
If [me] Is Nothing Then Throw ParseError(exprPos, Res.MissingAsClause)
propName = [me].Member.Name
End If
expressions.Add(expr)
properties.Add(New DynamicProperty(propName, expr.Type))
If tokenVal.id <> TokenId.Comma Then Exit Do
NextToken()
Loop
ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected)
NextToken()
'CODE added to support strongly-typed returns
Dim type As Type = If(newResultType, DynamicExpression.CreateClass(properties))
Dim bindings(properties.Count - 1) As MemberBinding
For i As Integer = 0 To bindings.Length - 1
**bindings(i) = Expression.Bind(type.GetProperty(properties(i).Name), expressions(i))**
Next
Return Expression.MemberInit(Expression.[New](type), bindings)
End Function

Linq Compiled Queries and int[] as parameter

I'm using the following LINQ to SQL compiled query.
private static Func<MyDataContext, int[], int> MainSearchQuery =
CompiledQuery.Compile((MyDataContext db, int[] online ) =>
(from u in db.Users where online.Contains(u.username)
select u));
I know it is not possible to use sequence input paramter for a compiled query and im getting “Parameters cannot be sequences” error when running it.
On another post here related , I saw that there is some solution but I couldn't understand it.
Does anyone know to use complied query with array as input paramter?
Please post example if you do.
Like the post that you referenced, it's not really possible out of the box. The post also references creating your own query provider, but it's a bit of overhead and complexity that you probably don't need.
You have a few options here:
Don't use a compiled query. Rather, have a method which will create a where clause from each item in the array resulting in something like this (psuedo-code):
where
online[0] == u.username ||
online[1] == u.username ||
...
online[n] == u.username
Note that you would have to use expression here to create each OR clause.
If you are using SQL Server 2008, create a scalar valued function which will take a table-valued parameter and a value to compare againt. It will return a bit (to indicate if the item is in the values in the table). Then expose that function through LINQ-to-SQL on your data context. From there, you should be able to create a CompiledQuery for that. Note that in this case, you should take an IEnumerable<string> (assuming username is of type string) instead of an array, just because you might have more than one way of representing a sequence of strings, and to SQL server for this operation, it won't matter what the order is.
One solution that I have found myself doing (for MS SQL 2005/2008). And I'm not sure if it is appropriate in all scenarios is to just write dynamic sql and execute it against the datacontext using the ExecuteQuery method.
For example, if I have an unbounded list that I am trying to pass to a query to do a contains...
' Mock a list of values
Dim ids as New List(of Integer)
ids.Add(1)
ids.Add(2)
' ....
ids.Add(1234)
Dim indivs = (From c In context.Individuals _
Where ids.Contains(c.Id) _
Select c).ToList
I would modify this query to create a SQL string to execute against the database directly like so...
Dim str As New Text.StringBuilder("")
Dim declareStmt as string = "declare #ids table (indivId int) " & vbcrlf)
For i As Integer = 0 To ids.Count - 1
str.Append("select " & ids(i).ToString() & " & vbcrlf)
If i < ids.Count Then
str.Append("union " & vbcrlf)
End If
Next
Dim selStatement As String = "select * From " & context.Mapping.GetTable(GetType(Individuals)).TableName & _
" indiv " & vbcrlf & _
" inner join #ids ids on indiv.id = ids.id"
Dim query = declareStmt & str.ToString & selStatement
Dim result = context.ExecuteQuery(of Individual)(query).ToList
So barring any syntax errors or bugs that I coded (the above is more or less psuedo code and not tested), the above will generate a table variable in SQL and execute an inner join against the desired table (Individuals in this example) and avoid the use of a "IN" statement.
Hope that helps someone out!

Resources