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
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.
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.
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.
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.
"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.
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.
@@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
@@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.
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)
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?
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 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
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?
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.
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.
@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?
@@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.
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.
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.
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.
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?
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
@@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 ;).
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!
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?
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
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?
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.
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.
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.
@@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
@@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... )
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?
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
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.
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.
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.
@@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.
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?
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
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! 😃
@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 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!
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.
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
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..
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
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.
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
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.
@@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.
Milan, I noticed after making these changes that I'm getting a circular dependency exception in regards to the MemberNameChangedDomainEvent. Any idea why?
@@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.
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.
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?
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.
@@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!
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.
@@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)
@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 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.
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!
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?
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 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!
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
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
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.
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 " 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.
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 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?
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 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.
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.
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
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.
@@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.
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
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
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.
@@AnotherFancyUser That's a nice idea, I'll add that topic to my list. Gotta think of a clever way to present the topic
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.
Thank you for providing the code for free
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.
I like to have one way to things, and UoW achieves this easily
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.
"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.
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.
@@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
@@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.
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)
That's a great use case and it makes a lot of sense
It would be nice to see this in action without EF, with things like Dapper or simply with repositories and ITransaction
You would have to implement your own Identith Map, would be interesting surely
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?
| 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.
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.
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
@@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
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?
Great video non the less. Thank you, Milan!
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.
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.
@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?
@@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.
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.
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.
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.
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?
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
@@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 ;).
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!
I think it's justified, as long as you accept that all repository methods will now have to complete the UoW
Excellent video, Milan! Thank you so much for sharing with us how your implementation of UoW + EF Core is!😀
Thanks! 😁
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?
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
We would like to make you a video about the Error response class & the FluentValidation implementation
Check this out:
th-cam.com/video/85dxwd8HzEk/w-d-xo.html
Great explication!, im new at Clean architecture and im learning a lot with your videos. Thanks Milan!
Happy to help!
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?
Why would we mix multiple domains a single business operation (transaction)?
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.
EF core service this purpose on its own. If you take a dependency on EF you may as well use it.
I am using it, just not exposing is as a dependency to the Application layer
You mentioned that postgreSql is smarter than SQL Server can you please explain the advantages about using postgreSql over SQL Server
I was talking the EF providers for SQL Server/PostgreSQL. The PostgreSQL provider handles change tracking better, from my experience.
If your ApplicationDBContext class starts implementing IUnitOfWork, you don't need to have an extra UnitOfWork class.
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.
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.
@@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
@@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... )
Milan applied the composition over inheritance pattern here for better decoupling.
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?
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
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.
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.
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.
@@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.
For web API-s do you prefer to use EntityFramework with connected change tracking or with disconnected change tracking?
When I want to insert/update/delete, then I want change tracking enabled
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 😐
Yes, I share it on my Patreon
What are the benefits when you take your logic out of interceptors and put it in savechanges method?
You'd have access to DI in the DbContext which can take in Scoped services
Other than that, no difference
@@MilanJovanovicTech Did you mean services from DI?
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?
Yes
learn.microsoft.com/en-us/ef/core/saving/transactions#default-transaction-behavior
@@MilanJovanovicTech Indeed, but your db needs to support this. So talk to your db admin first to make sure this scenario is supported.
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
No idea what's going on there
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! 😃
Or you can inject a service and resolve the userId
to the sky milan 😜thanks for sharing
You're most welcome! Thanks for watching 😁
In IUnitOfWork the SaveChangesAsync() method would this not be marked as async and then you would await _dbContext.SaveChangesAsync() as well?
You can make it async, and await _dbContext.SaveChangesAsync
It's awaited in any case in the end
@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!
Releasing a CICD video on Friday. If that's too late, I can find some resources for you
@@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!
why you don't add await keyword for SaveChangeAsync method on UnitOfWork Class
Don't need to await it there
Thanks for this video Milan!
You're more than welcome 😁
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.
Would you not first load the entity to memory before applying the PATCH?
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.
I dislike that approach
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
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..
@@MilanJovanovicTech What he means is to override SaveChangesAsync
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
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?
@@MilanJovanovicTech Totally agree with you
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.
Much appreciated!
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
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.
@@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.
@@meetingattender8132 I had tried this earlier with no luck, I will try again.
@@MilanJovanovicTech where can I download the source code that you used for this demo?
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?
You'd typically do that while creating the entity (constructor/factory method).
Milan,
I noticed after making these changes that I'm getting a circular dependency exception in regards to the MemberNameChangedDomainEvent. Any idea why?
How is that even possible? What's in there that could be causing a circular dependency injection?
@@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.
@@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.
@@hmsiegel79 Sent you a message
@@hmsiegel79 Seems I'm having issues sending you a message on Patreon. Can you try messaging me?
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.
It's the DbContext
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?
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.
@@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!
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.
@@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)
@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..?
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. 🤔
@@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.
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!
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?
I'd inject it using the HttpContext.User.Identity.Name
Hmm, I thought so but I was hesitant to go this route but your comment makes me comfortable to go through it. Thanks
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 ?
Yes, they are
@@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!
Please share github link for this complete example code.
I share the code with my Patreon supporters only
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
It's using EF Core under the hood
@@MilanJovanovicTech you mean some configuration inside the program.cs?
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
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.
Excellent video! Thank you!
Glad you liked it!
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.
What about explicit dependencies principle? With this UnitOfWork, it's not really clear which repository you need along with the UnitOfWork.
@@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.
Hi, does this mean that after I implemented the unit of work like this, then I can delete the ConvertDomainEventsToOutboxMessage Interceptor?
Yes
@@MilanJovanovicTech thanks man, your videos are golden + you reply to all comments. Legend💯
@@janhendrych1076 I try to bring value. If this is how I can be different (better?) form other creators, so be it. 😁
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?
I always use Scoped. When did you need to do it differently?
@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?
if i want to use transaction queries in my project .this pattern can be a right approach?
Might be
is there a better way?@@MilanJovanovicTech
Is it necessary to use UoW for read operations?
No
@@MilanJovanovicTech if I have uow and generic repo, is it correct just use the gerenic repo for read operations?
Now this is useful!
I appreciate your unbiased comments. 😁
@@MilanJovanovicTech Hahaha! Will try to do my best! 😉
Excellent
Thanks!
Unit of work with repositories inside via Lazy in my case…
I tend to avoid that approach, as I think it becomes complicated quickly
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 ?
1. Call it twice, yeah. Or generate ID on client side (Guid)
2. I think you're overthinking it 😁 DbContext = UoW, DbSet = Repository
@@MilanJovanovicTech Thanks Milan for sharing your advices.
What is the advantage of using razor pages, aspx when we have frontend frameworks like angular,react?????
What does that have to do with the video? 😂
@@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.
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.
Oh, it's worth it in the long run as the project grows in complexity
Great
Thanks!
Wouldn’t it be easier to use rails
Rails?
Nice pattern.
Many many thanks
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
Sorry 🤷♂️
it's awesome
Thanks!
So now you mean that I should revert my changes from when moving to interceptors.... ??? Haha
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.
@@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.
👋
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
Interesting approach 🤔