Why I Use The Unit of Work Pattern With EF Core | Clean Architecture

  Рет қаралды 70,611

Milan Jovanović

Milan Jovanović

Күн бұрын

Пікірлер: 190
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Get the source code for this video for FREE → the-dotnet-weekly.ck.page/unit-of-work Want to master Clean Architecture? Go here: bit.ly/3PupkOJ Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt
@AnotherFancyUser
@AnotherFancyUser 2 жыл бұрын
Hey Milan, how are you? Great content, please keep doing these videos!. I'm seeing a lot of questions that can be answered by going a little bit lower as to why we use patterns like these. Would you be interested in creating a video explaining SOLID with real examples? I know you make content for Ssr and up, but this can help a lot of people that maybe don't know about SOLID, and I think SOLID is the foundation to modern software, to have maintainable, testable, readable code. Also... which one do you like the most? a lot of people likes SRP, I do too but I love Dependency Inversion even more.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
@@AnotherFancyUser That's a nice idea, I'll add that topic to my list. Gotta think of a clever way to present the topic
@efimov90
@efimov90 Жыл бұрын
kzbin.info/www/bejne/rH_Cm2R4qJV0grc but actually at this point when you go to 46 line - you have update sql log in console. So there are update call to database. And after at 48 we have new insert sql call. Should't we use transaction? And there are no locks at database, what if you have 2 requests in same time that will change diffent properties? And still i see a problem if you will use several UnitOfWorks at the same time. As example in desktop application. Shouldn't you nest repositories inside of Unit Of Work? And several scopes.
@YehorBachurinDev
@YehorBachurinDev 8 ай бұрын
Thank you for providing the code for free
@cleitonosti1756
@cleitonosti1756 2 жыл бұрын
An excellent video, last week I ended up implementing it almost similar to what you show in the video. Although it is implicit that EF already has a Unit of Work behind it, many developers confuse the fact that the repository pattern is per entity, thinking that when giving savechanges only the entity itself will be saved. So the implementation of the pattern in this way is very clear and objective.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I like to have one way to things, and UoW achieves this easily
@stunna4498
@stunna4498 2 жыл бұрын
I also enjoy using unit of work with the transaction pattern where you would tell if you want to use a transaction or not . In my case i used it by default so you could call multiple save changes and unless everything went well nothing would be commited ( it was a requirement to be like this)
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
That's a great use case and it makes a lot of sense
@kvloover
@kvloover 2 жыл бұрын
uow and repositories get alot of undeserved hate these days. If you think UoW and Repo's are going to make your life easier use them. If you think you can manage and do without, don't use them. People should stop forcing their opinions on others. Good intro into why you use them and how you do so.
@AnotherFancyUser
@AnotherFancyUser 2 жыл бұрын
"these days" since they existed, but some people don't understand why they hate it. For example IStudentRepository, "you are just creating an abstraction on top of an abstraction, dbContext is a Unit of Work and DbSet is a Repository! harrr harr harrr!" (they say). But the day you want to change data providers, you have the repository abstraction and is only a matter of creating a new class that satisfies implementation detail for that new Data provider. Not only that, whoever uses your repositories don't use concrete classes, it uses an abstraction and that high level module wont depend upon a concretion ( new StudentRepository() ) but the other way around, these complies D in SOLID (High-level modules should not import anything from low-level modules, both should depend on abstractions (e.g., interfaces). Abstractions should not depend on details, details (concrete implementations) should depend on abstractions), which a lot of people don't use them and at the same time they want testable, maintainable, readable code (Insert John Travolta pulp fiction meme). Of course, make the interface as generic as possible, I mean, don't marry to concretions or expressions that maybe a data provider cant use (LINQ to SQL). But in these days EF is so big, so good (that's what she said) that we have NuGet packages to work with a lot of data providers out there with EF. Anyways, is all fine and dandy to know different strategies and design patterns, but never forget why they exist, they exist because there is a common underlying issue that can be solved by a particular design pattern but also these design patterns usually follow SOLID.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I try to be careful when talking about these topics. I always talk from a personal perspective, _how_ and _why_ I use certain patterns even though a large portion of the dev community dislikes them. At the end of the day, I'm happy with the choices I make on my projects and I'm yet to run into problems because of it.
@pilotboba
@pilotboba 2 жыл бұрын
@@AnotherFancyUser How many times have you replaced the database provider? EF even lets you do this. Sure, if you wrap EF you can change to say NHibernate. But who does this? YGNI
@psyaviah
@psyaviah 2 жыл бұрын
​@@AnotherFancyUser for most projects, this never happens in their lifespan. Be pragmatic IU'd say - you can then just change the actual provider you connect to EFCore when setting it up. Yes, yes, I know you will have to change some command handler things if the db doesn't support some things. But you'd have to rewrite all implementations of the repos & unitofwork too. Which is a ton more work. (I)DbContext effectively is flexible enough now, to change the dbprovider. And with the Command/Query pattern, well, change the query/db specifics. You have integration tests in place as well (I hope) to an actual db so you then can verify the outcome and see if it's still the same. So, personally, I see no benefit here. I used to see a benefit when this was all locked away and not open to change - but we moved on a long time ago. DbContext is flexible enough in 99% of the situations and I recommend people learning the intricate nature of it instead of using old patterns that add no value. So I don't like UoW or Repo-patterns. If you need more complicated setup, then you probably would write your own kind of ORM & db-setup even when you have to deal with very complex and specific stuff. Yeah, no repo-patterns, the logic belongs in the command. Even if it's kind of duplicate at first. You never know how your business logic may change. Sometimes code looks the same and you abstract it away in a repo or service, but is it actually the same business use case? No. So when something changes, you then have to split that. I like it when I have to change one thing only in the command-handler, and I know that affects then only one command handler.
@carmineos
@carmineos 2 жыл бұрын
It would be nice to see this in action without EF, with things like Dapper or simply with repositories and ITransaction
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
You would have to implement your own Identith Map, would be interesting surely
@graylee9567
@graylee9567 Жыл бұрын
Sometimes its required to perform several save points/SaveChanges() calls and wrap it into transaction, EF is already implemented as Unit Of Work but because there is no control over commands order (DELETE/UPDATE/INSERT) in that case UnitOfWork pattern should have some Begin/Commit/Rollback transaction inside.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
What do you mean there is no order? I'd say there's a very logical order in how EF executes DELETE/UPDATE/INSERT commands
@graylee9567
@graylee9567 Жыл бұрын
@@MilanJovanovicTech I mean in case you need that Add/Remove/Update to be executed in the same order they were called from code you need several SaveChanges and wrap it into transaction
@saidhalloun8640
@saidhalloun8640 7 ай бұрын
Great explication!, im new at Clean architecture and im learning a lot with your videos. Thanks Milan!
@MilanJovanovicTech
@MilanJovanovicTech 7 ай бұрын
Happy to help!
@MarcusKaseder
@MarcusKaseder 2 жыл бұрын
I'm a bit confused about your overall approach. You said one reason to use UoW is that you don't want do pollute your Application layer with entity framework. IoC magic. In another video, you've mentioned that you use the repository pattern only for edit purposes. Repositories read only data that are required for editing. For queries, you tend to use the dbcontext directly. Is that correct? Am I missing something? If so, you have to "pollute" your application layer with entity framework to do your queries, right?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
| For queries, you tend to use the dbcontext directly. Yes! I just abstract it behind an interface. I'll make a video explaining the idea here.
@adrielairaldo
@adrielairaldo Жыл бұрын
Hello Milan! First of all thanks for the material you share. I am interested in decoupling the Unit of Work with the Repositories, in terms of having to have the reference of each repository in the unit of work (it is tedious to have to update the UoW for each new repository), and I see that in your example you are doing it. The idea I'm implementing now is through Dependency Injection in Scoped mode, so that repositories can consume the UoW just like higher order services (like Handlers, for example). I would like to know your thoughts on this :) Thanks!
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
I think it's justified, as long as you accept that all repository methods will now have to complete the UoW
@amantinband
@amantinband 2 жыл бұрын
I keep going back and forth about both the repository and UoW patterns. It adds a cognitive load for engineers onboarding the project. Especially since these patterns usually come on top of CQRS, Clean Architecture, and DDD. Is it worth it? Do these patterns add enough value to justify their usage?
@amantinband
@amantinband 2 жыл бұрын
Great video non the less. Thank you, Milan!
@pilotboba
@pilotboba 2 жыл бұрын
I don't think it is. Also, you can mock dbcontext just as well as your customer unit of work if you provide an IDbcontext in your application layer. Yes, before interceptors were a thing the unit of work was a nice way to provide those types of features. But, even without that, you can add those types of things to your DbContext SaveChanges override too. Of course, YMMV.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I don't get where these complexity/cognitive-load arguments come from. It's one interface. With one method. How hard can it be? You want to persist changes at the end of your business operation, and there's only one way to do it with this design. I think that's as simple as we can get. Curious to hear what you think Amichai.
@alexkovanev1425
@alexkovanev1425 2 жыл бұрын
@Amichai Mantinband Short answer: if you don't wish to tie your application layer to the specific data access implementation, then repositories + UoW is an option. Real life example: you work for an organization which has a lot of different microservices leveraging both relational and nosql databases. For each microservice you have the same standards, like Clean Architecture, CQRS and so on. You expect the similar common code base, in particular for infrastructure both SQL Server + EF Core and MongoDB + MongoDB Driver respectively. (I'm not discussing + and - of that 'common code' approach here). Besides that, one day you may want to change the db. How would you achieve this w/o repositories and UoW?
@psyaviah
@psyaviah 2 жыл бұрын
@@alexkovanev1425 for most projects, this never happens in their lifespan. Be pragmatic - you can then just change the actual provider you connect to EFCore when setting it up. Yes, yes, I know you will have to change some command handler things if the db doesn't support some things. But you'd have to rewrite all implementations of the repos & unitofwork too. Which is a ton more work. (I)DbContext effectively is flexible enough now, to change the dbprovider. And with the Command/Query pattern, well, change the query/db specifics. You have integration tests in place as well (I hope) to an actual db so you then can verify the outcome and see if it's still the same. I see no benefit here. I used to see a benefit, but it's totally gone for me now. DbContext is flexible enough in most situations and I recommend people learning the intricate nature of it instead of using old patterns that add no value. So I don't like UoW or Repo-patterns. Yeah, no repo-patterns, the logic belongs in the command. Even if it's kind of duplicate at first. You never know how your business logic may change. Sometimes code looks the same and you abstract it away in a repo or service, but is it actually the same business use case? No. So when something changes, you then have to split that. I like it when I have to change one thing only in the command-handler, and I know that affects then only one command handler.
@vamvdotnet
@vamvdotnet 2 жыл бұрын
Excellent video, Milan! Thank you so much for sharing with us how your implementation of UoW + EF Core is!😀
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thanks! 😁
@sky3913
@sky3913 6 ай бұрын
Hi Milan, thanks for sharing. I have some questions 1. Should we also implement IDisposable in IUnitOfWork? 2. I see you defined IUnitOfWork in Domain.Repositories, but in other video, you defined in Application.Data, which one is appropriate and why? 3. I see in some resources, the repositories are defined in UnitOfWork, where do you define them? 4. I also saw in other video you're using TransactionScope, what's the difference with IDbContextTransaction for handling Transaction?
@MilanJovanovicTech
@MilanJovanovicTech 6 ай бұрын
1. If you're creating resources manually 2. That's for you to decide. Which one makes more sense, and why? 3. Separately 4. learn.microsoft.com/en-us/dotnet/framework/data/transactions/implementing-an-implicit-transaction-using-transaction-scope
@juankasem4911
@juankasem4911 2 жыл бұрын
We would like to make you a video about the Error response class & the FluentValidation implementation
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Check this out: kzbin.info/www/bejne/bmbHqaqaba2te80
@stef2528
@stef2528 4 ай бұрын
Thank's for this very competent nice presented and insightful explanations! I will definitlely recommend you anyone I know who could be intrerested in your channel.
@MilanJovanovicTech
@MilanJovanovicTech 4 ай бұрын
Much appreciated!
@fernandocalmet
@fernandocalmet 2 жыл бұрын
This is how we could add an optional parameter in the UnitOfWork to capture the Creator Guid to be able to set it in the audit columns. Excellent video Milan, Thanks! 😃
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Or you can inject a service and resolve the userId
@taleslopes9421
@taleslopes9421 Жыл бұрын
Thanks for sharing knowledge, my problem if UOW and Repository wrapping EF is to make queries with eager loading (Include), specific filters and group by, I have to make a method that will be used just 1 time, turning my repositories into a giant bloatware, there is any way to solve this and still detach the EF dependencies in the application layer?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
From what you're writing, you are probably using filtering/group by on the read side. I would suggest not using repositories to wrap Read queries. If you still want to abstract away EF for those use cases, you can create some simple abstraction like IDataRequest which has a generic request/response
@alexkovanev1425
@alexkovanev1425 2 жыл бұрын
If your ApplicationDBContext class starts implementing IUnitOfWork, you don't need to have an extra UnitOfWork class.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Agreed, but then you'll start putting a lot of logic inside of the DbContext. And I like to keep it clean. That being said, I've used the approach you're describing and it works great.
@InfinityFnatic
@InfinityFnatic 2 жыл бұрын
DbContext already implements a heavily abstracted Unit of Work, Repository and Transaction pattern, no need to abstract it even further and throw a leaky API at your team. It is beyond me why people are sticking to the custom repository and unit of work dogma.
@DoctorMatt6
@DoctorMatt6 2 жыл бұрын
@@InfinityFnatic I think it is much easier to unit test your business logic with repository/uow pattern, but I don't see any more benefits from using it
@juankasem4911
@juankasem4911 2 жыл бұрын
@@DoctorMatt6 And what if you need a shared dbcontext among multiple repositories that is needed to perform a single transaction(ex: case to update multiple database entities: update bank accounts for sender and recipient nd update the cash transfers tables, etc... )
@krccmsitp2884
@krccmsitp2884 2 жыл бұрын
Milan applied the composition over inheritance pattern here for better decoupling.
@DJohn001
@DJohn001 2 жыл бұрын
Sorry, probably I missed the answer about the question you give yourself. Why do you use Unit of Work? You have shown that is can be used to sent the outboxmessage enz. but that's not needed anymore. My personal opinion is that it is a nice pattern to know in a few specific cases but I think it's usage is most of the time something of the past. As you did before, you could add this example functionality better/also in a different way by using EF middleware (I don't know if that's the right name). To me it looks like EF itself is already a Unit of Work pattern. So, why duplicate that?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I'm sure you listened to what I said in the video, but let me reiterate: - UoW represents a transaction boundary - Exposes only one way to persist changes to the database - Allows for flexible design, where I can add logic before/after saving changes
@psyaviah
@psyaviah 2 жыл бұрын
@@MilanJovanovicTech but, - multiple DbContexts are possible with multiple DbSets if need be to seperate it if you'd want to have that boundary; or via an interface if you'd like - DbContext does indeed expose more ways to persist to the db, but that's the power of it. If you want to take that away, and you'll need it someday, you'll have to duplicate it again in the UoW class. Then others can use it as well and you'll be introducing exactly the same kind of "multiple ways to persist to the database" - EF Core 6 & 7 are enormously flexible in their design, I'd say more flexible even. In this example you only moved code from EF Core interceptors to your own baked UoW that worked before. I don't see tha argument that UoW is more flexible than EF Core's DbContext then ;).
@sorteslyngel2k
@sorteslyngel2k 2 жыл бұрын
Hi Milan, thanks for the content. Really appreciate it. Do you know if using saveChangesAsync like this actually acts as a transaction against the db? That is, if one of the inserts fails, they are all rolled back?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Yes learn.microsoft.com/en-us/ef/core/saving/transactions#default-transaction-behavior
@psyaviah
@psyaviah 2 жыл бұрын
@@MilanJovanovicTech Indeed, but your db needs to support this. So talk to your db admin first to make sure this scenario is supported.
@dejansavanovic4476
@dejansavanovic4476 Жыл бұрын
For web API-s do you prefer to use EntityFramework with connected change tracking or with disconnected change tracking?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
When I want to insert/update/delete, then I want change tracking enabled
@ismaelperezmesa524
@ismaelperezmesa524 2 жыл бұрын
@Milan Jovanović Thank you so much for all your effort by sharing your knowledge with us, every lesson is greater. I would like to ask you something that is really urgent to me: How can I dockerize a project like this(ddd clean arch) we are studying with you, for deploying as a container on a cloud? Please, Can you or another people here, share the way or an example to achieve it? Thanks a lot, you're amazing!
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Releasing a CICD video on Friday. If that's too late, I can find some resources for you
@ismaelperezmesa524
@ismaelperezmesa524 2 жыл бұрын
@@MilanJovanovicTech Thanks a lot! I really appreciate you for answering. I could wait until Friday, but really any advance before would be useful and saving for me. Thank you again!
@mahfoudbouabdallah6286
@mahfoudbouabdallah6286 2 жыл бұрын
You mentioned that postgreSql is smarter than SQL Server can you please explain the advantages about using postgreSql over SQL Server
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I was talking the EF providers for SQL Server/PostgreSQL. The PostgreSQL provider handles change tracking better, from my experience.
@02244
@02244 2 жыл бұрын
EF Core already implements UnitOfWork and Repository patterns. You just created an abstraction on top of the abstraction and lose all the benefits of EF Core, such as working with IQueryable, lazy loading & etc.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
How am I losing any benefits of EF Core if I'm using EF in the implementation? I would be careful calling lazy load an advantage, it introduces more problems than it solves.
@nanvlad
@nanvlad 2 жыл бұрын
It looks like an abstraction over abstraction. The only worthy case I see in using additional interface is when you don't want to expose dbContext for other assemblies and want to keep all db logic inside a single ef (persistence/dal) library. But in current implementation we just hide 2 additional methods behind IUnitOfWork interface and call them before SaveChanges(). As for me it makes logic more complicated but code become more clear. What if I need to make different method calls before SaveChanges(), e.g. in 1 scenario I want to call method1, in 2nd - method2 only, in 3rd - method2 and then method1? Should this be implemented via single IUnitOfWork interface, like UnitOfWorkMethod1 : IUnitOfWork, UnitOfWorkMethod2 : IUnitOfWork, and UnitOfWorkMethod2BeforeMethod1 : IUnitOfWork? It's just a mess, maybe I don't get the point of this pattern.
@Petrovich2049
@Petrovich2049 11 ай бұрын
@@nanvladI think it’s better to rename SaveChangesAsync to DoWork(), and call savechanges on dbcontext outside of unit of work, and/or add a flag to save changes with true/false as default value. This way you can compose units of work and save changes when needed.
@empireofhearts
@empireofhearts 2 жыл бұрын
In case of dealing with dependent entities where you have to save mutiple entities & need to call save changes only once for some transactions and save single entities in some other transactions thats where Unit of work will start crumbling as domain layer is not where you started UOW to know which domain will call SaveChanges and which ones doesnt. also wouldnt this add one more item to the list of DI constructor args that need to be maintained?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Why would we mix multiple domains a single business operation (transaction)?
@empireofhearts
@empireofhearts 2 жыл бұрын
So let's think of scenario where we have investigation and dependent on that is investigators. A single investigation can have multiple investigators so they stored in diff tables with investigation ID as the foreign key. When investigation data is getting selected investigators are also selected and sent along with it. So now both need to saved at same time as investigators cannot be saved until investigation is saved. The same scenario is applicable in various situations in real world business applications.
@bitukr.nirala3377
@bitukr.nirala3377 Жыл бұрын
With Entity Framework 4.x tried updating automapper and it crashesh due to multiple reason and finally again had to stay with same version. Please suggest the approach
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
No idea what's going on there
@majormartintibor
@majormartintibor 2 жыл бұрын
Thanks for this video Milan!
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
You're more than welcome 😁
@stevehiggin
@stevehiggin 2 жыл бұрын
In IUnitOfWork the SaveChangesAsync() method would this not be marked as async and then you would await _dbContext.SaveChangesAsync() as well?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
You can make it async, and await _dbContext.SaveChangesAsync It's awaited in any case in the end
@WayneMunro
@WayneMunro Жыл бұрын
EF core service this purpose on its own. If you take a dependency on EF you may as well use it.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
I am using it, just not exposing is as a dependency to the Application layer
@igeoorge3g
@igeoorge3g Жыл бұрын
to the sky milan 😜thanks for sharing
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
You're most welcome! Thanks for watching 😁
@tapetedepadaria
@tapetedepadaria Ай бұрын
I love the video, but I have a doubt. How the UnitOfWork is instantiated? I downloaded the sourcecode but could not find it anywhere! I understand there is an Injection, but could not find where nor how.
@MilanJovanovicTech
@MilanJovanovicTech Ай бұрын
It's the DbContext
@vAmp1que
@vAmp1que 2 жыл бұрын
Milan, great job again. Am I wrong that we could move our common logic of IRepositories to the generic IUnitOfWork? It looks like we incapsulate the dbContext data inside one place. What do you think about this point?
@AnotherFancyUser
@AnotherFancyUser 2 жыл бұрын
You want different classes to do what they suppose they have to do (depending on you of course), SRP (Single Responsibility Principle) states "A class should have a single responsibility and this responsibility should be entirely encapsulated by the class, a class should have one reason to change", so your UoW will save the changes, and the repositories will create the transactions.
@vAmp1que
@vAmp1que 2 жыл бұрын
@@AnotherFancyUser yeah sure you're right that my described case actually have large troubles, but it's interesting how our mate will answer the question:) Just conversation at tech topic, you know:) Huge respect to him and yours opinions, much love!
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Hey Alexey, how's it going? 😁 So how would this generic UoW behave? We would end up with one UoW per entity type? This kind of defeats the purpose of having the UoW in the first place. 🤔 Curious to hear more.
@vAmp1que
@vAmp1que 2 жыл бұрын
@@MilanJovanovicTech kinda well, and you? About subject: exactly. One UoW per entity type. It could prevent creation of repositories at some scenarios. But, as our friend above wrote, this usecase destroys single responsibility principle. But in my experience I prefer to use generic baseRepository, where I have all the logic and if I need something uncommon, I could extend my baseRepository using inheritance. Imho it's the best approach for me)
@pavelromashuk237
@pavelromashuk237 Жыл бұрын
What are the benefits when you take your logic out of interceptors and put it in savechanges method?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
You'd have access to DI in the DbContext which can take in Scoped services Other than that, no difference
@pavelromashuk237
@pavelromashuk237 Жыл бұрын
@@MilanJovanovicTech Did you mean services from DI?
@waleed-alshinawi
@waleed-alshinawi 2 жыл бұрын
Thank you Milan for the great videos that you making, am always learning new things from you :). I wanted to ask for your opinion for the approach that you've presented in this video, where you Set the Modified_Date in the UnitOfWork, isn't the responsibility of the Domain to set the modified_date / creation_date, because i've seen the same approach used for soft delete, where they change the entries with state = deleted to updated and set the deletion_date instead im just carious to know what you think about this point of view Thanks again for the amazing efforts you're putting
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
When I place the logic in UoW it saves me from writing a lot of code in the Domain entities. Don't you think so?
@waleed-alshinawi
@waleed-alshinawi 2 жыл бұрын
@@MilanJovanovicTech Totally agree with you
@jbb4play
@jbb4play 2 жыл бұрын
Hey, I was wondering if you could do a video on this one issue. On entity framework, lets assume we have a person entity with the following properties. firstname, lastname, age, email If we do a PUT from postman, we do some validation on each property, and tell entity framework to update all properties, easy peasy. However, if we do a PATCH, and only patch firstname and age, then if you are not careful in what do you, entity framework might possibly set the other properties to null or 0. I always find this to be rather annoying to handle, not difficult just VERY annoying, so I am very curious as how other people handle it.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Would you not first load the entity to memory before applying the PATCH?
@christopherfox6914
@christopherfox6914 4 ай бұрын
Do you think it is a bad idea to also create and set the Guid for created entities inside the UnitOfWork similarly to DateTimes of auditable entities?
@MilanJovanovicTech
@MilanJovanovicTech 4 ай бұрын
You'd typically do that while creating the entity (constructor/factory method).
@i.t.9015
@i.t.9015 2 жыл бұрын
In my opinion UnitOfWork pattern adds more complexity to project if it is not necessary. So, lets talk about situations when it becomes necessary and how often this situation can occur.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Where do you think the complexity lies? It's a relatively simple wrapper. It has one only responsibility. It's behind an interface, so it's easy to consume.
@kabal911
@kabal911 2 жыл бұрын
I think if you use repositories then unit of work is a no brainer. It makes transactions simple. If your are a heathen like me, that let’s his features/business logic use dbContext directly, as needed, on a case by case basis, then it doesn’t make much sense.
@omarjamil5688
@omarjamil5688 Жыл бұрын
why you don't add await keyword for SaveChangeAsync method on UnitOfWork Class
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Don't need to await it there
@bytejourneycodes
@bytejourneycodes 2 жыл бұрын
Do you have a complete example of the source code as well, I have been looking for a complete example for a very long time now 😐
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Yes, I share it on my Patreon
@hmsiegel79
@hmsiegel79 Жыл бұрын
Milan, I noticed after making these changes that I'm getting a circular dependency exception in regards to the MemberNameChangedDomainEvent. Any idea why?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
How is that even possible? What's in there that could be causing a circular dependency injection?
@hmsiegel79
@hmsiegel79 Жыл бұрын
@@MilanJovanovicTech I'm not sure. It's weird because when I switched to another box that I'm running, I don't get the error.
@hmsiegel79
@hmsiegel79 Жыл бұрын
@@MilanJovanovicTech So, I initially still had the lines in the ProcessOutboxMessagesJob for Polly , to retry on failure. With those in there, the applicaiton will not run. If I remove them and go with the default implementation, I can run the application but there's still an error in the console. I am a Patron, so we can continue there if that would be easier.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
@@hmsiegel79 Sent you a message
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
@@hmsiegel79 Seems I'm having issues sending you a message on Patreon. Can you try messaging me?
@RadixSort3
@RadixSort3 Жыл бұрын
Shouldn't the repositories be inside unit of work? Otherwise, they will work on different dbcontext and Unit of work save will not save repositories.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
I dislike that approach
@goolom
@goolom 2 жыл бұрын
Perhaps it would be better to tie those “update auditable entities” and the other method at 5:00 to the DbContext - OnSaveChangesEvent? Is there a reason why you didn’t do this? That seems like a global event, and it seems intuitive to tie global events to the dbcontext itself, rather than a wrapper that abstracts logic further away - what do you think? - I can see the case that perhaps you have multiple UnitOfWorks and you don’t want to tightly couple the logic to the DbContext itself
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I explored placing that logic inside of SaveChangesInterceptor in a separate video. I'm not aware of an _OnSaveChangesEvent_ on the DbContext? In any case, I wouldn't advise using events because it's more difficult to test..
@elpe21
@elpe21 2 жыл бұрын
@@MilanJovanovicTech What he means is to override SaveChangesAsync
@microtech2448
@microtech2448 2 жыл бұрын
Can you please create video on working with ef core and parallel foreach? In parallel foreach method with some max degree of parallelism greater than 1, when you use dbcontext to get and save entities, it crashes. Which is due to multiple threads. Can you create video on right implementation of it? Thank you
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
That's how it is supposed to work. The DbContext is not thread safe. If you want to achieve what you're talking about, you need to create one DbContext per thread and then use that.
@microtech2448
@microtech2448 2 жыл бұрын
@@MilanJovanovicTech I have read this at various places but I don't understand how it should be implemented? So, can you please create implementation video on same? So in nutshell, I have parallel foreach loop. Within it I am calling business layer method. This method calls data layer to fetch entity using dbcontext. I do some operations on fetched entity and calls another data layer method to save updated entity. And then after some iterations program crashes. I have configured dbcontext in startup as transient btw.
@microtech2448
@microtech2448 2 жыл бұрын
@@meetingattender8132 I had tried this earlier with no luck, I will try again.
@seekmanish
@seekmanish Жыл бұрын
​@@MilanJovanovicTech where can I download the source code that you used for this demo?
@microtech2448
@microtech2448 10 ай бұрын
Hello, in case of IAuditable, if we would want to save CreatedBy and UodatedBy as well, how would you plan to send existing logged in user identity into persistence layer within IUnitOfWork implementation?
@MilanJovanovicTech
@MilanJovanovicTech 10 ай бұрын
I'd inject it using the HttpContext.User.Identity.Name
@microtech2448
@microtech2448 10 ай бұрын
Hmm, I thought so but I was hesitant to go this route but your comment makes me comfortable to go through it. Thanks
@takkerutube
@takkerutube Жыл бұрын
Excellent video! Thank you!
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Glad you liked it!
@PaulSebastianM
@PaulSebastianM 2 жыл бұрын
Now this is useful!
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I appreciate your unbiased comments. 😁
@PaulSebastianM
@PaulSebastianM 2 жыл бұрын
@@MilanJovanovicTech Hahaha! Will try to do my best! 😉
@AdCodicemFR
@AdCodicemFR 10 ай бұрын
I'm not sure to understand why placing in the unitofwork the methods that were in the interceptors is better? Aren't the responsibilities of each interceptors merged into one big unitofwork ?
@MilanJovanovicTech
@MilanJovanovicTech 10 ай бұрын
Yes, they are
@AdCodicemFR
@AdCodicemFR 10 ай бұрын
@@MilanJovanovicTech So, to separate concerns it could be interesting to keep the interceptor. As you showed in your video on using the DbContext as the repository, I think that I'll use the same approach for the UnitOfWork pattern. Anyway, you make great videos, keep going!
@iliashterev38
@iliashterev38 Жыл бұрын
Well, first of all, my thinking is that those are hancy fancy games and philosophies that just complicate the code. But if I go and play the game I would say that what you explained was not a unit of work. It would say that this is an extension of a repository. Unit of work should not contain DbContext within it. It only gets multiple repositories injected in it and then it gets injected into say Controllers. In the controller it uses the injected repositories as properties and through them it just calls their methods, etc. Kind of another level of abstraction between the controller and the repositories. Ex: public class UnitOfWork : IUnitOfWork { public IProductRepository Products { get; } public UnitOfWork(IProductRepository productRepository) { Products = productRepository; } } ------------ public class ProductController : Controller { private readonly IUnitOfWork _unitOfWork; public ProductController(IUnitOfWork unitOfWork) { _unitOfWork = unitOfWork; } ---------- [HttpGet] public async Task GetAll() { var data = await _unitOfWork.Products.GetAllAsync(); return Ok(data); } public IProductRepository Products { get; } Now, to me the best of my knowledges injecting means instantiating an object and then injecting it. So here comes the big sh_t - if I have 50 entities in my project and respectively 50 repositories then each time I use that Unit of work then I have 50 object instantiated only to use 1 or 2.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
What about explicit dependencies principle? With this UnitOfWork, it's not really clear which repository you need along with the UnitOfWork.
@iliashterev38
@iliashterev38 Жыл бұрын
@@MilanJovanovicTech " it's not really clear which repository you need along". That was exactly what I was trying to say. If a projects has 50 entities then the unit of work will have 50 repositories instantiated each time and injected. And only one or two will be used.
@OrientAryan
@OrientAryan 2 жыл бұрын
Please share github link for this complete example code.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I share the code with my Patreon supporters only
@psyaviah
@psyaviah 2 жыл бұрын
@Milan Jovanović I don't understand the reasoning here. What is the actual benefit of using it - you did not explain it? Testing with DbContext is possible as well, and I always do that in an integration way aka testing against an actual db (localdb) as well as on my build server a localdb is setup, and then I can see if everything works on that end. As EF sometimes lets you write code that doesn't translate well to SQL, and you need to test that of course. Either way, I really don't see a benefit to use this. It might even complicate things further for newcomers. Yes, you expose a whole DbContext to newcomers, but isn't that the path to learn the developers and let them grow & know immediately. I feel this only adds complexity and code duplication - which I try to avoid. You mentioned you do use the DbContext on the "read"-side (aka queries) directly - even set changetracking off there for example with AsNoTracking(). Again, this might be me, but without more compelling arguments that would help junior developers, this is exactly the same and adds extra complexity. So it doesn't convince me. I would argue even; it would be more beneficial to abstract those things away for a READ-side, so you'd be able to mix both Dapper or EFCore behind a readonlyrepo for example + implement specifics on your reads/queries that you want optimized etc..?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
You just argued for using the DbContext in the first half of the comment, only to do a 180 and propose a repository for reading in the second half. 🤔
@psyaviah
@psyaviah 2 жыл бұрын
@@MilanJovanovicTech no, to clarify, I said I think IF you'd be using repos maybe you could optimize for codereuse & AsNoTracking better in the read-side already and abstract that away. But to be clear, not very happy with that either. I only said it would make more sense to me then.
@psyaviah
@psyaviah 2 жыл бұрын
Also, to be clear, I wouldn't do all of this and I advice against it. But I value other opinions, hence why I am still subscribed and I value other reasoning. We're all here to learn and exchange ideas in this (very young) software industry. And what works for someone, or some teams, might not for others. So thanks for making the videos & spreading your ideas and being open in the comment section! That is actually very helpful. Sorry if I'm brash/harsh - but mind that it is written/typed, it's not meant to be hateful!
@mohammad_mr
@mohammad_mr 2 жыл бұрын
Excellent
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thanks!
@janhendrych1076
@janhendrych1076 9 ай бұрын
Hi, does this mean that after I implemented the unit of work like this, then I can delete the ConvertDomainEventsToOutboxMessage Interceptor?
@MilanJovanovicTech
@MilanJovanovicTech 9 ай бұрын
Yes
@janhendrych1076
@janhendrych1076 9 ай бұрын
@@MilanJovanovicTech thanks man, your videos are golden + you reply to all comments. Legend💯
@MilanJovanovicTech
@MilanJovanovicTech 9 ай бұрын
@@janhendrych1076 I try to bring value. If this is how I can be different (better?) form other creators, so be it. 😁
@alessandrovangeli8395
@alessandrovangeli8395 Жыл бұрын
Can I see the implementation of a repository? I dont understand how it works. A unitofwork class is coupled to some repositories? It cannot reference to any repository class? Thanks
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
It's using EF Core under the hood
@alessandrovangeli8395
@alessandrovangeli8395 Жыл бұрын
@@MilanJovanovicTech you mean some configuration inside the program.cs?
@Andy01010
@Andy01010 2 жыл бұрын
Unit of work with repositories inside via Lazy in my case…
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I tend to avoid that approach, as I think it becomes complicated quickly
@PatrykZiarnik-y4c
@PatrykZiarnik-y4c 10 ай бұрын
Hi @MilanJovanovicTech Everything looks nice but it will only work if AddDbContext has Scoped lifetime. If not, repositories and UoW can have different DB context so I suppose your solution would not work. What about getting repositories via unit of work to be sure they share the same db context? How to solve this problem?
@MilanJovanovicTech
@MilanJovanovicTech 10 ай бұрын
I always use Scoped. When did you need to do it differently?
@PatrykZiarnik-y4c
@PatrykZiarnik-y4c 10 ай бұрын
​ @MilanJovanovicTech To be honest it does not matter when (I also used Scoped) but the problem is that there is such a possibility, so better to point this out at the beginning. Besides this I have few questions: 1. Why does your db Context implement IUnitOfWork? What's the point? 2. I am not convinced to have separately UnitOfWork and repositories. What about a custom AppUnitOfWork with db context and respositories in constructor (I assumed also here scoped "version") which provides repositories by Properties so then query/command handler or just a service has IAppUnitOfWork injected in constructor (not repositories)? What do you think? There is other advantage - if somehow you decide to use other than scoped db context, than you can just change AppUnitOfWork: Repositories would be created (new (...) with the same db context when they are called first time (unfortunately there is no possibbility I think to do it using DI) 3. When do you use IApplicationDbContext interface? I usually create db context without interface. In which situation do you use it?
@helen6400
@helen6400 2 жыл бұрын
Hi, Have you ever tried to create a middleware with this unitofwork class? Instead of injection to the services it will work at every request. Maybe we have to check if the request is not a get request. I am not suggesting. I am only asking if you know smt about using this pattern in a middleware
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I did. And it sounds like a good idea on the surface. But you could run into problems if at any point you _need_ to call SaveChanges more than once in a single request.
@SoheilKhosroshahi
@SoheilKhosroshahi 9 ай бұрын
if i want to use transaction queries in my project .this pattern can be a right approach?
@MilanJovanovicTech
@MilanJovanovicTech 9 ай бұрын
Might be
@SoheilKhosroshahi
@SoheilKhosroshahi 9 ай бұрын
is there a better way?@@MilanJovanovicTech
@BeyondAppearances-0
@BeyondAppearances-0 Жыл бұрын
Hi Milan, thank you for this super video ! I still have 2 unresolved points in my head, could you help ? 1. If this time, we had a CreateMemberCommand (instead of Update), and wanted to use this member created ID to update an other Repository (say XRepository) in the same handler method, so just before commiting with the Unit of Work (in order to keep consistency between the 2 corresponding tables (MemberRepositories and XRepositories)). Wouldn't it be mandatory to call twice the UoW commit method : i mean a first call to it, in order to get the member ID available provided by EF ? 2. In a DDD approach, they say : 1 Repository by Aggrgeate, where an Aggregate is responsible for ensuring consistency between the entities that it owns and controls. So does it mean (with EF) that the MyDbContext encapsulated into this MyRepository, will contain as many DbSet as necessary to handle the entities of the corresponding Aggregate, and so that this MyDbContext only makes sense for this particluar MyRepository ? While, there would be only 1 MyUnitOfWork for this MyDbContext , so related to only 1 Repository : MyRepository ? Finally meaning in most cases : 1 UoW by DbContext, and 1 DbContext by Repository, so 1 UoW by Repository (Aggregate) as indeed the UoW is also Responsible for consistency o f the persisted DbContext data ?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
1. Call it twice, yeah. Or generate ID on client side (Guid) 2. I think you're overthinking it 😁 DbContext = UoW, DbSet = Repository
@BeyondAppearances-0
@BeyondAppearances-0 Жыл бұрын
@@MilanJovanovicTech Thanks Milan for sharing your advices.
@yondaimefourth
@yondaimefourth 7 ай бұрын
Is it necessary to use UoW for read operations?
@MilanJovanovicTech
@MilanJovanovicTech 7 ай бұрын
No
@yondaimefourth
@yondaimefourth 7 ай бұрын
@@MilanJovanovicTech if I have uow and generic repo, is it correct just use the gerenic repo for read operations?
@metehanmutlu9187
@metehanmutlu9187 Жыл бұрын
Using repository and uow pattern on top of ef core only makes sense if you want to make your application layer orm/database agnostic and it does not worth the effort in my opinion.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Oh, it's worth it in the long run as the project grows in complexity
@samehkeshta89
@samehkeshta89 2 жыл бұрын
Great
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thanks!
@gauravsingh-qt2zo
@gauravsingh-qt2zo 2 жыл бұрын
What is the advantage of using razor pages, aspx when we have frontend frameworks like angular,react?????
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
What does that have to do with the video? 😂
@gauravsingh-qt2zo
@gauravsingh-qt2zo 2 жыл бұрын
@@MilanJovanovicTech sir i want your help. I am stuck at one issue from last 2 days. We are using proxy server. My task is to get the client ip address. Instead I am always getting the same ip address from different client machine and that ip address is 99 percent the ip of the proxy server. I have used useforwardheaders middleware with all the combination of parameters and also included the ip address of the proxy server in the knownnetwork option. Still i am not getting the client ip address. Please help me. Your help would save my job.
@antwanwimberly1729
@antwanwimberly1729 Жыл бұрын
Wouldn’t it be easier to use rails
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Rails?
@mohammadalikhaloi9571
@mohammadalikhaloi9571 11 сағат бұрын
it's awesome
@sauravbhatta5303
@sauravbhatta5303 2 жыл бұрын
Nice pattern.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Many many thanks
@rusenot
@rusenot 2 ай бұрын
It's necessary to introduce us to your model, cause it's hard to understand if you are just copy paste the code from the other part not explaining whats this code for
@MilanJovanovicTech
@MilanJovanovicTech 2 ай бұрын
Sorry 🤷‍♂️
@marna_li
@marna_li 2 жыл бұрын
So now you mean that I should revert my changes from when moving to interceptors.... ??? Haha
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I don't mean that! haha I just like to explore and present the many possible options we have. It's on you to choose what you like. This approach supports supports Scoped DI, so take that as a consideration.
@marna_li
@marna_li 2 жыл бұрын
@@MilanJovanovicTech Yes. It is about requirements and preference. I do get that it might be more logical to put logic in Unit of Work. Discoverable. I register my Interceptors and scoped in the DI. A bit of wiring in AddSqlServer/AddDbContext but it works.
@techpc5453
@techpc5453 Жыл бұрын
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
👋
@antwanwimberly1729
@antwanwimberly1729 Жыл бұрын
It doesn’t have to be with entity framework We did it with as user transaction at #lanetix If any code failed within the promise block which composes then guess what We j ew to rollback the request level transaction as you should only call that function once per request within the context of your request handler Wouldn’t it lead to nested transactions Remember the distributed transaction coordinator ?? Yiani was xxX Hmmmm Big Design Up Front
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Interesting approach 🤔
Using Domain Events To Build A Decoupled System The Scales
14:02
Milan Jovanović
Рет қаралды 26 М.
EF Core Performance Optimization Challenge | 233x FASTER
14:42
Milan Jovanović
Рет қаралды 69 М.
Disrespect or Respect 💔❤️
00:27
Thiago Productions
Рет қаралды 43 МЛН
А я думаю что за звук такой знакомый? 😂😂😂
00:15
Денис Кукояка
Рет қаралды 3,6 МЛН
Real Man relocate to Remote Controlled Car 👨🏻➡️🚙🕹️ #builderc
00:24
How I Use The Generic Repository Pattern In Clean Architecture
17:15
Milan Jovanović
Рет қаралды 40 М.
The Unit of Work Design Pattern Explained
12:37
ArjanCodes
Рет қаралды 25 М.
Ollama on Kubernetes: ChatGPT for free!
18:29
Mathis Van Eetvelde
Рет қаралды 6 М.
Make Your Business Rules Cleaner With Fluent Validation
15:14
Milan Jovanović
Рет қаралды 30 М.
Understand Clean Architecture in 7 Minutes
7:02
Amichai Mantinband
Рет қаралды 118 М.
Elegant Global Error Handling Using Middleware In ASP.NET Core
13:58
Milan Jovanović
Рет қаралды 89 М.
How To Use The Specification Design Pattern With EF Core 6
19:13
Milan Jovanović
Рет қаралды 40 М.
The Beginner's Guide to Clean Architecture
13:19
Milan Jovanović
Рет қаралды 30 М.
Disrespect or Respect 💔❤️
00:27
Thiago Productions
Рет қаралды 43 МЛН