Dotnet ve Nosql’ le İlişkili Tasarım Örneği

Yasar Can
4 min readJan 27, 2024

Neden SQL veya neden NoSQL(Non-relational) bunlara girmeye gerek olmadığını düşünüyorum, zaten bunlarla ilgili yazılar olduğu için geçiyorum.

Peki, SQL (Relational Database Management System) lerde nasıl ilişki kurulduğunu hepimiz biliyoruz değil mi? Fakat iş NoSQL e geldiği zaman pekte bir ilişkili tasarım örneklerini göremiyoruz. Oysa ki ilişki kurmak illaki projemizin tasarımına göre gerekli oluyor. Peki NoSQL kullandığımız bir sitemde tasarım kuramıyacak mıyız? Many to one, one to one nerede?Bunları tabiki kuracağız. Fakat burada tasarım normal SQL e göre farklı.

Sonuçta burada bir document bazı sistemle çalışıyoruz ve SQL de olan ilişki sistemi söz konusu değil.

NoSQL de iki çeşit ilişki sistemi bulunuyor:

  • Embeded
  • Reference

Embeded Relationships

Bu yaklaşım, bir belge içinde başka bir belgenin yerleştirilmesini sağlar. Örneğin, bir kullanıcının profili ve o kullanıcının gönderdiği mesajlar aynı belge içinde tutulabilir. Bu yöntem, bir belge içindeki ilişkili verilerin birlikte saklanması gerektiğinde kullanışlıdır.

Model One-to-Many Relationships with Embedded Documents — MongoDB Manual

Referenced Relationships

Referanslı ilişkilerde, bir belge başka bir belgeyi referans eder, ancak belge kendisi içinde tutulmaz. Örneğin, bir kullanıcı belgesi, kullanıcının profiline ait bir referans anahtarı içerebilir ve bu referans anahtarı başka bir koleksiyonda (örneğin, profiller koleksiyonu) bulunan profili işaret eder.

Kullanım 1
Kullanım 2

Örnek Bir Blog Projesi

Bir blog sitesi yaptığımızı düşünelim. Burada bir author var ve bu authorun birden fazla postları var, buradaki ilişki türü ise one-to-many olacak şekilde bir tasarım yapacağımızı düşebiliriz.

Entities:

Post eklerken authorla ilişki nasıl kuracağız?

public async Task AddPostAsync(AddPostModel addPostModel)
{
var author = await _authorCollection.Find(x => x.Id == ObjectId.Parse(addPostModel.AuthorId)).FirstOrDefaultAsync();

if (author == null)
{
throw new NotFoundException();
}

var post = _mapper.Map<Post>(addPostModel);
await _postCollection.InsertOneAsync(post);

var filter = Builders<Author>.Filter.Eq(x => x.Id, ObjectId.Parse(addPostModel.AuthorId));
var update = Builders<Author>.Update.Push(a => a.PostIds, post.Id);

await _authorCollection.UpdateOneAsync(filter, update);
}

Burada post eklerken ayriyetten authorumuzun PostIds dizisi kısmına postumuzun id sini ekliyoruz. Ayrıca postun içindeki AuthorId kısmına authorid sini ekliyoruz. Böylelikle yukarıdaki her iki kullanımıda kullanmış olduk.

Author Document:

{
"_id": {
"$oid": "65a93defc04e1135dcf5aff9"
},
"CreatedAt": {
"$date": "2024-01-18T15:04:15.548Z"
},
"Username": "malenes123",
"Email": "malenes@gmail.com",
"FirstName": "enes",
"LastName": "can",
"PasswordHash": {
"$binary": {
"base64": "0OiklIBRdDArwAwBvhELYXxQZpZxcRLznLLOykwS6udoeKqbrU4jhbQZu8YB6jeBwVS0YsJV37E3n92y9aOEoQ==",
"subType": "00"
}
},
"PasswordSalt": {
"$binary": {
"base64": "SYo4Izl/R7Wt8fsd66Q0mGC2HMlzwzTxUpPZjs3fzoZXDCmPuXNBItCp3En5vrcyl1JmyskdvEYO6mzRzvW9isvbjlfLD70eqPfA60S/dnEYEAd0RXYoi/eMdLZ/rlG9F65BR1tu29lvmyo/Tu8Yc7zjGYJiB82zSIIDUUwa31k=",
"subType": "00"
}
},
"PostIds": [
{
"$oid": "65aac0bc9c2a1e222c07355e"
}
]
}

Post Document:

{
"_id": {
"$oid": "65aac0bc9c2a1e222c07355e"
},
"CreatedAt": {
"$date": "2024-01-19T18:34:36.933Z"
},
"Title": ".NET Projesinde Rest Servise Nasıl İstek Atılır?",
"Content": "Bu yazımda Rest Service’e nasıl istek atabileceğiniz ile ilgili olcaktır. Bunun birden fazla yolu olsa da bu yazıda sizlere en yaygın ve performanslı yönetmenlerden biri olan RestSharp’ı göstereceğim. Bunun yerine HttpClientkütüphanesini de kullanabilirsiniz fakat bu yazımda HttpClient’ten bahsetmeyeceğim. Yazılımın diğer kısımlarında da olduğu gibi burada da kullanımın tek bir doğrusu yoktur.HttpClient vs RestSharp Bu yazı RestSharp vs HttpClient olmadığından dolayı bu konudan bahsetmeyeceğim. Aşağıdaki linkten bu konunun devamını araştırmak istiyorsanız sizlere bir link bıraktım. HttpClient vs RestSharp - Which One to Use in .NET - Code Maze HttpClient and RestSharp are HTTP Client libraries that we can use to consume APIs.",
"Thumbnail": "assets/images/blog/blog-post-thumb-1.jpg",
"Tags": [
"falan",
"filan"
],
"AuthorId": {
"$oid": "65a93defc04e1135dcf5aff9"
},
"UpdatedAt": null
}

Postları author ile birlikte getirmek için

public async Task<List<GetPostsModel>> GetPostsAsync()
{
var posts = await _postCollection.Find(_ => true).ToListAsync();

var authorIds = posts.Select(post => post.AuthorId).ToList();
var authorProjection = Builders<Author>.Projection
.Include(p => p.Username)
.Include(p => p.FirstName)
.Include(p => p.LastName);
var authors = await _authorCollection.Find(author => authorIds.Contains(author.Id)).Project<Author>(authorProjection).ToListAsync();

var postsModel = posts.Select(post => new GetPostsModel
{
Title = post.Title,
Content = post.Content.Substring(0, 300) + "...",
FullName = authors.FirstOrDefault(author => author.Id == post.AuthorId)?.FirstName + " " + authors.FirstOrDefault(author => author.Id == post.AuthorId)?.LastName,
AuthorUsername = authors.FirstOrDefault(author => author.Id == post.AuthorId)?.Username,
AverageReadingTime = CalculateAverageReadingTime(post.Content),
Thumbnail = post.Thumbnail,
CreatedAt = post.CreatedAt
}).ToList();

return postsModel;
}

Request response:

Ayrıca postu sildiğimizde authorun PostIds kısmından silmemiz gerekiyor, Post silme ise:

public async Task DeletePostAsync(ObjectId postId)
{
var post = await _postCollection.Find(x => x.Id == postId).FirstOrDefaultAsync();
var deleteResult = await _postCollection.DeleteOneAsync(x => x.Id == postId);

if (deleteResult.DeletedCount == 0 || post == null)
{
throw new NotFoundException();
}

var filter = Builders<Author>.Filter.Eq(x => x.Id, post.AuthorId);
var update = Builders<Author>.Update.Pull(u => u.PostIds, postId);

var updateResult = await _authorCollection.UpdateOneAsync(filter, update);
}

Teşekkürler…

mas963 (Yasar Can) (github.com)

LinkedIn

Kaynakça:

Model Relationships Between Documents — MongoDB Manual

--

--