C#为什么认为EF Include()返回可空对象?

huangapple go评论74阅读模式
英文:

Why does C# think EF Include() returns a nullable object?

问题

以下是您提供的文本的中文翻译部分:

"Include的文档位于1:"

public static System.Linq.IQueryable<T> Include<T, TProperty> (this System.Linq.IQueryable<T> source, System.Linq.Expressions.Expression<Func<T, TProperty>> path) where T : class;

但是对于以下代码行:

numCountySignups = context.Counties.Include(c => c.Events).ThenInclude(e => e.Signups).First(c => c.Id == county.Id).Events.Sum(ee => ee.Signups.Count);

我收到了警告:

严重性	Code	描述	项目	文件	行	抑制状态
警告	CS8620	不能将类型为'IIncludableQueryable<County, ICollection<Event>?>'的参数用于类型为'IIncludableQueryable<County, IEnumerable<Event>>'的参数'source'中的'IIncludableQueryable<County, Event, ICollection<Signup>?> EntityFrameworkQueryableExtensions.ThenInclude<County, Event, ICollection<Signup>?>(IIncludableQueryable<County, IEnumerable<Event>> source, Expression<Func<Event, ICollection<Signup>?>> navigationPropertyPath)',因为引用类型的nullability存在差异。	IntegrationTests	C:\git\LouisHowe\IntegrationTests\repository\TestCounty.cs	203	Active

如果我添加一个!,警告就消失了:

numCountySignups = context.Counties.Include(c => c.Events)!.ThenInclude(e => e.Signups).First(c => c.Id == county.Id).Events.Sum(ee => ee.Signups.Count);

由于Include()的声明显示它是非空的,为什么会收到此警告呢?

英文:

The documentation for Include is:

public static System.Linq.IQueryable&lt;T&gt; Include&lt;T,TProperty&gt; (this System.Linq.IQueryable&lt;T&gt; source, System.Linq.Expressions.Expression&lt;Func&lt;T,TProperty&gt;&gt; path) where T : class;

But for the line of code:

numCountySignups = context.Counties.Include(c =&gt; c.Events).ThenInclude(e =&gt; e.Signups).First(c =&gt; c.Id == county.Id).Events.Sum(ee =&gt; ee.Signups.Count);

I get the warning:

Severity	Code	Description	Project	File	Line	Suppression State
Warning	CS8620	Argument of type &#39;IIncludableQueryable&lt;County, ICollection&lt;Event&gt;?&gt;&#39; cannot be used for parameter &#39;source&#39; of type &#39;IIncludableQueryable&lt;County, IEnumerable&lt;Event&gt;&gt;&#39; in &#39;IIncludableQueryable&lt;County, ICollection&lt;Signup&gt;?&gt; EntityFrameworkQueryableExtensions.ThenInclude&lt;County, Event, ICollection&lt;Signup&gt;?&gt;(IIncludableQueryable&lt;County, IEnumerable&lt;Event&gt;&gt; source, Expression&lt;Func&lt;Event, ICollection&lt;Signup&gt;?&gt;&gt; navigationPropertyPath)&#39; due to differences in the nullability of reference types.	IntegrationTests	C:\git\LouisHowe\IntegrationTests\repository\TestCounty.cs	203	Active

which goes away if I add a ! as follows:

numCountySignups = context.Counties.Include(c =&gt; c.Events)!.ThenInclude(e =&gt; e.Signups).First(c =&gt; c.Id == county.Id).Events.Sum(ee =&gt; ee.Signups.Count);

As the declaration of Include() shows it to be non-nullable, why do I get this warning?

答案1

得分: 1

文档引用您提供的是来自EF 5(.Net Framework)而不是EF Core。对于EF Core,特别是在6.0之前的版本,使用NRT可能会导致编译器警告,这可能会带来问题。如果您正在使用EF Core 5,则应考虑升级到EF Core 6或7。

一个细节是确保在您的实体中初始化所有集合导航属性:

public class County
{
    // ...

    public virtual ICollection<Event> Events { get; private set; } = new List<Event>();
}

最后,您提供的示例查询是获取Sum的一种特别低效的方式:

numCountySignups = context.Counties
    .Include(c => c.Events)
        .ThenInclude(e => e.Signups)
    .First(c => c.Id == county.Id)
    .Events.Sum(ee => ee.Signups.Count);

这会将给定县的所有事件和注册加载到内存中,只是为了获取注册计数的总和。相反:

numCountySignups = context.Counties
    .Where(c => c.Id == county.Id)
    .Select(c => c.Events.Sum(ee => ee.Signups.Count))
    .First();

这将构建并执行针对数据库的SQL语句,仅检索该总和。

英文:

The documentation reference you included was from EF 5 (.Net Framework) rather than EF Core. NRTs can be problematic in EF with compiler warnings, especially with EF Core prior to 6.0. If you are running EF Core 5 then you should look to upgrade to EF Core 6 or 7.

One detail is to ensure that all collection navigation properties are initialized in your entities:

public class County
{
    // ...

    public virtual ICollection&lt;Event&gt; Events { get; private set; } = new List&lt;Event&gt;();
}

Lastly, the example query you provided is a particularly inefficient way to get the Sum:

numCountySignups = context.Counties
    .Include(c =&gt; c.Events)
        .ThenInclude(e =&gt; e.Signups)
    .First(c =&gt; c.Id == county.Id)
    .Events.Sum(ee =&gt; ee.Signups.Count);

This loads all events and signups for the given county into memory just to get a sum of the signup count. Instead:

numCountySignups = context.Counties
    .Where(c =&gt; c.Id == county.Id)
    .Select(c =&gt; c.Events.Sum(ee =&gt; ee.Signups.Count))
    .First();

This would build and execute an SQL statement against the database to retrieve just that sum.

huangapple
  • 本文由 发表于 2023年6月4日 22:47:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76400959.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定