UnitOfWork Implementation
Asked Answered
V

1

5

Ive been able to implement a little cool unit of work to work with entity framework.

I came up with ..

public class UnitOfWork : IUnitOfWork
    {
        private Database _database;
        private IDatabaseFactory _databaseFactory;

        private DbTransaction transaction;

        public UnitOfWork(IDatabaseFactory databaseFactory)
        {
            _databaseFactory = databaseFactory;
            _database = Database;

            transaction = _database.Database.Connection.BeginTransaction();
        }

        public Database Database
        {
            get { return _database ?? (_database = _databaseFactory.Get()); }
        }

        public void Dispose()
        {
            try
            {
                _database.SaveChanges();
                transaction.Commit();
            }
            catch (Exception ex)
            {
                transaction.Rollback();
            }
        }
    }

I am pretty sure everyone is jealous of this unit of work now. (Kidding)

But i have a little design problem in this service layer.

public class JournalService : IJournalService
    {
        IJournalRepository _journalRepository;

        public JournalService(IJournalRepository journalRepository)
        { 
            _journalRepository = journalRepository;
        }

        public void AjouterJournal(Journal j)
        {
           [B]using (IUnitOfWork uow = new UnitOfWork())[/B]
            {
                var journal = new Journal();
                journalRepository.AddJournal(journal);

            }
        }
    }

The problem is the unit of work need a database injection so i cant create an instance of it. I can't provide an unit of work directly in the service layer because it would make no sense since the unit of work need to be disposable.

And because i use repository to add my stuff there no need to access the unit of work directly, the saving will occur automaticly when it will be disposed anyway.

I could inject the IDatabaseFactory in my service layer but the idea is to not use it there. Actually the service layer shouldnt know about it.

How about an UnitOfWork factory ?

Any ideas or suggestion on how i can fix this?

Thanks.

Valletta answered 5/2, 2011 at 15:53 Comment(2)
A UnitOfWork factory seems to work. But the problem then goes to how you provide the IDatabaseFactory to your UnitOfWorkFactory.Langlauf
DI injection in constructor ?Valletta
U
7

You should inject UnitOfWork into service if you want to use your current architecture. Your service will not have inner (hidden) dependency on UnitOfWork implementation and it will be better testable. It goes hand in hand with many principles of object oriented architecture.

Other thing is that this implementation is only usable for simple CRUD operations. In more complex services you will end up with composition of multiple operations (possibly from multipe services) which will each operate with UnitOfWork. Calling multiple SaveChanges (and transactions) in one business operation is probably not what you usually want - in such case you want to call SaveChanges only once from some top level service or from service's caller. Typical scenario is that single business operation has one unit of work with one transaction but you can do a lot of service's operations as part of this business operation.

Another implication is construction of your Repositories. They probably need access to Database, don't they? So you probably already inject UoW into repository constructor. If you do this you can avoid relation between UoW and basic services at all.

Universe answered 6/2, 2011 at 12:39 Comment(4)
Could you explain a bit more the second part (about implementation is only usable for simple CRUD...) I am currently injecting the UoW and Repositories in my service layer. is there anything wrong with that ? normally the UoW start when the business operation is started and end after the business operation end in the service. I might end up calling others services but thoses services will only have relation with my business operation and those services cant be accessible through the controller directly. The controller will only use one service which is probably the service in the actual contexValletta
Idea behind second part is composition. If your business operation is AddJournal you are OK with wrapping UoW and SaveChanges into the service operation. But if your business operation is DoSomethingBig which consists of several AddJournal calls you will don't want to call SaveChanges and Commit in each AddJournal call. You will want to call it only once after all AddJournal calls.Universe
Good point dude i didnt know about that actually by callig add journal you do use the unit of work which isnt a good idea but why would you call addjournal from service if you can call repository.addjournal instead? I believe service class do more than crud operations. Correct me if i am wrong isnt the crud job mostly done by repository? if i ever have to add a journal i wont use the service method. Otherwise Where do you suggest to put the unit of work implementation ? Which level ?Valletta
Service operation usually does more work then just calling method on repository - there can be for example some business validation, security checks, logging etc. That is the reason why you sometimes want to compose calls to service operations (reuse them) instead of calling repository methods. In such case you can do it by dirty way - extract UoW from service layer and call it on layer using your services. Tidy way is to define two levels of service layers - low level without UoW calls and Facede level (reusing low level services) with UoW calls. But it is also about size of your project.Universe

© 2022 - 2024 — McMap. All rights reserved.