Entity Framework不支持受保护的导航属性吗?

时间:2016-07-15 12:29:45

标签: c# entity-framework

我已经声明了这样的实体(实际的类显然也有ID属性,映射完成等等,但不是问题,所以我在这里跳过它):

Sub Debugging()

Workbooks("Problem.xls").Worksheets(1).Activate

Cash_Rows = 5
Share_Rows = 6

If Cash_Rows <= Share_Rows Then

    Range("A1:A" & Cash_Rows).Select
    With Selection.Interior
        .ThemeColor = xlThemeColorAccent6
        .TintAndShade = 0.399975585192419

    Count_Cash = Application.WorksheetFunction.CountIf(Range("A:A"), "L*")

    For Each cell In Range("A1:A" & Cash_Rows)
        If CStr(cell.Value) Like "L*" Then
        Range("A" & cell.Row & ":" & "D" & cell.Row).Interior.Color = 65535
        Dim Index As Integer
        Index = Application.WorksheetFunction.Match(CStr(cell.Value), Range("F2:" & "F" & Share_Rows), 0)
        Range("F" & Index & ":" & "I" & Index).Interior.Color = 65535
        End If
    Next

    If Count_Cash = 0 Then
        MsgBox "You do not have any matching ID+Amount between Cash and Shares booking. It's OK!"
    Else
        MsgBox "You have " & Count_Cash & " matching transactions. Check them!"
    End If

Else

MsgBox "Do not worry. Be happy!"

End If
End Sub

这很完美:

public class Parent
{
    public virtual ICollection<Child> Children {get; set;}
}

现在我更改要保护的Children集合的可见性:

public class Consumer
{
    void DoBusiness()
    {
        using (var ctx = new MyDbContext())
        {
            var entity = ctx.Parents.Find(keyOfParent);
            // This is as expected: entity.Children refers to a collection which 
            // Entity Framework has assigned, a collection which supports lazy loading.
        }
    }
}

这会带来意想不到的结果:

public class Parent
{
    protected virtual ICollection<Child> Children {get; set;}
}

此外,如果我处于保护儿童的情况下,请尝试通过以下方式明确加载儿童:

public class Consumer
{
    void DoBusiness()
    {
        using (var ctx = new MyDbContext())
        {
            var entity = ctx.Parents.Find(keyOfParent);
            // This is NOT as expected: entity.Children is null. I would expect, that it 
            // had been referring to a collection which Entity Framework would have been 
            // assigning, a collection which should support lazy loading.
        }
    }
}

然后我得到了这个例外:

  

'Parent'类型的属性“Children”不是导航属性.Reference和Collection方法只能用于导航属性。使用Property或ComplexProperty方法。

因此:为了使用Entity Framework获得受保护的导航属性,我应该做些什么?

1 个答案:

答案 0 :(得分:1)

以下是我如何使其发挥作用。

public class Parent
{
    public int Id { get; set; }
    public string Name { get; set; }

    internal ICollection<Child> children;
    protected virtual ICollection<Child> Children { get { return children; } set { children = value; } }
    internal ICollection<Child> GetChildren() => Children;
    internal static Expression<Func<Parent, ICollection<Child>>> ChildrenSelector => p => p.Children;
}

public class Child
{
    public int Id { get; set; }
    public string Name { get; set; }

    internal Parent parent;
    protected virtual Parent Parent { get { return parent; } set { parent = value; } }
    internal Parent GetParent() => Parent;
    internal static Expression<Func<Child, Parent>> ParentSelector => c => c.Parent;
}

public class MyDbContext : DbContext
{
    public DbSet<Parent> Parents { get; set; }
    public DbSet<Child> Children { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Parent>()
            .HasMany(Parent.ChildrenSelector)
            .WithRequired(Child.ParentSelector)
            .Map(a => a.MapKey("ParentId"));

        base.OnModelCreating(modelBuilder);
    }
}

使用的显式字段不是必需的 - 我将它们放在那里以便能够观察其中的内容,您可以继续使用自动属性。

关键部分是提供属性访问器表达式并将其与Fluent API配置一起使用。如果没有显式配置,您将获得您描述的行为。有了它,一切正常。

例如,延迟加载:

var parent = ctx.Parents.Find(keyOfParent);
var children = parent.GetChildren();

或显式加载:

var parent = ctx.Parents.Find(keyOfParent);
db.Entry(parent).Collection("Children").Load();
var children = parent.children;

更新:非常奇怪,如果我们更换配置代码

modelBuilder.Entity<Parent>()
    .HasMany(Parent.ChildrenSelector)
    .WithRequired(Child.ParentSelector)
    .Map(a => a.MapKey("ParentId"));

完全等同于定义

modelBuilder.Entity<Child>()
    .HasRequired(Child.ParentSelector)
    .WithMany(Parent.ChildrenSelector)
    .Map(a => a.MapKey("ParentId"));

数据库表和FK是相同的,但加载不起作用!因此,工作解决方案无意中撞到了后门,或者EF中存在错误。在这两种情况下,该功能对我来说似乎都有问题,我只会使用public访问器以避免意外。