Can I use entities without context?
Let's say I have a User
Entity, and created partial User
class so I can add some methods (like with NHibernate). I added GetByID
to make getting user easier:
public static User GetByID(int userID)
{
using (var context = new MyEnti开发者_如何学Goties())
{
return context.Users.Where(qq => qq.UserID == userID).Single();
}
}
Now, somewhere in business logic I'd like to do something like this:
var user = User.GetByID(userID);
var posts = user.GetAllPostsForThisMonth();
foreach(var post in posts)
{
Console.WriteLine(post.Answers.Count);
}
GetAllPostsForThisMonth()
is similar to GetByID
- has context and is disposing it right after execution.
Normally I can't do this because context is disposed when I call post.Answers.Count
. This, I think, renders my methods useless... Or am I missing something? Can I anyhow use my entities like this? Or should I create method for every single query I use (like post.GetAnswersCount()
)? Thanks in advance!
The behavior you're lamenting is actually good, because it keeps you from shooting yourself in the foot. If you had been allowed to do this, it would have cause n
round-trips to the database (where n
is the number of posts), and each one of those round-trips would have pulled all the data for all the Answers, when all you wanted was the Count
. This could have an enormous performance impact.
What you want to do is construct an object that represents all the information you expect to need from the database, and then construct a LINQ query that will actually load in all the information you expect to use.
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();
}
}
This produces a single SQL query and, in a single round-trip, produces exactly the information you wanted without loading in a ton of information you didn't want.
Update
If NHibernate works anything like Java's Hibernate, it won't do lazy loading after the context is disposed, either. Entity Framework does give you a lot of options along these lines: which one works best for you will depend on your particular situation. For example:
- Rather than keeping your context scoped inside the data-access method, you can make the context last longer (like once per request in a web application), so that Lazy loading of properties will continue to work beyond your data-access methods.
- You can eagerly load whatever entity associations you know you're going to need.
Here's an example of eager loading:
public GetAllPostsAndAnswersForThisMonth()
{
using (var context = new MyEntities())
{
return context.Posts.Include("Answers")
.Where(p => p.UserID == UserID)
.ToList();
}
}
However, since Entity Framework basically constitutes your "Data Access" tier, I would still argue that the best practice will be to create a class or set of classes that accurately model what your business layer actually wants out of the data tier, and then have the data access method produce objects of those types.
One method is to explicitly load related objects that you know you will need before you dispose the context. This will make the related data available, with the downside that if you don't need the related info it is wasted time and memory to retrieve. Of course, you can also handle this with flags:
... 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;
}
}
精彩评论