Yani veritabanınızda aşağıdakilerle dolu bir tablo var Posts
. Her Post
sıfır veya daha fazla (belki bir veya daha fazla) Kullanıcı tarafından gönderilmiş gibi görünüyor. Bana öyle geliyor ki, senin de bir masan var. Users
. Her User
sıfır veya daha fazla yayınladı Posts
.
Bana öyle geliyor ki, aralarında çok-çok bir ilişki var Users
ve Posts
: Her Kullanıcı sıfır veya daha fazla Gönderi gönderdi; her Gönderi sıfır tarafından gönderildi (bir?) veya daha fazla Kullanıcı.
Normalde bir veritabanında özel bir tabloyla çok-çok ilişkisi uygularsınız: birleşim tablosu.
Bağlantı tablosunu kullanmıyorsun. Veritabanınız normalleştirilmedi.
Belki şu anki sorununuz veritabanını değiştirmeden çözülebilir, ancak çözmeniz gereken pek çok sorun görüyorum, belki şimdi değil, yakın gelecekte: bir kullanıcıyı silmek istiyorsanız ne kadar büyük bir çalışma yapmanız gerekir? "Kullanıcının [10] yayınladığı tüm Gönderileri" nasıl elde edersiniz Ve Kullanıcı [10] artık Yayın listesinde [23] belirtilmek istemiyorsa ne olur? Bu Kullanıcının nasıl önleneceği [10] Post'ta iki kez belirtilmiştir[23]:
UserIds = 10, 3, 5, 10, 7, 10
Veritabanını normalleştir
Veritabanını bir bağlantı tablosuyla güncellemeyi ve dize sütunundan kurtulmayı düşünün Post.UserIds
. Bu, tüm bu sorunları bir kerede çözecektir.
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;}
}
Ve bağlantı tablosu:
public UsersPost
{
public int UserId {get; set;}
public int PostId {get; set;}
}
Not: [UserId, PostId] benzersizdir. Bunu birincil anahtar olarak kullanın
Entity framework'te tabloların sütunları sanal olmayan özelliklerle temsil edilir. Sanal özellikler tablolar arasındaki ilişkileri yansıtır (bire-çok, çok-çok)
Not: yabancı anahtar tablodaki gerçek bir sütundur, dolayısıyla yabancı anahtar sanal değildir.
Çok-çok yapılandırmak için Fluent API'sini kullanabilirsiniz:
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});
}
Sorununuza geri dönün
Bağlantı tablosunu uyguladıktan sonra veri isteğiniz kolay olacaktır:
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(),
});
Veya herhangi bir kullanıcı verisi istemiyorsanız, Gönderilerle başlayın:
var postsOfUser = dbContext.Posts
.Where(post => post.Users.Any(user => user.Id == userId))
.Select(post => new {...});
Bazı insanlar sanal koleksiyonları kullanmaktan hoşlanmazlar veya bunu desteklemeyen entity framework'ün bir sürümünü kullanırlar. Bu durumda, Katılmayı kendiniz yapmanız gerekecek:
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,
...
}
}
Normalleştirilmiş veritabanı olmadan çözüm
Proje liderinizi uygun bir veritabanının gelecekte birçok sorunu önleyeceğine gerçekten ikna edemiyorsanız, sizin için uygun gönderileri alan bir SQL metni oluşturmayı düşünün.
Dbcontext'iniz veritabanınızın geçerli uygulamasını temsil eder. Tablolar ve tablolar arasındaki ilişkileri tanımladı. Bir kullanıcının Gönderilerini almak için bir yöntem eklemek bana DbContext için okunaklı bir yöntem gibi görünüyor.
Sql'im biraz paslı, bunu sql'de nasıl yapacağımı benden daha iyi bileceksin. Sanırım özünü anlayacaksın.:
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);
}