I have a question about declaring a datatable variable.
Is there any difference if I declared a datatable variable as:
Dim xDt as new datatable
or
Dim xDt as new datatable()
??
Firstly, you're not declaring a class there. You are declaring a variable of type DataTable. The declaration is irrelevant anyway. What matters is that you are creating an instance of the DataTable class by invoking a constructor with the New keyword. A constructor is just a special method and, in VB, you can omit the parentheses when calling a method without arguments. This:
Dim table As New DataTable
is functionally equivalent to this:
Dim table As DataTable = New DataTable
which is functionally equivalent to this:
Dim table As DataTable
table = New DataTable
As you can see there, the last line is what matters and the variable declaration is irrelevant. That last line is equivalent to this:
table = New DataTable()
Whether you include the parentheses or not is up to you but I would suggest that you pick an option for a logical reason and stick to it consistently.
Personally, I always include parentheses on standard method calls but I always omit them from constructors. I do the former to make methods easily distinguishable from properties, so I would do this:
Dim str = obj.ToString()
rather than this:
Dim str = obj.ToString
With constructors though, there's little risk of mistaking them for properties but there is some risk of mistaking them for arrays, e.g.
Dim tables As DataTable()
Declares a variable of type DataTable array without creating an object while this:
Dim table As New DataTable()
declares a variable of type DataTable and assigns a new object to it. In my opinion, omitting the parentheses on constructors reduces the likelihood of confusion while including it on other methods does the same.
Related
In declaration of variables and objects, when exactly should I use "New" word, and when shouldn't I use it?
I know that I should declare a string without "New" word:
Dim mystring As String
I also know I should use it declaring a datatable:
Dim mytable As New Datatable()
New creates an object that is an instance of the specified class. If you just write the following then you have a reference, but the reference is Nothing as you didn't actually create a Datatable for it to refer to:
Dim mytable As Datatable
You don't typically use New for value types (Numbers, Dates, Booleans, Structures, Enums - a full list is here), as they always have a value (cannot be Nothing). For example this outputs 0:
Dim num as Int32
Console.WriteLine(num)
I wouldn't worry too much about this, but some value types (structures) can be initialised with New, which is somewhat inconsistent, for example:
Dim dec = New Decimal(2, 3, 4, True, 5)
I understand that everything in vb.net is a object. If that is the case, why is new keyword not used when creating a datatable object?
Dim dt as Datatable
dt.coloumns.add()
vs
Dim dt as Datatable = new Datatable
dt.coloumns.add()
Both seem to do the same things. However, in which scenario should I use new keyword? Are therere specific objects I don't need to use the new keyword? I understand that for common things like string, integer etc you don't need to instantiate the object. Is it the same case for DataTable too?
Dim dt as Datatable is merely declaration of the variable. It does not initialise it, so by default the value of dt is null (Nothing in VB I believe). Note that only declaring the value is not illegal at all, so you are perfectly within your rights to do so.
On the other hand, Dim dt as Datatable = new Datatable declares as well as initialises the variable. That is to say, new will initialise the declared variable with the appropriate value. If an class has a constructor which accepts parameters, then you can use new along with the constructor to create a new instance of that class and assign your chosen values to the class properties instead of using the default values.
The difference between your examples is that dt.Columns.add() will throw an error in the first example, since you are trying to call a method on a null object. In the second case, you have used new to provide an initial value to the variable. As a result, you can access the Columns property of a non-null object without any issue.
Now let's come to your other point - "I understand that for common things like string, integer etc you don't need to instantiate the object. Is it the same case for DataTable too?" Things like Integer are primitive datatypes, and so they have default non-null values. If you don't explicitly initialise with a value, they will take the default values. e.g. Dim x As Integer will automatically make x equal to 0
For objects, the default value is null, so it will cause problems if you try to do anything with that object without assigning a non-null value to it first. There are 2 ways to assign the non-null value:
Use new to initialise it.
Directly assign a value which is the result of some other processing in your code.
Please see the code below:
Public Function ExecuteDynamicQuery(ByVal strSQL As String, ByVal list As List(Of clsType), ByVal tyType As clsType) As List(Of clsType) Implements IGenie.ExecuteDynamicQuery
Dim objParameterValues As New clsParameterValues
Dim iConnectionBLL As iConnectionBLL
Dim objCon As DbConnection
Dim objDR As DbDataReader
Dim paramValues() As DbParameter
objParameterValues = New clsParameterValues
iConnectionBLL = New clsConnectionBLL()
objCon = iConnectionBLL.getDatabaseTypeByDescription("Genie2")
Using objCon
paramValues = objParameterValues.getParameterValues
objDR = clsDatabaseHelper.ExecuteReader(objCon, CommandType.Text, strSQL, paramValues)
Do While objDR.Read
Dim tyType2 As clsType = tyType
tyType.PopulateDataReader(objDR)
list.Add(tyType2)
Loop
objDR.Close()
Return list
End Using
End Function
An SQL statement is passed to the function along with clsType (the base type). A list of types is returned e.g. a list of Persons. For example, in this case strSQL = "SELECT * FROM Persons". A list of 500 persons is returned but they are all the same person (the last person added to the list). I realise this is because the list is referncing the same object for each entry. How do I change this?
This is a situation where making the method generic would be useful. For instance:
Public Function MyGenericMethod(Of T As New)() As List(Of T)
Dim results As New List(Of T)()
For i As Integer = 0 To 9
Dim item As New T()
' Populate item ...
results.Add(item)
Next
Return results
End Function
For what it's worth, though, I see people trying do this kind of thing often, and it never sits well with me. I'm always the first one in line to suggest that common code should be encapsulated and not duplicated all over the place, but, I've never been convinced that creating some sort of data access layer that encapsulates the calls to ADO, but doesn't also encapsulate the SQL, is a good idea.
Consider for a moment that ADO, is in-and-of-itself an encapsulation of that part of the data-access layer. Sure, it can take a few more lines of code than you might like to execute a simple SQL command, but that extra complexity is there for a reason. It's necessary in order to support all of the features of the data source. If you try to simplify it, inevitably, you will one day need to use some other feature of the data source, but it won't be supported by your simplified interface. In my opinion, each data access method should use all of the necessary ADO objects directly rather than trying to some how create some common methods to do that. Yes, that does mean that many of your data access methods will be very similar in structure, but I think you'll be happier in the long run.
I've reduced your original code. The following sample is functionally equivalent to what you posted. Without knowing more about your types, it will hard to give you anything more than this, but maybe the reduction will make the code clear enough for you to spot a solution:
Public Function ExecuteDynamicQuery(ByVal sql As String, ByVal list As List(Of clsType), ByVal type As clsType) As List(Of clsType) Implements IGenie.ExecuteDynamicQuery
Dim paramValues() As DbParameter = New clsParameterValues().getParameterValues()
Using conn As DbConnection = iConnectionBLL.getDatabaseTypeByDescription("Genie2"), _
rdr As DbDataReader = clsDatabaseHelper.ExecuteReader(conn, CommandType.Text, sql, paramValues)
While rdr.Read()
type.PopulateDataReader(rdr)
list.Add(type)
End While
Return list
End Using
End Function
There are a few additional bits of advice I can give you:
You must have some way to accept parameter information for your query that is separate from the query itself. The ExecuteReader() method that you call supports this, but you only ever pass it an empty array. Fix this, or you will get hacked.
A implementation that uses Generics (as posted in another answer) would be much simpler and cleaner. The Genie interface you're relying doesn't seem to be adding much value. You'll likely do better starting over with a system that understands generics.
The problem of re-using the same object over and over can be fixed by creating a new object inside the loop. As written, the only way to do that is with a New clsType (and it seems you may have Option Strict Off, such that this could blow up on you at run time), through some messy reflection code, a switch to using generics as suggested in #2, or a by accepting a Func(Of clsType) delegate that can build the new object for you.
Im trying to convert the output of a LINQ query to a datatable, I have the following code but it shows a syntax error in the (Of DataRow) part:
Dim X As New Entities
Dim query As IEnumerable(Of DataRow) = From cajero In X.CAJERO.AsEnumerable
Select cajero
Dim bla = query.CopyToDataTable(Of DataRow)()
I'm using this question as a guide:
Filling a DataSet or DataTable from a LINQ query result set
If i use
query.CopyToDataTable()
'instead of the overload
query.CopyToDataTable(Of DataRow)
it throws an invalidCastException.
I'm looking for an easy way to accomplish this task, and this seemed to be the easiest one, without too much code and having to implement any "shredder" methods or such, but if that's the only way, then please point me in the right direction.
This is the error that throws(I localized it to english, its a bit different in spanish):
Cannot convert object of type WhereSelectEnumerableIterator to object of type IEnumerable System.Data.DataRow
I have tried the following:
Declare an extension method that would create the datatable like this:
_
Public Function myToDataTable(Of T)(source As IEnumerable(Of T)) As DataTable
Dim properties As PropertyInfo() = GetType(T).GetProperties()
Dim output As New DataTable()
For Each prop In properties
output.Columns.Add(prop.Name, prop.PropertyType)
Next
For Each item In source
Dim row As DataRow = output.NewRow()
For Each prop In properties
row(prop.Name) = prop.GetValue(item, Nothing)
Next
output.Rows.Add(row)
Next
Return output
End Function
But it always throws an exception while adding the columns to the datatable:
DataSet does not support System.Nullable
I also changed the linq query to this:
Dim query = From cajero In X.CAJERO
Select cajero
Dim bla = query.myToDataTable
Following Jon's suggestion I found this question:
.NET - Convert Generic Collection to DataTable
Which just gave me the last bit of code I needed:
output.Columns.Add(prop.Name, If(Nullable.GetUnderlyingType(prop.PropertyType), prop.PropertyType))
and
row(prop.Name) = If(prop.GetValue(item, Nothing), DBNull.Value)
I believe the problem is that you're trying to convert an IEnumerable<T> of an arbitrary entity type - whereas CopyToDataTable() always requires the input to be a sequence of some kind of DataRow.
Unless your entity type actually derives from DataRow, that's not going to work. You could potentially write a LINQ query which creates a DataRow from each instance, but I believe you'll have to write that code yourself.
I believe in the question you referenced, the OP already had a strongly typed DataSet - at least the answer suggested that's the case.
This seems like a really basic thing that I'm doing, yet I'm tearing my hair out trying to make it work.
My situation is this: I have a project which contains a large number of lookup tables, and I have all of these lookup tables represented in a single typed DataSet, which contains TableAdapters for each lookup. I've designed an editor for these lookup tables, which should allow editing of one of these at a time. My front-end is written in VB and WinForms, the back-end is a SOAP web service; I can successfully pass the changes to the DataSet back to the web service, but can't find a way to use a TableAdapter to update the single table that has been changed.
What I'm trying to do is instantiate the appropriate TableAdapter for the updated DataTable by sending the name of the table back to the web service along with the DataSet, then referring to the TableAdapter with a dynamic name. The normal way to instantiate a TableAdapter is this:
Dim ta As New dsLookupsTableAdapters.tlkpMyTableTableAdapter
What I'd like to do is this, but of course it doesn't work:
strTableName = "tlkpMyTable"
Dim ta As New dsLookupsTableAdapters(strTableName & "TableAdapter")
Is there any way to achieve this, or am I taking the wrong approach altogether? My other alternative is to write separate code for each table, which I'd prefer to avoid!
You can use Activator to create an instance of your TableAdapter from its string name, just like you want:
object adapter = Activator.CreateInstance(Type.GetType("My.Namespace.MyDataSetTableAdapters." + myTable.Name + "TableAdapter"));
Then, because TableAdapters don't have a common interface, you should use reflection to call its Update method:
adapter.GetType().GetMethod("Update").Invoke(adapter, null);
http://msdn.microsoft.com/en-us/library/system.type.getmethod.aspx
This is from memory, but roughly close enough. You can also use GetProperty to get the connection property and set it as required.
Not sure I 100% understand, do you have a single DataTable in your DataSet, or one DataTable per lookup table?
Anyway, perhaps you could you this approach to filter by lookup table?
It's pretty easy to create types at runtime given the (string) type name.
Here's a self-contained VB class which illustrates one way to do it: use System.Activator.CreateInstance to create instances of types using a string representation of the type name. Then you can cast it to a DataAdapter base class and use it like any other DataAdapter.
Public Class dsLookupsTableAdapters
Public Function CreateInstance(ByVal strName As String) As Object
CreateInstance = Nothing
For Each a As System.Reflection.Assembly In System.AppDomain.CurrentDomain.GetAssemblies()
Try
Dim strAssemblyName As String() = a.FullName.Split(New Char() {","c})
Dim strNameTemp As String = strAssemblyName(0) & "." & strName
Dim instance As Object = System.Activator.CreateInstance(a.FullName, strNameTemp)
If instance IsNot Nothing Then
Dim handle As System.Runtime.Remoting.ObjectHandle
handle = CType(instance, System.Runtime.Remoting.ObjectHandle)
Dim o As Object = handle.Unwrap()
CreateInstance = o
Exit For
End If
Catch ex As System.Exception
Continue For ' ignore exception, means type isn't there
End Try
Next
End Function
Public Class tlkpMyTableTableAdapter
Inherits System.Data.Common.DataAdapter
End Class
Public Sub Test()
' define type name. note that, in this sample, tlkpMyTableTableAdapter is a nested
' class and dsLookupsTableAdapters is the containing class, hence the "+". If, however,
' dsLookupsTableAdapters is a namespace, replace the "+" with a "."
Dim typeName As String = "dsLookupsTableAdapters+tlkpMyTableTableAdapter"
Dim adapter As System.Data.Common.DataAdapter
Dim o As Object = CreateInstance(typeName)
adapter = CType(o, System.Data.Common.DataAdapter)
End Sub
End Class
If you are using VB.Net 2008, then use the tableadaptermanager (http://msdn.microsoft.com/en-us/library/bb384426.aspx). I think this would be much easier to code against :)
Wade