How to create global object in VB NET - vb.net

I'm triying to build a language dictionary in VB NET in order to be able to have several languages in the application.
This dictionary has a init method that loads from database the entire dictionary and stores it in memory.
In the rest of the classes of the project, I added the reference and I can use directly the object without create a new instance because their methods are shared.
My question is how i have to load the dictionary content in order that the rest of classes only accessing to Language.GetWord method obtains the properly record of the collection, same way as My.Settings class.
When My.Settings is used from any class the values are loaded. I'm looking for the same effect.
This is the code of the class:
Public Class LanguageProvider
Private Shared CurrentLanguage As Collection
Public Shared ReadOnly Property GetWord(ByVal Label As String)
Get
Try
Return CurrentLanguage.Item(Label)
Catch ex As Exception
Return Label
End Try
End Get
End Property
Public Shared Sub LoadLanguage(ByVal CultureId As Integer)
Dim SQLController As New SqlDataAccessController
SQLController.SQLSentence = "SELECT DNF_CULTURE_LANGUAGE_LABELS.LABEL, DNF_CULTURE_LANGUAGE_LABELS.VALUE FROM DNF_CULTURE_LANGUAGE_LABELS WHERE DNF_CULTURE_LANGUAGE_LABELS.CULTUREID = " & CultureId & " ORDER BY LABEL;"
SQLController.OpenConnection()
Dim dr As SqlClient.SqlDataReader
dr = SQLController.GetData()
CurrentLanguage = New Collection
While dr.Read()
CurrentLanguage.Add(dr.Item("Value"), dr.Item("Label"))
End While
dr.Close()
SQLController.CloseConnection()
End Sub
End Class
Function LoadLanguage must be called when the application loads, in order to access once to database.
After that property GetWord must access to the collection with the words and return the result. The problem is that the instances are not the same and the dictionary is not loaded when a class uses it.
Thanks.

Something along the lines of
Public Shared ReadOnly Property GetWord(ByVal Label As String)
Get
Try
If CurrentLanguage Is Nothing then
LoadLanguage(theDefaultCultureIdYouWant)
' Now CurrentLanguage is not Nothing any more,
' so LoadLanguage won't be called again
end if
Return CurrentLanguage.Item(Label)
Catch ex As Exception
Return Label
End Try
End Get
End Property
Of course, you have to provide a value for theDefaultCultureIdYouWant, depends on what you want to happen if the user just calls GetWord without explicitly mentioning a specific culture.

Related

ByRef Local Variable using Linq

I'm having trouble with selecting only part of a collection and passing it by reference.
So I have a custom class EntityCollection which is , who guessed, a collection of entities. I have to send these entities over HTTPSOAP to a webservice.
Sadly my collection is really big, let's say 10000000 entities, which throws me an HTTP error telling me that my request contains too much data.
The method I am sending it to takes a Reference of the collection so it can further complete the missing information that is autogenerated upon creation of an entity.
My initial solution:
For i As Integer = 0 To ecCreate.Count - 1 Step batchsize
Dim batch As EntityCollection = ecCreate.ToList().GetRange(i, Math.Min(batchsize, ecCreate.Count - i)).ToEntityCollection()
Q.Log.Write(SysEnums.LogLevelEnum.LogInformation, "SYNC KLA", "Creating " & String.Join(", ", batch.Select(Of String)(Function(e) e("nr_inca")).ToArray()))
Client.CreateMultiple(batch)
Next
ecCreate being an EntityCollection.
What I forgot was that using ToList() and ToEntityCollection() (which I wrote) it creates a new instance...
At least ToEntityCollection() does, idk about LINQ's ToList()...
<Extension()>
Public Function ToEntityCollection(ByVal source As IEnumerable(Of Entity)) As EntityCollection
Dim ec As New EntityCollection()
'ec.EntityTypeName = source.FirstOrDefault.EntityTypeName
For Each Entity In source
ec.Add(Entity)
Next
Return ec
End Function
Now, I don't imagine my problem would be solved if I change ByVal to ByRef in ToEntityCollection(), does it?
So how would I actually pass just a part of the collection byref to that function?
Thanks
EDIT after comments:
#Tim Schmelter it is for a nightly sync operation, having multiple selects on the database is more time intensive then storing the full dataset.
#Craig Are you saying that if i just leave it as an IEnumerable it will actually work? After all i call ToArray() in the createmultiple batch anyway so that wouldn't be too much of a problem to leave out...
#NetMage you're right i forgot to put in a key part of the code, here it is:
Public Class EntityCollection
Implements IList(Of Entity)
'...
Public Sub Add(item As Entity) Implements ICollection(Of Entity).Add
If IsNothing(EntityTypeName) Then
EntityTypeName = item.EntityTypeName
End If
If EntityTypeName IsNot Nothing AndAlso item.EntityTypeName IsNot Nothing AndAlso item.EntityTypeName <> EntityTypeName Then
Throw New Exception("EntityCollection can only be of one type!")
End If
Me.intList.Add(item)
End Sub
I Think that also explains the List thing... (BTW vb or c# don't matter i can do both :p)
BUT: You got me thinking properly:
Public Sub CreateMultiple(ByRef EntityCollection As EntityCollection)
'... do stuff to EC
Try
Dim ar = EntityCollection.ToArray()
Binding.CreateMultiple(ar) 'is also byref(webservice code)
EntityCollection.Collection = ar 'reset property, see below
Catch ex As SoapException
Raise(GetCurrentMethod(), ex)
End Try
End Sub
And the evil part( at least i think it is) :
Friend Property Collection As Object
Get
Return Me.intList
End Get
Set(value As Object)
Me.Clear()
For Each e As Object In value
Me.Add(New Entity(e))
Next
End Set
End Property
Now, i would still think this would work, since in my test if i don't use Linq or ToEntityCollection the byref stuff works perfectly fine. It is just when i do the batch thing, then it doesn't... I was guessing it could maybe have to do with me storing it in a local variable?
Thanks already for your time!
Anton
The problem was that i was replacing the references of Entity in my local batch, instead of in my big collection... I solved it by replacing the part of the collection that i sent as a batch with the batch itself, since ToList() and ToEntityCollection both create a new object with the same reference values...
Thanks for putting me in the correct direction guys!

VB SQL CommandText property has not been initialized

I'm new to working with background workers, and I'm trying to run the following code. But I recieve a run-time error on the m._Value_CreatedDate = m._MyCMD.ExecuteScalar() line. The error is:
Additional information: ExecuteScalar: CommandText property has not
been initialized
Try
Dim m As MyParameters = DirectCast(e.Argument, MyParameters)
m._Con.Open()
m._QueryStr = "SELECT TOP 1 CONVERT(varchar(10),aCreated,103) FROM Account WHERE aMember_ID = " & m._Selected_MemberID & ""
m._MyCMD.CommandType = CommandType.Text
m._Value_CreatedDate = m._MyCMD.ExecuteScalar()
Catch ex As Exception
m._Value_CreatedDate = "N/A"
End Try
Here's the Parameter's I'm using:
Class MyParameters
Friend _QueryStr As String
Friend _Value_CreatedDate As Object
Friend _AccountID As Object
Friend _Selected_MemberID As String = Committee_Database.GridView1.GetFocusedRowCellValue("Unique ID").ToString
Friend _Con As New SqlConnection('Connection string ommitted)
Friend _MyCMD As New SqlCommand(_QueryStr, _Con)
End Class
Please forgive me if I'm doing something extremely wrong, I'm self taught and experimenting with the backgroundworker. It's worth noting that _QueryStr will change multiple times as the background worker runs multiple queries against the same database and (as I understand it) stores each of the returned values from the queries into variables - _Value_CreatedDate is the variable I am using in this code. I've included an example of how I'm recycling the _QueryStr variable below and storing the returned value into a diffent Variable each time.
Try
m._QueryStr = "SELECT TOP 1 aAccount_ID FROM Account WHERE aUser_Name='" & _Selected_AccountID & "'"
m._MyCMD.CommandType = CommandType.Text
m._AccountID = m._MyCMD.ExecuteScalar()
Catch ex As Exception
End Try
Am I doing something wrong?
In the implementation of your class MyParameters you initialize the SqlCommand directly with the declaration using the value of the variable _QueryStr. At that point in time the variable _QueryStr is not yet initialized, so it is an empty string.
After the initialization of an instance of MyParameters, you change the value of _QueryStr (many times according to you) but these changes are not automatically passed to the CommandText of your SqlCommand. It still contains the empty initial value.
You could fix this problem building appropriate getter and setter for a new QueryStr property. When someone tries to set the property the code below change the value of the internal field _QueryStr (now private) and reinitializes the CommandText property of your SqlCommand.
Class MyParameters
Private _QueryStr As String
Public Property QueryStr() As String
Get
Return _QueryStr
End Get
Set(ByVal value As String)
_QueryStr = value
_MyCMD.CommandText = _QueryStr
End Set
End Property
..... other properties
Friend _MyCMD As New SqlCommand(_QueryStr, _Con)
End Class
Now when you write
Try
m.QueryStr = "SELECT ...."
....
the new command text is correctly assigned to your command.
As a side note: I suggest to use plain ADO.NET objects (or learn how to use an ORM tool). Do not try to encapsulate them in custom classes unless you have a very deep understanding on how these ADO.NET objects works. You gain nothing and expose your code to many problems. For example your code is very easy to exploit with Sql Injection because the MyParameters class has no provision to use a parameterized query. Your code has no method to close and dispose the SqlConnection embedded in your class thus leading to resource leaks.

Reading and writing to an xml file with VB.Net

I am rather new to the programming world ( I'm a network guy ). I have however been asked to develop a front end that configures an xml file for a console application. The console application reads from this xml file and opens multiple instances of a browser, one instance per monitor (6 monitors in total). There are 4 control centers, each of which has 6 monitors. Each control center is run off a seperate pc. These pc's are not on the network and do not have access to each other. I have been told I can also not use a database.
Each monitor will display one website at a time, there could be multiple sites listed to be displayed on the specific monitor so they will periodically change. Each control center will show different sites.
My first question: Is this XML valid?
<ControlCenter>
<Monitor>
<monitor_id>0</monitor_id>
<browser_short_na>ie</browser_short_na>
<url_list>
<url>
<url_id>0</url_id>
<url_na><![CDATA[http://www.hmv.com]]></url_na>
<parameter><![CDATA[]]></parameter>
</url>
<url>
<url_id>1</url_id>
<url_na><![CDATA[http://www.amazon.com]]></url_na>
<parameter><![CDATA[]]></parameter>
</url>
<url>
<url_id>2</url_id>
<url_na><![CDATA[http://www.google.com]]></url_na>
<parameter><![CDATA[]]></parameter>
</url>
</url_list>
</Monitor>
<Monitor>
<monitor_id>1</monitor_id>
<browser_short_na>ie</browser_short_na>
<url_list>
<url>
<url_id>0</url_id>
<url_na><![CDATA[http://www.amazon.com]]></url_na>
<parameter><![CDATA[]]></parameter>
</url>
</url_list>
</Monitor>
</ControlCenter>
What I do so far is open the xml file and add all the monitors to a combobox
Dim dom As New Xml.XmlDocument
dom.Load("test.xml")
ComboBox1.Items.Clear()
Dim monitorid As String = String.Empty
For Each node As Xml.XmlNode In dom.SelectNodes("//ControlCenter/Monitor/monitor_id")
monitorid = node.InnerText
ComboBox1.Items.Add(monitorid)
Next
This is now where I am stuck. Once the users selects one of the monitors from the combobox I then need to get all the information for that monitor. So I need the browser_short_na, and all the urls all based on the monitor_id selected.
I have tried creating a dataset, loading the xmlfile using readxml. I then tried creating a dataview pointing to that dataset. Tried adding a RowFilter to the dataview.
Dim val As String = ComboBox1.SelectedItem.ToString
Dim dsXmlFile As New DataSet
dsXmlFile.ReadXml("test.xml")
Dim dv As New DataView
dv.Table = dsXmlFile.Tables(0)
Dim drv As DataRowView
dv.RowFilter = "monitor_id = " & val
Dim url As String = ""
'Retrieve my values returned in the result
For Each drv In dv
url = drv("url_na")
Next
When I step through the code and look at the for each loop it fails with the message "url_na is neither a DataColumn nor a DataRelation for table Monitor."
I am thinking I am not handling the url_list section correctly.
Once all the information for the selected monitor is read I will display the values in textboxes/listboxes which the users can then edit. If they then save it should write the new values to the xml file. They could also choose to add additional urls to the list, or even create an entirely new monitor section.
Any help/suggestions would be greatly appreciated.
I would go the other way around for your issue:
Define a Class (or several classes) that will hold the data you need.
Use a simple serializer to load/save from/to that file.
If you do so, then your issue with the selection is just a classical WPF issue.
Public Class Monitor
Public Property MonitorId As integer
Public Property ListOfUrl As List(Of String)
End Class
MonitorsConfiguration will then reference a List(Of Monitor) object.
You can use a ViewModel object to handle a MonitorsConfiguration easily.
This ViewModel has a SelectedMonitorIndex property, and updates a UrlForThisMonitor List of url when the index property changes.
(Obviously it should implement INotifyPropertyChanged)
Ok so a little view on what the ViewModel might look like :
Public Class MonitorsConfigurationVM
Implement INotifyPropertyChanged
' creates a new VM. Throws exception if file name is not valid
Public Sub New(ConfigFileName As String)
_FileName = ConfigFileName
_MonitorsConfiguration = // Deserialization of the file //
_MonitorIndex = 0
End Sub
Public Property MonitorIndex As integer
Get
return _MonitorIndex
End Get
Set (Value)
if (_MonitorIndex = value) then return
' you might want to perform check here and allow only valid index
_MonitorIndex = value
_UrlIndex=0
NotifyPropertyChanged("MonitorIndex")
NotifyPropertyChanged("MonitorUrls")
NotifyPropertyChanged("HasUrl")
NotifyPropertyChanged("UrlIndex")
End Set
End Property
Public ReadOnly Property HasUrl As Boolean
Get
return Not (MonitorUrls Is Nothing OrElse MonitorUrls.count = 0 )
' ( might be used to disable the listbox bound to MonitorUrl )
End Get
End Property
Public ReadOnly Property MonitorUrls As List(Of String)
Get
return _MonitorConfiguration(_MonitorIndex).ListOfUrl '(you might want to chk)
End Get
End Property
Public Property UrlIndex As Integer
Get
return _UrlIndex
End Get
Set (value)
if value = _UrlIndex then return
' you might want to perform some check here
_UrlIndex = value
NotifyPropertyChanged("UrlIndex")
End Set
End Property
' And Also : AddMonitor / AddUrl / SaveConfiguration / ...
Private _FileName As String = Nothing
Private _MonitorsConfiguration As List(Of Monitor)=Nothing
Private _MonitorIndex As integer = 0
Protected Sub NotifyPropertyChanged(ByVal name As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
End Sub
Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
You should use XPath for parsing the XML Document.
Load the xml file in XMLDocument
Create a XMLNodeList and use XPath to select nodes from loaded xml
document.
Then parse the nodes list and extract all info for selected monitor
node based on id.
Here are XPath expression helpful to you:
For selecting monitor nodes from file: "/ControlCenter/Monitor"
For selecting browser_na field based on monitor id: "/ControlCenter/Monitor[monitor_id='0']/browser_short_na"
For selection urls based on monitor id: "/ControlCenter/Monitor[monitor_id='0']/url_list/url/url_na"
Hope it works for you.

Multiple forms in vb.net

I am trying to develop a vb programe where i am using List to store data. I want to use all datas saved in that list from another class.
In Class 1, i am trying to include a method that will return my list. And form class 2, i will call that method and get that list.
But code isnt working.
Can anyone plz help. ! ! !
Thanks.
For Example.
In class one, Name- Add.vb' My thie method was supposed to return list.
Public Property getList() As List(Of Car)
Get
Return CarDetails
End Get
Set(ByVal value As List(Of Car))
End Set
End Property
and In class 2.
Private addobject As Add
Private details As New List(Of Car)
addobject = New Add()
details = addobject.getList()
But I am not getting the list.
You want to create an external class, or class library to the get the list data, and have both forms call that function.
I am not sure this is the problem you are facing, but the getlist in the Add class is a property, not a method. This means that you should not have the trailing paranthesis when calling it. You can change your code into this:
' create a new instance of Add '
Dim addobject As New Add()
' get the list from the property '
Dim details As List(Of Car) = addobject.getlist

How to mock a method (custom behavior) with Rhino Mocks in VB.NET

How can I mock one method with RhinoMocks in VB.Net? The reference I found is in C#:
Expect.Call(delegate{list.Add(0);}).IgnoreArguments()
.Do((Action<int>)delegate(int item) {
if (item < 0) throw new ArgumentOutOfRangeException();
});
SharpDevelop converts this to:
Expect.Call(Function() Do
list.Add(0)
End Function).IgnoreArguments().Do(DirectCast(Function(item As Integer) Do
If item < 0 Then
Throw New ArgumentOutOfRangeException()
End If
End Function, Action(Of Integer)))
But that doesn't work either (it doesn't compile).
This is what I want to do: create a new object and call a method which sets some properties of that method. In real-life this method, will populate the properties with values found in the database. In test, I would like to mock this method with a custom method/delegate so that I can set the properties myself (without going to the database).
In pseudo-code, this is what I'm trying to do:
Dim _lookup As LookUp = MockRepository.GenerateMock(Of LookUp)()
_luvalue.Expect(Function(l As LookUp) l.GetLookUpByName("test")).Do(Function(l As LookUp) l.Property = "value")
Unfortunately you're attempting to do both a Sub lambda and a Statement Lambda. Neither are supported in VS2008 (but will be in the upcoming version of VS). Here is the expanded version that will work for VB
I'm guessing at the type of m_list
Class MockHelper
Dim m_list as new List(Of Object)
Public Sub New()
Expect(AddressOf CallHelper).IgnoreArguments().Do(AddressOf Do Hepler)
End Sub
Private Sub CallHelper()
m_list.Add(0)
End Sub
Private Sub DoHelper(ByVal item as Integer)
if item < 0 Then
Throw New ArgumentOutOfRangeException
End If
End Sub
End Class
I have never mocked something w/ both a delegate and a lambda so I can't give a full solution to this problem, but I did want to share some example code for the usual "AssertWasCalled" function in Rhino Mocks 3.5 for vb developers because I spent some time trying to grok this... (keep in mind the below is kept simple for brevity)
This is the method under test - might be found inside a service class for the user object
Public Sub DeleteUserByID(ByVal id As Integer) Implements Interfaces.IUserService.DeleteUserByID
mRepository.DeleteUserByID(id)
End Sub
This is the interactive test to assert the repository method gets called
<TestMethod()> _
Public Sub Should_Call_Into_Repository_For_DeleteProjectById()
Dim Repository As IUserRepository = MockRepository.GenerateStub(Of IUserRepository)()
Dim Service As IUserService = New UserService(Repository)
Service.DeleteUserByID(Nothing)
Repository.AssertWasCalled(Function(x) Wrap_DeleteUserByID(x))
End Sub
This is the wrap function used to ensure this works w/ vb
Function Wrap_DeleteUserByID(ByVal Repository As IUserRepository) As Object
Repository.DeleteUserByID(Nothing)
Return Nothing
End Function
I found this to be a very nasty solution, but if it helps someone w/ the same issues I had it was worth the time it took to post this ;)