【问题标题】:EF not grabbing an object when its navigation property is null当导航属性为空时,EF 不抓取对象
【发布时间】:2020-09-17 04:42:07
【问题描述】:

我的问题是 - 如果数据库中的导航属性为“null”,我的查询将不会返回我正在查询的主要对象。

为了清楚起见,我的问题不是 .Include() 中的导航属性返回 null。我的问题是该属性预计为空,当它是时,市场不会被返回。

这是我的意思的一个例子:

我的代码:

            markets = await _context.Markets
                .Include(x => x.Agency)
                .Include(x => x.Location)?.ThenInclude(x => x.State)?
                .Include(x => x.Location).ThenInclude(x => x.Country)
                .Where(x => x.Deleted == false
                && x.Agency.Deleted == false).ToListAsync();

我有一些市场,它们具有导航属性Location,而后者又具有导航属性State。对于某些LocationState 为空。在Location 模型中定义的State 的外键是long?

但是,由于某种原因,结果列表中没有返回我的具有 null 状态的市场,而是返回了所有这些字段都包含在 .Include() 语句中且不为 null 的实体。

我最初的代码在包含之后没有?... 比如:

.Include(x => x.Location).ThenInclude(x => x.State) 并没有奏效。我添加了可以为空的想法,它可能会有所帮助,尽管它没有。

这里有人有什么建议吗?运行此语句时,我收到此异常: .SqlNullValueException: Data is Null. This method or property cannot be called on Null values. 但是,该异常不会中断流程,并且返回的对象不具有 null State

任何和所有提示将不胜感激!

编辑:

重要的是要注意,当我的所有导航属性都不为空时,代码可以正常工作。当 State 为 null(唯一可以为 null 的导航属性)时,就会出现此问题。

型号:

public class Market
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long MarketId { get; set; }
        
        [ForeignKey("Location")]
        public long HeadquartersId { get; set; }
        
        [Required]
        public virtual Location Location { get; set; }
}

public class Location
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long LocationId { get; set; }
        
        [ForeignKey("State")]
        public long? StateId { get; set; }

        [ForeignKey("Country")]
        public long CountryId { get; set; }

        [Required]
        public virtual State State { get; set; }

        [Required]
        public virtual Country Country { get; set; }
    }

【问题讨论】:

  • 你试过这个.Include("Location").Include("Location.State")吗?
  • @SowmyadharGourishetty 有趣的想法,我试了一下,不幸的是它没有用:(
  • 你能展示模型及其配置吗?
  • @ESG 当然,我刚刚将我的模型包含在原始帖子的编辑中 - 希望它可以阐明这里出了什么问题。
  • 必需的可能是一个问题,因为框架可能假定这些关系不是可选的。您是否尝试过在 DbContext 中将它们设为可选 va OnModelCreating

标签: c# linq entity-framework-core


【解决方案1】:

这有点远,但看看您是否可以手动配置 DBContext 的OnModelCreating 中的关系。

例子:

protected override void OnModelCreating(ModelBuilder builder)
{
    builder.Entity<Market>(a =>
    {
        a.HasOne(e => e.Location).WithMany().HasForeignKey(e => e.HeadquartersId).IsRequired(false);                
    });

    builder.Entity<Location>(a =>
    {
        a.HasOne(e => e.State).WithMany().HasForeignKey(e => e.StateId).IsRequired(false);                
        a.HasOne(e => e.Country).WithMany().HasForeignKey(e => e.CountryId).IsRequired(false);                
    });

}

【讨论】:

  • 只是好奇...你有很好的资源来解释 .HasOne .WithMany...以及其他 Fluent 选项。
  • 这个解决方案实际上没有用,但是在我的位置导航属性上删除 [Required](而不是将 ForeignKey 设置为 .IsRequired(false))确实有效。但在我的情况下,我必须保留 [Required],所以我必须在下面的答案中解决问题
【解决方案2】:

最终,问题在于我的位置模型中状态导航属性上的 [必需] 注释。

不幸的是,这是 Entity Framework 和 Blazor 使用相同数据注释的众多冲突示例之一。

在我的示例中,State 是必需的,因为 Blazor 中的表单验证需要它。但是,在我的数据库中,StateId 实际上是一个可为空的外键,因为后端不一定需要它。

我提出了两个解决方法,如果您知道更好的解决方案,请随时添加其他解决方法。

  1. 为 Blazor 创建单独的前端模型,为 EF 创建后端模型。我知道,丑陋的重复代码。但是,这可以将关注点分开,并避免“这里需要,但这里不需要”类型的问题。

  2. 在我的情况下,我也可以有一个单独的查询,首先检查我的国家。如果 Country 是某个值,我不会 .Include(x => x.State)。这更具体,但对于已经存在无法进行太多更改的代码库的类似情况下的某些情况可能是必要的。

【讨论】:

  • 无论如何,您确实应该将 DTO 和视图模型分开。它们是两个完全不同的领域。这就是为什么存在像 Automapper 这样的库来在两者之间进行转换的原因。
猜你喜欢
  • 2021-08-13
  • 1970-01-01
  • 2015-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-27
  • 1970-01-01
相关资源
最近更新 更多