实体框架ICollection导航属性仅在测试

时间:2016-06-07 21:48:07

标签: c# asp.net-mvc entity-framework navigation-properties

我在MVC 5项目中使用Entity Framework 6.1.3,并且我在单元测试时遇到ICollection导航属性为null的问题。我正在使用SQL Express中的实际测试数据库,所以这可能更像是纯粹主义者的集成测试。无论你怎么称呼它,我都想解决这个问题。

我已经阅读了许多相似的声音问题的答案,但它们似乎都没有遇到我在这里遇到的同样问题。我在很大程度上理解EF,我启用了延迟加载,我的课程是公开的,并且我在导航属性上使用virtual

以下是我尝试做的简化示例:

模型:

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

    // Navigation Property
    public virtual ICollection<File> Files { get; set; }
}

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

    public int SessionId { get; set; }
    public virtual Session Session { get; set; }
}

测试方法:

[TestMethod]
public void Test_TotalFileCount1()
{
    ApplicationDbContext context = new ApplicationDbContext();

    // Create session with no files
    var session = new Session() { Name = "Session1" };
    context.Sessions.Add(session);
    context.SaveChanges();

    // This line blows up because session.Files == null
    Assert.AreEqual(0, session.Files.Count);
}

[TestMethod]
public void Test_TotalFileCount2()
{
    ApplicationDbContext context = new ApplicationDbContext();

    // Create session
    var session = new Session() { Name = "Session2" };
    context.Sessions.Add(session);
    context.SaveChanges();

    // Create file for session
    var file = new File() { Name = "File1", Session = session };
    context.Files.Add(file)
    context.SaveChanges();

    // This test passes because session.Files is a
    // collection of one file
    Assert.AreEqual(1, session.Files.Count);
}

上面的第一个测试失败,因为session.Files会抛出ArgumentNullException。但是,当我在完整的MVC应用程序中调用相同的代码时,session.Files不是null,而是带有Count = 0的空集合。第二次测试通过,因为session.Files是一个File的集合,正如我所料。在第二种情况下,导航属性显然正在做他们应该做的事情,但在第一种情况下却没有。

为什么EF表现得像这样?

我能够通过将Files初始化为构造函数中的空列表来解决此问题。我知道我可以在吸气剂中有条件地做到这一点,但我认为我不应该做这些事情中的任何一件事,因为它只是在它正常运转时才有效。

public Session()
{
    this.Files = new List<File>();
}

有没有人对这里发生的事情有任何见解?

1 个答案:

答案 0 :(得分:2)

如果您正在使用延迟加载,并且如果您希望在将具有外键属性的对象添加到上下文后填充导航属性,则必须使用DbSet的Create方法(而不是使用new实例化对象) :

var session = context.Sessions.Create();

使用活动延迟加载,这将创建一个代理对象,以确保加载导航属性。

[TestMethod]
public void Test_TotalFileCount1()
{
    ApplicationDbContext context = new ApplicationDbContext();

    // Create session with no files
    var session = context.Sessions.Create();
    session.Name = "Session1";  

    context.Sessions.Add(session);
    context.SaveChanges();

    // This line blows up because session.Files == null
    Assert.AreEqual(0, session.Files.Count);
}

[TestMethod]
public void Test_TotalFileCount2()
{
    ApplicationDbContext context = new ApplicationDbContext();

    // Create session
    var session = context.Sessions.Create();
    session.Name = "Session2";
    session.Files = new List<File>()
    {
        new File() { Name = "File1" }
    };

    context.Sessions.Add(session);
    context.SaveChanges();


    // This test passes because session.Files is a
    // collection of one file
    Assert.AreEqual(1, session.Files.Count);
}

详细了解Proxies