any way to create awaitable EF query? - vb.net

Is there any way to create an async function that returns an EF query result?
Any way I try to code it VS doesn't like it and says they aren't awaitable.
Private Async Function GetZiptaxByZipcode(ByVal zipcode As String) As Task(Of List(Of ziptax))
Using db
Dim list As List(Of ziptax) = Await (From d In db.ziptaxes
Where d.PostalCode = zipcode
Select d).ToList()
End Using
End Function
Error: System.Collections.Generic.List(of T) is not awaitable

You can't use async/wait this way. await must be applied on a Task, and your ToList() method doesn't return a task.
So you would need to use ToListAsync.
Do note this requires Entity Framework 6. See http://msdn.microsoft.com/en-us/data/jj819165.aspx

Related

How to get return value from function used in thread in vb. Net

Public function myfn1(byval pRequest as string) as string
Dim param(1) object
param(0)=pRequest
Dim T as new thread(Addresof myfn2)
T. Start(param)
End function
Private function myfn2(byval pReq as string) as string
'////some stuff here////
Return lstrResponse
End function
Here myfn1 is accepting requests from user. Sometimes the requests may be concurrent at a time. So I have used thread in myfn1. Myfn2 is actually processing the request and returning the response. So I am willing to get that response in myfn1 after the thread processed the task. What should I do? Or is there any other way out, Pls suggest
You should look into using the Async/Await structure : https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/concepts/async/
For Doing CPU bound work on a separate thread there are a couple options. I like using Task.Run() doc here: https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.run?view=netframework-4.8
You can awaitthe task you create and get the result when it's done like:
SomeVariable = Await Task.Run(Function() FunctionName)

Why Async function returning System.Threading.Tasks.Task`1[System.String]?

I have a VB.NET function as below:
Public Shared Async Function GetIdDoc() As Task(Of String)
Dim result As String = ""
'Dim Uri As String = "http://localhost:53917/api/Documenti/GetNextIdDocumenti"
Dim Uri As String = apiUri & ApiEndPoints.GetNextIdDocumenti
Using client = New HttpClient()
Using response = Await client.GetAsync(Uri)
If response.IsSuccessStatusCode Then
Dim DocumentiIDJsonString = Await response.Content.ReadAsStringAsync()
result = DocumentiIDJsonString.ToString()
End If
End Using
End Using
Return result
End Function
I'm trying to return the Document ID from the DB but I'm getting
System.Threading.Tasks.Task`1[System.String]
Where actually it should return "2". Please help me on this: what am I doing wrong with this function?
Update
here is the function called:
txtIDDoc_Detail.Text = ApiData.GetIdDoc().ToString()
But inside the textbox I'm getting the above text. thanks.
I'm from C# but should work the same. In newer .Net Versions (>= 4.5) async/await is implemented. So if a method is marked as async and returns a Task (which should always be the case), you need to await it. This implicate that you have to mark your Method as async too. So your call should look like this:
txtIDDoc_Detail.Text = await ApiData.GetIdDoc();
The await waits till the long running Task is ready and returns it's inner value. All async Methods should return Task. If the Method is void it would be Task. Else it could be Task<int> or any other type. So await it and you can keep running ;)
#Sebi gives a great explanation of how to properly use async and await in this case, but I'm going to expand on exactly why you are getting the result that you see.
txtIDDoc_Detail.Text = ApiData.GetIdDoc().ToString()
Is returning
System.Threading.Tasks.Task`1[System.String]
Because you are calling .ToString on the instance of the Task Task(Of String), not the actual result. Types that don't override .ToString inherit the behavior of Object, which just returns the name of the type as a string.
You probably want this (asynchronous call):
txtIDDoc_Detail.Text = await ApiData.GetIdDoc()
Or this (synchronous call):
txtIDDoc_Detail.Text = ApiData.GetIdDoc().Result
Either of these calls will actually result of the task after it has completed.

Partitioning lists to execute parallel tasks

I fire tasks to download multiple URLs.
Dim downloadTasksQuery As IEnumerable(Of Task(Of Boolean)) =
From company In companies Select DownloadCompanyFromYahooAsync(company, numberOfDays)
' ***Use ToList to execute the query and start the download tasks.
Dim downloadTasks As IEnumerable(Of Task(Of Boolean)) = downloadTasksQuery.ToList()
Await Task.WhenAll(downloadTasks)
The companies list is kind of containing 2000 URLs. I am observing that URLs added towards the end of the list are more frequently timing out. I have retry logics in place and am handling this timeout situation, which downloads the URL on the next try. However, I dont want to give preferential treatment to a URL just because it appears in the beginning of the list.
Hence was trying to think if we can fork 4 main tasks chunking the URL list into 500 each (probably more manageable) and then use the above code. However, am not able to figure out a way to introduce that without having to rewrite too much in the above code. Any help is greatly appreciated.
EDIT:
Something more like this:
Dim chunkPart As OrderablePartitioner(Of Tuple(Of Integer, Integer)) = Partitioner.Create(1, companies.Count, 500)
Parallel.ForEach(chunkPart, Sub(chunkRange)
For i As Integer = chunkRange.Item1 To chunkRange.Item2 - 1
Dim downloadTasksQuery As IEnumerable(Of Task(Of Boolean)) =
From company In companies.Skip(chunkRange.Item1).Take((chunkRange.Item2 - chunkRange.Item1) + 1) Select DownloadCompanyFromYahooAsync(company, numberOfDays)
Dim downloadTasks As IEnumerable(Of Task(Of Boolean)) = downloadTasksQuery.ToList()
Await Task.WhenAll(downloadTasks)
Next
End Sub
This is with minimal code changes, but the issue is that I cannot use Await inside a Parallel.ForEach.
Any suggestions pls to change this.
Not a VB.NET guy, but I think Stephen Toub's good post on implementing a simple ForEachAsync might be helpful to you.
Some code snippet from his post, it allow you to limit the number of operatons that are able to run in parallel.
public static Task ForEachAsync<T>(this IEnumerable<T> source, int dop, Func<T, Task> body)
{
return Task.WhenAll(
from partition in Partitioner.Create(source).GetPartitions(dop)
select Task.Run(async delegate {
using (partition)
while (partition.MoveNext())
await body(partition.Current);
}));
}
For your specific question, you can then use this as such:
public async Task DownloadForAllCompanies(List<string> companies, int numberOfDays)
{
await companies.ForEachAsync(4, async company =>
{
await DownloadCompanyFromYahooAsync(company, numberOfDays);
});
}

How do I optimize this EF query and implement paging?

I have about 14,000 rows of data. If use the following EF query, its a long time to load because I suspect it is loading all 14,000 rows and only then is any additional filtering done. This is my Select method in my repository.
Public Function SelectAll() As IEnumerable(Of be_Posts) Implements IPostRepository.SelectAll
Dim posts As IEnumerable(Of be_Posts)
Using db As Ctx = New Ctx
posts = db.be_Posts.OrderByDescending(Function(x) x.DateCreated).ToList
Return posts
End Using
And Controller:
Function Index(page As Integer?) As ActionResult
Dim PageSize = System.Web.Configuration.WebConfigurationManager.AppSettings("PageSize")
Dim pageNumber As Integer = If(page, 1)
Dim posts = _repo.SelectAll()
Return View(posts.ToPagedList(pageNumber, PageSize))
End Function
Helper in View:
#Html.PagedListPager((Model), Function(page) Url.Action("Index", New With { _
.page = page _
}), PagedListRenderOptions.ClassicPlusFirstAndLast)
Now If add in a take clause, for example .Take(500) then things are dramatically faster. How can I make this query faster and still work will all the records? I am also using Troy Goode's PagedList extension to get paging functionality. Everything is fine as long as i get just a few hundred records. So what to do? Most if not all examples of paging that I can find that use Troy's library involve all the code directly in the controller.
Calling ToList executes the query and, as you say, it's getting every record. Paging is done using Skip and Take, e.g.
Dim page = list.Skip(pageSize * (pageNumber - 1)).Take(pageSize)
The source list can be the table itself or the result of a Where or OrderBy call or whatever. Just make sure that ToList or, preferably, ToArray is called last.

Iterator pattern in VB.NET (C# would use yield!) [duplicate]

This question already has answers here:
Yield in VB.NET
(8 answers)
Closed 9 years ago.
How do implement the iterator pattern in VB.NET, which does not have the yield keyword?
This is now supported in VS 2010 SP1, with the Async CTP, see: Iterators (C# and Visual Basic) on MSDN and download Visual Studio Async CTP (Version 3).
Code such as this, works:
Private Iterator Function SomeNumbers() As IEnumerable
' Use multiple yield statements.
Yield 3
Yield 5
Yield 8
End Function
VB.NET does not support the creation of custom iterators and thus has no equivalent to the C# yield keyword. However, you might want to look at the KB article How to make a Visual Basic .NET or Visual Basic 2005 class usable in a For Each statement for more information.
C#'s yield keyword forces the compiler to create a state machine in the background to support it. VB.Net does not have the yield keyword. But it does have a construct that would allow you to create a state machine within a function: Static function members.
It should be possible to mimic the effects of a yield return function by creating a generic class that implements IEnumerable as well as the needed state machine and placing an instance as a static member inside your function.
This would, of course, require implementing the class outside of the function. But if done properly the class should be re-usable in the general case. I haven't played with the idea enough to provide any implementation details, though.
Hmm, looks like you might be out of luck:
I was struggling with an issue today when converting some C# to VB.NET. C# has a really cool "yield return" statement that is used in an iterator block to provide a value to the enumerator object. VB.NET does not have the "yield" keyword. So, there are a few solutions (none of which are really clean) to get around this. You could use a return statement to return the value if you are looping through and would like to break an enumerator and return a single value. However, if you'd like to return the entire enumeration, create a List() of the child type and return the list. Since you are usually using this with an IEnumerable, the List() will work nice.
That was written a year ago, not sure if anyone has come up with anything else better since then..
Edit: this will be possible in the version 11 of VB.NET (the one after VS2010), support for iterators is planned. The spec is available here.
Keep in mind that deferred execution and lazy evaluation properties of LINQ expresssions and methods allow us to effectively implement custom iterators until the yield statement is available in .NET 4.5. Yield is used internally by LINQ expressions and methods.
The following code demonstrates this.
Private Sub AddOrRemoveUsersFromRoles(procName As String,
applicationId As Integer,
userNames As String(),
rolenames As String())
Dim sqldb As SqlDatabase = CType(db, SqlDatabase)
Dim command As DbCommand = sqldb.GetStoredProcCommand(procName)
Dim record As New SqlDataRecord({New SqlMetaData("value", SqlDbType.VarChar,200)})
Dim setRecord As Func(Of String, SqlDataRecord) =
Function(value As String)
record.SetString(0, value)
Return record
End Function
Dim userNameRecords As IEnumerable(Of SqlDataRecord) = userNames.Select(setRecord)
Dim roleNameRecords As IEnumerable(Of SqlDataRecord) = rolenames.Select(setRecord)
With sqldb
.AddInParameter(command, "userNames", SqlDbType.Structured, userNameRecords)
.AddInParameter(command, "roleNames", SqlDbType.Structured, roleNameRecords)
.AddInParameter(command, "applicationId", DbType.Int32, applicationId)
.AddInParameter(command, "currentUserName", DbType.String, GetUpdatingUserName)
.ExecuteNonQuery(command)
End With
End Sub
Below gives output: 2, 4, 8, 16, 32
In VB.NET
Public Shared Function setofNumbers() As Integer()
Dim counter As Integer = 0
Dim results As New List(Of Integer)
Dim result As Integer = 1
While counter < 5
result = result * 2
results.Add(result)
counter += 1
End While
Return results.ToArray()
End Function
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
For Each i As Integer In setofNumbers()
MessageBox.Show(i)
Next
End Sub
In C#
private void Form1_Load(object sender, EventArgs e)
{
foreach (int i in setofNumbers())
{
MessageBox.Show(i.ToString());
}
}
public static IEnumerable<int> setofNumbers()
{
int counter=0;
//List<int> results = new List<int>();
int result=1;
while (counter < 5)
{
result = result * 2;
counter += 1;
yield return result;
}
}