在NHibernate中,有一个where映射,允许您在属性映射上指定一个条件,该条件会影响它从数据库中的提取方式。例如,如果我想实现软删除并从集合中排除所有删除的项目,则可以这样映射它:

流利的冬眠

// in class ParentMap : ClassMap
HasMany(x => x.Children).Where("IsDeleted = 0");


血红蛋白

<class name="Parent" table="[Parents]">
    <bag cascade="all" lazy="true" name="Children" where="IsDeleted = 0">
        <!-- rest of map here -->
    </bag>
</class>


实体框架6中是否有类似内容?

我找到的最接近的东西是一个名为EntityFramework.Filters的库,它允许您为属性添加全局过滤器,但是当该属性是一个集合时,它似乎不起作用。



为了给出一个更好的例子说明为什么为什么需要这样的映射,假设我有一个类,该类包含具有递归子实体关系的对象的集合(即相同类型的对象的集合)。它们遵循以下基本结构:

public class ReportOutline
{
    public long Id { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public virtual ICollection<OutlineItem> OutlineItems { get; set; }
}

public class OutlineItem
{
    public long Id { get; set; }
    public string Name { get; set; }

    public long ReportOutlineId { get; set; }
    public long? ParentOutlineItemId { get; set; }

    public virtual ReportOutline ReportOutline { get; set; }
    public virtual OutlineItem ParentOutlineItem { get; set; }

    public virtual ICollection<OutlineItem> OutlineItems { get; set; }
}


这些都通过EF fluent API进行映射,如下所示:

modelBuilder.Entity<ReportOutline>()
    .HasKey(o => o.Id)
    .HasMany(o => o.OutlineItems)
    .WithRequired(i => i.ReportOutline)
    .HasForeignKey(i => i.OutlineId);

modelBuilder.Entity<OutlineItem>()
    .HasKey(p => p.Id)
    .HasMany(p => p.OutlineItems)
    .WithOptional(c => c.ParentOutlineItem)
    .HasForeignKey(c => c.ParentOutlineItemId);


这样可以产生正确的数据库结构,并且我的记录看起来还不错。这是一个OutlineItems表在ReportOutline上有两个项目的情况下的示例,如果一个项目有两个子项目(共四个):

Id    Name           ReportOutlineId    ParentOutlineItemId
1     Introduction   1                  NULL
2     Pets           1                  NULL
3     Cats           1                  2
4     Dogs           1                  2


但是,当ReportOutline通过DbContext加载时,由于ReportOutlineId与大纲的Id匹配,所以ReportOutline.OutlineItems会填充所有四个项目。这导致子项同时出现在父项和主大纲本身下:

Title:  My Report
Author: valverij

I.  Introduction  (Id: 1)
II. Pets          (Id: 2)
    A. Cats       (Id: 3)
    B. Dogs       (Id: 4)
III. Cats         (Id: 3) <--- Duplicated
IV.  Dogs         (Id: 4) <--- Duplicated


现在,如果我将NHibernate与FluentNhibernate一起使用,则可以在实体映射上指定一个where条件,以便ReportOutline.OutlineItems仅提取父项:

// in ReportOutlineMap
HasMany(x => x.OutlineItems).Where("ParentOutlineItemId IS NULL");


否则,我将不得不记住只能通过预先编写的查询来访问ReportOutline对象,该查询明确处理OutlineItem集合。

最佳答案

您可以将“ RootOutlineItems”属性添加到为您过滤的ReportOutline类中,然后在只需要第一级时调用它:

public class ReportOutline
{
    public long Id { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public virtual ICollection<OutlineItem> OutlineItems { get; set; }
    public ICollection<OutlineItem> RootOutlineItems {
        get {
            return OutlineItems.Where(p=> p.ParentOutlineItem == null);
        }
    }
}


另一个选择是使OutlineItem的ReportOutline为可空状态,并且只设置ReportOutline或ParentOutlineItem属性,但这有点麻烦,而且如果要使用所有项目,则必须导航树。

10-01 14:56