Is there a way that I can insert values into a VB.NET Dictionary when I create it? I can, but don't want to, do dict.Add(int, "string") for each item.
Basically, I want to do "How to insert values into C# Dictionary on instantiation?" with VB.NET.
var dictionary = new Dictionary<int, string>
{
{0, "string"},
{1, "string2"},
{2, "string3"}
};
If using Visual Studio 2010 or later you should use the FROM keyword like this:
Dim days = New Dictionary(Of Integer, String) From {{0, "string"}, {1, "string2"}}
See: http://msdn.microsoft.com/en-us/library/dd293617(VS.100).aspx
If you need to use a prior version of Visual Studio and you need to do this frequently you could just inherit from the Dictionary class and implement it yourself.
It might look something like this:
Public Class InitializableDictionary
Inherits Dictionary(Of Int32, String)
Public Sub New(ByVal args() As KeyValuePair(Of Int32, String))
MyBase.New()
For Each kvp As KeyValuePair(Of Int32, String) In args
Me.Add(kvp.Key, kvp.Value)
Next
End Sub
End Class
This is not possible versions of Visual Basic prior to 2010.
In VB2010 and later, you can use the FROM keyword.
Dim days = New Dictionary(Of Integer, String) From {{0, "Sunday"}, {1, "Monday"}}
Reference
http://msdn.microsoft.com/en-us/library/dd293617(VS.100).aspx
What you're looking at is a feature of C# called "collection initializers". The feature existed for VB as well, but was cut prior to the release of Visual Studio 2008. It doesn't help you right now, but this is expected to be available in Visual Studio 2010. In the meantime, you'll have to do it the old fashioned way — call the .Add() method of your new instance.
Related
After I open a SqlConnection, I try to open a recordset like I did it thousands times before in VBA. Yes, I know that there are differences between VBA and VB.NET, but maybe it's to simple to see it.
Public Class Form1
Const m_cstrCnnString As String = "MyCorrectCnnString"
Dim cnn As SqlConnection
Dim rcs As New ADODB.Recordset
Private Sub MyConnection()
cnn = New SqlConnection(m_cstrCnnString)
cnn.Open()
'so far it works, my cnn.State is 1
rcs.Open("SELECT * FROM dbo.myTable", cnn) 'this line doesn´t work
End Sub
I tried the rcs.Open with and without CursorTypeEnum, LockTypeEnum, but I always get the same error:
System.Runtime.InteropServices.COMException: The arguments are of the wrong type, are out of scope, or are inconsistent with each other.
Background: Win10, Connection to a MS SQL Server Express by VB.NET.
First I import System.Data.SqlClient
Microsoft ActiveX Data Objects 6.1 Library is activated.
Also I tried it with Microsoft ActiveX Data Objects Recordset 6.0 Library.
And I'm new in VB.NET
There's some high-level stuff I need to cover before we can get into the details of what this code should look like.
First, if you're using VB.Net, you should not be opening a RecordSet object. Recordset is from ADO, which is not the same as ADO.Net. You definitely can't use an ADO.Net SqlConnection to open a classic ADO RecordSet. Again: these are two completely different libraries. The classic library exists in .Net only for backwards compatibility and to aid in porting forward old code. It should not be used for new development.
Additionally, when you move forward to ADO.Net you need to be aware of a feature called Connection Pooling. This feature takes care of caching connection objects for you, such that it's counter-productive (uses more RAM and makes things slower) to try to keep a single connection option ready for use throughout the application. Instead, it really is better to create a new connection object for most queries.
Finally, classic ADO had a problem with properly closing resources, such that some of those older applications could occasionally even lock out the database. .Net provides a way to handle this such that you are sure everything is closed and disposed properly and promptly: a Using block.
The weakness of this mechanism is DataReader objects (which replace RecordSet) need the connection to remain open for the life of the reader. This makes it hard to build a good data abstraction to hide all the boiler plate code that goes with data access.
The best pattern I've seen for offering the best of both worlds requires some moderate and advanced VB.Net language features (lambda methods, iterator blocks, and IEnumerable) that are unfamiliar to many traditional VB coders. It's going to require me to go a lot deeper than normal in this answer. That said, once you wrap your head around it, the result is really nice.
To show what this might look like, I need an example that's a little more concrete. We'll pretend you have an Employee table with columns for ID, FirstName, and LastName, and a class Employee with properties of the same name:
Public Class Employee
Public Property ID As Integer
Public Property FirstName As String
Public Property LastName As String
Public Shared Function FromDataRecord(record As IDataRecord) As Employee
Return New Employee() With {
ID = record("ID"),
FirstName = record("FirstName"),
LastName = record("LastName")
}
End Function
End Class
That's right: VB sometimes uses braces now. The Shared method at the end is optional, but it will help make some of what comes next easier to follow. Here is what modern ADO.Net code might look like. Pay special attention to the use of Private and Public
Public Module DB
Private Shared Property ConnectionString As String = "MyCorrectCnnString"
Private Iterator Function GetData(Of T)(SQL As String, transform As Func(Of IDataRecord, T), addParameters As Action(Of SqlParameterCollection)) As IEnumerable(Of T)
Using cn As New SqlConnection(ConnectionString)
Using cmd As New SqlCommand(SQL, cn)
If addParameters IsNot Nothing Then addParameters(cmd.Parameters)
cn.Open()
Using rdr As SqlDataReader = cmd.ExecuteReader()
While rdr.Read()
Yield transform(rdr)
End While
End Using
End Using
End Using
End Function
Public Function GetEmployees() As IEnumerable(Of Employee)
Dim SQL As String = "SELECT * FROM dbo.Employee"
Return GetData(SQL, Employee.FromDataRecord, Nothing)
End Function
Public Function GetEmployeeById(ID As Integer) As Employee
Dim SQL As String = "SELECT * FROM dbo.Employee WHERE ID= #ID"
Return GetData(SQL, Employee.FromDataRecord,
Sub(pc) pc.Add("#ID", SqlDbType.Integer).Value = ID
).FirstOrDefault()
End Function
Public Function GetEmployeesByLastName(LastName As String) As IEnumerable(Of Employee)
Dim SQL As String = "SELECT * FROM dbo.Employee WHERE LastName= #LastName"
Return GetData(SQL, Employee.FromDataRecord,
Sub(pc) pc.Add("#LastName", SqlDbType.NVarChar, 25).Value = LastName
)
End Function
End Module
Now the main part of the application NEVER deals in SQL directly, or even has to know there's a relational database involved. It's just calling methods on a regular module, such that the code in your form for reading data looks more like this:
Dim employees = DB.GetEmployeesByLastName(TextBox1.Text)
DataGridView1.DataSource = employees
You make similar methods in the new Module for writing INSERT/UPDATE/DELETE queries to save changes. These methods will end up calling the cmd.ExecuteNonQuery() method, but they should NEVER accept an SQL statement string as input.
As an application grows you might have a number of classes with FromDataRecord() methods, such that you instead refactor these methods to their own Module. You might also end up with enough Public methods in the DB Module to divide them up into a number of related Modules. At this point, these Modules would all go into a separate class library project (using Friend in some places instead of Private).
where does it go wrong?
my coding
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Imports System.Net
Public Class DigiposAJA
Private Sub CekPaket()
Dim json As String = (New WebClient).DownloadString("http://192.168.101.1:100/list_product?username=SIP12&category=ROAMING&to=0811&payment_method=LINKAJA&json=1")
Dim jarr As JArray = Linq.JArray.Parse(json)
Dim sKatagori As String
For Each jtk As JToken In jarr
sKatagori = jtk.SelectToken("kategori")
DgvDigipos.Rows.Add()
DgvDigipos.Rows(DgvDigipos.Rows.Count - 1).Cells("DgvKategori").Value = sKatagori
Next
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
CekPaket()
End Sub
End Class
after I debug the result is an error like this.
Newtonsoft.Json.JsonReaderException: 'Error reading JArray from JsonReader. Current JsonReader item is not an array: StartObject. Path '', line 1, position 1.'
Can you help me to get a great result
Most likely this is a result of your call to the web service not returning the result you expect.
This is actually a good example of the benefits of separation of concerns and strongly typed objects. Your sub CekPaket should be broken down into 3 parts. 1) get the string. This should be a function and should use some sort of configuration to get the end point and have appropriate guards for failure, 2) parse the string into a strongly typed object (IEnumerable of whatever), this should validation to make sure that the input is good. You might want to make this function public for easy testing. And finally 3) bind your results to your UI. It looks like you are doing this part by hand, whenever possible you should allow the frame work to do this for you by providing a data source and a template for the display.
I am trying to put columns names from a table inside a Microsoft Access database inside a list variable. I have done this so far but the line where I am trying to add it to the topic variable does not work and is coming up with the error
predefined type ‘valuetuple(of,,,)’ is not defined or imported.
The code is:
Dim topic = topic()
Dim filtervalues = {Nothing, Nothing, "Results", Nothing}
Dim counter As Integer = 0
Using con = _
New OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Database.mdb")
Dim columns = con.GetSchema("columns", filtervalues)
For Each row As DataRow In columns.Rows
topic(counter) = ("{0,-20}{1}", row("column_name"), row("data_type"))
counter = +1
Next
End Using
According to the documentation on Value Tupels you must get the NuGet package System.ValueTuple, if you are working with Framework version prior to 4.7:
Important
Tuple support requires the ValueTuple type. If the .NET Framework 4.7 is not installed, you must add the NuGet package System.ValueTuple, which is available on the NuGet Gallery. Without this package, you may get a compilation error similar to, "Predefined type 'ValueTuple(Of,,,)' is not defined or imported."
In Visual Studio 2017 right click on your solution and select "Manage NuGet Packages for Solution...". In the search-box enter "valuetuple". Select "System.ValueTuple" and on the right click the check boxes of the projects where you want to install the package and click Install.
See: NuGet Package Manager UI
Also, you must declare the list variable as
Dim topic = New List(Of (String, String, String))
and add new elements with
topic.Add(("{0,-20}{1}", row("column_name"), row("data_type")))
The counter is not needed anymore.
Alternatively, you could use a list of strings and format the string with string interpolation
Dim topic = New List(Of String)
Dim filtervalues = {Nothing, Nothing, "Results", Nothing}
Using con =
New OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Database.mdb")
Dim columns = con.GetSchema("columns", filtervalues)
For Each row As DataRow In columns.Rows
topic.Add($"{row("column_name"),-20}{row("data_type")}")
Next
End Using
Judging from the "{0,-20}{1}", you forgot the String.Format in
topic(counter) = String.Format("{0,-20}{1}", row("column_name"), row("data_type"))
and did not intend to use a tuple.
Being new to programming and having it introduced to me through my course I've been doing tasks in and out of College in Visual Basic using Visual Studio to make games and other little applications. However in my most recent project i've experienced a problem in one of my arrays I have never come across before. The specific error im getting comes up with this when highlighted:
Data type(s) of the type parameter(s) in method 'Public Shared Overloads Function IndexOf(Of T)(array As T(), value As T) As Integer' cannot be inferred from these arguments. Specifying the data type(s) explicitly might correct this error.
I have all the arrays (18, for 9 different buttons each containing a question and an answer variant) running in form load as it was the only way I could get the arrays to work with a randomiser and show the question in the button. Then the array index is being found and created in the submit answer button. I'll give some snippets for further context below from various points where the array is being referenced :)
I created this to make the string global but I have an inkling this is wrong?
Public Class Form3
Public QBox1 As String
Public QBoxA1 As String
This is where the arrays are and how i've structured them
Public Sub Form3_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BOX 1 (TOP LEFT)
Dim QBox1() As String = {"√81", "4x6", "16/4", "21+18", "81-23"}
Dim QBoxA1() As Integer = {"9", "24", "4", "39", "58"}
And finally this is within the submit answer button where the error is
If QBoxA1.Contains(txt_AnswerAttempt.Text) Then
Dim question_index = Array.IndexOf(QBox1, btn_Q1.Text)
Dim answer_index = Array.IndexOf(QBoxA1, answerAttemptDisplay.Text)
If question_index = answer_index Then
MsgBox("Correct Answer!")
Else
MsgBox("Wrong Answer!")
End If
End If
Sorry for information overload, I wanted to be thorough right off the bat! Cheers for giving this a read if you made it to the end xD
Your declaration isn't right. Since you want an array, don't declare it as a single string. It should be:
Public QBox1() As String
then in the load event, don't re-declare it with a Dim statement. Just re-populate it:
QBox1 = {"√81", "4x6", "16/4", "21+18", "81-23"}
I am trying to figure out how to query an access database to take info from the database and put it into a class array. I know how to do some querying using basic LINQ, but not sure how to do it the way I want it to. I have a character class I am using, I am storing all the character traits in a database. I want to query the database and add that info to a class array. Any help would be greatly appreciated.
Here at the class private variables which are the same traits in the database:
Public Class Character
Private _strName As String
Private _intLevel As Integer
Private _intHealth As Integer
Private _intDamage As Integer
Private _intScore As Integer
First of all. I have to say this is not a good question.
Actually what you are saying is...: Please build me the dataconnections for this acces database and a visual studio form.
But because I'm in a good mood, That's what I did for you.
I created a little database file in acces.I saved it into a folder called database. Which is located in the project directory.
I made 2 classes. 1 CharactersForm, and 2 Gateway
the gateway is to make an connection to your Acces database.
The CharactersForm is the form what you see when you debug
Here is the CharactersForm class.
Make sure you drag and drop a listbox and a button
Call the listbox ListBoxCharacters.
Call the button InladenButton. Or call it whatever you want, But make sure it will fit the code given.
Option Explicit On
Option Strict On
Option Infer On
Public Class CharactersForm
Dim _CharactersGateway As New Gateway
Private Sub InladenButton_Click(sender As Object, e As EventArgs) Handles InladenButton.Click
_CharactersGateway.LoadDataTableCharacters()
ListBoxCharacters.DataSource = _CharactersGateway.CharactersDataTable
ListBoxCharacters.DisplayMember = "Name"
End Sub
End Class
The Gateway Class ( to make connection to your acces file)
If somehow visual studio cannot reach the database then it means the connectionstring is not correct. Make sure the connection string is exactly as where your accesfile.accdb is located
Option Explicit On
Option Strict On
Option Infer On
Imports System.IO
Imports System.Data.OleDb
Public Class Gateway
Public Shared AppPath As String = Application.StartupPath()
'\Debug
Public Shared DirectoryUp1 As String = Path.GetDirectoryName(AppPath)
'\bin
Public Shared DirectoryUp2 As String = Path.GetDirectoryName(DirectoryUp1)
'\CharactersStackOverFlow
Public Shared DirectoryUp3 As String = Path.GetDirectoryName(DirectoryUp2)
'\CharactersStackOverFlow
Public Shared AccesDatabaseFilePath As String = DirectoryUp3 & "\DataBase\Characters.accdb"
Dim ConnectionString As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source= " & AccesDatabaseFilePath)
Public Property DataSetCharacters As New DataSet
Public ReadOnly Property CharactersDataTable As DataTable
Get
CharactersDataTable = DataSetCharacters.Tables("Characters")
End Get
End Property
Public CharactersDataAdapter As OleDbDataAdapter
Public Sub LoadDataTableCharacters()
CharactersDataAdapter = New OleDbDataAdapter("SELECT * FROM Characters", ConnectionString)
CharactersDataAdapter.Fill(DataSetCharacters, "Characters")
Dim kamersCommandBuilder As New OleDbCommandBuilder(CharactersDataAdapter)
End Sub
End Class
This is as Yuriy Galanter said. You need the OleDbConnection.
If you are using office 2010 and you get thsi warning ::
'Microsoft.ACE.OLEDB.12.0' provider is not registered on the local machine
You need to download the 2007 Office System Driver. It works also for 2010 don't ask me why.
Type '2007 Office System Driver' in google. And from microsoft you can download and install it.
ALL DONE!
if you run the program and press the load button. The names of your characters will come in the listbox.
If you want to put values of the fields of the database in variables. Then it's just about making a for loop and put them in dimmed variables. That is something I'm sure you can do yourself. The connection to the database and the loading in the program is already sorted out now!
Good luck
Jonathan