定义Navigation属性时列出vs IEnumerable vs IQueryable

时间:2012-02-12 03:11:55

标签: linq asp.net-mvc-3 entity-framework

我想在ASP.NET MVC Web应用程序中创建一个名为Movie_Type的新模型对象。如果我将此类的导航属性定义为以下ListICollectionIQueryable,会有什么不同?

public partial class Movie_Type
{ 
    public int Id { get; set; }
    public string Name { get; set; } 
    public string Description { get; set; } 
    public List<Movie> Movies { get; set; }
}

OR

public partial class Movie_Type
{ 
    public int Id { get; set; }
    public string Name { get; set; } 
    public string Description { get; set; } 
    public IQueryable<Movie> Movies { get; set; }
}

OR

public partial class Movie_Type
{
    public int Id { get; set; }
    public string Name { get; set; } 
    public string Description { get; set; } 
    public ICollection<Movie> Movies { get; set; }
}

修改: - @Tomas Petricek。 感谢您的回复。在我的情况下,我使用数据库第一种方法,然后我使用DbContext模板来映射我的表,自动为所有导航属性创建ICollection,所以我的问题是: - 1.这是否意味着它并不总是使用Icollection的最佳选择。我应该更改自动生成的类以最适合我的情况。 2.其次,我可以设法通过定义.include(例如

)来选择延迟或预先加载
var courses = db.Courses.Include(c => c.Department);

无论我使用什么来定义导航属性。所以我无法理解你的观点。 3.我没有找到任何使用IQuerable定义导航属性的示例或教程,那么可能是什么原因? BR

4 个答案:

答案 0 :(得分:15)

您不能使用IQueryable<T>类型的导航属性。您必须使用ICollection<T>或某些实现ICollection<T>的集合类型 - 例如List<T>。 (IQueryable<T>未实现ICollection<T>。)

导航属性只是内存中的对象或对象集合,或者是null或集合为空。

从数据库加载包含导航属性的父对象时,永远不会从数据库加载它。

您必须明确表示要将导航属性与急切加载的父级一起加载:

var movieTypes = context.Movie_Types.Include(m => m.Movies).ToList();
// no option to filter or sort the movies collection here.
// It will always load the full collection into memory

或者它将由延迟加载加载(如果导航属性为virtual,则默认启用):

var movieTypes = context.Movie_Types.ToList();
foreach (var mt in movieTypes)
{
    // one new database query as soon as you access properties of mt.Movies
    foreach (var m in mt.Movies)
    {
        Console.WriteLine(m.Title);
    }
}

最后一个选项是显式加载,这与我的意图最接近:

var movieTypes = context.Movie_Types.ToList();
foreach (var mt in movieTypes)
{
    IQueryable<Movie> mq = context.Entry(mt).Collection(m => m.Movies).Query();
    // You can use this IQueryable now to apply more filters
    // to the collection or sorting, for example:
    mq.Where(m => m.Title.StartWith("A"))   // filter by title
      .OrderBy(m => m.PublishDate)          // sort by date
      .Take(10)                             // take only the first ten of result
      .Load();                              // populate now the nav. property
    // again this was a database query

    foreach (var m in mt.Movies)    // contains only the filtered movies now
    {
        Console.WriteLine(m.Title);
    }
}

答案 1 :(得分:8)

有两种可能的方式来看待事物:

  1. 结果是否作为对象实例的一部分存储在内存中?
     如果选择ICollection,结果将存储在内存中 - 如果数据集非常大或者您并不总是需要获取数据,这可能不是一个好主意。另一方面,当您将数据存储在内存中时,您将能够从程序中修改数据集。

  2. 您可以优化发送到SQL服务器的查询吗?
     这意味着您可以在返回的属性上使用LINQ,并且其他LINQ运算符将转换为SQL - 如果您不选择此选项,则额外的LINQ处理将在内存中运行。

  3. 如果要将数据存储在内存中,则可以使用ICollection。如果您希望能够优化查询,则需要使用IQueryable。这是一个摘要表:

    |                 | Refine query | Don't change query |
    |-----------------|--------------|--------------------|
    |  In-memory      |    N/A       |    ICollection     |
    |  Lazy execution | IQueryable   |    IEnumerable     |
    

答案 2 :(得分:0)

更多的标准是IEnumerable,因为它是最不常见的分母。

如果您想为调用者提供额外的查询功能而无需10个存储库方法来处理不同的查询方案,则可以返回Iqueryable。

缺点是无数可能'count()'缓慢但如果对象实现了ICollection,则首先检查此接口是否有该值,而不必枚举所有项目。

还要注意,如果将iqueryable返回给不受信任的调用者,他们可以对iqueryable执行一些转换和方法调用,并获取对上下文,连接,连接字符串,运行查询等的访问权。

另请注意,nhibernate例如有一个查询对象,您可以将其传递给存储库以指定选项。使用实体框架,您需要返回IQueryable以增强查询条件

答案 3 :(得分:0)

如果您使用虚拟导航属性,实体框架实际为您创建的集合实现ICollection,但不实现IQueryable,因此您不能将IQueryable用于导航属性,如Slauma所说。

您可以自由地将属性定义为IEnumerable,因为ICollection扩展了IEnumerable,但是如果您这样做,那么您将失去向这些导航属性添加新子项的能力。