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

แชร์
ฝัง
  • เผยแพร่เมื่อ 29 ต.ค. 2024

ความคิดเห็น • 189

  • @MilanJovanovicTech
    @MilanJovanovicTech  2 ปีที่แล้ว +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 2 ปีที่แล้ว +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  2 ปีที่แล้ว +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 ปีที่แล้ว

      th-cam.com/video/vN_j1Bs0ALU/w-d-xo.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 7 หลายเดือนก่อน

      Thank you for providing the code for free

  • @cleitonosti1756
    @cleitonosti1756 2 ปีที่แล้ว +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 ปีที่แล้ว +2

      I like to have one way to things, and UoW achieves this easily

  • @stunna4498
    @stunna4498 2 ปีที่แล้ว +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)

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว +4

      That's a great use case and it makes a lot of sense

  • @carmineos
    @carmineos 2 ปีที่แล้ว +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 ปีที่แล้ว +2

      You would have to implement your own Identith Map, would be interesting surely

  • @saidhalloun8640
    @saidhalloun8640 6 หลายเดือนก่อน +1

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

  • @vamvdotnet
    @vamvdotnet 2 ปีที่แล้ว +1

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

  • @kvloover
    @kvloover 2 ปีที่แล้ว +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 2 ปีที่แล้ว +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  2 ปีที่แล้ว +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 ปีที่แล้ว +3

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

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

  • @amantinband
    @amantinband 2 ปีที่แล้ว +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 2 ปีที่แล้ว +1

      Great video non the less. Thank you, Milan!

    • @pilotboba
      @pilotboba 2 ปีที่แล้ว +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 ปีที่แล้ว +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 2 ปีที่แล้ว +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.

  • @majormartintibor
    @majormartintibor 2 ปีที่แล้ว +1

    Thanks for this video Milan!

  • @juankasem4911
    @juankasem4911 2 ปีที่แล้ว +2

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

      Check this out:
      th-cam.com/video/85dxwd8HzEk/w-d-xo.html

  • @adrielairaldo
    @adrielairaldo ปีที่แล้ว +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  ปีที่แล้ว

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

  • @igeoorge3g
    @igeoorge3g ปีที่แล้ว +1

    to the sky milan 😜thanks for sharing

  • @MarcusKaseder
    @MarcusKaseder 2 ปีที่แล้ว +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  2 ปีที่แล้ว +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.

  • @fernandocalmet
    @fernandocalmet 2 ปีที่แล้ว +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! 😃

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว +3

      Or you can inject a service and resolve the userId

  • @i.t.9015
    @i.t.9015 2 ปีที่แล้ว +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  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 ปีที่แล้ว +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.

  • @alexkovanev1425
    @alexkovanev1425 2 ปีที่แล้ว +6

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว +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 2 ปีที่แล้ว +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 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 ปีที่แล้ว +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 2 ปีที่แล้ว +1

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

  • @ismaelperezmesa524
    @ismaelperezmesa524 2 ปีที่แล้ว +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  2 ปีที่แล้ว +1

      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!

  • @sky3913
    @sky3913 5 หลายเดือนก่อน

    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  5 หลายเดือนก่อน

      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

  • @sorteslyngel2k
    @sorteslyngel2k 2 ปีที่แล้ว +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  2 ปีที่แล้ว

      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.

  • @stef2528
    @stef2528 3 หลายเดือนก่อน

    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.

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

  • @takkerutube
    @takkerutube ปีที่แล้ว

    Excellent video! Thank you!

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

  • @waleed-alshinawi
    @waleed-alshinawi 2 ปีที่แล้ว +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  2 ปีที่แล้ว +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 2 ปีที่แล้ว

      @@MilanJovanovicTech Totally agree with you

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

  • @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 ปีที่แล้ว +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.

  • @stevehiggin
    @stevehiggin 2 ปีที่แล้ว +1

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว +1

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

  • @DJohn001
    @DJohn001 2 ปีที่แล้ว +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  2 ปีที่แล้ว +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 ;).

  • @02244
    @02244 2 ปีที่แล้ว +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  2 ปีที่แล้ว +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 10 หลายเดือนก่อน

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

  • @vAmp1que
    @vAmp1que 2 ปีที่แล้ว +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 2 ปีที่แล้ว +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 2 ปีที่แล้ว +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  2 ปีที่แล้ว +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 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)

  • @mahfoudbouabdallah6286
    @mahfoudbouabdallah6286 2 ปีที่แล้ว +1

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว +1

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

  • @christopherfox6914
    @christopherfox6914 3 หลายเดือนก่อน

    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  3 หลายเดือนก่อน +1

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

  • @bytejourneycodes
    @bytejourneycodes 2 ปีที่แล้ว +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 😐

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

  • @microtech2448
    @microtech2448 9 หลายเดือนก่อน

    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  9 หลายเดือนก่อน

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

    • @microtech2448
      @microtech2448 9 หลายเดือนก่อน

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

  • @tapetedepadaria
    @tapetedepadaria 6 วันที่ผ่านมา

    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.

  • @PaulSebastianM
    @PaulSebastianM 2 ปีที่แล้ว +1

    Now this is useful!

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

      I appreciate your unbiased comments. 😁

    • @PaulSebastianM
      @PaulSebastianM 2 ปีที่แล้ว

      @@MilanJovanovicTech Hahaha! Will try to do my best! 😉

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

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

  • @mohammad_mr
    @mohammad_mr ปีที่แล้ว +1

    Excellent

  • @RadixSort3
    @RadixSort3 ปีที่แล้ว +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.

  • @omarjamil5688
    @omarjamil5688 ปีที่แล้ว +1

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

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

  • @microtech2448
    @microtech2448 2 ปีที่แล้ว +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 ปีที่แล้ว +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?

  • @janhendrych1076
    @janhendrych1076 8 หลายเดือนก่อน

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  8 หลายเดือนก่อน

      Yes

    • @janhendrych1076
      @janhendrych1076 8 หลายเดือนก่อน +1

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  8 หลายเดือนก่อน +1

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

  • @goolom
    @goolom 2 ปีที่แล้ว +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 ปีที่แล้ว +3

      @@MilanJovanovicTech What he means is to override SaveChangesAsync

  • @SoheilKhosroshahi
    @SoheilKhosroshahi 8 หลายเดือนก่อน

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

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

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

  • @AdCodicemFR
    @AdCodicemFR 9 หลายเดือนก่อน

    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  9 หลายเดือนก่อน

      Yes, they are

    • @AdCodicemFR
      @AdCodicemFR 9 หลายเดือนก่อน

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

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

  • @PatrykZiarnik-y4c
    @PatrykZiarnik-y4c 9 หลายเดือนก่อน

    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  9 หลายเดือนก่อน

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

    • @PatrykZiarnik-y4c
      @PatrykZiarnik-y4c 9 หลายเดือนก่อน

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

  • @OrientAryan
    @OrientAryan ปีที่แล้ว +1

    Please share github link for this complete example code.

  • @samehkeshta89
    @samehkeshta89 ปีที่แล้ว +1

    Great

  • @Andy01010
    @Andy01010 2 ปีที่แล้ว +1

    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

  • @helen6400
    @helen6400 2 ปีที่แล้ว +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 ปีที่แล้ว +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.

  • @BeyondAppearances-0
    @BeyondAppearances-0 ปีที่แล้ว +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  ปีที่แล้ว +1

      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.

  • @metehanmutlu9187
    @metehanmutlu9187 ปีที่แล้ว +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  ปีที่แล้ว

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

  • @sauravbhatta5303
    @sauravbhatta5303 2 ปีที่แล้ว +1

    Nice pattern.

  • @rusenot
    @rusenot หลายเดือนก่อน

    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

  • @antwanwimberly1729
    @antwanwimberly1729 11 หลายเดือนก่อน +1

    Wouldn’t it be easier to use rails

  • @gauravsingh-qt2zo
    @gauravsingh-qt2zo 2 ปีที่แล้ว +1

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว +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.

  • @marna_li
    @marna_li 2 ปีที่แล้ว +1

    So now you mean that I should revert my changes from when moving to interceptors.... ??? Haha

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว +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 ปีที่แล้ว +1

  • @antwanwimberly1729
    @antwanwimberly1729 11 หลายเดือนก่อน +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