Results 1 to 14 of 14

Thread: Conversion Question

  1. #1

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,069

    Conversion Question

    I haven't done much with C#, but this just seems odd to me, so I figured I'd ask to see what is going on.

    I have a datareader that gets one field from a database. That field is usually a string, but in one case, I'm looking for an integer field.

    I had this line:
    Code:
    New SelectedListItem(dr[0].ToString(),dr[0].ToString())
    that worked fine, as there is a form of the constructor that takes two strings as arguments. However, I then realized that I needed to do something slightly different in one highly specialized case, so I wrote this line:

    Code:
    string st1 = dr[0].ToString();
    This gives a warning because dr[0] could be null. It can't actually, because that line is inside the else case where I do something different if dr[0] is null, but that's unimportant.

    My first question is why is it fine to use the code as an argument to a constructor that takes a string (but not a nullable string, as far as I can see), but it isn't okay to assign it to a string variable? Is the constructor assuming that it's arguments might just be nullable? Why wouldn't the argument be string? in that case?

    Anyways, I then tried to do this:

    Code:
    string st1 =dr.GetString(0);
    but that doesn't work because dr[0] is not always a string. In that one case, it is an integer, so GetString() throws an exception.

    So, my second question is: There must be some easy way to solve this, though I'm not seeing it, and I'm not quite sure how to put it in the form of a searchable question. So, what am I missing?
    Last edited by Shaggy Hiker; Mar 21st, 2024 at 03:59 PM. Reason: Fixed the title.
    My usual boring signature: Nothing

  2. #2
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,368

    Re: Conversion Question

    The warning is not telling you that dr[0].ToString() could be null, so it has nothing to do with the constructor. It's telling you that dr[0] could be null, so calling ToString could throw a NullReferenceException. I'm not sure why the first code snippet doesn't generate the same warning - maybe you are already using dr[0] before that so any NullReferenceException would be thrown earlier. We'd need to see the full code to get a better idea.

    I'm not sure what version of C# you're using - 7.3 if it's a .NET Framework project - but there are two ways that this might be addressed. In newer versions, if you have a nullable reference that you know will not be null then you can tell the compiler like this:
    Code:
    string st1 = dr[0]!.ToString();
    That's probably not available to you. The other option is to use null propagation:
    Code:
    string st1 = dr[0]?.ToString();
    That tells the compiler to return null if the reference before the operator is null, rather than executing the remainder of the expression, thus avoiding a NullReferenceException. If you know that the reference will never be null, you know that ToString will always be called anyway, but you keep the compiler happy. You may already be familiar with this option as it's available in VB too.

  3. #3

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,069

    Re: Conversion Question

    Quote Originally Posted by jmcilhinney View Post
    The warning is not telling you that dr[0].ToString() could be null, so it has nothing to do with the constructor. It's telling you that dr[0] could be null, so calling ToString could throw a NullReferenceException. I'm not sure why the first code snippet doesn't generate the same warning - maybe you are already using dr[0] before that so any NullReferenceException would be thrown earlier. We'd need to see the full code to get a better idea.
    I misspoke. I did understand what the warning was telling me, I didn't understand why it wasn't telling me that before. Yeah, if I had been interacting with dr[0] earlier, that might matter, but originally, it was just this (from memory, and not all C#):
    Code:
    Do While dr.Read
     someList.Add(New SelectedListItem(dr[0].ToString(),dr[0].ToString()));
    Loop
    Since that time, I explicitly checked whether dr[0] was null, and did something different if it was, but what is shown there is what happened if dr[0] was not null. Interestingly, if dr[0] was null, with the preceding code, what I got was a SelectedListItem with what looked like an empty string as both arguments. It seems like, if .ToString would throw an exception if dr[0] was null, then that loop shouldn't have worked at all, but it did.

    This is .NET 8 (or 7, I may be misremembering).

    I'm on the wrong computer today, but I'll have to put it back to the way it was to take a closer look at it. As far as I can tell, it should not have worked, and yet it did. dr[0] certainly was null, and .ToString was certainly called on it. Had an exception been thrown, it would have given a very obvious result (it would have been somewhat swallowed, but not invisibly), so that didn't happen. That's very odd.
    My usual boring signature: Nothing

  4. #4
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,368

    Re: Conversion Question

    I'm not sure how dr[0] could ever be null anyway. If you're getting nulls from the database then they would be represented by DBNull.Value, not null, and DBNull.ToString does return an empty string. I'm not sure exactly what provider you're using but the .NET 8 documentation for both System.Data.SqlClient and Microsoft.Data.SqlClient indicates that the SqlDataRedaer indexer return type is object, not object?, so that seems to suggest that it's not allowed to be null at all. I'd be interested to see a larger code snippet that demonstrates the issue.

  5. #5

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,069

    Re: Conversion Question

    Here's a function that I stripped down enough to show the problem with a just a few extras:
    (what happened to the buttons on quick reply? They're gone...again!)

    Code:
    public List<SelectListItem> GetList2()
    {
        List<SelectListItem> listCollection = new List<SelectListItem>();
        using (SqlConnection cn = new SqlConnection(mConString))
        {
            cn.Open();
            using (SqlCommand cmd = cn.CreateCommand())
            {
                cmd.CommandText = "SELECT DISTINCT someField FROM SomeTable";
                using (SqlDataReader dr = cmd.ExecuteReader())
                {
                   while (dr.Read())
                    {
                        if (dr.IsDBNull(0))
                        {
                            listCollection.Add(new SelectListItem("Is Null", ""));
                        }
                        else
                        {
                           //The following line has a warning.
                            string st1 = dr[0].ToString();
                            listCollection.Add(new SelectListItem(st1, st1));
                            //This has no warnings.
                            listCollection.Add(new SelectListItem(dr[0].ToString(), dr[0].ToString()));
                        }
    
                    }
                }
            }
        }
        return listCollection;
    }
    The SQL string doesn't matter, of course, nor does the If statement checking for null, since it makes no difference whether a field is null or not for the question, but the code is explicitly checking for null, so the warning is a bit weirder, as the value can't be null there. Whether or not the compiler notes that the path is only followed if the field is not null...well, it isn't, but I wouldn't expect it to.

    I also added the two items being added to the listCollection to show that there is no issue with dr[0].ToString if used as an argument, but there is if used to put the value into a string variable.

    In VS2022 targeting .NET 8, this line:

    string st1 = dr[0].ToString();

    is showing a warning, as noted in the original question.
    My usual boring signature: Nothing

  6. #6
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,767

    Re: Conversion Question

    Quote Originally Posted by Shaggy Hiker View Post
    ... but the code is explicitly checking for null, so the warning is a bit weirder, as the value can't be null there...
    That's not correct. DBNull and null are not the same thing and what your code is doing is checking the former not the latter.

    I'm sure that you're aware of this, but I'm going to include it anyways. DBNull represents an explicit database NULL value whereas null is the C# literal. So if you wanted the warning to go away without adding the exclamation mark or question mark, then you need to check if the value is not null in your else statement:
    Code:
    if (dr.IsDBNull(0))
    {
        // ...
    }
    else if (dr[0] != null)
    {
        // ...
    }
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  7. #7

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,069

    Re: Conversion Question

    Yeah, I did know that, but I overlooked the point you were making. Is that even possible? Considering that dr is a datareader, I would expect that the compiler would know that dr[0] couldn't be null, though it could be DBNull.

    Nonetheless, the warning either shouldn't be on the line that it is on, or this line should have the same issue:

    listCollection.Add(new SelectListItem(dr[0].ToString(), dr[0].ToString()));

    Those arguments have to be strings, not nullable strings. Why is the compiler complaining that dr[0] might be null in one situation, but blithely ignoring that possibility in the next?
    My usual boring signature: Nothing

  8. #8
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,767

    Re: Conversion Question

    That's a good point. There's definitely something going on under the hood here because the constructor for SelectListItem is:
    Code:
    public SelectListItem (string text, string value);
    And it is not:
    Code:
    public SelectListItem (string? text, string? value);
    Actually wait, what if you switch the statements around to:
    Code:
    listCollection.Add(new SelectListItem(dr[0].ToString(), dr[0].ToString())
    
    string st1 = dr[0].ToString();
    listCollection.Add(new SelectListItem(st1, st1));
    Do you get the warning on the SelectListItem initialization but not on the declaration of st1? If so then I wonder if since the compiler knows that if a potential null reference exception is not thrown on the first instance then it knows that any instance after that it could not be null unless the value changes.
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  9. #9

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,069

    Re: Conversion Question

    It makes no difference. I tried it just to be sure, but the whole part about assigning to a string variable came later. I never would have tried:

    Code:
    listCollection.Add(new SelectListItem(dr[0].ToString(), dr[0].ToString())
    had I known that just assigning dr[0].ToString() to a string variable would have caused trouble.

    What has happened to our buttons on quick replies?
    My usual boring signature: Nothing

  10. #10
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,767

    Re: Conversion Question

    Good question, they've been gone at least a week.
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  11. #11

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,069

    Re: Conversion Question

    They were gone, then they came back, then they were gone....and now, I see, they are back.


    Them much magic!!
    My usual boring signature: Nothing

  12. #12
    Karen Payne MVP kareninstructor's Avatar
    Join Date
    Jun 2008
    Location
    Oregon
    Posts
    6,686

    Re: Conversion Question

    A little late.

    Consider the following were one parameter is nullable and the other not nullable. First, no warning, second gets a warning.

    Code:
    public class Operations
    {
        public static readonly string ConnectionString = 
            """
            Data Source=.\SQLEXPRESS;Initial Catalog=ForumExample;
            Integrated Security=True;Encrypt=False
            """;
    
        public static List<SelectListItem> GetList()
        {
            List<SelectListItem> listCollection = new();
            using SqlConnection cn = new(ConnectionString);
            cn.Open();
            using SqlCommand cmd = new() {Connection = cn};
    
            cmd.CommandText =
                """
                SELECT DISTINCT COALESCE(LastName, 'Is Null') AS LastName
                FROM dbo.DemoNull;
                """;
            using var dr = cmd.ExecuteReader();
            while (dr.Read())
            {
                /*
                 * First is nullable, no warning
                 * Second not nullable, warning
                 */
                listCollection.Add(new SelectListItem(dr[0].ToString(), dr[0].ToString()));
            }
    
            return listCollection;
        }
    }
    
    public class SelectListItem
    {
        public SelectListItem(string? value1, string value2)
        {
            if (value1 == "Is Null")
            {
                
            }
            else
            {
                
            }
        }
    }

  13. #13

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,069

    Re: Conversion Question

    Okay, that is what I would expect, but in this case, both arguments are non-nullable, and yet there is no warning for either one. You created your own SelectListItem, but I am not. I'm using this:

    https://learn.microsoft.com/en-us/do...aspnetcore-8.0

    And the documentation doesn't suggest that the arguments are nullable. Of course, the documentation could be wrong.
    My usual boring signature: Nothing

  14. #14
    Karen Payne MVP kareninstructor's Avatar
    Join Date
    Jun 2008
    Location
    Oregon
    Posts
    6,686

    Re: Conversion Question

    True I was using my own rather than using from an ASP.NET Core project using EF Core as shown below.

    Code:
    public class IndexModel : PageModel
    {
    
        public IDataProtector Protector;
        private readonly Context _context;
    
        public IndexModel(Context context, IDataProtectionProvider provider)
        {
            _context = context;
            Protector = provider.CreateProtector(nameof(UserLogin));
        }
    
        public List<SelectListItem> UserOptions { get; set; }
    
        public string EncryptedId { get; set; }
    
        public void OnGet()
        {
    
            ViewData["UserId"] = new SelectList(_context.UserLogin.AsNoTracking(), "Id", "EmailAddress");
    
        }
    }
    Last edited by kareninstructor; Mar 14th, 2024 at 03:26 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width