【问题标题】:Navigation Property not loading when only the ID of the related object is populated仅填充相关对象的 ID 时导航属性未加载
【发布时间】:2011-05-26 19:44:40
【问题描述】:

我正在尝试建立多对一的关系。表示“许多”的实体具有指向父实体的导航属性。它看起来像这样:

public abstract class BaseEntity
{

    /// <summary>
    /// Key Field for all entities
    /// </summary>
    /// 
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }


    /// <summary>
    /// Date entity was created
    /// </summary>
    public DateTime DateCreated { get; set; }

    /// <summary>
    /// Last date Modified
    /// </summary>
    public DateTime DateModified { get; set; }

    /// <summary>
    /// keep track of Row Version used for concurrency
    /// </summary>
    [Timestamp]
    public Byte[] RowVersion { get; set; }

}

public abstract class Document : BaseEntity
{
    #region Primitive Properties   


    /// <summary>
    /// Boolean value to determine if Document is in an active state
    /// </summary>
    public bool IsActive { get; set; }

    /// <summary>
    /// Document comments and information
    /// </summary>
    [Required]
    public string Description { get; set; }

    #endregion

    #region Navigation Properties

    public ICollection<Comment> Comments { get; set; }

    /// <summary>
    /// FK back to User who owns document
    /// </summary>
    //public Guid OwnerId { get; set; }

    public Guid OwnerId { get; set; }
    /// <summary>
    /// Navigation Back to User who owns document
    /// </summary>
    public User Owner { get; set; }

    #endregion
}

public class Project : BaseEntity
{
    public string Name { get; set; }
    public string ProjectNumber { get; set; }
    public string Description { get; set; }

    public string CreatedBy { get; set; }
    public string ModifiedBy { get; set; }
    public string Currency { get; set; }

    #region Navigation Properties

    public virtual Address Address { get; set; }
    public virtual CompanyCode CompanyCode { get; set; }
    public virtual ICollection<Contact> TeamMembers { get; set; }

    #endregion
}    

 public class Rfi : Document
 {
    public string Number { get; set; }

    #region Navigation Properties

    //This points back to a Project Entity
    public virtual Guid ProjectId { get; set; }
    public virtual Project Project { get; set; }

    #endregion
}

因此,当我插入上述实体时,我将 ProjectId 从应用程序传递到 Rfi 实体(而不是整个 Project 实体)。一切都很好。我遇到的问题是,当我将 Rfi 对象从数据库中拉出时,正在填充 ProjectId,但 Project 实体为空。我默认使用延迟加载。我是否也需要在 Project 实体上指定导航属性?我真的不想。除非,我可以在我的 Rfi 上执行映射来完成此操作。

更新: 我假设 EF 4.1 会为我加载我的对象,但似乎有时我需要明确地包含我想要加载的对象。我不完全确定为什么。我正在使用存储库来查询我的实体。下面是我用来查询 Rfi 对象的方法:

    public IQueryable<TEntity> GetQuery(Expression<Func<TEntity, bool>> predicate)
    {
       return _context.Set<TEntity>().AsQueryable();
    }

我最终做了什么,在我的服务层我这样称呼它:

public Rfi FindByNumber(string number)
{
     var rfi = rfiRepository.GetQuery(r => r.Number == number).Include(r => r.Project).Single;
     return rfi
}

【问题讨论】:

  • ProjectDocument 类的外观如何?您不一定需要另一侧的导航属性。你所描述的应该正常工作。问题可能隐藏在其他类或任何其他映射中。
  • 我更新了代码示例。 Document 派生自 BaseEntity 类型。
  • 您使用的是 SQL Server 吗?我认为DatabaseGeneratedOption.Identity 不适用于SQL Server 中的Guid(尽管模型允许此设置,但它不是数据库中的标识)。但这可能不是您的问题的原因。使用 IncludeProject 属性进行预加载是否有效?
  • 我正在使用 SQL 服务器,它可以工作。
  • 对不起,你是对的,这是 EF 1 中的限制。从 EF 4 开始它可以工作。

标签: c# ef-code-first entity-framework-4.1 entity-relationship navigation-properties


【解决方案1】:

您必须将导航属性设置为virtual 才能使延迟加载工作。

虽然这在实现方面很有意义,但 EF 忽略问题并仅返回 null 的策略是一个糟糕的设计决策。

另一方面,NHibernate 默认情况下不允许您使用不具有所有虚拟属性的类。

为了避免这个问题,我编写了一个测试来验证每个引用属性都标记为虚拟。这样我可以立即发现,而不是处理路上的奇怪错误。


您也可以尝试明确指定 FK/Navigation 属性:

public Guid ProjectId { get; set; }
[ForeignKey("ProjectId")]
public virtual Project Project { get; set; }

【讨论】:

  • 他们是?如果您查看我的 Rfi 对象,则属性设置为虚拟。
  • @DDiVita:哦,我看到一些属性不是(所有者,评论)并且实际上没有检查Project。尝试将这些虚拟化,并检查我上次的编辑。
  • 虽然不是你的问题,但我一整天都在摸不着头脑,为什么我要返回空值,这个答案提醒了我 - 一节课中缺少虚拟!很容易忘记,没有什么警告你。我在一个 MVC 项目上遇到了很多困难,所有这些都是一条线,如果不是一个字就可以解决,因为你需要记住一些晦涩的东西。
【解决方案2】:

原来我为属性创建的映射是在数据库中创建一个重复的 ID。例如,我在数据库中有ProjectIdProject_ID。当一个新项目被保存到上下文时,我正在填充ProjectId,但没有填充_ID。这就是 EF 4.1 用来关联数据的方法。在我的映射中,我试图设置项目,所以它不会CascadeOnDelete。这是我的映射的样子:

HasOptional(rfi => rfi.Project)
    .WithOptionalDependent()
    .WillCascadeOnDelete(false);

此映射在数据库中创建了 2 个 ID。删除映射后,一切正常。我只需要找出正确的映射,这样我就可以删除CascadeOnDelete,将属性设为可选,并且只有一个 ID。

我在EF Power Tools 的帮助下弄清楚了。您可以将您的数据库反向工程为 POCO。我将上面的行改为:

HasOptional(r => r.Project)
    .WithMany()
    .HasForeignKey(r => r.ProjectId)
    .WillCascadeOnDelete(false);

即使使用流畅的接口映射也有点难以掌握。为了了解如何在 EF 中映射关系,我用我的表和外键分配创建了一个简单的数据库。然后,我首先使用 Power Tools 选项进行逆向工程代码。太棒了!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-05
    • 2016-11-21
    • 1970-01-01
    • 1970-01-01
    • 2016-12-21
    相关资源
    最近更新 更多