假设我有一个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()
)?提前谢谢!
答案 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一样工作,它也不会在上下文处理后进行延迟加载。实体框架确实为您提供了许多选项:哪一个最适合您将取决于您的具体情况。例如:
以下是热切加载的示例:
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;
}
}