为什么EF导航属性返回null?

23

我有两个模型 1)

public class Indicator
{
    public long ID { get; set; }
    public string Name { get; set; }
    public int MaxPoint { get; set; }
    public string Comment { get; set; }
    public DateTime DateChanged { get; set; }
    public DateTime DateCreated { get; set; }

    public virtual IList<CalculationType> CalculationTypes { get; set; }
    public virtual IList<TestEntity> TestEntitys { get; set; }
    public virtual IndicatorGroup IndicatorGroup { get; set; }
}

2)

public class CalculationType
{
    public long ID { get; set; }
    public string UnitName { get; set; }
    public int Point { get; set; }
    public DateTime DateCreated { get; set; }
    public DateTime DateChanged { get; set; }

    public virtual Indicator Indicator { get; set; }
    public virtual IList<Сalculation> Calculations { get; set; }
}
我正在执行这段代码。
var indicator = DataContext.Indicators.FirstOrDefault(i => i.ID == indicatorID);
var test = DataContext.CalculationTypes.FirstOrDefault();

在导航属性CalculationTypes上,第一行返回null。 enter image description here

第二行返回空集合。enter image description here 为什么呢?

更新 数据库快照 enter image description hereenter image description here 项目链接 https://github.com/wkololo4ever/Stankin

添加了Calculation

    public class Сalculation
{
    public long ID { get; set; }

    public virtual CalculationType CalculationType { get; set; }
    public virtual ApplicationUser Creator { get; set; }
}

@FarhadJabiyev 懒加载已启用 - kriper
我想分享你的映射可能会有帮助。 - jbl
这可能会有所帮助 https://dev59.com/9G855IYBdhLWcg3w7pCg - jbl
@kriper,你解决了这个问题吗?我也遇到了同样的问题,但是没有成功。 - Mohammed Noureldin
@MohammedNoureldin 是的。在我的情况下,我已经在代码中附加了空对象:) - kriper
显示剩余2条评论
7个回答

10

1) 是否启用了延迟加载?如果没有,您需要使用'.Include'语法显式加载导航属性。

2) 您确定 EF 能够检测到该关系吗?您是使用 Code First 还是 Database First?

编辑:3) 您确定数据库中有数据,并且 Indicator 到 IndicatorGroup 的外键对于该特定记录具有值吗?我之所以这样说是因为如果没有数据,值“null”是有效的。

附注:如果您在 Indicator 上看不到名为“IndicatorGroupId”的外键,那么在表“IndicatorGroup”上可能会有一个“IndicatorId”,在这种情况下 - 根据您提供的名称 - 您的数据库配置不正确,您需要使用流畅语法或数据属性来指示 EF 如何制作外键。


我在我的帖子中添加了快照数据库数据。 - kriper
在我的上下文类中,我没有 context.Configuration.ProxyCreationEnabledcontext.Configuration.LazyLoadingEnabled 属性。这是不是意味着它们默认为启用状态? - Cavid

6

试试这个:

DbContext.Configuration.ProxyCreationEnabled = true;    
DbContext.Configuration.LazyLoadingEnabled = true;  

如果DbContext.Configuration.ProxyCreationEnabled被设置为false,则DbContext将不会为某些父对象加载子对象,除非在父对象上调用Include方法。将DbContext.Configuration.LazyLoadingEnabled设置为true或false对其行为没有影响。
如果DbContext.Configuration.ProxyCreationEnabled被设置为true,则子对象将自动加载,并且DbContext.Configuration.LazyLoadingEnabled值将控制何时加载子对象。
我认为这是一个问题:
编辑:3)您确定数据库中有数据并且Indicator到IndicatorGroup的外键具有该特定记录的值吗?我之所以说这个,是因为如果根本没有数据,则“null” 值是有效的。
注意:如果您在Indicator上看不到名为“IndicatorGroupId”的外键,可能在“IndicatorGroup”表上有一个“IndicatorId”,在这种情况下 - 根据您提供的名称 - 您的数据库配置错误,您需要使用流畅语法或数据属性来指示EF如何创建外键。
尝试执行此操作并确保外键已更正。
public class CalculationType
{
    public long ID { get; set; }
    public string UnitName { get; set; }
    public int Point { get; set; }
    public DateTime DateCreated { get; set; }
    public DateTime DateChanged { get; set; }
    [ForeignKey("IndicatorID")]
    public string IndicatorId { get; set; } //this is the foreign key, i saw in your database is: Indicator_ID, avoid this, rename it to IndicatorID or IndicatorId

    public virtual Indicator Indicator { get; set; }
    public virtual IList<Сalculation> Calculations { get; set; }
}

所有这些属性都已启用。 - kriper
我看了第二张图片,它返回一个空列表,你有那方面的数据吗? - toannm
我没有那方面的数据。当我有了数据,它们就会立即加载。我不明白为什么懒加载不起作用。 - kriper
1
你似乎误解了懒加载的实际含义。懒加载意味着当你访问一个导航属性时,你的数据会自动加载。 - toannm
1
如果DbContext.Configuration.ProxyCreationEnabled设置为true,则子对象将自动加载,并且DbContext.Configuration.LazyLoadingEnabled值将控制何时加载子对象。但这并不完全正确。当关闭LazyLoading时,实体只有在调用Include()时才会被加载。 - Tim Pohlmann
显示剩余9条评论

6

虽然表现形式相同,但根本原因与所选答案不同:

如果您关闭了myContext.Configuration.AutoDetectChangesEnabled,导航属性也可能为空。

这很明显,但在我实施一些性能改进时,这让我困扰了一段时间。


它至少解决了我的一些问题。猜测,在某些多线程场景下也可能出现这种行为。 - LuckyLikey

4

看这个:使用 Code First 建立导航属性。它提到了导航属性为空的原因以及解决方法。

默认情况下,导航属性是 null 的,它们不会被加载。为了加载导航属性,我们使用 IQueryable 的 “include” 方法,这种类型的加载称为 Eager loading(急切加载)。

Eager loading:它是一种查询一种实体类型时作为查询的一部分加载相关实体的过程,它通过 IQueryable 的 “include” 方法实现。


2

我遇到了这个问题,即使有Include语句存在,导航属性也没有加载。

问题是由于SQL Server和EF6使用.NET之间的字符串比较差异引起的。我在我的customers表中使用了一个VARCHAR(50)字段作为主键,并且还将其用作audit_issues表中的外键字段。我没有意识到的是,在customers表中的键末尾有两个额外的空格字符;这些字符在我的audit_issues表中不存在。

然而,SQL Server会自动填充字符串比较中的空格。这适用于WHERE和JOIN子句,以及对FOREIGN KEY约束的检查。也就是说,数据库告诉我字符串是等价的并且约束通过了。因此,我假设它们实际上完全相等。但那是错误的。一个字段的DATALENGTH = 10,而另一个字段的DATALENGTH = 8。

EF6会正确地组合SQL查询以拉取外键相关字段,我会在生成的Sql查询和结果中看到它们。然而,EF6在加载导航属性时会默默失败,因为.NET不认为这些字符串相等。注意字符串类型外键字段中的空格!


0
这是Keytrap答案的一个变体。使用.NET 6EF Core 6,我创建了一个ContextPartials.cs文件,用于任何自定义配置,我不希望EF的Scaffold命令覆盖:
所需包:
Install-Package Microsoft.EntityFrameworkCore.Proxies

代码(ContextPartials.cs):

// NOTE:  I am not using the new file-scoped namespace on purpose
namespace DataAccess.Models.MyDatabase
{
    // NOTE:  This is a partial outside of the generated file from Scaffold-DbContext
    public partial class MyDatabaseContext
    {
        // NOTE:  This enables foreign key tables to become accessible
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            => optionsBuilder.UseLazyLoadingProxies();
    }
}

0

这篇文章对我很有帮助。

总之:

Install-Package Microsoft.EntityFrameworkCore.Proxies

Startup.cs中。

using Microsoft.EntityFrameworkCore;

public void ConfigureServices(IServiceCollection services)
        {
              ...
            services.AddDbContext<MyDbContext>(builder =>
            {
                builder.UseLazyLoadingProxies(); // <-- add this
            }, ServiceLifetime.Singleton);

这篇博客文章充满了不准确的内容。但即使完全正确,除非有非常好的理由,我也不会启用懒加载。在我看来,这是一种反模式。在这里他们只需要使用“Include”。 - Gert Arnold
我使用了另一个变体,并将其放入ContextPartial.cs文件中(以便EF可以重新构建基本上下文文件而不会丢失我的更改)。-->public partial class MyTableContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseLazyLoadingProxies(); } - Cryptc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接