Могу ли я использовать объекты без контекста? - PullRequest
2 голосов
/ 06 июля 2011

Допустим, у меня есть объект 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 ]

5 голосов
/ 06 июля 2011

На самом деле поведение, которое вы оплакиваете, хорошее, потому что оно не дает вам выстрелить себе в ногу.Если бы вам было разрешено это сделать, это привело бы к 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 работает так же, как Hibernate в Java, он не будет выполнять отложенную загрузку после удаления контекста.Entity Framework действительно дает вам много вариантов в этих направлениях: какой из них работает лучше всего, будет зависеть от вашей конкретной ситуации.Например:

  • Вместо того, чтобы ограничивать свой контекст в методе доступа к данным, вы можете продлить контекст (как один раз для каждого запроса в веб-приложении), так что отложенная загрузка свойств будетпродолжайте работать за пределами ваших методов доступа к данным.
  • Вы можете с готовностью загружать любые ассоциации сущностей, которые, как вы знаете, вам понадобятся.

Вот пример активной загрузки:

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

Однако, поскольку Entity Framework в основном составляет ваш уровень «Доступ к данным», я все равно буду утверждать, что наилучшей практикой будет создание класса или набора классов, которые точно моделируют то, чего на самом деле хочет ваш бизнес-уровень.уровень данных, а затем иметь метод доступа к данным производить объекты этих типов.

1 голос
/ 06 июля 2011

Один метод заключается в явной загрузке связанных объектов , которые, как вы знаете, вам понадобятся до того, как вы удалите контекст. Это сделает соответствующие данные доступными, но с другой стороны, если вам не нужна связанная информация, это будет потрачено впустую время и память для извлечения. Конечно, вы также можете справиться с этим с флагами:

... 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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...