Code Monkey home page Code Monkey logo

Comments (5)

nolisj avatar nolisj commented on July 17, 2024

I've just recently come across this library and still trying to understand the whole thing. But given that, here's my two cents on some of your concerns based on my current understanding.

  1. The Fund Transfer scenario (ie. TransferMoney and WithdrawMoney; btw, I think you meant DepositMoney and WithdrawMoney since those are what's involved in a fund transfer). In this case, based on the sample code of mehdime, the closest steps will probably be to do the ff:
  • Create the DepositMoney method (with its own SaveChanges call since I think this is a distinct operation on its own);
  • Create the WithdrawMoney method (with its own SaveChanges call since I think this is a distinct operation on its own too);
  • Create a FundTransfer method that calls the DepositMoney and WithdrawMoney methods inside. The FundTransfer method will create its own scope which becomes the parent scope of the two (2) "child" methods. This way the commit and the rollback will be consistent.

That's the way I understand it. I haven't tested it yet but theoretically it should work.

My main concern however is different. It's related to this example code:

var user = dbContextScope.DbContexts.Get.Set.Find(userId);

(note: angle brackets are being removed in the sample code above; MyDbContext and User are missing);

It is the fact that in a Service method I can access a different DbContext and therefore directly call the Find, for example, on the entity on that different context, even if my design involves a repository. Ideally, if I wrapped my data access calls in a Repository, all of my data access calls should be encapsulated in that repository class and my service class simply calls the methods in the Repository. I think the way it is implemented right now is a bit leaky.

I wonder what will happen if the DbContexts collection is hidden from the caller.

Thanks.

from dbcontextscope.

ryanlangton avatar ryanlangton commented on July 17, 2024

I find readonly scopes to be particularly "magic" in their usage.

 public UserDto GetUser(Guid userId)
 {
    using (var dbContextScope = _dbContextScopeFactory.CreateReadOnly())
    {
        var user = _userRepository.Get(userId);
        var userDto = _mapper.Map<UserDto>(user);
        return userDto;
    }
}

Why was I required to create a scope? It's not used explicitly anywhere. This leads to fragile code where refactorings can cause run time errors.

from dbcontextscope.

MarcusChamberlain avatar MarcusChamberlain commented on July 17, 2024

For anyone coming across this question now, an answer the question by carbonrobot.

To transfer money, you would not have:

_service.TransferMoney(2000, account);
_service.WithdrawMoney(1200);

because as he points out, each of those two services would each call SaveChanges(), creating a request that is not atomic and can't be rolled back. Instead the way to handle this would be the following:

using (var dbContextScope = _dbContextScopeFactory.Create())
{
  TransferMoney(2000, account);
  WithdrawMoney(1200);
  dbContextScope.SaveChanges();
}

void TransferMoney(int amount, Account account)
{
  using (var dbContextScope = _dbContextScopeFactory.Create())
  {
    //transfer money...

    dbContextScope.SaveChanges();
  }
}

void WithdrawMoney(int amount)
{
  using (var dbContextScope = _dbContextScopeFactory.Create())
  {
    //withdraw money...

    dbContextScope.SaveChanges();
  }
}

TransferMoney and Withdraw money still include their own SaveChanges() calls (because they can still be called on their own), but the SaveChanges method is ignored because DbContextScope detects an ambient DbContext that has already been created! This means you can avoid situations of messy and confusing DbContext scopes.

In comparison to the answer by nolisj. In that example you would have seperate method called FundTransfer with its own SaveChanges() call, but they wouldn't be able to implement TransferMoney or WithdrawMoney because they already implement their own DbContexts and call SaveChanges(). That's where without DbContextScope it starts to get messy.
(Edit: I misread the post by nolisj, his method is correct)

Likewise, in carbonrobots final example:

var transactions = new List<Transaction>(){
    new TransferMoneyTransaction(2000, account),
    new WithdrawMoneyTransaction(1200)
};
_service.Commit(transactions);

Would only work if TransferMoneyTransaction and WithdrawMoneyTransaction are designed not to be used on their own and do not call Commit/SaveChanges themselves, which could lead to a messy codebase when developers still need to use those actions on their own.

You can also see the power of this in the CreateListOfUsers method in the sample project where a service can either call CreateUser() on its own and the user will be persisted back to the database, or they can create a new ambient DbContextScope and then call CreateUser several times in a loop - it's the exact same method being called, but now because the ambient scope exists CreateUser() wont actually save the changes back to the DB - it's only once the loop is completed that the service then calls SaveChanges() to persist the updates in a single request for all the new users.

Hope that helps!

from dbcontextscope.

nolisj avatar nolisj commented on July 17, 2024

Boolean0101,

Thanks for your input. If you read closely, your answer is the same as mine. Your two independent "child" functions (TransferMoney and WithdrawMoney) become contained in a "parent" (aka container) function. It's just that you did not name your parent function. In my case, the DepositMoney and WithdrawMoney are the child functions which are called in the FundTransfer function. Each child function is independent (since you can deposit or withdraw separately) and can commit their own individual operations if called individually but can also participate in the fund transfer scenario if you so desire (which means there's a DbContextScope in the FundTransfer parent function also) . I hope that clarifies what I meant.

from dbcontextscope.

MarcusChamberlain avatar MarcusChamberlain commented on July 17, 2024

Hi nolisj

Right you are - I misread your last bullet point as not calling SaveChanges on itself and thought you were relying on the child methods to persist changes, which I realize wasn't what you meant. I think my post still adds some details to the answer though, so I'll update it to make it more clear.

from dbcontextscope.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.