当仅填充相关对象的ID时,不加载导航属性
我正在努力建立多对一的关系。 表示“many”的实体具有指向父实体的导航属性。 它看起来像这样:
public abstract class BaseEntity { /// /// Key Field for all entities /// /// [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid Id { get; set; } /// /// Date entity was created /// public DateTime DateCreated { get; set; } /// /// Last date Modified /// public DateTime DateModified { get; set; } /// /// keep track of Row Version used for concurrency /// 1541567432 public Byte[] RowVersion { get; set; } } public abstract class Document : BaseEntity { #region Primitive Properties /// /// Boolean value to determine if Document is in an active state /// public bool IsActive { get; set; } /// /// Document comments and information /// [Required] public string Description { get; set; } #endregion #region Navigation Properties public ICollection Comments { get; set; } /// /// FK back to User who owns document /// //public Guid OwnerId { get; set; } public Guid OwnerId { get; set; } /// /// Navigation Back to User who owns document /// 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 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实体为null。 我默认使用Lazy Loading。 我是否还需要在Project实体上指定导航属性? 我真的不想。 除非,我可以在我的Rfi上执行映射来实现这一目标。
更新:我假设EF 4.1会为我加载我的对象,但似乎有时我需要明确包含我想要加载的对象。 我不完全确定为什么。 我正在使用存储库来查询我的实体。 这是我用来查询Rfi对象的方法:
public IQueryable GetQuery(Expression<Func> predicate) { return _context.Set().AsQueryable(); }
我最终做了什么,在我的服务层我称之为:
public Rfi FindByNumber(string number) { var rfi = rfiRepository.GetQuery(r => r.Number == number).Include(r => r.Project).Single; return rfi }
您必须使您的导航属性virtual
以使延迟加载工作。
虽然这在实现方面是有意义的,但EF忽略问题并且仅返回null
的策略是一个糟糕的设计决策。
另一方面,NHibernate默认情况下不允许您使用虚拟所有属性的类。
为了避免这个问题,我编写了一个测试,validation每个引用属性是否标记为虚拟。 这样我立即发现,而不是在路上处理奇怪的错误。
您还可以尝试显式指定FK / Navigation属性:
public Guid ProjectId { get; set; } [ForeignKey("ProjectId")] public virtual Project Project { get; set; }
结果我为proeprty创建的映射是在数据库中创建一个重复的ID。 例如,我在数据库中有ProjectId
和Project_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的选项进行逆向工程代码。 辉煌!