From 51d0dbb69919de28e7770ad0c91cf7babbdf2f50 Mon Sep 17 00:00:00 2001 From: vVladEr Date: Tue, 22 Oct 2024 15:04:02 +0500 Subject: [PATCH 1/5] task 1 --- Game/Domain/MongoUserRepositoty.cs | 44 ++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/Game/Domain/MongoUserRepositoty.cs b/Game/Domain/MongoUserRepositoty.cs index 51aeba5..3b0896b 100644 --- a/Game/Domain/MongoUserRepositoty.cs +++ b/Game/Domain/MongoUserRepositoty.cs @@ -11,35 +11,64 @@ public class MongoUserRepository : IUserRepository public MongoUserRepository(IMongoDatabase database) { userCollection = database.GetCollection(CollectionName); + var indexKeysDefinition = Builders.IndexKeys.Ascending(user => user.Login); + var options = new CreateIndexOptions + { + Unique = true, + Name = "login_index" + }; + userCollection.Indexes.CreateOne(new CreateIndexModel(indexKeysDefinition, options)); } public UserEntity Insert(UserEntity user) { //TODO: Ищи в документации InsertXXX. - throw new NotImplementedException(); + /* var foundUser = userCollection.Find(u => u.Login == user.Login).FirstOrDefault(); + if (foundUser != null) + throw new MongoWriteException();*/ + userCollection.InsertOne(user); + /* try + { + + } + catch (Exception ex) + { + throw; + }*/ + return user; } public UserEntity FindById(Guid id) { + return userCollection.Find(user => user.Id == id).FirstOrDefault(); //TODO: Ищи в документации FindXXX - throw new NotImplementedException(); } public UserEntity GetOrCreateByLogin(string login) { //TODO: Это Find или Insert - throw new NotImplementedException(); + lock (userCollection) + { + var user = userCollection.Find(u => u.Login == login).FirstOrDefault(); + if (user == null) + { + user = new UserEntity(Guid.NewGuid()) { Login = login }; + Insert(user); + } + return user; + } + } public void Update(UserEntity user) { //TODO: Ищи в документации ReplaceXXX - throw new NotImplementedException(); + userCollection.ReplaceOne(u => u.Id == user.Id, user); } public void Delete(Guid id) { - throw new NotImplementedException(); + userCollection.DeleteOne(u => u.Id == id); } // Для вывода списка всех пользователей (упорядоченных по логину) @@ -47,7 +76,10 @@ public void Delete(Guid id) public PageList GetPage(int pageNumber, int pageSize) { //TODO: Тебе понадобятся SortBy, Skip и Limit - throw new NotImplementedException(); + var items = userCollection.Find(_ => true).SortBy(u => u.Login).Skip((pageNumber - 1) * pageSize).Limit(pageSize).ToList(); + var totalCount = userCollection.CountDocuments(_ => true); + return new PageList(items, totalCount, pageNumber, pageSize); + } // Не нужно реализовывать этот метод From 04c084c09ede45c003fb0ebd6a6dd013676eabbb Mon Sep 17 00:00:00 2001 From: voinov Date: Tue, 22 Oct 2024 15:26:11 +0500 Subject: [PATCH 2/5] task 2 --- Game/Domain/MongoUserRepositoty.cs | 22 ++++++++++++---------- Tests/MongoUserRepositoryShould.cs | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Game/Domain/MongoUserRepositoty.cs b/Game/Domain/MongoUserRepositoty.cs index 3b0896b..ceee009 100644 --- a/Game/Domain/MongoUserRepositoty.cs +++ b/Game/Domain/MongoUserRepositoty.cs @@ -46,20 +46,22 @@ public UserEntity FindById(Guid id) public UserEntity GetOrCreateByLogin(string login) { - //TODO: Это Find или Insert - lock (userCollection) + var update = Builders.Update.SetOnInsert(u => u.Id, Guid.NewGuid()) + .SetOnInsert(u => u.Login, login); + + var options = new FindOneAndUpdateOptions { - var user = userCollection.Find(u => u.Login == login).FirstOrDefault(); - if (user == null) - { - user = new UserEntity(Guid.NewGuid()) { Login = login }; - Insert(user); - } - return user; - } + IsUpsert = true, + ReturnDocument = ReturnDocument.After + }; + var user = userCollection.FindOneAndUpdate( + u => u.Login == login, update, options + ); + return user; } + public void Update(UserEntity user) { //TODO: Ищи в документации ReplaceXXX diff --git a/Tests/MongoUserRepositoryShould.cs b/Tests/MongoUserRepositoryShould.cs index 6037761..41fa0b9 100644 --- a/Tests/MongoUserRepositoryShould.cs +++ b/Tests/MongoUserRepositoryShould.cs @@ -115,7 +115,7 @@ public void Delete() [Test(Description = "Тест на наличие индекса по логину")] [Explicit("Это дополнительная задача Индекс")] - [MaxTime(15000)] + [MaxTime(35000)] public void SearchByLoginFast() { for (int i = 0; i < 10000; i++) From 92764f2bed93589562981bce7642387007ea6e2b Mon Sep 17 00:00:00 2001 From: voinov Date: Tue, 22 Oct 2024 15:35:38 +0500 Subject: [PATCH 3/5] task 2.3 --- ConsoleApp/Program.cs | 13 ++++++++++++- Game/Domain/MongoUserRepositoty.cs | 19 +++++++++---------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/ConsoleApp/Program.cs b/ConsoleApp/Program.cs index cf39300..5d7906a 100644 --- a/ConsoleApp/Program.cs +++ b/ConsoleApp/Program.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using Game.Domain; +using MongoDB.Driver; namespace ConsoleApp { @@ -12,10 +13,20 @@ class Program private Program(string[] args) { - userRepo = new InMemoryUserRepository(); + userRepo = CreateUserRepository(); gameRepo = new InMemoryGameRepository(); } + private static MongoUserRepository CreateUserRepository() + { + var mongoConnectionString = Environment.GetEnvironmentVariable("PROJECT5100_MONGO_CONNECTION_STRING") + ?? "mongodb://localhost:27017?maxConnecting=100"; + var mongoClient = new MongoClient(mongoConnectionString); + var db = mongoClient.GetDatabase("game-tests"); + db.DropCollection(MongoUserRepository.CollectionName); + return new MongoUserRepository(db); + } + public static void Main(string[] args) { new Program(args).RunMenuLoop(); diff --git a/Game/Domain/MongoUserRepositoty.cs b/Game/Domain/MongoUserRepositoty.cs index ceee009..dd99b4e 100644 --- a/Game/Domain/MongoUserRepositoty.cs +++ b/Game/Domain/MongoUserRepositoty.cs @@ -46,22 +46,21 @@ public UserEntity FindById(Guid id) public UserEntity GetOrCreateByLogin(string login) { - var update = Builders.Update.SetOnInsert(u => u.Id, Guid.NewGuid()) - .SetOnInsert(u => u.Login, login); - - var options = new FindOneAndUpdateOptions + var user = userCollection.Find(u => u.Login == login, new FindOptions { - IsUpsert = true, - ReturnDocument = ReturnDocument.After - }; + Hint = "login_index" + }).FirstOrDefault(); + + if (user != null) return user; + user = new UserEntity(Guid.NewGuid()) { Login = login }; + userCollection.InsertOne(user); - var user = userCollection.FindOneAndUpdate( - u => u.Login == login, update, options - ); return user; } + + public void Update(UserEntity user) { //TODO: Ищи в документации ReplaceXXX From c41daba26930cad49fab6b8875a81c7623a35e98 Mon Sep 17 00:00:00 2001 From: voinov Date: Tue, 22 Oct 2024 15:58:57 +0500 Subject: [PATCH 4/5] task 3 --- ConsoleApp/MongoDbExtensions.cs | 19 +++++++++++++++++++ ConsoleApp/Program.cs | 13 ++++++------- Game/Domain/GameEntity.cs | 8 ++++++++ Game/Domain/MongoGameRepository.cs | 25 +++++++++++++++++++------ Game/Domain/MongoUserRepositoty.cs | 22 +++++++++++----------- Game/Domain/Player.cs | 6 ++++++ Game/Domain/UserEntity.cs | 12 +++++++++++- 7 files changed, 80 insertions(+), 25 deletions(-) create mode 100644 ConsoleApp/MongoDbExtensions.cs diff --git a/ConsoleApp/MongoDbExtensions.cs b/ConsoleApp/MongoDbExtensions.cs new file mode 100644 index 0000000..bef4495 --- /dev/null +++ b/ConsoleApp/MongoDbExtensions.cs @@ -0,0 +1,19 @@ +using Game.Domain; +using MongoDB.Driver; + +namespace ConsoleApp; + +public static class MongoDbExtensions +{ + public static IGameRepository GetGameRepository(this IMongoDatabase mongoDatabase) + { + mongoDatabase.DropCollection(MongoGameRepository.CollectionName); + return new MongoGameRepository(mongoDatabase); + } + + public static IUserRepository GetUserRepository(this IMongoDatabase mongoDatabase) + { + mongoDatabase.DropCollection(MongoUserRepository.CollectionName); + return new MongoUserRepository(mongoDatabase); + } +} \ No newline at end of file diff --git a/ConsoleApp/Program.cs b/ConsoleApp/Program.cs index 5d7906a..b4de68c 100644 --- a/ConsoleApp/Program.cs +++ b/ConsoleApp/Program.cs @@ -13,18 +13,17 @@ class Program private Program(string[] args) { - userRepo = CreateUserRepository(); - gameRepo = new InMemoryGameRepository(); + var db = CreateMongoDb(); + userRepo = db.GetUserRepository(); + gameRepo = db.GetGameRepository(); } - - private static MongoUserRepository CreateUserRepository() + + private static IMongoDatabase CreateMongoDb() { var mongoConnectionString = Environment.GetEnvironmentVariable("PROJECT5100_MONGO_CONNECTION_STRING") ?? "mongodb://localhost:27017?maxConnecting=100"; var mongoClient = new MongoClient(mongoConnectionString); - var db = mongoClient.GetDatabase("game-tests"); - db.DropCollection(MongoUserRepository.CollectionName); - return new MongoUserRepository(db); + return mongoClient.GetDatabase("game-tests"); } public static void Main(string[] args) diff --git a/Game/Domain/GameEntity.cs b/Game/Domain/GameEntity.cs index ec7b5ec..7215be9 100644 --- a/Game/Domain/GameEntity.cs +++ b/Game/Domain/GameEntity.cs @@ -1,18 +1,22 @@ using System; using System.Collections.Generic; using System.Linq; +using MongoDB.Bson.Serialization.Attributes; namespace Game.Domain { public class GameEntity { + [BsonElement] private readonly List players; + [BsonConstructor] public GameEntity(int turnsCount) : this(Guid.Empty, GameStatus.WaitingToStart, turnsCount, 0, new List()) { } + [BsonConstructor] public GameEntity(Guid id, GameStatus status, int turnsCount, int currentTurnIndex, List players) { Id = id; @@ -22,6 +26,7 @@ public GameEntity(Guid id, GameStatus status, int turnsCount, int currentTurnInd this.players = players; } + [BsonElement] public Guid Id { get; @@ -31,10 +36,13 @@ public Guid Id public IReadOnlyList Players => players.AsReadOnly(); + [BsonElement] public int TurnsCount { get; } + [BsonElement] public int CurrentTurnIndex { get; private set; } + [BsonElement] public GameStatus Status { get; private set; } public void AddPlayer(UserEntity user) diff --git a/Game/Domain/MongoGameRepository.cs b/Game/Domain/MongoGameRepository.cs index 86873d4..a811ae3 100644 --- a/Game/Domain/MongoGameRepository.cs +++ b/Game/Domain/MongoGameRepository.cs @@ -7,39 +7,52 @@ namespace Game.Domain // TODO Сделать по аналогии с MongoUserRepository public class MongoGameRepository : IGameRepository { + private readonly IMongoCollection gameCollection; public const string CollectionName = "games"; public MongoGameRepository(IMongoDatabase db) { + gameCollection = db.GetCollection(CollectionName); } public GameEntity Insert(GameEntity game) { - throw new NotImplementedException(); - } + gameCollection.InsertOne(game); + return game; } public GameEntity FindById(Guid gameId) { - throw new NotImplementedException(); + return gameCollection.Find(game => game.Id == gameId).FirstOrDefault(); } public void Update(GameEntity game) { - throw new NotImplementedException(); + var result = gameCollection.ReplaceOne(g => g.Id == game.Id, game); + if (!result.IsAcknowledged) + { + throw new Exception("Update operation was not acknowledged."); + } } // Возвращает не более чем limit игр со статусом GameStatus.WaitingToStart public IList FindWaitingToStart(int limit) { //TODO: Используй Find и Limit - throw new NotImplementedException(); + return gameCollection.Find(game => game.Status == GameStatus.WaitingToStart) + .Limit(limit) + .ToList(); } // Обновляет игру, если она находится в статусе GameStatus.WaitingToStart public bool TryUpdateWaitingToStart(GameEntity game) { //TODO: Для проверки успешности используй IsAcknowledged и ModifiedCount из результата - throw new NotImplementedException(); + var result = gameCollection.UpdateOne( + g => g.Id == game.Id && g.Status == GameStatus.WaitingToStart, + Builders.Update.Set(g => g.Status, game.Status) + ); + + return result.IsAcknowledged && result.ModifiedCount > 0; } } } \ No newline at end of file diff --git a/Game/Domain/MongoUserRepositoty.cs b/Game/Domain/MongoUserRepositoty.cs index dd99b4e..93499ba 100644 --- a/Game/Domain/MongoUserRepositoty.cs +++ b/Game/Domain/MongoUserRepositoty.cs @@ -46,21 +46,21 @@ public UserEntity FindById(Guid id) public UserEntity GetOrCreateByLogin(string login) { - var user = userCollection.Find(u => u.Login == login, new FindOptions + lock (userCollection) { - Hint = "login_index" - }).FirstOrDefault(); + var user = userCollection.Find(u => u.Login == login, new FindOptions + { + Hint = "login_index" + }).FirstOrDefault(); - if (user != null) return user; - user = new UserEntity(Guid.NewGuid()) { Login = login }; - userCollection.InsertOne(user); + if (user != null) return user; + user = new UserEntity(Guid.NewGuid()) { Login = login }; + userCollection.InsertOne(user); - return user; + return user; + } } - - - - + public void Update(UserEntity user) { //TODO: Ищи в документации ReplaceXXX diff --git a/Game/Domain/Player.cs b/Game/Domain/Player.cs index 9c4f838..7e07b2f 100644 --- a/Game/Domain/Player.cs +++ b/Game/Domain/Player.cs @@ -1,4 +1,5 @@ using System; +using MongoDB.Bson.Serialization.Attributes; namespace Game.Domain { @@ -7,27 +8,32 @@ namespace Game.Domain /// public class Player { + [BsonConstructor] public Player(Guid userId, string name) { UserId = userId; Name = name; } + [BsonElement] public Guid UserId { get; } /// /// Снэпшот имени игрока на момент старта игры. Считайте, что это такое требование к игре. /// + [BsonElement] public string Name { get; } /// /// Ход, который выбрал игрок /// + [BsonElement] public PlayerDecision? Decision { get; set; } /// /// Текущие очки в игре. Сколько туров выиграл этот игрок. /// + [BsonElement] public int Score { get; set; } } } \ No newline at end of file diff --git a/Game/Domain/UserEntity.cs b/Game/Domain/UserEntity.cs index 671510c..0f65329 100644 --- a/Game/Domain/UserEntity.cs +++ b/Game/Domain/UserEntity.cs @@ -1,19 +1,23 @@ using System; +using MongoDB.Bson.Serialization.Attributes; namespace Game.Domain { public class UserEntity { + [BsonConstructor] public UserEntity() { Id = Guid.Empty; } + [BsonConstructor] public UserEntity(Guid id) { Id = id; } - + + [BsonConstructor] public UserEntity(Guid id, string login, string lastName, string firstName, int gamesPlayed, Guid? currentGameId) { Id = id; @@ -24,6 +28,7 @@ public UserEntity(Guid id, string login, string lastName, string firstName, int CurrentGameId = currentGameId; } + [BsonElement] public Guid Id { get; @@ -34,19 +39,24 @@ public Guid Id /// /// Логин должен быть уникальным в системе. Логин решено не делать идентификатором, чтобы у пользователей была возможность в будущем поменять логин. /// + [BsonElement] public string Login { get; set; } + [BsonElement] public string LastName { get; set; } + [BsonElement] public string FirstName { get; set; } /// /// Количество сыгранных игр /// + [BsonElement] public int GamesPlayed { get; set; } /// /// Идентификатор игры, в которой этот пользователь участвует. /// Нужен, чтобы искать игру по первичному индексу, а не по полю Games.Players.UserId. В частности, чтобы не создавать дополнительный индекс на Games.Players.UserId /// + [BsonElement] public Guid? CurrentGameId { get; set; } // Для того, чтобы использовать индекс по Game.Id, а не искать игру по индексу на Game.Players.UserId public override string ToString() From 7dd28872ba577e1ac73533a909cc0e4b0cb5b189 Mon Sep 17 00:00:00 2001 From: voinov Date: Tue, 22 Oct 2024 16:19:11 +0500 Subject: [PATCH 5/5] fix bug --- ConsoleApp/Program.cs | 3 ++- Game/Domain/GameEntity.cs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ConsoleApp/Program.cs b/ConsoleApp/Program.cs index b4de68c..c2a6234 100644 --- a/ConsoleApp/Program.cs +++ b/ConsoleApp/Program.cs @@ -23,7 +23,7 @@ private static IMongoDatabase CreateMongoDb() var mongoConnectionString = Environment.GetEnvironmentVariable("PROJECT5100_MONGO_CONNECTION_STRING") ?? "mongodb://localhost:27017?maxConnecting=100"; var mongoClient = new MongoClient(mongoConnectionString); - return mongoClient.GetDatabase("game-tests"); + return mongoClient.GetDatabase("game"); } public static void Main(string[] args) @@ -86,6 +86,7 @@ private bool TryJoinToGame(GameEntity game, UserEntity user) user.CurrentGameId = game.Id; userRepo.Update(user); + gameRepo.Update(game); return true; } diff --git a/Game/Domain/GameEntity.cs b/Game/Domain/GameEntity.cs index 7215be9..70b08a1 100644 --- a/Game/Domain/GameEntity.cs +++ b/Game/Domain/GameEntity.cs @@ -34,6 +34,7 @@ public Guid Id private set; } + [BsonElement] public IReadOnlyList Players => players.AsReadOnly(); [BsonElement]