我可以使用没有上下文的实体吗

时间:2011-07-06 17:53:20

标签: c# entity-framework

假设我有一个User实体,并创建了部分User类,所以我可以添加一些方法(比如NHibernate)。我添加了GetByID以便让用户更轻松:

public static User GetByID(int userID)
{
    using (var context = new MyEntities())
    {
        return context.Users.Where(qq => qq.UserID == userID).Single();
    }
}

现在,在商业逻辑的某个地方,我想做这样的事情:

var user = User.GetByID(userID);
var posts = user.GetAllPostsForThisMonth();
foreach(var post in posts)
{
    Console.WriteLine(post.Answers.Count);
}

GetAllPostsForThisMonth()GetByID类似 - 具有上下文,并在执行后立即处置它。

通常我不能这样做,因为当我呼叫post.Answers.Count时,上下文被处理掉了。我认为这会使我的方法变得无用......或者我错过了什么?我能不能这样使用我的实体?或者我应该为我使用的每个查询创建方法(如post.GetAnswersCount())?提前谢谢!

2 个答案:

答案 0 :(得分:5)

你感叹的行为实际上很好,因为它可以防止你在脚下射击。如果您被允许这样做,那么它将导致n往返数据库(其中n是帖子数量),并且每次往返都将全部拉出当你想要的只是Count时,所有答案的数据。这可能会对性能产生巨大影响。

您要做的是构造一个对象,该对象表示您希望从数据库中获取所需的所有信息,然后构造一个LINQ查询,该查询将实际加载您希望使用的所有信息。

public class PostSummary
{
    public Post Post {get;set;}
    public int AnswerCount {get;set;}
}

public IEnumerable<PostSummary> GetPostSummariesByUserAndDateRange(
                                   int userId, DateTime start, DateTime end)
{
    using (var context = new MyEntities())
    {
        return context.Posts
               .Where(p => p.UserId == userId)
               .Where(p => p.TimeStamp < start && p.TimeStamp > end)
               .Select(new PostSummary{Post = p, AnswerCount = p.Answers.Count()})
               .ToList();
    }
}

这会生成一个SQL查询,并且在一次往返中,可以生成您想要的信息,而无需加载大量您不想要的信息。

更新

如果NHibernate像Java的Hibernate一样工作,它也不会在上下文处理后进行延迟加载。实体框架确实为您提供了许多选项:哪一个最适合您将取决于您的具体情况。例如:

  • 您可以使上下文的持续时间更长(比如Web应用程序中的每个请求一次),这样Lazy加载的属性将继续超出您的数据访问方法,而不是将您的上下文保留在数据访问方法中。
  • 您可以急切地加载您知道自己需要的任何实体关联。

以下是热切加载的示例:

public GetAllPostsAndAnswersForThisMonth()
{
    using (var context = new MyEntities())
    {
        return context.Posts.Include("Answers")
                   .Where(p => p.UserID == UserID)
                   .ToList();
    }
}

但是,由于实体框架基本上构成了您的“数据访问”层,我仍然认为最佳做法是创建一个类或一组类,准确地模拟业务层实际需要的数据层,然后让数据访问方法生成这些类型的对象。

答案 1 :(得分:1)

在处置上下文之前,您知道需要explicitly load related objects的一种方法。这将使相关数据可用,但缺点是如果您不需要相关信息,则会浪费时间和内存来检索。当然,您也可以使用标志来处理:

... GetAllPostsForThisMonth(bool includeAnswers)
{
    using (var context = new MyEntities())
    {
        context.ContextOptions.LazyLoadingEnabled = false;

        // code to get all posts for this month here
        var posts = ...;

        foreach (var post in posts)
            if (!post.Answers.IsLoaded)
                post.Answers.Load();

        return posts;
    }
}