Это видео недоступно.
Сожалеем об этом.

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

Поделиться
HTML-код
  • Опубликовано: 1 авг 2024
  • Get the source code for this video for FREE → the-dotnet-weekly.ck.page/uni...
    ☄️ Master the Modular Monolith Architecture: bit.ly/3SXlzSt
    📌 Accelerate your Clean Architecture skills: bit.ly/3PupkOJ
    🚀 Support me on Patreon to access the source code: / milanjovanovic
    In this video, I talk about how to implement the Unit of Work pattern with EF Core. I discuss what I think are the benefits of using the Unit of Work pattern and what could be some of the drawbacks. Do you consider the Unit of Work an anti-pattern with EF Core? I don't, but I have a good reason why.
    Join my weekly .NET newsletter:
    www.milanjovanovic.tech
    Read my Blog here:
    www.milanjovanovic.tech/blog
    Subscribe for more:
    ruclips.net/user/MilanJovano...
    Chapters
    0:00 Benefits of the Unit of Work
    3:23 Adding behavior to Unit of Work
    5:55 Unit of Work in action

Комментарии • 185

  • @MilanJovanovicTech
    @MilanJovanovicTech  Год назад +4

    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 Год назад +1

      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  Год назад +1

      @@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 Год назад

      ruclips.net/video/vN_j1Bs0ALU/видео.html 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 4 месяца назад

      Thank you for providing the code for free

  • @cleitonosti1756
    @cleitonosti1756 Год назад +4

    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

  • @vamvdotnet
    @vamvdotnet Год назад +1

    Excellent video, Milan! Thank you so much for sharing with us how your implementation of UoW + EF Core is!😀

  • @stunna4498
    @stunna4498 Год назад +9

    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)

  • @majormartintibor
    @majormartintibor Год назад +1

    Thanks for this video Milan!

  • @takkerutube
    @takkerutube Год назад

    Excellent video! Thank you!

  • @saidhalloun8640
    @saidhalloun8640 3 месяца назад +1

    Great explication!, im new at Clean architecture and im learning a lot with your videos. Thanks Milan!

  • @igeoorge3g
    @igeoorge3g Год назад +1

    to the sky milan 😜thanks for sharing

  • @kvloover
    @kvloover Год назад +8

    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 Год назад +1

      "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  Год назад +3

      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 Год назад

      ​@@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 Год назад +5

    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

  • @mohammad_mr
    @mohammad_mr Год назад +1

    Excellent

  • @adrielairaldo
    @adrielairaldo 11 месяцев назад +1

    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  11 месяцев назад

      I think it's justified, as long as you accept that all repository methods will now have to complete the UoW

  • @fernandocalmet
    @fernandocalmet Год назад +1

    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! 😃

  • @graylee9567
    @graylee9567 Год назад +2

    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 Год назад +1

      @@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

  • @PaulSebastianM
    @PaulSebastianM Год назад +1

    Now this is useful!

  • @alexkovanev1425
    @alexkovanev1425 Год назад +6

    If your ApplicationDBContext class starts implementing IUnitOfWork, you don't need to have an extra UnitOfWork class.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +7

      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 Год назад +11

      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 Год назад

      @@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 Год назад +1

      @@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 Год назад +1

      Milan applied the composition over inheritance pattern here for better decoupling.

  • @stef2528
    @stef2528 22 дня назад

    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.

  • @juankasem4911
    @juankasem4911 Год назад +2

    We would like to make you a video about the Error response class & the FluentValidation implementation

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Check this out:
      ruclips.net/video/85dxwd8HzEk/видео.html

  • @ismaelperezmesa524
    @ismaelperezmesa524 Год назад +1

    @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  Год назад +1

      Releasing a CICD video on Friday. If that's too late, I can find some resources for you

    • @ismaelperezmesa524
      @ismaelperezmesa524 Год назад

      @@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!

  • @sorteslyngel2k
    @sorteslyngel2k Год назад +3

    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  Год назад

      Yes
      learn.microsoft.com/en-us/ef/core/saving/transactions#default-transaction-behavior

    • @psyaviah
      @psyaviah Год назад

      @@MilanJovanovicTech Indeed, but your db needs to support this. So talk to your db admin first to make sure this scenario is supported.

  • @samehkeshta89
    @samehkeshta89 Год назад +1

    Great

  • @stevehiggin
    @stevehiggin Год назад +1

    In IUnitOfWork the SaveChangesAsync() method would this not be marked as async and then you would await _dbContext.SaveChangesAsync() as well?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      You can make it async, and await _dbContext.SaveChangesAsync
      It's awaited in any case in the end

  • @waleed-alshinawi
    @waleed-alshinawi Год назад +1

    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  Год назад +1

      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 Год назад

      @@MilanJovanovicTech Totally agree with you

  • @taleslopes9421
    @taleslopes9421 Год назад +1

    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

  • @amantinband
    @amantinband Год назад +6

    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 Год назад +1

      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  Год назад +1

      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 Год назад +1

      @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.

  • @dejansavanovic4476
    @dejansavanovic4476 Год назад +1

    For web API-s do you prefer to use EntityFramework with connected change tracking or with disconnected change tracking?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      When I want to insert/update/delete, then I want change tracking enabled

  • @vAmp1que
    @vAmp1que Год назад +1

    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 Год назад +1

      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 Год назад +1

      @@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  Год назад +1

      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 Год назад

      @@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)

  • @MarcusKaseder
    @MarcusKaseder Год назад +4

    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  Год назад +1

      | 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.

  • @bitukr.nirala3377
    @bitukr.nirala3377 Год назад +1

    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

  • @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  Год назад

      Why would we mix multiple domains a single business operation (transaction)?

    • @empireofhearts
      @empireofhearts Год назад

      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.

  • @jbb4play
    @jbb4play Год назад +1

    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  Год назад

      Would you not first load the entity to memory before applying the PATCH?

  • @mahfoudbouabdallah6286
    @mahfoudbouabdallah6286 Год назад +1

    You mentioned that postgreSql is smarter than SQL Server can you please explain the advantages about using postgreSql over SQL Server

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      I was talking the EF providers for SQL Server/PostgreSQL. The PostgreSQL provider handles change tracking better, from my experience.

  • @sky3913
    @sky3913 2 месяца назад

    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  2 месяца назад

      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

  • @microtech2448
    @microtech2448 Год назад +1

    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 Год назад

      @@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 Год назад

      @@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?

  • @hmsiegel79
    @hmsiegel79 Год назад +1

    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?

  • @WayneMunro
    @WayneMunro Год назад +2

    EF core service this purpose on its own. If you take a dependency on EF you may as well use it.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      I am using it, just not exposing is as a dependency to the Application layer

  • @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  Год назад

      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 Год назад +3

      @@MilanJovanovicTech What he means is to override SaveChangesAsync

  • @bytejourneycodes
    @bytejourneycodes Год назад +1

    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 😐

  • @02244
    @02244 Год назад +12

    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  Год назад +10

      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 Год назад +11

      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 8 месяцев назад

      @@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.

  • @omarjamil5688
    @omarjamil5688 Год назад +1

    why you don't add await keyword for SaveChangeAsync method on UnitOfWork Class

  • @pavelromashuk237
    @pavelromashuk237 Год назад +1

    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 Год назад +1

      @@MilanJovanovicTech Did you mean services from DI?

  • @user-xn5do6xc1u
    @user-xn5do6xc1u Год назад +2

    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.

  • @christopherfox6914
    @christopherfox6914 Месяц назад

    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  Месяц назад +1

      You'd typically do that while creating the entity (constructor/factory method).

  • @OrientAryan
    @OrientAryan Год назад +1

    Please share github link for this complete example code.

  • @alessandrovangeli8395
    @alessandrovangeli8395 10 месяцев назад

    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

  • @janhendrych1076
    @janhendrych1076 5 месяцев назад

    Hi, does this mean that after I implemented the unit of work like this, then I can delete the ConvertDomainEventsToOutboxMessage Interceptor?

    • @MilanJovanovicTech
      @MilanJovanovicTech  5 месяцев назад

      Yes

    • @janhendrych1076
      @janhendrych1076 5 месяцев назад +1

      @@MilanJovanovicTech thanks man, your videos are golden + you reply to all comments. Legend💯

    • @MilanJovanovicTech
      @MilanJovanovicTech  5 месяцев назад +1

      @@janhendrych1076 I try to bring value. If this is how I can be different (better?) form other creators, so be it. 😁

  • @i.t.9015
    @i.t.9015 Год назад +9

    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  Год назад

      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 Год назад +3

      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.

  • @DJohn001
    @DJohn001 Год назад +6

    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  Год назад +1

      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 Год назад

      @@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 ;).

  • @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  Год назад

      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 Год назад

      @@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 Год назад

      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!

  • @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  Год назад +1

      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.

  • @user-yx4po9tt8z
    @user-yx4po9tt8z 5 месяцев назад

    if i want to use transaction queries in my project .this pattern can be a right approach?

  • @yondaimefourth
    @yondaimefourth 4 месяца назад

    Is it necessary to use UoW for read operations?

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      No

    • @yondaimefourth
      @yondaimefourth 4 месяца назад

      @@MilanJovanovicTech if I have uow and generic repo, is it correct just use the gerenic repo for read operations?

  • @rickyrick6901
    @rickyrick6901 10 месяцев назад +1

    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  10 месяцев назад +1

      1. Call it twice, yeah. Or generate ID on client side (Guid)
      2. I think you're overthinking it 😁 DbContext = UoW, DbSet = Repository

    • @rickyrick6901
      @rickyrick6901 10 месяцев назад

      @@MilanJovanovicTech Thanks Milan for sharing your advices.

  • @Andy01010
    @Andy01010 Год назад +1

    Unit of work with repositories inside via Lazy in my case…

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      I tend to avoid that approach, as I think it becomes complicated quickly

  • @gauravsingh-qt2zo
    @gauravsingh-qt2zo Год назад +1

    What is the advantage of using razor pages, aspx when we have frontend frameworks like angular,react?????

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +7

      What does that have to do with the video? 😂

    • @gauravsingh-qt2zo
      @gauravsingh-qt2zo Год назад

      @@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.

  • @AdCodicemFR
    @AdCodicemFR 6 месяцев назад

    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  6 месяцев назад

      Yes, they are

    • @AdCodicemFR
      @AdCodicemFR 6 месяцев назад

      @@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!

  • @user-fe8mu4zj5g
    @user-fe8mu4zj5g 6 месяцев назад

    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  6 месяцев назад

      I always use Scoped. When did you need to do it differently?

    • @user-fe8mu4zj5g
      @user-fe8mu4zj5g 6 месяцев назад

      ​ @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?

  • @sauravbhatta5303
    @sauravbhatta5303 Год назад +1

    Nice pattern.

  • @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.

  • @antwanwimberly1729
    @antwanwimberly1729 9 месяцев назад +1

    Wouldn’t it be easier to use rails

  • @marna_li
    @marna_li Год назад +1

    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 Год назад

      @@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.

  • @metehanmutlu9187
    @metehanmutlu9187 11 месяцев назад +1

    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  11 месяцев назад

      Oh, it's worth it in the long run as the project grows in complexity

  • @techpc5453
    @techpc5453 Год назад +1

  • @antwanwimberly1729
    @antwanwimberly1729 9 месяцев назад +1

    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

  • @microtech2448
    @microtech2448 6 месяцев назад

    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  6 месяцев назад

      I'd inject it using the HttpContext.User.Identity.Name

    • @microtech2448
      @microtech2448 6 месяцев назад

      Hmm, I thought so but I was hesitant to go this route but your comment makes me comfortable to go through it. Thanks