所以你的数据库有一个用Posts 填充的表。每个Post 似乎都由零个或多个(可能是一个或多个)用户发布。在我看来,您还有一张Users 的表格。每个User 都发布了零个或多个Posts。
在我看来Users 和Posts 之间存在多对多关系:每个用户都发布了零个或多个帖子;每个帖子都由零个(一个?)或多个用户发布。
通常在数据库中,您会使用特殊表实现多对多关系:联结表。
您不使用联结表。您的数据库未标准化。
也许您当前的问题可以在不更改数据库的情况下解决,但是我看到您必须解决很多问题,可能不是现在,而是在不久的将来:如果您想删除用户,您需要做哪些巨大的工作?您如何获得所有“用户 [10] 已发布的帖子”以及如果用户 [10] 不想在帖子 [23] 的发布列表中被提及怎么办?如何防止在 Post[23] 中两次提到 User [10]:
UserIds = 10, 3, 5, 10, 7, 10
规范化数据库
考虑用联结表更新数据库并去掉字符串列Post.UserIds。这将一次性解决所有这些问题。
class User
{
public int Id {get; set;}
public string Name {get; set;}
...
// every user has posted zero or more Posts:
public virtual ICollection<Post> Posts {get; set;}
}
class Post
{
public int Id {get; set;}
public string Title {get; set;}
public Datetime PublicationDate {get; set;}
...
// every Post has been posted by zero or more Users:
public virtual ICollection<User> Users {get; set;}
}
还有联结表:
public UsersPost
{
public int UserId {get; set;}
public int PostId {get; set;}
}
注意:[UserId, PostId] 是唯一的。使用这个作为主键
在实体框架中,表的列由非虚拟属性表示。虚拟属性反映了表之间的关系(一对多、多对多)
注意:外键是表中的真实列,因此外键是非虚拟的。
要配置多对多,可以使用 Fluent API:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// User - Post: many-to-many
modelBuilder.Entity<User>()
.HasMany<Post>(user => user.Posts)
.WithMany(post => post.Users)
.Map(userpost =>
{
userpost.MapLeftKey(nameof(UserPost.UserId));
userpost.MapRightKey(nameof(UserPost.PostId));
userpost.ToTable(nameof(UserPost));
});
// primary key of UserPost is a composite key:
modelBuilder.Entity<UserPost>()
.HasKey(userpost => new {userpost.UserId, userpost.PostId});
}
回到你的问题
一旦你实现了联结表,你的数据请求就会很容易:
int userId = ...
// get this User with all his Posts:
var userWithPosts= dbContext.Users
.Where(user => user.Id == userId)
.Select(user => new
{
// Select only the user properties that you plan to use
Name = user.Name,
...
Posts = user.Posts.Select(post => new
{
// Select only the Post properties that you plan to use
Id = post.Id
PublicationDate = post.PublicationDate,
...
})
.ToList(),
});
或者,如果您不想要任何用户数据,请从帖子开始:
var postsOfUser = dbContext.Posts
.Where(post => post.Users.Any(user => user.Id == userId))
.Select(post => new {...});
有些人不喜欢使用虚拟 ICollections,或者他们使用不支持此功能的实体框架版本。在这种情况下,您必须自己加入:
int userId = ...
var postsOfThisUser = dbContext.UserPosts
// keep only the UserPosts of this user:
.Where(userPost => post.UserId == userId)
// join the remaining UserPosts with Posts
.Join(dbContext.Posts,
userpost => userpost.PostId, // from every UserPost get the foreign key to Post
post => post.Id, // from every Post, get the primary key
// parameter resultSelector: from every UserPost with matching Post make one new
(userPost, post) => new
{
Title = post.Title,
PublicationDate = post.PublicationDate,
...
}
}
没有规范化数据库的解决方案
如果您真的无法让项目负责人相信一个合适的数据库将在未来避免很多问题,请考虑创建一个 SQL 文本来为您获取合适的帖子。
您的 DbContext 代表您的数据库的当前实现。它描述了表格和表格之间的关系。在我看来,添加一个获取用户帖子的方法似乎是 DbContext 的合法方法。
我的 SQL 有点生疏了,你会比我更了解如何在 SQL 中执行此操作。我想你会明白要点的:
public IEnumerable<Post> GetPostsOfUser(int userId)
{
const string sqlText = "Select Id, ... from Posts where ..."
object[] parameters = new object[] {userId};
return this.Database.SqlQuery(sqlText, parameters);
}