Save a Collection of objects to disk Windows 10 Universal App - vb.net

I have a collection of objects which also contain collections, and I would like to save them to a file and read later. This is Windows 10 UNIVERSAL app .net 4.5 and in VB
I've this, but it gives the error "Synchronous operations should not be performed on the UI thread. Consider wrapping this method in Task.Run."}
Public Sub SaveSpRules(FileName As String)
Dim fs As New FileStream(FileName, FileMode.OpenOrCreate)
Dim serializer As New XmlSerializer(GetType(Rule))
Dim writer As New StreamWriter(fs)
serializer.Serialize(writer, r)
End Sub
Rule is class and the collection is called SpecialRulesCollection, this code does compile but doesn't work
Any help is gratefully received. PLEASE note this for Windows 10 Universal app, I can't get BinaryWriter to compile as this isn't include in the Universal app.

Thanks Oyiwai I found the answer
Public Async Sub SaveSpRules(FileName As String, t As Object)
Dim myfile As StorageFile
myfile = Await ApplicationData.Current.LocalFolder.CreateFileAsync("test.dat", CreationCollisionOption.ReplaceExisting)
Dim KnownTypeList As New List(Of Type)
KnownTypeList.Add(GetType(Rules))
KnownTypeList.Add(GetType(Rule))
Dim r As Streams.IRandomAccessStream
r = Await myfile.OpenAsync(FileAccessMode.ReadWrite)
Using outStream As Streams.IOutputStream = r.GetOutputStreamAt(0)
Dim serializer As New DataContractSerializer(GetType(Rules), KnownTypeList)
serializer.WriteObject(outStream.AsStreamForWrite(), t)
Await outStream.FlushAsync()
outStream.Dispose()
r.Dispose()
End Using
End Sub
Public Async Sub ReadFile()
Dim t As New Collection(Of Rule)
Dim myfile As Stream
Dim r As New Rules
Dim KnownTypeList As New List(Of Type)
KnownTypeList.Add(GetType(Rules))
Dim serializer As New DataContractSerializer(GetType(Rules), KnownTypeList)
myfile = Await ApplicationData.Current.LocalFolder.OpenStreamForReadAsync("test.dat")
r = serializer.ReadObject(myfile)
End Sub

Related

How can i pass a non specific class to sub

I have code which takes a Soap Response and serializes into a xml so i can write it to console. Code works great but as i am trying to clean up my code i am trying to stick the code into a sub so i can use it for different responses.
Dim Response As ATTSoapReference.ATT_ADDR_VAL_RESP = newRequest.submitAddVal(ATT_ADDR)
Dim serxml = New System.Xml.Serialization.XmlSerializer(Response.GetType())
Dim ms = New MemoryStream()
serxml.Serialize(ms, Response)
Dim xml As String = Encoding.UTF8.GetString(ms.ToArray())
Console.WriteLine(xml)
If i want to do it as a sub for only this request type i can use
Private Sub DebugXML(byval myResponse As ATTSoapReference.ATT_ADDR_VAL_RESP)
Dim serxml = New System.Xml.Serialization.XmlSerializer(myResponse.GetType())
Dim ms = New MemoryStream()
serxml.Serialize(ms, myResponse)
Dim xml As String = Encoding.UTF8.GetString(ms.ToArray())
Console.WriteLine(xml)
End Sub
But i am looking for something that will allow me to pass any Soap Data Class to it instead of ATTSoapReference.ATT_ADDR_VAL_RESP
Try as said below and see if it works.
Private Sub DebugXML(byval myResponse As Object)
'May be you require casting, for some specific things.
End Sub

VB.NET Return Form Object using Form Name

I'm basically writing a custom Error Logging Form for one of my applications because users cannot be trusted to report the errors to me.
I am obtaining the Form Name using the 'MethodBase' Object and then getting the DeclaringType Name.
Dim st As StackTrace = New StackTrace()
Dim sf As StackFrame = st.GetFrame(1)
Dim mb As MethodBase = sf.GetMethod()
Dim dt As String = mb.DeclaringType.Name
How can I then use this to obtain the Form Object so I can pass this to my 'screenshot method' that screenshots the particular form referenced.
Public Sub SaveAsImage(frm As Form)
'Dim fileName As String = "sth.png"
'define fileName
Dim format As ImageFormat = ImageFormat.Png
Dim image = New Bitmap(frm.Width, frm.Height)
Using g As Graphics = Graphics.FromImage(image)
g.CopyFromScreen(frm.Location, New Point(0, 0), frm.Size)
End Using
image.Save(_LogPath & Date.Now.ToString("ddMMyyyy") & ".png", format)
End Sub
I posted the same solution to a similar question. Try this:
Dim frm = Application.OpenForms.Item(dt)

Serialization of a class cause error

Windows 10 universal app. VB
I have the class below which I want to save to file and read from file, Saving works but when I attempt to load I get the error.
Message=Error in line 1 position 249. Element 'http://schemas.datacontract.org/2004/07/KoW_Universal_v2:MagicItem' contains data of the 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:ArrayOfKeyValueOfstringMagicItemyoeMIiQz' data contract. The deserializer has no knowledge of any type that maps to this contract. Add the type corresponding to 'ArrayOfKeyValueOfstringMagicItemyoeMIiQz' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.
Source=System.Private.DataContractSerialization
Imports System.Runtime.Serialization
Imports Windows.Storage
Public Class MagicItem
Property MagicItemName As String
Property MagicItemCost As Integer
Property MagicItemDescripyion As String
Property LimitToHero As Boolean
Property LimitToSpecialRule As String 'key to the special rule
End Class
Public Class clsMagicItems
Private MagicItems As New Dictionary(Of String, MagicItem)
Async Sub SaveMagicItems()
' StorageFile File = Await openPicker.PickSingleFileAsync();
Dim myfile As StorageFile
myfile = Await ApplicationData.Current.LocalFolder.CreateFileAsync("MagicItems.dat", CreationCollisionOption.ReplaceExisting)
Dim KnownTypeList As New List(Of Type)
KnownTypeList.Add(GetType(clsMagicItems))
KnownTypeList.Add(GetType(MagicItem))
Dim r As Streams.IRandomAccessStream
r = Await myfile.OpenAsync(FileAccessMode.ReadWrite)
Using outStream As Streams.IOutputStream = r.GetOutputStreamAt(0)
Dim serializer As New DataContractSerializer(GetType(clsMagicItems), KnownTypeList)
serializer.WriteObject(outStream.AsStreamForWrite(), MagicItems)
Await outStream.FlushAsync()
outStream.Dispose()
r.Dispose()
End Using
End Sub
Async Sub LoadMagicItems()
Try
Dim myfile As Stream
Dim KnownTypeList As New List(Of Type)
KnownTypeList.Add(GetType(clsMagicItems))
KnownTypeList.Add(GetType(MagicItem))
Dim serializer As New DataContractSerializer(GetType(clsMagicItems), KnownTypeList)
myfile = Await ApplicationData.Current.LocalFolder.OpenStreamForReadAsync("MagicItems.dat")
'LINE BELOW RAISE ERROR
MagicItems = serializer.ReadObject(myfile)
Catch
'failed to load the file
End Try
End Sub
End Class
Fixed it, should have had the following code
KnownTypeList.Add(GetType(Dictionary(Of String, MagicItem)))
...
Dim serializer As New DataContractSerializer(GetType(Dictionary(Of String, MagicItem)), KnownTypeList)

VB.NET How to parse and add the result into the list(of string) from json file?

I'm creating a launcher for Minecraft. I have a problem, My launcher using json files to load and check files. How can I add the strings from this json (example) into the AssetsList? https://s3.amazonaws.com/Minecraft.Download/indexes/1.8.json
My code, if it helps you to understand me (I'm using Newtonsoft.json to parse json):
The MCAssets class:
Public Class MCAssets
Public hash As String
End Class
The list:
Public AssetsList As New List(Of String)
The funchtion to get the assets:
Public Async Function GetAssets() As Task
If Not Directory.Exists(Root + "\assets\indexes") Then
Directory.CreateDirectory(Root + "\assets\indexes")
End If
Dim client = New WebClient()
Await client.DownloadFileTaskAsync(New Uri(String.Format("http://s3.amazonaws.com/Minecraft.Download/indexes/{0}.json", AssetIndex)), String.Format(Root + "\assets\indexes\{0}.json", AssetIndex))
Dim reader As New StreamReader(Root + "\assets\indexes\" + AssetIndex + ".json")
Dim assets As String = reader.ReadToEnd()
reader.Close()
Dim jsonresult = JsonConvert.DeserializeObject(Of Object)(assets)
For Each i In jsonresult("objects").Children()
AssetsList.Add(i.ToObject(Of MCAssets).hash)
Next
End Function
If you want know more about Minecraft assets, visit this: https://github.com/tomsik68/mclauncher-api/wiki/Minecraft-1.6-resources
You can do something like this:
Dim assetsObject = JsonConvert.DeserializeObject(Of JObject)(assets) 'assets is your json file
Dim allAssets = (From i In assetsObject("objects").Children() _
Select New MCAssets() With {.hash = i.First.Value(Of String)("hash")}).ToList()
By the way, there is not really a need to make a custom class. You can just add all of the hashes to a list of string like so:
Dim assetsObject = JsonConvert.DeserializeObject(Of JObject)(assets)
Dim allAssets = (From i In assetsObject("objects").Children() _
Select i.First.Value(Of String)("hash"))

How to throttle concurrent Async webrequests

I often need to make a large number of webrequests, without overloading the network
I currently do this by running synchronous requests in parallel, utilizing ThreadPool.SetMinThreads and MaxDegreeOfParallelism to exactly specify how many requests run concurrently
Now this works just fine, but it feels wrong.
I would really like to utilize async methods, but i cant work out how to limit the number of concurrent requests.
A simplified example of my parallel way of doing this( using a webclient and no error handling for brevity):
Private Function SearchSitesForKeywordInParallel(ByVal keyword As String, ByVal sites As String(), ByVal maxConcurrency As Integer) As String()
Dim po As New ParallelOptions
po.MaxDegreeOfParallelism = maxConcurrency
Threading.ThreadPool.SetMinThreads(maxConcurrency, 2)
Dim sitesContainingKeyword As New Concurrent.ConcurrentBag(Of String)
Parallel.For(0, sites.Count, po, Sub(i)
Dim wc As New Net.WebClient
wc.Proxy = Nothing
Dim pageSource As String = wc.DownloadString(sites(i))
If pageSource.Contains(keyword) Then
sitesContainingKeyword.Add(sites(i))
End If
End Sub)
Return sitesContainingKeyword.ToArray
End Function
This is a blocking function, which is what i require.
Now i have tested the webclient.downloadStringAsync method in a regular for loop, and it will fire all the requests pretty much at once, overloading the network.
What i would like to do is initially make X requests, then make new ones as each response comes back.
I am fairly sure tasks is the way to go, and im positive a have read some very nice implementations in c#, but my c# experience is limited, and i have a hard time translating c# lambadas to vb.net.
I am also limited to vs2010 and .net4, so the niceties of .net4.5 async await are not an option for me.
Any help very much appreciated
Not sure, if I understand completey, what exactly you want to achieve, but if you want to use aync methods, you can do it like this:
Dim google As String = "http://www.google.com/#&q="
Dim qsites As New Concurrent.ConcurrentQueue(Of String)
For Each k In {"foo", "bar", "john", "jack", "stackoverflow", "basic", "ship", "car", "42"}
qsites.Enqueue(google & k)
Next
Dim cde As New System.Threading.CountdownEvent(qsites.Count)
Dim strings As New Concurrent.ConcurrentBag(Of String)
Dim completedhandler = Sub(wco As Object, ev As Net.DownloadStringCompletedEventArgs)
Dim wc = DirectCast(wco, Net.WebClient)
Debug.Print("got one!")
strings.Add(ev.Result)
cde.Signal()
Dim s As String = String.Empty
If qsites.TryDequeue(s) Then
Debug.Print("downloading from {0}", s)
wc.DownloadStringAsync(New Uri(s))
End If
End Sub
Dim numthreads As Integer = 4
System.Threading.Tasks.Task.Factory.StartNew(Sub()
For i = 1 To numthreads
Dim s As String = String.Empty
If qsites.TryDequeue(s) Then
Dim wc As New Net.WebClient
wc.Proxy = Nothing
AddHandler wc.DownloadStringCompleted, completedhandler
Debug.Print("downloading from {0}", s)
wc.DownloadStringAsync(New Uri(s))
End If
Next
End Sub)
cde.Wait()
You only need to "start" the async downloads in a different thread/task because (afaik) the WC's downloadcompleted events fire in the UI thread (or currentsync..context) and the cde.wait would then not allow the events to be handled.
I just want to add another answer to this as I have recently solved a similar problem (note the code snippet is in C#, but should give the idea).
I used to have number of parallel http synchronous requests sent to http server on different thread and used to limit the number of requests I sent using semaphore.
Now, I have adapted to new TPL (c# 5.0 - aysn/await - quite handy (basically continuation introduced in TPL sound natural to me - and with async/await it has become much easier to use)), to invoke network I/O asynchronously.
i.e. ideally now I will be using only one thread in caller (unless I really need to get results before continuing), and let .net, os and I/o completion port threads work together to invoke my continuation code in thread pool to complete operation (basically 'callback' in APM, on completed event in event based pattern, 'continuation' in TPL, code after await in C# 5.0 (4.5 .net))
The principle I followed when I have embraced async i/o is simple - don't let thread wait and waste CPU and resources, unless it is really necessary!
You can do this asynchronously in VB.NET using the Wintellect Powerthreading library's AsyncEnumerator class, which you can get from NuGet.
This gives you some of the functionality of Await but works in VS2010 with .Net 2.0 to 4.0 while giving you an upgrade path to the 4.5 async features.
The downside is that the WebClient async methods require an EAP-to-APM shim based on Task<> to be used with AsyncEnumerator, so the code is quite a lot more complicated.
The simplest way to control the number of concurrent requests is to initiate X async operations, then just initiate another every time one completes.
Example code:
Imports System.Collections.Generic
Imports System.Runtime.CompilerServices
Imports System.Threading.Tasks
Imports System.Net
Imports Wintellect.Threading.AsyncProgModel
Module TaskExtension
REM http://msdn.microsoft.com/en-us/library/hh873178.aspx
<Extension()>
Public Function AsApm(Of T1)(ByVal task As Task(Of T1), callback As AsyncCallback, state As Object) As IAsyncResult
If (task Is Nothing) Then
Throw New ArgumentNullException("task")
End If
Dim tcs = New TaskCompletionSource(Of T1)(state)
task.ContinueWith(Sub(t As Task(Of T1))
If (t.IsFaulted) Then
tcs.TrySetException(t.Exception.InnerExceptions)
ElseIf t.IsCanceled Then
tcs.TrySetCanceled()
Else : tcs.TrySetResult(t.Result)
End If
If (Not callback Is Nothing) Then
callback(tcs.Task)
End If
End Sub, TaskScheduler.Default)
Return tcs.Task
End Function
End Module
Module ApmAsyncDownload
Public Function DownloadStringAsync(url As Uri) As Task(Of String)
Dim tcs As New TaskCompletionSource(Of String)
Dim wc As New WebClient()
AddHandler wc.DownloadStringCompleted, Sub(s As Object, e As System.Net.DownloadStringCompletedEventArgs)
If (Not (e.Error Is Nothing)) Then
tcs.TrySetException(e.Error)
ElseIf e.Cancelled Then
tcs.TrySetCanceled()
Else : tcs.TrySetResult(e.Result)
End If
End Sub
wc.DownloadStringAsync(url)
Return tcs.Task
End Function
Public Function BeginDownloadString(url As Uri, callback As AsyncCallback, state As Object) As IAsyncResult
Return DownloadStringAsync(url).AsApm(callback, state)
End Function
Public Function EndDownloadString(asyncResult As IAsyncResult) As String
Dim castToTask As Task(Of String) = asyncResult
Return castToTask.Result
End Function
End Module
Public Class AsyncIterators
Private Shared Iterator Function SearchUrl(ae As AsyncEnumerator(Of Boolean), keyword As String, uri As Uri) As IEnumerator(Of Int32)
ae.Result = False
ApmAsyncDownload.BeginDownloadString(uri, ae.End(0, AddressOf ApmAsyncDownload.EndDownloadString), Nothing)
Yield 1
If (ae.IsCanceled()) Then
Return
End If
Try
Dim page As String = ApmAsyncDownload.EndDownloadString(ae.DequeueAsyncResult)
ae.Result = page.Contains(keyword)
Catch ex As AggregateException
End Try
End Function
Public Shared Iterator Function SearchIterator(ae As AsyncEnumerator(Of List(Of String)), keyword As String, urls As List(Of Uri)) As IEnumerator(Of Int32)
ae.Result = New List(Of String)
'Control how many searches are started asynchonously
Dim startSearches = Math.Min(3, urls.Count)
Dim enumerator = urls.GetEnumerator
Dim toBeCompleted = urls.Count
Do Until (toBeCompleted <= 0)
While (startSearches > 0)
If enumerator.MoveNext Then
Dim subAe = New AsyncEnumerator(Of Boolean)()
subAe.SyncContext = Nothing
subAe.BeginExecute(SearchUrl(subAe, keyword, enumerator.Current), ae.End(0, Function(ar As IAsyncResult) As AsyncEnumerator.EndObjectXxx
subAe.EndExecute(ar)
End Function), enumerator.Current)
End If
startSearches = startSearches - 1
End While
'Wait for first async search to complete
Yield 1
toBeCompleted = toBeCompleted - 1
If (ae.IsCanceled()) Then
Exit Do
End If
'Get result of the search and add to results
Dim result = ae.DequeueAsyncResult()
Dim completedAe = AsyncEnumerator(Of Boolean).FromAsyncResult(result)
If (completedAe.EndExecute(result)) Then
Dim uri As Uri = result.AsyncState
ae.Result.Add(uri.OriginalString)
End If
'Start 1 more search
startSearches = startSearches + 1
Loop
End Function
End Class
Module Module1
Sub Main()
Dim searchAe = New AsyncEnumerator(Of List(Of String))()
searchAe.SyncContext = Nothing
Dim urlStrings = New List(Of String) From {"http://www.google.com", "http://www.yahoo.com", "http://www.dogpile.com"}
Dim uris = urlStrings.Select(Function(urlString As String) As Uri
Return New Uri(urlString)
End Function).ToList()
For Each Str As String In searchAe.EndExecute(searchAe.BeginExecute(AsyncIterators.SearchIterator(searchAe, "search", uris), Nothing, Nothing))
Console.WriteLine(Str)
Next
Console.ReadKey()
End Sub
End Module
And I now see what you mean about translating c# lambdas!