C# and SqlDataReader class - basic Q

OK I'm completely new to C# and .NET ... I'm not a developer!  My only previous developerish experience is VBScript.

I wrote a script in PowerShell (which I've not done much in before either) to query a SQL database and display results.  I've fiddled with ADO from VBScript about twice before...

I got my script working.  It uses a SqlDataReader to pull records based on a SQL query and then gets individual fields with this line after doing a Read():
$callID = $dataReader.Item("CallID")

Open in new window


I've been trying to convert this all to C# since PowerShell syntax is very similar (this will be my first ever C# program!)  If I try and replace the above line with
 
string callID = dataReader.Item["CallID"];

Open in new window

I get an error before even running anything:

"'System.Data.SqlClient.SqlDataReader' does not contain a definition for 'Item' and no extension method 'Item' accepting a first argument of type 'System.Data.SqlClient.SqlDataReader' could be found (are you missing a using directive or an assembly reference?)"

I've seen an example online where it appears the syntax may need to be:
string callID = (string)dataReader["CallID"];

Open in new window

but I don't understand why this should be, since the SDK shows Item[<string>] as a property of the SqlDataReader object.

What's going on?  The SDK makes it look as though it should be easy and indeed from PowerShell it was.
I'm sure there must be a simple explanation.  Thanks!
LVL 16
cantorisAsked:
Who is Participating?
 
käµfm³d 👽Commented:
There is a comment to this flaw on this page. I agree, it is confusing, but your third example is how you would retrieve the value from the SqlDataReader. You can use column index (starting from zero, and in the order you selected in your query) as opposed to column name when you index SqlDataReader.

Please note, the cast you use in your 3rd is necessary. When you index the SqlDataReader, the object returned is returned as type object, and there is no implicit conversion from object to any type.
0
 
Easwaran ParamasivamCommented:
C# uses indexer to get the values. Please do refer http://www.codeproject.com/KB/cs/Indexer_in_C__Programmin.aspx for basic details. Where the above concept may not applicable to Powershell or Vbscript that you have used previously.
0
 
cantorisAuthor Commented:
I understand what the syntax of my third code example means but I don't understand why it was required; why do I see the .NET SqlDataReader SDK documentation say there's a property called .Item[<string>] but I seem unable to access this as a property of the object from C# when I can from PowerShell (which also accesses it as a .NET class too).

I'm wondering what else I'm going to read in the documentation that's also going to trip me up when I try and access what looks like a straightforward member of an object.
0
Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

 
Jacques Bourgeois (James Burger)PresidentCommented:
VBScript is very relaxed as far as types are concerned. You can assign a Sring to an Integer without any fuss, the Integer will automatically be converted from "10" to 10. But what would happen if the String contained "10Z" by mistake. There would be a conversion error. This is a run-time error, that happens when the user is working in the application, and it can be hard to pinpoint the problem, because users usually are not very careful about recording the steps that lead them to an error.

So modern languages (VBSscript is almost 20 years old) are a lot more restrictive about type conversions. If they see a possible conversion problem, they won't compile. This is a lot better, because this happens in your developement environment. As a programmer, you should have the knowledge and the tools to analyze the situation and act accordingly.

That forces you to work a little more however. You need to specify the conversion on almost all assignations when the value assigned is not of the same type as the one of the variable or parameter that will receive it. Amateurs sometime see that feature as a pain, professionals see it as a blessing. It saves a lot of painful debugging and maintenance time in the medium to long run.

The DataReader receives different types, depending on the data returned by the database. When you type string callID = dataReader.Item["CallID"], the compiler is actually asking you "Are you sure that the value that will be here at runtime will be a string?". And your way to answer yes is to implicitely force the cast from an Object (that is returned by the DataReader) to a String.

If you do not like that way of working, use Visual Basic instead of C#. In VB, this stuff is a compiler option. Professional usually turn it on, amateurs usually turn it off.
0
 
cantorisAuthor Commented:
Thanks for the info so far.

I'm happy with the strictness around types in C#.  If that were the sole cause of what I'm seeing, then why can't I do the following to ensure I am indeed getting a string:
 
string callID = dataReader.Item["CallID"].ToString();

Open in new window

I still get the same error.

My problem understanding is not from the fact I have to cast the returned value to a string but the fact that the documented property "Item[<string>]" acts as though it doesn't exist in this class when accessed through C# and yet I see it and use it fine through PowerShell which is built on .NET too and uses a largely C#-like syntax itself?

Is it just the SqlDataReader or is there a more general set of circumstances (which I can in the future go on to predict) when this sort of phenomenon is seen?
0
 
Jacques Bourgeois (James Burger)PresidentCommented:
Strange thing indeed. I mostly do VB, so I came with my VB experience with my answer.

Did some testing. Strangely, VB accepts Item(String), but C# gives me the same error as the one you get, although the string is listed in IntelliSense. Seems to be some kind of quirk in the C# compiler.

Since Item is the default property of the DataReader however, you do not have to specify it. The following does work:

string callID = (string)dataReader["CallID"];
0
 
käµfm³d 👽Commented:
the fact that the documented property "Item[<string>]" acts as though it doesn't exist in this class when accessed through C#
The property is documented because VB uses that syntax. The property in C#, for lack of a better word, is "mapped" onto the index operator ( [] ).

yet I see it and use it fine through PowerShell which is built on .NET too and uses a largely C#-like syntax itself?
I've felt like PS was super-charged VBScript, but I certainly appreciate that there is C#-edness going on there too. My only guess as to this behavior is that because you can use both VB and C# constructs in PS, the Item that shows up in PS is a part of the VB side of things.
0
 
Jacques Bourgeois (James Burger)PresidentCommented:
@kaufmed

The property in C#, for lack of a better word, is "mapped" onto the index operator ( [] )

Could explain how you can "map" a property to one overload when there are 2. This is a concept I have never encountered.

I do not see how an overload that is documented in the official documentation and is in Intellisense could be "excluded".

If you look at the declaration for the 2 overloads, they do not differ, so what could make int different than string.
public override Object this[
      int i
  ] { get; }

Open in new window

vs
public override Object this[
      string name
  ] { get; }

Open in new window

I use [string] all the time with other ADO.NET collections. Is this something I do not know in C# that has to do to the fact that Item is the default property?
0
 
cantorisAuthor Commented:
Found something interesting in the  System.Collections.Hashtable.Item  property docs which perhaps sheds more light on what is going on:

"The C# language uses the 'this' keyword to define the indexers instead of implementing the 'Item' property. Visual Basic implements 'Item' as a default property, which provides the same indexing functionality."

So perhaps this is the case for all Item properties generally for C#?
0
 
käµfm³d 👽Commented:
Could explain how you can "map" a property to one overload when there are 2.
Heheh. That's why I put "map" in quotes. I really didn't want to use it, but it's the best I could come up with.

Could explain how you can "map" a property to one overload when there are 2. This is a concept I have never encountered.

I do not see how an overload that is documented in the official documentation and is in Intellisense could be "excluded".
From http://msdn.microsoft.com/en-us/library/az06zx4y(v=vs.71).aspx:

If an overloaded property is a default property, all overloaded properties with that same name must also specify Default.

I don't believe C# has the concept of "default properties"--not to say you can't apply the attribute to a class, but syntactically I don't know how it would work. VB uses array-indexing notation for accessing the default property; C# will only have that similar syntax if you overload the indexer operator ( [] ). I don't see anything similar in VB which corresponds to the indexer operator. I imagine the Item property is the equivalent. For SqlDataReader, the Item property in VB has the same overloads that the corresponding indexer in C# has--and of course we know that it exhibits the same, desired behavior. This is the reason I said "map"--because you have two different names/operators, but they are yielding the same result. TMK, there is no Item property in C#.

I think cantoris' latest quote probably sums all that up  ; )
0
 
Jacques Bourgeois (James Burger)PresidentCommented:
The fact that Item is the default property in VB is only a convention. Any property can be the default... as long as it has at least one parameter, what we see as an indexer in a collection.

Even if VB and C# have different ways of declaring properties with indexes, default or not, that has to do with the creation of the class, not with the way we access it. And the discussion here is about accessing that Item property.

There is not a SqlDataReader written in VB and another one written in C#. There is but one SqlDataReader. If Item[string] works in VB, then it is built into the SqlDataReader class. And since it is public, it is available to anybody, no matter the language used to access it. And C# is able to work with collection items that can be accessed either by a string or an int. See the Parameters collection of the SqlCommand class, that is by the way defined in the same namespace as the SqlDataReader. So, why does the SqlDataReader behave differently than other collections?

I really would like to have somebody from Microsoft in this one.

But you have your problem solved cantoris, isn't it?
0
 
käµfm³d 👽Commented:
Even if VB and C# have different ways of declaring properties with indexes
But I don't think we are talking about "properties with indexes" so much as we are talking about a special property. VB still maintains the legacy functions like Mid, InStr, and Left, and they are mapped onto the .NET functions. C# doesn't have any of these functions (the classic VB ones). I'm taking it to be along the same vein as Shared vs static, Shadows vs new, etc. I understand your argument as to it existing within the class itself, so it should be in both languages, and I don't have an answer. The only thing I can think is that it's just one of the things that is unique to VB, and since it exists in legacy VB code, the VB team carried it over. Perhaps now that the C# and VB teams are working as one team, that particular bit will get consolidated.

If you write code for both languages and then examine the IL, you will see that both resolve to the same virtual method call:


Untitled.png
0
 
käµfm³d 👽Commented:
Code for the above screenshot:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace _27525524_CS
{
    class Program
    {
        static void Main(string[] args)
        {
            System.Data.SqlClient.SqlDataReader x = null;

            string s = (string)x["column"];
        }
    }
}

Open in new window


VB
Module Module1

    Sub Main()
        Dim x As System.Data.SqlClient.SqlDataReader = Nothing

        Dim s As String = CStr(x.Item("column"))
    End Sub

End Module

Open in new window

0
 
Jacques Bourgeois (James Burger)PresidentCommented:
VB does not maintain the legacy functions. They are not part of the language. They are defined in a namespace (Microsoft.VisualBasic)designed to ease the transition from VB6. If you bring a reference to that namespace in a C# application, you can also use those functions from C#. They are only static methods in a library. Good VB programmers do not use them anymore. They will use String.Substring instead of Mid, Right and Left.

One of the first thing I tell my students in my basic training session is to remove the Imports (equivalent to using in C#) to that namespace at the project level so that the legacy functions are not readily available. From then on, VB is used just like C#, by calling the equivalent framework functions that are a lot more interesting to use anyway.
0
 
käµfm³d 👽Commented:
I was only using those for example, and I'm not sure why we're having a debate over the specifics of other language elements, but I'll play along...

Doesn't the fact that they exist at all mean they are "maintained"? I understand why they are included. Try this in C#:  add your reference to Microsoft.VisualBasic.dll (and your using statement), and then try running this line of code:

string result = Mid("Hello World", 1, 5)

Open in new window


NOT this:

string result = Strings.Mid("Hello World", 1, 5)

The fact is even though both languages use the .NET Framework, each still has its own "quirks" which the other does not. The reason the first line above works in VB is because the Strings "class" is defined as a module. Public functions in modules are available throughout an application. When compiled, however, modules are turned into classes. This is why we can use the Strings "class" in C#.
0
 
cantorisAuthor Commented:
A lot of interesting info that's currently rather over my head but I think MS's comment sort of makes sense to me.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.