C
C#10mo ago
Aljo_M

❔ MongoDb transactions in Asp.net core web api

Hello, i need some advice on what is the best thing to do. I am building asp.net core Web API with MongoDb identity. The logic goes like this controller > service > dbManagers. I want to implement transactions so i can revert db operation if anything goes wrong. Now i dont want to repeat myself and add using (var session = await _dbClient.StartSessionAsync()) in every method, instead i created a middleware:
public class DbTransactionMiddleware
{
private readonly RequestDelegate _next;
private readonly DbClient _dbClient;

public DbTransactionMiddleware(RequestDelegate next, DbClient dbClient)
{
_next = next;
_dbClient = dbClient;
}

public async Task InvokeAsync(HttpContext context)
{
using (var session = await _dbClient.StartSessionAsync())
{
session.StartTransaction();

try
{
await _next(context);
await session.CommitTransactionAsync();
}
catch (Exception ex)
{
await session.AbortTransactionAsync();
throw;
}
}
}
}
public class DbTransactionMiddleware
{
private readonly RequestDelegate _next;
private readonly DbClient _dbClient;

public DbTransactionMiddleware(RequestDelegate next, DbClient dbClient)
{
_next = next;
_dbClient = dbClient;
}

public async Task InvokeAsync(HttpContext context)
{
using (var session = await _dbClient.StartSessionAsync())
{
session.StartTransaction();

try
{
await _next(context);
await session.CommitTransactionAsync();
}
catch (Exception ex)
{
await session.AbortTransactionAsync();
throw;
}
}
}
}
But how can i access session object in my service. And what would be the better way to do something like this. Like what is best practice?
31 Replies
Unknown User
Unknown User10mo ago
Message Not Public
Sign In & Join Server To View
Aljo_M
Aljo_M10mo ago
so i should put
using (var session = await _dbClient.StartSessionAsync())
using (var session = await _dbClient.StartSessionAsync())
in all my service methods?
Unknown User
Unknown User10mo ago
Message Not Public
Sign In & Join Server To View
Aljo_M
Aljo_M10mo ago
okay sorry i misunderstood so, i should delete that middleware and register it as a scoped service, then inject it in lets say my UserService? let me just give you example:
public async Task<BaseResponse> RemoveUserAsync(string userId)
{
var user = await _userManager.GetByIdAsync(userId);
if (user == null)
throw new UserNotFoundByIdException(userId);

var removeFromCompanyResult = await
_companyManager.RemoveUserFromAllCompaniesAsync(user.UserName);
if (!removeFromCompanyResult.IsAcknowledged || removeFromCompanyResult.ModifiedCount > 1)
throw new DatabaseErrorException($"Failed to remove user from companies that have it. User was not removed, run it again.", removeFromCompanyResult);

var deleteUserResult = await _userManager.RemoveOneAsync(userId);
if (!deleteUserResult.IsAcknowledged || deleteUserResult.DeletedCount != 1)
throw new DatabaseErrorException($"Failed to remove user.", deleteUserResult);

return BaseResponse.Succeeded("User deleted successfully");
}
public async Task<BaseResponse> RemoveUserAsync(string userId)
{
var user = await _userManager.GetByIdAsync(userId);
if (user == null)
throw new UserNotFoundByIdException(userId);

var removeFromCompanyResult = await
_companyManager.RemoveUserFromAllCompaniesAsync(user.UserName);
if (!removeFromCompanyResult.IsAcknowledged || removeFromCompanyResult.ModifiedCount > 1)
throw new DatabaseErrorException($"Failed to remove user from companies that have it. User was not removed, run it again.", removeFromCompanyResult);

var deleteUserResult = await _userManager.RemoveOneAsync(userId);
if (!deleteUserResult.IsAcknowledged || deleteUserResult.DeletedCount != 1)
throw new DatabaseErrorException($"Failed to remove user.", deleteUserResult);

return BaseResponse.Succeeded("User deleted successfully");
}
this is in my UserService.cs how to implement it here?
Unknown User
Unknown User10mo ago
Message Not Public
Sign In & Join Server To View
Aljo_M
Aljo_M10mo ago
i actually have dbClient and all dbManagers registered as singletons
Unknown User
Unknown User10mo ago
Message Not Public
Sign In & Join Server To View
Aljo_M
Aljo_M10mo ago
i am a complete beginner, sorry but okay UserService is scoped
Unknown User
Unknown User10mo ago
Message Not Public
Sign In & Join Server To View
Aljo_M
Aljo_M10mo ago
what would you do?
Unknown User
Unknown User10mo ago
Message Not Public
Sign In & Join Server To View
Aljo_M
Aljo_M10mo ago
why is UserService scoped? okay, i can make it transient
Unknown User
Unknown User10mo ago
Message Not Public
Sign In & Join Server To View
Aljo_M
Aljo_M10mo ago
i was asking what is better to do instead of that, in case that is not common practice?
Unknown User
Unknown User10mo ago
Message Not Public
Sign In & Join Server To View
Aljo_M
Aljo_M10mo ago
i saw some example, and didnt want to use start transaction in all my methods
Unknown User
Unknown User10mo ago
Message Not Public
Sign In & Join Server To View
Aljo_M
Aljo_M10mo ago
yes i understand that
Unknown User
Unknown User10mo ago
Message Not Public
Sign In & Join Server To View
Aljo_M
Aljo_M10mo ago
yes ofcourse
Unknown User
Unknown User10mo ago
Message Not Public
Sign In & Join Server To View
Aljo_M
Aljo_M10mo ago
yes okay
Unknown User
Unknown User10mo ago
Message Not Public
Sign In & Join Server To View
Aljo_M
Aljo_M10mo ago
i get it
Unknown User
Unknown User10mo ago
Message Not Public
Sign In & Join Server To View
Aljo_M
Aljo_M10mo ago
sure okay thanks
Unknown User
Unknown User10mo ago
Message Not Public
Sign In & Join Server To View
Aljo_M
Aljo_M10mo ago
so i created a scoped service and kept the middleware. in the middleware i run the scoped service to create transaction and then set the session object into context like this:
context.Items["MongoSession"] = session;
context.Items["MongoSession"] = session;
this is just to play around. what do you think about it?
Unknown User
Unknown User10mo ago
Message Not Public
Sign In & Join Server To View
Aljo_M
Aljo_M10mo ago
😂 okay thank you :)
Accord
Accord10mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.