我使用Linq2Sql作为我的数据访问,但我很快注意到我做错了什么。
我有一个帖子列表,我需要能够以几种不同的方式过滤它。例如:
每个方法都返回一个名为PostViewModel
的模型。它是帖子的扩展版本,以及它的一些外键。 (作者姓名等......)
帖子如下:
class Post
{
int Id;
DateTime DateCreated;
int AuthorId;
}
和PostViewModel将是:
class PostViewModel
{
int Id;
DateTime DateCreated;
string AuthorName;
}
上面的每个方法都类似于
from p in db.Posts
where p.StatusID == (int)PostStatus.Visible //this can be different for each
order by <something> //this can be different for each
select new PostViewModel() //this is the same for each
{
//snip
}
我觉得我做错了什么。如果我对PostViewModel进行更改,我将不得不更改每个方法。可能需要注意的是,我将对象扩展为PostViewModel,因此每当我迭代结果并显示它时,我都不必返回数据库来获取AuthorName(以及其他一些)。
我可以在这做什么?如果需要的话,我不怕做出重大改变。答案 0 :(得分:4)
首先,如果您的查询相对简单,我倾向于避免创建一组数据访问辅助方法,例如GetNewest()
,GetOldest()
,GetForUser(int)
,{{1这些方法可能适得其反,因为它们隐藏了对调用代码可能很重要的底层细节,并且它们使得组合查询变得困难。
例如,您可以像这样实现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
这样做。如果有任何进一步让我知道。我希望这会有所帮助。
答案 1 :(得分:1)
首先,您可以创建一组帮助您执行常见任务的扩展方法,例如:
static class PostQueryExtensions
{
public static IQueryable<PostViewModel> Materialize(this IQueryable<Post> query)
{
return from post in query
select new PostViewModel()
{
....
}
}
}
class PostDataAccess
{
public IEnumerable<PostViewModel> GetOldest()
{
var query = db.Posts
.OrderBy(x => x.DateCreated)
.Materialize();
}
public IEnumerable<PostViewModel> GetNewest()
{
var query = db.Posts
.OrderByDescending(x => x.DateCreated)
.Materialize();
}
}
您可以随意添加任何类型的扩展方法。包括排序,过滤等。因此可以在查询扩展中转换重复的任务。
如果你害怕你的模型将来可能会改变(或要求),你甚至可以为每个简单的基本任务扩展它。让我们举一个例子:要求是大多数方法按PosterName排序。所以在大多数查询中你都有类似的东西:按p.Name排序。现在如果突然使用LastName扩展Name字段。您现在必须对2个字段进行排序,而不必仅在“名称”字段上进行排序。但是,如果您在开头创建了一个Query扩展名,如:
public static IQueryable<Post> OrderByName(this IQueryable<Post> query) { ... }
您只需修改OrderByName的实现,并且使用此方法的每个方法都将自动受到影响。 (许多管道模式称之为。)
答案 2 :(得分:0)
这是一种方式:
public enum PostType
{
Oldest,
Newest,
ForUser,
ByCat
}
public List<PostViewModel> GetPosts(PostType pt, YourDataContext db, int UserId = 0)
{
List<PostViewModel> v = db.Posts.Select(i => new PostViewModel() { /* snip */});
switch(pt)
{
case pt.Oldest:
v = v.Where(i => p.StatusID == (int)PostStatus.Visible).OrderDescendingBy(i => i.DateCreated).ToList();
break;
case pt.ByUser:
v = v.Where(i => p.UserId == UserId).ToList();
break;
...
}
return v;
}