仅填充相关对象的 ID 时导航属性未加载
我正在尝试建立多对一的关系。代表“多”的实体具有指向父实体的导航属性。它看起来像这样:
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 实体为空。默认情况下,我使用延迟加载。我是否还需要在项目实体上指定导航属性?我真的不想。除非我可以在 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
}
I am trying to establish a many-to-one relationship. The entity that represents the “many” has a navigation property pointing back to the parent entity. It looks like this:
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
}
So, when I insert the above entity, I am passing the ProjectId from the application into the Rfi entity (not the entire Project entity). Everything saves fine. The issue I am having is, when I pull the Rfi object back out of the database, the ProjectId is being populated, but the Project entity is null. I am using Lazy Loading, by default. Do I need to specify a navigation property on the Project entity, too? I don’t really want to. Unless, I can perform a mapping on my Rfi to accomplish this.
Update:
I assumed EF 4.1 would load my objects for me, but it seems, sometimes I need to explicitly include what objects I want to load. I am not entirely sure why. I am using a repository to query my entities. Here is the method I used to query the Rfi object:
public IQueryable<TEntity> GetQuery(Expression<Func<TEntity, bool>> predicate)
{
return _context.Set<TEntity>().AsQueryable();
}
What I ended up doing, in my Service layer I call it like this:
public Rfi FindByNumber(string number)
{
var rfi = rfiRepository.GetQuery(r => r.Number == number).Include(r => r.Project).Single;
return rfi
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您必须将导航属性设为
虚拟
,延迟加载才能发挥作用。虽然这在实现方面是有意义的,但 EF 忽略问题并仅返回
null
的策略是一个糟糕的设计决策。另一方面,NHibernate 默认情况下不允许您使用所有属性不是虚拟的类。
为了避免这个问题,我编写了一个测试来验证每个引用属性是否被标记为虚拟。这样我就能立即发现问题,而不是在路上处理奇怪的错误。
您还可以尝试显式指定 FK/Navigation 属性:
You must make your navigation properties
virtual
for Lazy Loading to work.While this makes sense implementation-wise, EF's strategy of ignoring the problem and just returning
null
is a terrible design decision.NHibernate, on the other hand, by default doesn't let you use classes that don't have all of their properties virtual.
To avoid this problem, I wrote a test that verifies every reference property is marked as virtual. That way I find out immediately, instead of dealing with strange bugs down the road.
You can also try specifying the FK/Navigation properties explicitly:
事实证明,我为该属性创建的映射在数据库中创建了重复的 ID。例如,我的数据库中有
ProjectId
和Project_ID
。当新项目保存到上下文时,我正在填充ProjectId
,但_ID
没有被填充。这就是 EF 4.1 用于关联数据的方法。在我的映射中,我试图设置项目,这样它就不会CascadeOnDelete
。这就是我的映射的样子:此映射在数据库中创建 2 个 ID。一旦我删除了映射,一切就正常了。我只需要找出正确的映射,这样我就可以删除 CascadeOnDelete,使该属性成为可选的,并且只有一个 ID。
我想通了 EF 电动工具。您可以将 DB 逆向工程为 POCO。我将上面的行更改为:
即使使用流畅的接口映射也有点难以掌握。为了了解关系在 EF 中的映射方式,我使用表和外键分配创建了一个简单的数据库。然后我首先使用 Power Tools 的选项进行逆向工程代码。杰出的!
Turns out the mapping I created for the proeprty was creating a duplicate ID in the database. I had, for example,
ProjectId
andProject_ID
in the database. I was populatingProjectId
when a new item was saved to the context, but the_ID
was not being populated. This is what EF 4.1 is using to relate the data. In my mapping I was tring to set the Project so it would notCascadeOnDelete
. This is what my mapping looks like:This mapping was creating 2 IDs in the database. Once I removed the mapping everything was working. I just need to figure out the correct mapping so I can remove the
CascadeOnDelete
, make the property optional, and only have one ID.I figured if out with the Help of EF Power Tools. You can reverse engineer your DB into POCOs. I changed the above line to:
Even with a fluent interface mappings are a bit difficult to master. To understand how relationships are mapped in EF I created a simple database with my tables and foreign key assignments. I then used the Power Tools’ option for reverse engineering code first. Brilliant!