Compressing a database to a single file? - vb.net

In a contact manager program I've been storing data in CSV files for each contact and would like a way to compress this data into a single file.
I have attempted using data entry tools in the visual studio toolbox and template class, though I have never quite figured out how to use them. What would be especially convenient is if I could somehow store a generic class instance as opposed to having to come up with a string representation of it, and then parse it.
I'd also need to figure out how to tell the program what to do when a file is opened (I've noticed in the properties how to associate a file type with the program though am not sure how to tell it what to do when it's opened).

IMHO, switch to sqlite. You'll be able to query faster, compress it, and much more then working with a csv file.

Found this article about Serialization and it works very well!
http://www.vbdotnetheaven.com/UploadFile/Nimusoft/Serialization105022008052559AM/Serialization1.aspx
edit: Figured I should probably post more. I have a class IOwner, and list of this class contains all of my database. So I added serialization tags to this class and others it references then substitute in those properties with the ones shown on the article:
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.IO
Namespace BinarySerialization
<Serializable()> _
Public Class IFile
Implements ISerializable
Public Contacts As List(Of IOwner)
Public Self As IOwner
Public Cars As List(Of Vehicle)
Public path As String
Public Sub New()
End Sub
Public Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
Data.Self = info.GetValue("Self", GetType(IOwner))
Data.Contacts = info.GetValue("Contacts", GetType(List(Of IOwner)))
Data.Cars = info.GetValue("Cars", GetType(List(Of Vehicle)))
End Sub
Public Sub WriteFile()
Dim s As New FileStream(path, FileMode.Create, FileAccess.ReadWrite)
Dim B As New BinaryFormatter()
B.Serialize(s, Me)
s.Close()
End Sub
Public Function ReadFile() As IFile
Dim Fs As New FileStream(path, FileMode.Open, FileAccess.Read)
Dim F As New BinaryFormatter()
Dim s1 As IFile = DirectCast(F.Deserialize(Fs), IFile)
Fs.Close()
Return s1
End Function
#Region "ISerializable Members"
Public Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext) Implements ISerializable.GetObjectData
info.AddValue("Self", Data.Self)
info.AddValue("Contacts", Data.Contacts)
info.AddValue("Cars", Data.Cars)
End Sub
#End Region
End Class
End Namespace
UPDATE 2016
Binary serialization is not advisable for complex structures that are subject to change. Using a database storage method such as SQLite and using an ODB (object database model) would have been preferable.

Related

Visual Studio - Autocomplete words for custom Objects / Libraries

When typing code in Visual Studio 2015 it automatically gives me alternatives on what I can do with my code:
I was wondering if there is a way to enable autocomplete on imported Objects / Libraries (.dll) files?
I am using IBM 3270 and have imported all of the libraries and added them as references to my project, but I still don't get any alternatives.
Here is an example of the code and what I believe it should do:
' The Imports are already referenced to the project.
' I have added them here to visually display that I am using the libraries.
Imports AutConnListTypeLibrary
Imports AutConnMgrTypeLibrary
Imports AutOIATypeLibrary
Imports AutPSTypeLibrary
Imports AutSystemTypeLibrary
Public Class IBM3270
Public Shared autECLConnList As Object
autECLConnList = CreateObject("PCOMM.autECLConnList")
autECLConnList. ' <- I believe that I should get autocomplete words here, but I don't.
' Some of the alternatives that should pop up are:
' autECLConnList.Count
' autECLConnList.Refresh
End Class
Link to IBM Knowledge Center
EDIT: I have rewritten the code and the Autocomplete words appears, but I can't figure out how I can execute it now. Previously I used CreateObject("PCOMM.autECLConnList") to access the object.
Here is what I have:
Imports AutConnListTypeLibrary
Public Class IBM3270
Implements AutConnList
Default Public ReadOnly Property ConnInfo(Index As Object) As Object Implements IAutConnList.ConnInfo
Get
Throw New NotImplementedException()
End Get
End Property
Public ReadOnly Property Count As Integer Implements IAutConnList.Count
Get
Throw New NotImplementedException()
End Get
End Property
Public Sub Refresh() Implements IAutConnList.Refresh
Throw New NotImplementedException()
End Sub
Public Shared Sub Test() ' Here is where I am stuck.
Dim autECLConnList As AutConnList ' I believe I can't reference AutConnList. ("PCOMM.autECLConnList") should probably be added someplace.
autECLConnList.Refresh
End Sub
End Class

RAII XmlWriter VisualBasic

I'm just picking up VB, so this is probably a noob question. Apologies in advance.
I'm trying to wrap an XmlWriter with a class to manage memory, flushing, closing etc... so that clients don't need to worry about it.
My code is:
Option Explicit On
Option Strict Off
Imports System.Xml
Imports System.IO
Module MainModule
Sub Main()
Dim xmltest As XmlOutput = New XmlOutput("output.xml", "test")
xmltest.writeData("hello", "19.95")
End Sub
End Module
Public Class XmlOutput
Private xmlWriter As XmlWriter
Public Sub New(ByVal filename As String, ByVal calculator As String)
xmlWriter = XmlWriter.Create(filename)
xmlWriter.WriteStartDocument(True)
xmlWriter.WriteStartElement(calculator)
End Sub
Protected Overrides Sub Finalize()
xmlWriter.WriteEndElement()
xmlWriter.WriteEndDocument()
xmlWriter.Dispose()
End Sub
Public Sub writeData(ByVal head As String, ByVal value As String)
xmlWriter.WriteElementString(head, value)
End Sub
End Class
When I run this I get the following error:
System.ObjectDisposedException was unhandled
HResult=-2146232798
Message=Cannot access a closed file.
ObjectName=""
Source=mscorlib
StackTrace:
at System.IO.__Error.FileNotOpen()
at System.IO.FileStream.Flush(Boolean flushToDisk)
at System.IO.FileStream.Flush()
at System.Xml.XmlUtf8RawTextWriter.Close()
at System.Xml.XmlRawWriter.Close(WriteState currentState)
at System.Xml.XmlWellFormedWriter.Close()
at System.Xml.XmlWriter.Dispose(Boolean disposing)
at System.Xml.XmlWriter.Dispose()
at XmlOutputTest.XmlOutput.Finalize()
InnerException:
This looks like the XmlWriter has closed the FileStream before the XmlWriter is closed. Since the XmlWriter has responsibility for the FileStream I expected it to know when the FileStream had been closed.
Questions:
Why is the XmlWriter trying to close something it's already closed (or maybe I'm interpreting the stack trace incorrectly)?
What do I need to do to fix this?

Showing progress of ZipFiles Class

I was wondering, how can I get the percentage of this being done, so I can display it on a progress bar?
ZipFile.CreateFromDirectory("C:\temp\folder", "C:\temp\folder.zip")
and also
ZipFile.ExtractToDirectory("C:\temp\folder.zip", "C:\temp\folder")
This doesnt have any events or callbacks that you can use to report progress. Simply means you cant with the .Net version. If you used the 7-Zip library you can do this easily.
I came across this question while checking for related questions for the identical question, asked for C# code. It is true that the .NET static ZipFile class does not offer progress reporting. However, it is not hard to do using the ZipArchive implementation, available since earlier versions of .NET.
The key is to use a Stream wrapper that will report bytes read and written, and insert that in the data pipeline while creating or extracting the archive.
I wrote a version in C# for an answer to the other question, and since I didn't find any VB.NET examples, figured it would be helpful to include a VB.NET version on this question.
(Arguably, I could include both examples in a single answer and propose closing one of the questions as a duplicate of the other. But since it's doubtful the close vote would result in an actual closure, the connection between the two questions would not be as obvious as it should be. I think for best visibility to future users trying to find the solution appropriate for their needs, leaving this as two different questions is better.)
The foundation of the solution is the Stream wrapper class:
StreamWithProgress.vb
Imports System.IO
Public Class StreamWithProgress
Inherits Stream
' NOTE For illustration purposes. For production code, one would want To
' override *all* of the virtual methods, delegating to the base _stream object,
' to ensure performance optimizations in the base _stream object aren't
' bypassed.
Private ReadOnly _stream As Stream
Private ReadOnly _readProgress As IProgress(Of Integer)
Private ReadOnly _writeProgress As IProgress(Of Integer)
Public Sub New(Stream As Stream, readProgress As IProgress(Of Integer), writeProgress As IProgress(Of Integer))
_stream = Stream
_readProgress = readProgress
_writeProgress = writeProgress
End Sub
Public Overrides ReadOnly Property CanRead As Boolean
Get
Return _stream.CanRead
End Get
End Property
Public Overrides ReadOnly Property CanSeek As Boolean
Get
Return _stream.CanSeek
End Get
End Property
Public Overrides ReadOnly Property CanWrite As Boolean
Get
Return _stream.CanWrite
End Get
End Property
Public Overrides ReadOnly Property Length As Long
Get
Return _stream.Length
End Get
End Property
Public Overrides Property Position As Long
Get
Return _stream.Position
End Get
Set(value As Long)
_stream.Position = value
End Set
End Property
Public Overrides Sub Flush()
_stream.Flush()
End Sub
Public Overrides Sub SetLength(value As Long)
_stream.SetLength(value)
End Sub
Public Overrides Function Seek(offset As Long, origin As SeekOrigin) As Long
Return _stream.Seek(offset, origin)
End Function
Public Overrides Sub Write(buffer() As Byte, offset As Integer, count As Integer)
_stream.Write(buffer, offset, count)
_writeProgress?.Report(count)
End Sub
Public Overrides Function Read(buffer() As Byte, offset As Integer, count As Integer) As Integer
Dim bytesRead As Integer = _stream.Read(buffer, offset, count)
_readProgress?.Report(bytesRead)
Return bytesRead
End Function
End Class
The wrapper class can be used to implement progress-aware versions of the ZipFile static methods:
ZipFileWithProgress.vb
Imports System.IO
Imports System.IO.Compression
NotInheritable Class ZipFileWithProgress
Private Sub New()
End Sub
Public Shared Sub CreateFromDirectory(
sourceDirectoryName As String,
destinationArchiveFileName As String,
progress As IProgress(Of Double))
sourceDirectoryName = Path.GetFullPath(sourceDirectoryName)
Dim sourceFiles As FileInfo() = New DirectoryInfo(sourceDirectoryName).GetFiles("*", SearchOption.AllDirectories)
Dim totalBytes As Double = sourceFiles.Sum(Function(f) f.Length)
Dim currentBytes As Long = 0
Using archive As ZipArchive = ZipFile.Open(destinationArchiveFileName, ZipArchiveMode.Create)
For Each fileInfo As FileInfo In sourceFiles
' NOTE: naive method To Get Sub-path from file name, relative to
' input directory. Production code should be more robust than this.
' Either use Path class Or similar to parse directory separators And
' reconstruct output file name, Or change this entire method to be
' recursive so that it can follow the sub-directories And include them
' in the entry name as they are processed.
Dim entryName As String = fileInfo.FullName.Substring(sourceDirectoryName.Length + 1)
Dim entry As ZipArchiveEntry = archive.CreateEntry(entryName)
entry.LastWriteTime = fileInfo.LastWriteTime
Using inputStream As Stream = File.OpenRead(fileInfo.FullName)
Using outputStream As Stream = entry.Open()
Dim progressStream As Stream = New StreamWithProgress(inputStream,
New BasicProgress(Of Integer)(
Sub(i)
currentBytes += i
progress.Report(currentBytes / totalBytes)
End Sub), Nothing)
progressStream.CopyTo(outputStream)
End Using
End Using
Next
End Using
End Sub
Public Shared Sub ExtractToDirectory(
sourceArchiveFileName As String,
destinationDirectoryName As String,
progress As IProgress(Of Double))
Using archive As ZipArchive = ZipFile.OpenRead(sourceArchiveFileName)
Dim totalBytes As Double = archive.Entries.Sum(Function(e) e.Length)
Dim currentBytes As Long = 0
For Each entry As ZipArchiveEntry In archive.Entries
Dim fileName As String = Path.Combine(destinationDirectoryName, entry.FullName)
Directory.CreateDirectory(Path.GetDirectoryName(fileName))
Using inputStream As Stream = entry.Open()
Using outputStream As Stream = File.OpenWrite(fileName)
Dim progressStream As Stream = New StreamWithProgress(outputStream, Nothing,
New BasicProgress(Of Integer)(
Sub(i)
currentBytes += i
progress.Report(currentBytes / totalBytes)
End Sub))
inputStream.CopyTo(progressStream)
End Using
End Using
File.SetLastWriteTime(fileName, entry.LastWriteTime.LocalDateTime)
Next
End Using
End Sub
End Class
The .NET built-in implementation of IProgress(Of T) is intended for use in contexts where there is a UI thread where progress reporting events should be raised. As such, when used in a console program, like which I used to test this code, it will default to using the thread pool to raise the events, allowing for the possibility of out-of-order reports. To address this, the above uses a simpler implementation of IProgress(Of T), one that simply invokes the handler directly and synchronously.
BasicProgress.vb
Class BasicProgress(Of T)
Implements IProgress(Of T)
Private ReadOnly _handler As Action(Of T)
Public Sub New(handler As Action(Of T))
_handler = handler
End Sub
Private Sub Report(value As T) Implements IProgress(Of T).Report
_handler(value)
End Sub
End Class
And naturally, it's useful to have an example with which to test and demonstrate the code.
Module1.vb
Imports System.IO
Module Module1
Sub Main(args As String())
Dim sourceDirectory As String = args(0),
archive As String = args(1),
archiveDirectory As String = Path.GetDirectoryName(Path.GetFullPath(archive)),
unpackDirectoryName As String = Guid.NewGuid().ToString()
File.Delete(archive)
ZipFileWithProgress.CreateFromDirectory(sourceDirectory, archive,
New BasicProgress(Of Double)(
Sub(p)
Console.WriteLine($"{p:P2} archiving complete")
End Sub))
ZipFileWithProgress.ExtractToDirectory(archive, unpackDirectoryName,
New BasicProgress(Of Double)(
Sub(p)
Console.WriteLine($"{p:P0} extracting complete")
End Sub))
End Sub
End Module
Additional notes regarding this implementation can be found in my answer to the related question.

'Sub Main' was not found. Service management console application error?

There is possibly a few things wrong with the below. Haven't had a chance to test/debug the code yet as can't run it. It stating that no main method has been found. But there is? i've even changed it to shared etc. It's probably something obvious?
It's flagging - 'Sub Main' was not found in 'ConsoleApplication1.Module1' error.
Also the main method wasn't always a separate class, I was just trying stuff. I'm importing a reference - system.processes. Was initially created as a vb.form but realised i didn't want the form part and recreated as a console app (which is very possibly where the problem lies as it's one of the first console apps i've done).
Code is basically planned to act on a service dying. Report and try and manage the restart (not finished, ideas welcome).
Imports System
Imports System.Management
Imports System.ServiceProcess
Imports System.Diagnostics
Imports System.Threading
Imports System.IO
Module Module1
Public Class Control
Public Sub Main() 'Public Sub Main(ByVal sArgs() As String)
Dim restart As New Rest
restart.startTime = DateTime.Now()
restart.cr20Services()
restart.Report()
End Sub
End Class
Public Class Rest
public startTime As String
Dim logPath As String = "C:\cr20\restart.txt"
'Dim fileExists As Boolean = File.Exists(strFile)
Dim arrcr20ServicesInitialStatus As New ArrayList
Dim failedServices As New ArrayList
Dim arrcr20Services As New ArrayList
Public Sub cr20Services()
'cr20 Services
arrcr20Services.Add("cr20 service")
arrcr20Services.Add("cr20 router")
For Each cr20Service In arrcr20Services
arrcr20ServicesInitialStatus.Add(cr20Service & " - " & cr20Status(cr20Service))
cr20Restore(cr20Service)
Next
End Sub
Private Function cr20Status(ByVal cr20Service As String)
Dim service As ServiceController = New ServiceController(cr20Service)
Return service.Status.ToString
End Function
Private Sub cr20Restore(ByVal cr20Service As String)
Dim service As ServiceController = New ServiceController(cr20Service)
'Dim p() As System.Diagnostics.Process = System.Diagnostics.Process.GetProcessesByName("calc")
If (service.Status.Equals(ServiceControllerStatus.Stopped)) Or (service.Status.Equals(ServiceControllerStatus.StopPending)) Then
failedServices.Add(service)
service.Stop()
Thread.Sleep(10000) 'give service 10 seconds to stop
service.Start()
End If
End Sub

VB Polymorphism Constructors Default and Properties. Similar Class to Listbox

I've been banging my head against a wall for sometime on this one.
I'm trying to create a class for storing data on People with another class to store their Bank Transactions.
Ideally, this all be hidden away and leave only simple statments, declarations and functions available to the programmer. These will include:
Dim Clients As New ClientList
Clients.Count 'readonly integer
Clients.Add("S")
Clients.Refresh()
Clients(n).Remove()
Clients(n).Transaction.Add()
Clients(n).Transaction(n).Remove()
I know this is possible as these exist in the Listbox Class though can't figure out how it's done.
Any help would be appreciated.
Thanks in advance!
Create a Transaction and a Client class
Public Class Transaction
'TODO: Implement Transaction
End Class
Public Class Client
Public ReadOnly Transactions As List(Of Transaction) = New List(Of Transaction)
Public Sub New(ByVal name As String)
Me.Name = name
End Sub
Public Name As String
End Class
Create a ClientList class
Public Class ClientList
Inherits List(Of Client)
Public Overloads Sub Add(ByVal name As String)
Add(New Client(name))
End Sub
Public Sub Refresh()
' Do what ever you want Refresh to do
End Sub
End Class
You can then use the client list like this
Dim clients As New ClientList
clients.Add("S")
' Or
clients.Add(New Client("T"))
Dim n As Integer = clients.Count
Dim m As Integer = clients(0).Transactions.Count
clients.Refresh()
clients.RemoveAt(5)
clients(n - 1).Transactions.Add(New Transaction())
clients(n - 1).Transactions.RemoveAt(2)
Dim name As String = clients(0).Name
Dim client As Client = clients(0)
Use the generic List(Of T) class, specialized to hold your Client objects. It already provides all of the methods you want without your having to write a single line of code!
So you would first write a Client class that contained all of the properties (data) and methods (actions) relating to a "client":
Public Class Client
Public Property Name As String
Public Property AmountOwned As Decimal
Public Sub Bill()
BillingManager.BillClient(Me)
End Sub
' ... etc.
End Class
Then, you would create the List(Of T) to hold all of your instances of the Client class:
Dim clients As New System.Collections.Generic.List(Of Client)
If, for whatever reason, you needed to specialize the behavior of the Add, Remove, etc. methods provided by the collection class, or add additional methods, you would need to change strategies slightly. Instead of using List(Of T), you would inherit from Collection(Of T) and create a custom collection class like so:
Public Class ClientCollection
Inherits System.Collections.ObjectModel.Collection(Of T)
' ... customize as desired ...
End Class
The WinForms ListBox class doesn't do it exactly like this because it was written before generics were introduced to the framework. But since they're here now, and you should always use them when possible, you can completely ignore how WinForms does things.