Search code examples
c#linqvisual-studio-2022nullable-reference-types

LINQ queries give CS8600 and CS1662 warnings in VS2022 and C#10


Started a new task today of migrating a console app to VS2022 and C#10 and ran into a show stopper that I can't find a workaround for, other than using a compiler directive to suppress the warnings. I'm hoping to avoid that as this app has hundreds if not thousands of LINQ queries that use a similar syntax. Here are the basics of what I've run into.

public static class ResponseCodes
{
    public static string MEI0066 => "ME-I0066";
    public static string MEI0069 => "ME-I0069";
    public static string MEI0070 => "ME-I0070";
}

public class DebugLog
{
    public DebugLog()
    {
        this.DebugLogId = null;
        this.LogType = string.Empty;
        this.ResponseCode = string.Empty;
        this.Explanation = string.Empty;
    }

    public DebugLog(int? p_id, string p_logType, string p_responseCode, string p_explaination)
    {
        this.DebugLogId = p_id;
        this.LogType = p_logType;
        this.ResponseCode = p_responseCode;
        this.Explanation = p_explaination;
    }

    public int? LogId { get; set; }
    public string ResponseCode { get; set; }
    public string Explanation { get; set; }
    public string LogType { get; set; }
}

public enum LogEntryTypes
{
    BlackListedIP,
    DNSQuerySuccess,
    DNSQueryFailed,
    WhiteListedIP
}

public class QueryLogs
{
    public QueryLogs()
    {
        this.DebugLogQueue = new List<DebugLog>();
    }

    public List<DebugLog> DebugLogQueue {get;set;}

    public DebugLog? Query()
    {
        DebugLog _debugLog = new DebugLog()
        {
            LogId = 0,
            LogType = LogEntryTypes.DNSQuerySuccess.ToString(),
            Explanation = "IP was valid",
            ResponseCode = ResponseCodes.MEI0070
        };

        DebugLogQueue.Add(_debugLog);

        _debugLog = new DebugLog(1, LogEntryTypes.BlackListedIP.ToString(), "This a test", ResponseCodes.MEI0066);
        DebugLogQueue.Add(_debugLog);

        _debugLog = new DebugLog(2, LogEntryTypes.DNSQueryFailed.ToString(), "This string is not null", ResponseCodes.MEI0069);
        DebugLogQueue.Add(_debugLog);

        // This LINQ query gives a CS8600 warning "Converting null literal or possible 
        // null value to non-nullable type.
        DebugLog returnValue = DebugLogQueue
            .FirstOrDefault(x =>
                (x.ResponseCode == ResponseCodes.MEI0069) 
                && (x.LogType == LogEntryTypes.DNSQueryFailed.ToString()));

        // This form uses the null-coalescing operator but gives CS1662 warning
        // "Cannot convert lambda expression to intended delegate type because some of 
        // the return types in the block are not implicitly convertible to to the
        // delegate return type."

        returnValue = DebugLogQueue
            .FirstOrDefault(x => (x.ResponseCode ?? ResponseCodes.MEI0069));

        return returnValue;
    }
}

What's been tried:

Because class ResponseCodes is static its values will never be null.

Because the enum LogEntryTypes is converted to a string when used, they'll never return a null value.

As suggested by other posts I've tried the following:

  1. Changing static class ResponseCodes to have nullable values by using "static string? MEI0066 => "ME-I0066";"
  2. Changing the property getter/setter of DebugLog class to use "public string? ResponseCode { get; set; }"
  3. Changing LogEntryTypes to a string const such as dNSQueryFailed = LogEntryTypes.DNSQueryFailed, but that's no different than using the static values from class ResponseCodes.
  4. The Query method in class QueryLogs allows a nullable DebugLog to be returned so that it works with the .FirstOrDefault query operator, which doesn't apply to the DebugLog property values.
  5. Setting compiler directive to: "#pragma warning disable CS8600 // Converting null literal or possible null value to a non-nullable type." eliminates the warning and the code runs, but doing this on hundreds of queries will most definitely come back to bite me later.

So the CS8600 and CS1662 warnings have to be coming from the LINQ side when the property value is emitted and returned, but for the life of me, I haven't been able to come up with the correct syntax to get the query written so that the warning goes away. I'm not even sure where to look any further on this issue.

So am I missing something here or is this a non-starter approach for LINQ queries in C#10?


Solution

  • The most problems when using net6, are caused by this new stupid nullable feature of c#. The worst thing is that it is going in new projects by default and the most people spend lots of time trying to find why it sudddenly stoped working or give strange warnings. Just make this small chanage in your net 6 propjects files

     <TargetFramework>net6.0</TargetFramework>
        <!--<Nullable>enable</Nullable>-->