Docs: DTO constructors do not play well with linq2sql

Created on 17 Sep 2019  Â·  15Comments  Â·  Source: dotnet/docs

Hi, I'm trying to incorporate nullable reference types into my project but have a problem when adding constructors to my DTO classes.

public class Employee
{
    public Employee(Office office)
    {
        Office = office;
    }

    public Office Office { get; }
}

When I use this DTO in a query, I get the dreaded "Operation not supported in SQL".

I could leave them nullable like this:
public Office? Office { get; set; }

But then I'm left with a lot of compiler warnings when using the valid Linq feature where properties of null objects can be query'd:
var query = persons.Where(p => p.Office.HasWindows);

I can fix them with #pragma's but that looks ugly:
````

pragma warning disable CS8602 // Dereference of a possibly null reference.

var query = persons.Where(p => p.Office.HasWindows);

pragma warning restore CS8602 // Dereference of a possibly null reference.

````
Do you have a solution for this issue?


Document Details

⚠ Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

Area - C# Guide Technology - C# Null safety P2 Pri2 discussion doc-enhancement dotnet-csharprod

Most helpful comment

@mrdnote DTOs are difficult for nullable reference types. The problem stems from the fact that deserialization, in effect, initializes the object. But, from the standpoint of static analysis, it's not a constructor.

I've done this:

public class Employee
{
    public Office Office { get; set;  } = null!;
}

The ! operator suppresses the warning on the initialization statement.

/cc @jaredpar who is collecting a series of recommendations.

All 15 comments

Or even a simplere example of excessive warnings when using Linq-2-Sql (I guess the same goes for L2EF):

// The following line shows warning CS8629: repository.Employees.Where(e => e.Office.HasWindows);

@mrdnote DTOs are difficult for nullable reference types. The problem stems from the fact that deserialization, in effect, initializes the object. But, from the standpoint of static analysis, it's not a constructor.

I've done this:

public class Employee
{
    public Office Office { get; set;  } = null!;
}

The ! operator suppresses the warning on the initialization statement.

/cc @jaredpar who is collecting a series of recommendations.

Hi Bill, thanks for the suggestion. I didn't know about the exclamation mark construct. Does it go anywhere other than behind a null constant?

Hi Bill, sorry to get off topic, but after reinstalling visual studio and switching my projects from 4.7.1 to 4.7.2 suddenly Debug.Assert isn't suppressing nullable warnings (CS8602) anymore:
image

Any clues?

UPDATE: Switching back to 4.7.1 didn't solve it

@mrdnote

Just a hunch (because of a related conversation): Check your .csproj file for the <LangVersion> element. If it's present, just remove it. You might be getting the C# 8 default behavior, even though you're targeting 4.7.*.

Bill, if I remove the LangVersion the compiler falls back to C# 7.3 which is of course not what I want.

One thing I noticed tho, that since my fresh VS reinstall is that the Advance Build Settings UI behaves differently. Before I could change the language version, and now I cannot:

image

Could this be related?

Another discovery, when I replace the Assert with a conditional raised exception, the warning goes away:

if (root == null) { throw new Exception(); }

You'd almost think something's wrong when compiling for DEBUG, but I have Debug selected and when I step thru my code, the Assert's are executed.

First, on the language selection, we've updated the documents for the latest changes on LangVersion. That explains how the UI changed based on your target framework, and the new defaults.

Second, My thinking is that nullable static analysis takes into account conditional compilation. @jaredpar would know for sure.

Hi Bill, sorry to get off topic, but after reinstalling visual studio and switching my projects from 4.7.1 to 4.7.2 suddenly Debug.Assert isn't suppressing nullable warnings (CS8602) anymor

This shouldn't be specific to 4.7.1 or 4.7.2 but rather the expected behavior when targeting any version of the desktop framework. The nullable annotations were only added to netcoreapp30 which means the signature for Debug.Assert is observably different:

// net47*
public static void Assert(bool b) 

// netcoreapp30
public static void Assert([DoesNotReturnIf(false)]bool b) 

Hence on desktop the Debug.Assert call adds no new information to the nullable analysis.

Hi Jared, thanks for this explanation. In terms of a solution, can you make a suggestion? Do I have to rewrite all my asserts?

I noticed String.IsNullOrEmpty has no effect on analysis either. So nullable annotation is not a language feature that is particularly useful with .NET Framework?

@mrdnote I believe one option is to use ReferenceAssemblyAnnotator to provide nullable annotations for .Net Framework assemblies. Though it's not an officially-supported solution.

@cartermp

In meanwhile, to circumvent these warning issues, one can define own compiler attributes in .NET Standard library (or use one available from github https://github.com/manuelroemer/Nullable) and then write your own Asserts to use these attributes.

The only endorsed suggestion is to move to .NET Core (which would mean dropping LinqToSql).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sime3000 picture sime3000  Â·  3Comments

gmatv picture gmatv  Â·  3Comments

sebagomez picture sebagomez  Â·  3Comments

LJ9999 picture LJ9999  Â·  3Comments

Eilon picture Eilon  Â·  3Comments