Для начала, если ваши запросы относительно просты, я бы не стал создавать набор вспомогательных методов доступа к данным, таких как GetNewest()
, GetOldest()
, GetForUser(int)
, GetByCategory(int)
и т. Д.методы могут быть непродуктивными, поскольку они скрывают базовые детали, которые могут быть важны для вызывающего кода, и затрудняют объединение запросов.
Например, вы можете реализовать GetNewest()
следующим образом:
public IQueryable<Post> GetNewest()
{
return
from p in db.Posts
orderby p.DateCreated descending
select p;
}
Но затем позже включите предложение where p.StatusID == (int)PostStatus.Visible
следующим образом:
public IQueryable<Post> GetNewest()
{
return
from p in db.Posts
where p.StatusID == (int)PostStatus.Visible
orderby p.DateCreated descending
select p;
}
Теперь весь вызывающий код по-прежнему читается как GetNewest()
, поэтому, просто взглянув на вызывающий код, вы не знаете,Вы становитесь видимыми или нет, или каков порядок.И на самом деле, некоторый существующий код может ожидать, что GetNewest()
возвращает невидимые сообщения, и теперь вы взломали этот код, не осознавая этого.
С другой стороны, вы не хотите, чтобы запросы были разбросаны по всей вашейкод.Если вы измените схему своей базы данных, у вас может появиться много неработающего кода.
Правильный путь - это думать о запросах как о последовательности составных, атомарных операций.
Так вот что яЯ предлагаю вам сделать для своего кода доступа к данным:
public static IQueryable<Post> WhereStatusVisible(
this IQueryable<Post> posts)
{
return
from p in posts
where p.StatusID == (int)PostStatus.Visible
select p;
}
public static IQueryable<Post> OrderByDateCreatedDescending(
this IQueryable<Post> posts)
{
return
from p in posts
orderby p.DateCreated descending
select p;
}
Теперь вы можете написать такой код вызова:
var query =
db.Posts
.WhereStatusVisible()
.OrderByDateCreatedDescending();
Это становится очень ясно, что происходит, и это не такВероятно, что базовая реализация этих методов расширения когда-либо изменится, и все особенности схемы будут скрыты в методах расширения.
Затем вы можете расширить это, чтобы обработать создание моделей представления.Теперь, поскольку модели представлений не являются частью вашей базы данных, я бы перешел с IQueryable<Post>
на IEnumerable<PostViewModel>
, и, поскольку кажется, что вам нужно присоединиться к автору (предположительно из таблицы User
), я бы сделал что-то вроде этого:
public static IEnumerable<PostViewModel> ToPostViewModels(
this IQueryable<Post> posts,
IQueryable<User> users)
{
var query =
from p in posts
join u in users on p.AuthorId equals u.Id
select new { p, u };
return
query
.ToArray()
.Select(q => new PostViewModel()
{
Id = q.p.Id,
DateCreated = q.p.DateCreated,
AuthorName = q.u.Name,
})
.ToArray();
}
Теперь вызывающий код выглядит следующим образом:
var postViewModels =
db.Posts
.WhereStatusVisible()
.OrderByDateCreatedDescending()
.ToPostViewModels(db.Users);
Я бы тогда тоже посмотрел на выполнение этого вида кода:
public static IQueryable<Post> GetByCategories(
this IQueryable<Post> posts,
IQueryable<Category> categories)
{
return
from p in posts
join c in categories on p.CategoryId equals c.Id
select p;
}
public static IQueryable<Category> WhereStartsWith(
this IQueryable<Category> categories,
string startsWith)
{
return
from c in categories
where c.Name.StartsWith(startsWith)
select c;
}
Это позволило быкод вызова:
var postViewModelsInCategoriesStartingWithN =
db.Posts
.GetByCategories(db.Categories.WhereStartsWith("N"))
.WhereStatusVisible()
.ToPostViewModels(db.Users);
Итак, суть такого подхода в том, что вы:
- предоставляете атомарные, составные операторы, которые действуют на
IQueryable<T>
и переходят на IQueryable<T>
. - используйте
IQueryable<>
в базе данных и переходите к IEnumerable<>
при работе с классами, не относящимися к базе данных. - избегайте того, чтобы ваш код вызова зависел от особенностей базы данных (т.е. не
int
, User
или IQueryable<User>
).
Если есть что-то еще, дайте мне знать.Надеюсь, это поможет.