DataReader ordinal-based lookups vs named lookups - sql

Microsoft (and many developers) claim that the SqlDataReader.GetOrdinal method improves the performance of retrieving values from a DataReader versus using named lookups ie. reader["ColumnName"]. The question is what is the true performance difference if dealing with small, paged record sets? Is it worth the extra overhead of finding and referencing ordinal indexes throughout the code?

Microsoft recommends not calling GetOrdinal within a loop.
That would include indirect calls with the string indexer.
You can use GetOrdinal at the top of your loop put the ordinals in an array and have the indexes in the array be const or have an enum for them (no GetOrdinal at all) or use GetOrdinal into individual variables with descriptive names.
Only if your sets are small would I really consider this to be premature optimization.
It's apparently a 3% penalty.

Any difference will be more than outweighed by maintenance overhead.
If you have that much data that it makes a noticeable difference, I'd suggest you have too much data in your client code. Or this is when you consider use ordinals rather than names

Yes and no.
If you're dealing with a massive amount of data then you'd certainly benefit from using the ordinals rather than the column names.
Otherwise, keep it simple, readable, and somewhat safer - and stick with the column names.
Optimize only when you need to.

I created a wrapper for SqlDataReader that stores orindals in a dictionary with the column name as the key.
It gives me ordinal performance gains while keeping the code more readable and less likely to break if someone changes the column order returned from stored procedures.
Friend Class DataReader
Implements IDisposable
Private _reader As SqlDataReader
Private _oridinals As Dictionary(Of String, Integer)
Private Shared _stringComparer As StringComparer = StringComparer.OrdinalIgnoreCase 'Case in-sensitive
Public Sub New(reader As SqlDataReader)
Me._reader = reader
Me.SetOrdinals()
End Sub
Private Sub SetOrdinals()
Me._oridinals = New Dictionary(Of String, Integer)(_stringComparer)
For i As Integer = 0 To Me._reader.FieldCount - 1
Me._oridinals.Add(Me._reader.GetName(i), i)
Next
End Sub
Public Function Read() As Boolean
Return Me._reader.Read()
End Function
Public Function NextResult() As Boolean
Dim value = Me._reader.NextResult()
If value Then
Me.SetOrdinals()
End If
Return value
End Function
Default Public ReadOnly Property Item(name As String) As Object
Get
Return Me._reader(Me.GetOrdinal(name))
End Get
End Property
Public Function GetOrdinal(name As String) As Integer
Return Me._oridinals.Item(name)
End Function
Public Function GetInteger(name As String) As Integer
Return Me._reader.GetInt32(Me.GetOrdinal(name))
End Function
Public Function GetString(ordinal As Integer) As String
Return Me._reader.GetString(ordinal)
End Function
Public Function GetString(name As String) As String
Return Me._reader.GetString(Me.GetOrdinal(name))
End Function
Public Function GetDate(name As String) As Date
Return Me._reader.GetDateTime(Me.GetOrdinal(name))
End Function
Public Function GetDateNullable(name As String) As Nullable(Of Date)
Dim o = Me._reader.GetValue(Me.GetOrdinal(name))
If o Is System.DBNull.Value Then
Return Nothing
Else
Return CDate(o)
End If
End Function
Public Function GetDecimal(name As String) As Decimal
Return Me._reader.GetDecimal(Me.GetOrdinal(name))
End Function
Public Function GetBoolean(name As String) As Boolean
Return Me._reader.GetBoolean(Me.GetOrdinal(name))
End Function
Public Function GetByteArray(name As String) As Byte()
Return CType(Me._reader.GetValue(Me.GetOrdinal(name)), Byte())
End Function
Public Function GetBooleanFromYesNo(name As String) As Boolean
Return Me._reader.GetString(Me.GetOrdinal(name)) = "Y"
End Function
'Disposable Code
End Class

Related

How to create a Boolean Function to list objects in a Generic Collection?

After creating a List(Of T), I want to create aBoolean function. First, we will ask data to add an object in the list. However, in case this new object has the same "DNI" (String attribute from the class Aspirante), then we cannot include this new Object in the list. Therefore, it should be True when we have an Object with the same attribute and False when we don´t, so we can add the new object.
Below is the code I did:
Public Class Oposicion
Private datos As New List(Of Aspirante)()
Public Function Alta(ByRef objAspirante As Aspirante) As Boolean
If datos.Contains(objAspirante.DNI) Then
Return True
Else
datos.Add(objAspirante)
Return False
End If
End Function
End Class
However it doesn´t work. I have no clue on how to do it. Sorry if I was not clear enough.
This doesn't answer your question directly but it involves a significant amount of code, so it won't work in a comment.
You probably shouldn't be using a List(Of T) in the first place. The HashSet(Of T) already includes functionality to prevent adding duplicate items, so that may be a better option. If you want to compare objects on a specific property value then you need to first create a comparer based on that:
Public Class Thing
Public Property Stuff As String
End Class
Public Class ThingComparer
Implements IEqualityComparer(Of Thing)
Public Overloads Function Equals(x As Thing, y As Thing) As Boolean Implements IEqualityComparer(Of Thing).Equals
Return x.Stuff.Equals(y.Stuff)
End Function
Public Overloads Function GetHashCode(obj As Thing) As Integer Implements IEqualityComparer(Of Thing).GetHashCode
Return obj.GetHashCode()
End Function
End Class
You then create a HashSet(Of T) that uses that comparer to determine equality:
Dim things As New HashSet(Of Thing)(New ThingComparer)
You can then just add items as you please by calling Add. That will either add the new item and return True or it will not not add the duplicate item and return False:
Dim variousStuff = {"One", "Two", "One"}
For Each stuff In variousStuff
Dim something As New Thing With {.Stuff = stuff}
If things.Add(something) Then
Console.WriteLine($"'{stuff}' was added successfully.")
Else
Console.WriteLine($"'{stuff}' is a duplicate and was not added.")
End If
Next
The potential drawback is that HasSet(Of T) does not implement IList(Of T), so you cannot access items by index. It does implement ICollection(OF T) though, so it does have a Count property and you can enumerate it with a For Each loop.
You can create a List(Of String) containing the just the DNI property of each object in datos. Then see if the DNI of objAspirante is contained in lst.
Public Class Oposicion
Private datos As New List(Of Aspirante)()
Public Function Alta(objAspirante As Aspirante) As Boolean
Dim lst As New List(Of String)
For Each a As Aspirante In datos
lst.Add(a.DNI)
Next
If lst.Contains(objAspirante.DNI) Then
Return True
Else
datos.Add(objAspirante)
Return False
End If
End Function
End Class
If you can change the type of datos, this might be easier.
Public Class Oposicion
Private datos As New Dictionary(Of String, Aspirante)()
Public Function Alta(objAspirante As Aspirante) As Boolean
If datos.ContainsKey(objAspirante.DNI) Then
Return True
Else
datos.Add(objAspirante.DNI, objAspirante)
Return False
End If
End Function
End Class
If you want to stick with your existing List(Of Aspirante), then simply use .Any() and pass it a Lambda to determine if one already exists. It'd look something like this:
Public Function Alta(ByVal objAspirante As Aspirante) As Boolean
If Not datos.Any(Function(x) x.DNI = objAspirante.DNI) Then
datos.Add(objAspirante)
Return True ' your description and code did not match for these!
End If
Return False ' your description and code did not match for these!
End Function
Note my comment on the Return lines...your code and description did not match here.

Best method to compare strings?

Scenario: I created a software that calculates the hash of a file, and compares it to a hash list file in my possession (about 1 mln - growing), currently in txt format.
Which is the best way to make the comparison as fast as possibile?
I'm using this function:
Public HashList As New List(Of String)
Private Sub LoadHash()
For Each hash As String In IO.File.ReadAllLines("C:\test\hash.txt")
HashList.Add(hash)
Next
End Sub
Private Function CheckFile(ByVal filename As String) As Boolean
If HashList.Contains(MD5(filename)) Then
Return True
End If
Return False
End Function
Any suggestions for improve this code?
are there better methods?
Try using a better collection type like a HashSet. There are a lot in .NET that all have their use.
Public HashList As New HashSet(Of String)

Reflection - creating datatype from a string literal value

Please see the code below:
Public Function Test()
Dim o As Object = getVariable("Integer")
If TypeOf o Is Integer Then
'Do some processing on the integer
ElseIf TypeOf o Is Decimal Then
'Do some processing on the integer
End If
End Function
Public Function getVariable(ByVal strDataType As String)
If strDataType = "Integer" Then
Return New Integer
ElseIf strDataType = "Decimal" Then
Return New Decimal
ElseIf strDataType = "Double" Then
Return New Double
End If
End Function
I suspect there is an easier way (fewer lines of code) of doing this with Reflection?
You can use Type.GetType together with Activator.CreateInstance:
Public Function getVariable(ByVal strDataType As String)
Return Activator.CreateInstance(Type.GetType(strDataType))
End Function
For strDataType you need to be using System.Int32, System.Decimal and System.Double respectively. If you want to keep it as Integer etc., you need to incorporate string translation, for example, have a Dictionary(Of String, String), with entries like ("Integer", "System.Int32").
The real question is what ars you trying to solve? Activator will work. Are trying to create a factory or an IOC container. Could you provide more detail? You could also create a dictionary were the key is of type string and then item stores delegates used to actual create the type.

VB.Net to optimize execution by keeping data other than fetching data from database everytime

I am newbie in vb.net I am trying to optimize my code execution that i am working on . i am working on a program having more than 45 forms. In every form its calling the function IsPowerUser to check is power user
If we can store all details about user while logging in , then we need to use these values every time when we needed instead of collecting data from database . May be this question belongs to VB Basics.
You can use lazy initialization to postpone the database call until you actually need the information (if ever), and then store the value for re-use.
Private _isPowerUser As New Lazy(Of Boolean)(AddressOf GetIsPowerUser)
Public Function IsPowerUser() As Boolean
Return _isPowerUser.Value
End Function
Private Function GetIsPowerUser() As Boolean
' Retrieve the information from the database and return
Return True Or False
End Function
In this sample, _isPowerUser is a backing field that uses lazy initialization. It is first initialized when someone calls IsPowerUser. Under the covers, Lazy(Of T) then calls the delegate GetIsPowerUser() that does all the heavy lifting to retrieve the value from the database.
Lazy(Of T) only needs to call GetIsPowerUser() once.
In other words: any consecutive calls to IsPowerUser() do not trigger GetIsPowerUser().
EDIT (1/2)
Lazy(Of T) was not introduced until .NET 4, so here is a code sample that works for .NET 2 and above:
Private _isPowerUser As Boolean?
Private lazyLock As New Object
Public Function IsPowerUser() As Boolean
If Not _isPowerUser.HasValue Then
SyncLock lazyLock
If Not _isPowerUser.HasValue Then
_isPowerUser = GetIsPowerUser()
End If
End SyncLock
End If
Return _isPowerUser.Value
End Function
Private Function GetIsPowerUser() As Boolean
' Retrieve the information from the database and return
Return True Or False
End Function
EDIT (2/2)
I changed my code sample to work in a multi-user environment:
Private _powerUsers As New Dictionary(Of Long, Boolean)
Private ReadOnly LazyLock As New Object
Public Function IsPowerUser(userId As Long) As Boolean
If Not _powerUsers.ContainsKey(userId) Then
SyncLock LazyLock
If Not _powerUsers.ContainsKey(userId) Then
_powerUsers.Add(userId, GetIsPowerUser(userId))
End If
End SyncLock
End If
Return _powerUsers.Item(userId)
End Function
Private Function GetIsPowerUser(UId As Long) As Boolean
' Retrieve the information from the database and return
Return True Or False
End Function
Usage example:
Dim Steven As Long = 454151
If IsPowerUser(Steven) Then
Console.WriteLine("Steven is a power user.")
Else
Console.WriteLine("Steven is a normal user.")
End If

VB: Problems with using variable from another class + what to do with not used interface`s functions

I have a problem with getting variable from another class and cannot understand what to do with interface`s functions which have already existed in another class.
What I have:
Form where clicking on a button I should see reversed string:
(I want to call pooraja.StringReverse which is below)
Private Sub btnPoora1_Click(sender As System.Object, e As System.EventArgs) _
Handles btnPoora1.Click
'Dim text As PrjTekstiPooraja.ITeisendused = New PrjTekstiPooraja.CtekstiPooraja
Dim text As PrjTekstiPooraja.ITeisendused = New PrjTekstiPooraja.CtekstiPooraja
Dim pooraja As PrjTekstiPooraja.ITeisendused = New PrjTekstiPooraja.CAlgrotimilinePooraja
text.strText = txtSisendTekst.Text
txtValjundTekst1.Text = pooraja.stringReverse
text.intStart = 1
text.intEnd = Len(txtSisendTekst.Text)
ascFSymbol.Text = text.ascFirstSymbol
ascLSymbol.Text = text.ascLastSymbol()
End Sub
CtekstiPooraja:
(Thiss class will be used to store data.Under data I mean strPooratavText. Data will be used in CAlgoritmilinePooraja)
Public Class CtekstiPooraja
Implements ITeisendused
Public intStartSymbol As Integer
Public intEndSymbol As Integer
Public strPooratavText As String
Private Property intEnd As Integer Implements ITeisendused.intEnd
Get
Return intEndSymbol
End Get
Set(ByVal value As Integer)
intEndSymbol = value
End Set
End Property
Private Property intStart As Integer Implements ITeisendused.intStart
Get
Return intStartSymbol
End Get
Set(ByVal value As Integer)
intStartSymbol = value
End Set
End Property
Public Function pooraText() As String Implements ITeisendused.pooraText
Return StrReverse(strPooratavText)
End Function
Public Property strText As String Implements ITeisendused.strText
Get
Return strPooratavText
End Get
Set(ByVal value As String)
strPooratavText = value
MsgBox(strPooratavText)
End Set
End Property
Public Sub teisendaText(ByRef strSisendText As String) Implements ITeisendused.teisendaText
strPooratavText = StrReverse(strSisendText)
End Sub
Public Function ascFirstSymbol() As String Implements ITeisendused.ascFirstSymbol
Return Asc(GetChar(strPooratavText, intStartSymbol))
End Function
Public Function ascLastSymbol() As String Implements ITeisendused.ascLastSymbol
Return Asc(GetChar(strPooratavText, intEndSymbol))
End Function
Public Function stringReverse() As String Implements ITeisendused.stringReverse
Return Nothing
End Function
End Class
CAlgrotimilinePooraja:
(This class will be called by form button. There I need to use stringReverse function with data from CtekstiPooraja. The problem is that everywhere is used the same interface and there is some functions and procedures from this interface which isnt necessary. I dont know what value should return these unused functions/procedures. Just using "return Nothing or return 0/ "" is bad idea, may be there is possible somehow referenceto to CTekstiPooraja functions/procedures variables")
Public Class CAlgrotimilinePooraja
Implements ITeisendused
Private x As New PrjTekstiPooraja.CtekstiPooraja
Public Function stringReverse() As String Implements ITeisendused.stringReverse
MsgBox(x.strPooratavText)
Dim i As Integer = 0
Dim j As Integer
Dim characters(j) As Char
Dim newString(j) As Char
characters = x.strPooratavText.ToCharArray()
newString = x.strPooratavText.ToCharArray()
Do While i <= j - 1
newString(i) = characters(j - 1)
newString(j - 1) = characters(i)
i += 1
j -= 1
Loop
Return newString
End Function
Public Function ascFirstSymbol() As String Implements ITeisendused.ascFirstSymbol
Return x.ascFirstSymbol()
End Function
Public Function ascLastSymbol() As String Implements ITeisendused.ascLastSymbol
Return Nothing
End Function
Public Property intEnd As Integer Implements ITeisendused.intEnd
Get
Return x.intEndSymbol
End Get
Set(ByVal value As Integer)
End Set
End Property
Public Property intStart As Integer Implements ITeisendused.intStart
Get
Return x.intStartSymbol
End Get
Set(ByVal value As Integer)
End Set
End Property
Public Function pooraText() As String Implements ITeisendused.pooraText
Return x.pooraText()
End Function
Public Property strText As String Implements ITeisendused.strText
Get
Return x.strPooratavText
End Get
Set(ByVal value As String)
End Set
End Property
Public Sub teisendaText(ByRef strSisendText As String) Implements ITeisendused.teisendaText
x.strPooratavText = StrReverse(strSisendText)
End Sub
End Class
MyInterface:
Public Interface ITeisendused
Property intStart As Integer
Property intEnd As Integer
Property strText As String
Function pooraText() As String
Function ascFirstSymbol() As String
Function ascLastSymbol() As String
Function stringReverse() As String
Sub teisendaText(ByRef strSisendText As String)
End Interface
I cannot understand how to get variable strPooratavText from CTekstiPooraja to CAlgrotimilinePooraja. Usually that instancewhich I create worked but not now. And I cannot understand what to do with already existed function and procedures in CAlgoritmilinePooraja when the same function and procedures has in another class. Maybe, it is possible to reference them somehow to existed functions/procedures in CTekstiPooraja? Could you explain me how to id, already tired to surf Internet to find a solution for it, have already try a lot.
Well, I think you have a fundamental problem with understanding interfaces. They describe data and behavior, it should be extremely rare to want to implement part of an interface.
That said, if you do want to implement part of an interface, instead of returning bogus data, throw an exception for behavior you don't implement.
Your specific problem is that CAlgoritmilinePooraja works on an instance of CtekstiPooraja, but it creates a new instance instead of using an existing one. Add
Sub New(incomingX as CtekstiPooraja)
x = incomingX
End Sub
to CAlgoritmilinePooraja. And then in your event, use....
Dim text As PrjTekstiPooraja.CtekstiPooraja = New PrjTekstiPooraja.CtekstiPooraja
text.strText = txtSisendTekst.Text
Dim pooraja As PrjTekstiPooraja.ITeisendused = New PrjTekstiPooraja.CAlgrotimilinePooraja(text)
That is the minimum change to your design that gets what you want to happen to happen but it's problably not what you should do. Other than implementing strReverse, CtekstiPooraja seems to be what you want, CAlgrotimilinePooraja looks to do just one thing, the actual string reversal.
I would move the implementation of strReverse into CtekstiPooraja, and then eliminate CAlgrotimilinePooraja.
PS I would try to stick to English for class names as well as functions and variables.