Тёмный

Unit of Work in ASP.NET Core 

Raw Coding
Подписаться 71 тыс.
Просмотров 18 тыс.
50% 1

The Unit of work "design pattern" 🤦‍♂️ is presented as a wrapper around a db context and some repositories, which is a LIE. Unit of Work is the object that does the change tracking and makes the decision of what get's saved to the database.
Patreon 🤝 / raw_coding
Courses 📚 learning.raw-coding.dev
Shop 🛒 shop.raw-coding.dev
Discord 💬 / discord
Twitter 📣 / anton_t0shik
Twitch 🎥 / raw_coding
🕰️ Timestamps
00:00 Introductory Rant
03:20 Implementation
11:20 Child Services
#csharp #aspnetcore #patterns

Опубликовано:

 

8 июл 2024

Поделиться:

Ссылка:

Скачать:

Готовим ссылку...

Добавить в:

Мой плейлист
Посмотреть позже
Комментарии : 83   
@stefan-d.grigorescu
@stefan-d.grigorescu Год назад
Man! I found out the same thing when I was told to implement unit of work for a college project! I was asking around "Why do we need this if a Scoped DbContext already does that stuff?" And more people then seemed to make the same wrappers, no one gave an answer why
@kneeh
@kneeh 9 месяцев назад
Chances are low but to keep the right to migrate from EF to Dapper for example. You can ask the same question as to why work with repositories if "EF is already a database abstraction".
@stefan-d.grigorescu
@stefan-d.grigorescu 9 месяцев назад
@@kneeh actually I use repos only a few times, for stuff that are queried the same way all the time. But must of the times I find that using dbContext directly in a query handler makes perfect sense, since queries are generally different for each case
@kneeh
@kneeh 9 месяцев назад
​@@stefan-d.grigorescu And that's perfectly fine, that all depends on the requirements. 1. I was involved in a project that needed fast POC, so we use EF and migrated to Dapper, Abstraction was our savior. 2. I rather see a method name than a chain of LINQ calls from EF, and if I already refactor LINQ to a method, why would abstract it entirely, it will also clean up testing by much (the testing suite doesn't need to know about EF).
@stefan-d.grigorescu
@stefan-d.grigorescu 9 месяцев назад
@@kneeh For 2. I also like extracting such parts of a bigger method into more focused smaller methods in the same handler class That way, the top level method still reads nice
@allinvanguard
@allinvanguard Год назад
Man, spot on. I don't know what it is, but the space around EF, UoW and Repositories is apparently something people just love to regurgitate something they read somewhere else without doing their own research. Love that you are calling this out.
@adrian_franczak
@adrian_franczak Год назад
good stuff - never have chance to implement UoW so idea of saving this in middleware is something what I will save for the future - thanks
@chiro5533
@chiro5533 Год назад
Here's a tip if you're looking for information on design and architecture, consider checking out the articles and books written by Martin Fowler, Robert C. Martin, Kent Beck, and others. In my personal opinion, Microsoft's documentation is inadequate when it comes to best practices in these areas. It seems that the Microsoft developers may not be well-versed in best practices and patterns.
@RawCoding
@RawCoding Год назад
I recommend Christopher Alexander
@fahimahmedali
@fahimahmedali Год назад
​@@RawCoding Hello Anton Are you talking about a specific book or something? Can you please share the name or some URL. Thanks.
@CarlosMoreno-go8fx
@CarlosMoreno-go8fx 8 месяцев назад
it's the first video that i saw of your channel and i understood the name: "Raw Coding" hahaha nice content !
@numannebuni
@numannebuni Год назад
These kind of videos is why I love this channel.
@ardavaztterterian1211
@ardavaztterterian1211 Год назад
Finally, someone said it! However, in some cases we do need to save the changes of a service operation to be able to proceed with the next service operation which depends on the previous result. How do we achieve this using the method in the video?
@RawCoding
@RawCoding Год назад
unit of work is meant to be a buffer for an operation, so to speak. Transition between the operations is not a concern of unit of work, so the answer is you don't..
@adrian_franczak
@adrian_franczak Год назад
its more like hack but you can cast and save manually xD
@mikewiebel
@mikewiebel Год назад
Subscribed 25 seconds in. Keep up the great work.
@dmoth1610
@dmoth1610 Год назад
Great video, thanks! I also was questioning this myself a lot in the past. I feel that many RU-vidrs simply need to produce content, which is why they always talk about these buzzwords and patterns… However, I did come up with a situation where I personally liked to still have a wrapper around the DbContext, which is when having a large Domain layer in DDD with different kinds of persistence technologies. Because here, I feelt it useful to separate the persistence logic from the domain part completely. In this layered architecture, you have the hierarchy Application -> Persistence -> Domain, where a UnitOfWork type living in the Persistence layer proxies the SaveChanges call and is used to submit events from the aggregate root to an event bus within the same transaction. I like to use one repository per aggregate root to have an explicit bounded context that must be taken care of. This has the advantage that you can use different technologies as persistence store (like ef core + nosql) for different bounded context within the same application, but you can manage cross bounded context changes within one transaction.
@Kosa39
@Kosa39 Год назад
5:45 - calling BuildServiceProvider is dangerous because this will instantiate your singletons twice (i know that you don't have any but in real case scenerio you'd probably have some). Singleton as the name suggests it is something that is single - only one, sometimes instantiating singleton twice wouldn't do any harm but other times it can break stuff, process something twice etc. .It will be better to use decorator pattern in here i think. Besides that, great work ;)
@Kosa39
@Kosa39 Год назад
Another thing is that without "bulk update" and no rollback mechanisms in place the implementation of SaveChangesAsync does not provide atomicity which UnitOfWork pattern should provide, so again in real world you should have rollback mechanism for situation in which saving throws an exception half way through your dirty objects.
@RawCoding
@RawCoding Год назад
good shout!
@vasiliyfofanov2605
@vasiliyfofanov2605 Год назад
@@RawCoding It's easy to use Decorate extension for IServiceCollection from Scrutor nuget package, IMHO
@dondaniel7643
@dondaniel7643 Год назад
@@Kosa39 I would love to see a gist of your solution on that. Would you catch the exception, delete all the inserted objects and throw a new exception? And I'm not following the first comment about solving it with a decorator (sry I'm plowing a lot of videos about this right now, not grasping it yet)
@Kosa39
@Kosa39 Год назад
Basically answer to the question: "How to handle exceptions when adding/modyfing multiple records in a atomic manner" is as always: It depends. You need to find out what happens in a given situation, maybe no rollback mechanism in your case would be sufficient enough. Let's say you're saving using unit of work items to the redis cache, half way through your objects redis crashes and exception is raised, now let's think how much damage it will do to the system. Well if it is just the cache, user requesting the data will have to reach out to the source system to get the fresh data, is it painfull ? Sometimes it would but more often not i would say, so you can just idk. log the exception and forget about it. However i would highly reconsider is the pattern used correctly, because we know that this is not atomic, and we cannot guarantee atomicity, so we have not acomplished main principle of this pattern. Now, another scenerio, in your UnitOfWork firing bunch of POST requests in attempt to store some data in some external API, again, halfway through API service dies (maybe because you spammed it so hard, lol), is it painfull ? I would say yes, you only saved half of the data that should be saved in a "all or nothing" manner (atomicity), at this point we already know that UnitOfWork pattern is misused in here, you cannot guarantee atomicity (it would be a different matter if your POST request accepted in the body list of the objects you're about to save, in that case you can guarantee the atomicity). Now we're entering the world of distributed computing etc. but in a nutshell you have three options: 1. Eventually rollback commited data 2. Eventually save everything (eventual consistency) 3. Inform a user in some way that the operation succeded partially, and let him handle that There're numbers of ways to achieve this i.e. SAGA pattern for multi staged distributed transactions, Outbox pattern etc. For your second question about decorator pattern, I would simply use Scrutor nuget which has Decorate extension method and does everything for you. An example can be found here: andrewlock.net/adding-decorated-classes-to-the-asp.net-core-di-container-using-scrutor/#using-scrutor-to-register-decorators-with-the-asp-net-core-di-container
@milutinbeslic4754
@milutinbeslic4754 Год назад
Quick question Why do you remove redis cache service from the service collection? Won't redis implementation be overwritten by the Services.AddScoped(...)?
@RawCoding
@RawCoding Год назад
correct, I unregister it because not many people know how to do it so I wanted to show it, and hopefully someone asks a question like you did. If we don't unregister there will be 2 implementations in DI, so you could resolve an IEnumerable which I didn't want.
@milutinbeslic4754
@milutinbeslic4754 Год назад
@@RawCoding Ah ok, fair enough... Threw me for a loop for a moment. Thanks for cutting through the bullshit with your videos!
@zagoskintoto
@zagoskintoto 10 месяцев назад
In case of EF it may look kind of ridiculous, but I think people mostly do it because "you have to implement the SaveChanges" somewhere. If you have an operation that calls for multiple repositories, following the uow you would have to just allow one call for save changes from the unit of work. If you expose that functionality in your repositories, it doesn't matter which one calls save changes if all of them use the same context. But yeah it looks kind of weird with EF.
@gerarduab9960
@gerarduab9960 Год назад
I full agree with you. But what if several dbcontext must perform some transaction you ending with some kind of interface that manage that behavior between them? It's not considered a uow right?
@RawCoding
@RawCoding Год назад
I've read the definition of UoW at the beginning, let me know if that sounds like one to you. hint: what's considered or what you might have heard might not be correct. imo if you need to use 2 dbcontext's in the same place, you fked up
@kostasgkoutis8534
@kostasgkoutis8534 Год назад
Hmm, this explanation eerily reminds me of 2 phase commit.. In any case, buffering the changes to delay their flashing to datastore is one thing, but I didn't see how you would handle rollbacks in case of exceptions. How do you bring everything inside the body of the same transaction? Does the redit client do it automatically for you?
@RawCoding
@RawCoding Год назад
no youd have to implement that
@tanglesites
@tanglesites Год назад
Wow! I guess I am guilty of this too! I thought the UoW was just a way of abstracting out the Database implementation: aka My Repositories. But this makes more sense. It seems like UoW is a way of exposing as much of the DB API to the framework or application. This can be used to manage transactions, snapshots, and pooling, which is very interesting.
@TheAzerue
@TheAzerue Год назад
Hi One point i didn't understand. DbContext implements UnitOfWork right. Normally its a scoped service means if it is requested many times during the life cycle of a request, we will get same instance and changes will not be written to database unless SaveChangesAsync is called. Why do we need IDistributedCache assuming we are not dealing with microservices. DbContext will temporarily hold the data until all the services are done and then finally changes can be written to database.
@RawCoding
@RawCoding Год назад
Scoped means 1 per request. The reason we’re doing cache is because I’m illustrating what implementing a unit of work entails - it’s entity change tracking per scope.
@mdzieg
@mdzieg Год назад
so if we want to abstract data store implementation we should name it differently. what would you suggest? reason for this is to be able to replace ef with sth else without having to touch services layer.
@RawCoding
@RawCoding Год назад
People talk a great deal about "abstracting data store implementation" But their wrapper/interface whatever will still rely on the underlying change tracking done by EF Core, effectively having a leaking abstraction, if you were to replace it with something else you'd have your whole app break. And then there is obviously the "easy to test" camp of people, again due to leaking abstraction your tests are useless in this scenario because they depend on the leaking behaviour. My suggestion is use DbContext directly, you are abstracting the data provider via their Provider implementations.
@Kosa39
@Kosa39 Год назад
​@@RawCoding So basically what you're saying is that Clean Architecture or Port and Adapters architecture doesen't make any sens since it's all about seperating business concerns from the implementation details ?
@RawCoding
@RawCoding Год назад
They do make sense, I'm saying DbContext is that separation
@Kosa39
@Kosa39 Год назад
@@RawCoding got ya
@roko567
@roko567 Год назад
@@RawCoding repositories are a leaky abstraction in this sense only if you assume that they commit changes. Which they should, if you're being dogmatic about the meaning of repositories. But from a pragmatic point of view, separating the DAL into repositories and unit of work makes testing and development so much easier and faster. As long as you don't assume that a repository is committing changes and assert that SaveChanges is called, the tests are not useless.
@rankarat
@rankarat Год назад
Can you use larger fonts in your ide/editor?
@RawCoding
@RawCoding Год назад
Are you watching on a phone?
@roko567
@roko567 Год назад
At the current project I'm working on, we're using the unit of work pattern to: - keep a buffer of integration events - ensure the integration events do not get flushed if the database transaction fails - implement a rollback functionality that allows us to discard events and tracked entity changes The last one is important because sometimes you want to discard and rollback the previous changes (entity modifications and integration events) and make new changes (for example, setting a job state to failed) within the same scope.
@henkoberholzer8081
@henkoberholzer8081 Год назад
I'm interested to know, how do you ensure the integration events are flushed after confirming that the database transaction succeeded? Could the system be left in an inconsistent state if flushing the events fails for some reason?
@anthonytrad
@anthonytrad Год назад
Great that you're investing more in the channel! Explaining the concept and use cases is what makes things usable... I totally agree with the shit part, wait till you see the DDD and "Clean Architecture" videos.
@RawCoding
@RawCoding Год назад
I am planning on coming after the OOP & Architecture community later, need more data :D
@pyce.
@pyce. 8 месяцев назад
Yeah, cause anemic models are waaaay better...
@Metalyga
@Metalyga Год назад
UoW and clean architecture is most topics where people mash things together and not doing it in a proper way...
@austin.valentine
@austin.valentine Год назад
The issue is really more caused from needing custom Unit of Work and Repository wrapping EF. There are features that people want or need that aren’t built into EF that people are putting in their wrapper abstractions. Some that I have seen include caching, translation to DTOs, and simply defining a locked down but clear data access interface specifically to see preclude IQueryable abuse and be able to see all the “queries” in one place easily. I’m not saying all of these are good, but they are reasons. And since Microsoft hasn’t built and provided the necessary abstractions to reuse as a buffer layer, you have to build your own unfortunately. Most people do it half-ass and let the EF context UoW do the change tracking on the actual data, but they do everything else in their Unit of Work/repository, even if it is just writing a query and returning the results. The whole thing seems crummy and like it takes away the power of Ef, but people don’t want to deal with entities in their application or UI code
@RawCoding
@RawCoding Год назад
Sounds like blind dogmatic belief
@austin.valentine
@austin.valentine Год назад
@@RawCoding To some extent, yes. But, which part are you referring to? Caching data temporarily for performance reasons is legitimate, so is translating to DTOs in certain contexts. So while there is generally some dogma involved with people using “UoW/Repository over EF”, you can see why they think it is necessary, right? I am asking you to provide an alternative explanation because I have learned things on your channel.
@RawCoding
@RawCoding Год назад
I’m referring to bolting things on to / around the db context and calling it unit of work.
@austin.valentine
@austin.valentine Год назад
@@RawCoding I don’t think we disagree, but what you are saying is vague. As a counterpoint, doesn’t it follow logically that extending a library unit of work needs to still support and “hook into” that unit of work? I am trying to nail down the crux of what makes people do this specifically with EF. Your point about the general concept of unit of work is correct, I am not disagreeing there. But instead of trying to understand why people do this with specifically over EF, you demonstrated the general concept of unit of work using a completely unrelated example. While I think that is helpful in its own right, it fails to address why people do the behavior you are addressing. It comes down to more than just not understanding what unit of work is, even though I agree that many don’t. The issue is needing to extend EFs unit of work functionality and/or provide abstraction from Ef. There is some legitimacy to those ideas (not that the conclusions are correct) that I think addressing would clear the air more in addition to the content in this video, which I am saying is good; I’m not disagreeing with it. It just isn’t enough.
@RawCoding
@RawCoding Год назад
It’s hard to explain why, tho let’s start with a list of how people try to extend the dbcontext 1. The main thing I see is they wrap the dbcontext behind interfaces to remove a dependency (which is bs) or to make it more testable (which is also bs) They rely on change tracking behind the interface which the interface doesn’t guarantee, so it’s a bad interface and bad tests which don’t test what actually happens. People can’t see this because they just took someone elses word for it that this is best practice, how can you not when Microsoft blog says this. If you can continue this list I’ll let you know which points are legit and perhaps why people try to bolt this on to the dbcontext
@gordonfreimann
@gordonfreimann Год назад
finally someone did put an end to this horse crap. btw redis has pipelines so you can bulk send the entire operations.
@soohyunjeon9348
@soohyunjeon9348 4 месяца назад
I would like to purchase and watch your lectures. However, I am Korean and I hope that your paid lecture videos will have Korean subtitles. Is it possible?
@RawCoding
@RawCoding 4 месяца назад
They dont
@pavfrang
@pavfrang 11 месяцев назад
Building UnitofWork around the EF context is silly, however it is more meaningful to build around Dapper connections.
@zaharivaklinov
@zaharivaklinov Год назад
Overall, all of what you just showed us is already implemented by default by the Db context's UnitOfWork, correct? This seems too complicated to me, probably because you're not going into details around each of the lines of code.
@RawCoding
@RawCoding Год назад
exactly, DbContext implements unit of work putting stuff around it and calling it unit of work is bullshit "change tracking" and "figuring out what to write to db" are capabilities of unit of work
@TheHrt5
@TheHrt5 11 месяцев назад
Isnt the whole point of implementing UoW on your own in order to be able to swap out implementation without worrying about it in the future ? You mentioned that EF Core already has that built in so that adding a UoW "wrapper" is "bunch of bullshit". But what if you decide to replace EF Core with something which does not have UoW implemented in it ? Isnt that the whole point of implementing additional wrapper ? So that you always have UoW ready, regardless of the implementation ? Im pretty sure that most of the "youtubers" you called out are quite aware of the fact that EF already uses UoW. I mean, im not a professional, but even to me it was quite obvious the moment I started using EF.
@RawCoding
@RawCoding 11 месяцев назад
As soon as you swap out ef core it’s no longer unit of work because no tracking is happening of what should be written back to the database. You have to make sure whatever you are swapping it to has UoF under the hood as well. So no interfaces and wrappers are not unit of work.
@TheHrt5
@TheHrt5 11 месяцев назад
@@RawCoding Hmm, valid point. Will try to dive more into this once I have more time. Thanks for the explanation.
@quranthecompanion4528
@quranthecompanion4528 Год назад
bro, spot on. there's a lot bandwagons out there.
@HighWeed08
@HighWeed08 Год назад
No more LinqPad?
@RawCoding
@RawCoding Год назад
No LinqPad on Mac :(
@ivandrofly
@ivandrofly Год назад
I like how you call "shit" by its name 😂😂
@RawCoding
@RawCoding Год назад
it is what it is
@meetingattender8132
@meetingattender8132 Год назад
Lmao this was awesome
@thethomasproject
@thethomasproject 8 месяцев назад
This is great but the real problem here is that there isn't a good overall tutorial that takes you from start to finish, explaining the proper way to implement. Your tutorial does point out the problems, but I'd love to see something a little more expansive. Remember some of us are a bit dumber, lol.
@pyce.
@pyce. 8 месяцев назад
The purpose of hiding DbContext behind an interface is to be implementation agnostic. You did demonstrate how to implement unit of work for something that doesn't support it built-in, but you failed to point out why hiding DbContext's existence (which comes from a library you might not want to depend on later) is, as you phrased, "shit".
@julealgon
@julealgon 11 месяцев назад
This video does a disservice to the community in how the decorator is registered. Building the service provider manually like that is a _terrible_ practice that one should _never_ do. If you need a decorator registration, use a manual factory registration or even better, rely on Scrutor to do it for you. It's ironic how you called out other youtubers for misrepresenting the unit of work pattern and then you go and create a video that spreads an absolutely terrible practice due to sheer lack of skill and/or understanding. You should consider doing another video correcting this massive mistake at least so your community is not too negatively affected.
@pickle1987
@pickle1987 6 месяцев назад
Actually you don't really understand why people use UoW
@diamondkingdiamond6289
@diamondkingdiamond6289 2 месяца назад
Is there anything this implementation would not cover for a reason you would use the UoW pattern?
@dimitryjo0n
@dimitryjo0n 3 месяца назад
One of the worst teaching videos I've ever seen is this video. He is working for himself and explain for himself. Too bad!
Далее
Understanding CORS with ASP.NET Core C#
17:44
Просмотров 11 тыс.
100❤️
00:19
Просмотров 2,9 млн
Strawberry Cat?! 🙀 #cat #cute #catlover
00:42
Просмотров 10 млн
Don't let MediatR hold you back, try this...
17:48
Просмотров 12 тыс.
The Unit of Work Design Pattern Explained
12:37
Просмотров 21 тыс.
Introduction to Plugin Architecture in C#
21:26
Просмотров 21 тыс.
Don't Use Polly in .NET Directly. Use this instead!
14:58
Exceptions are evil. This is what I do instead.
24:41
Using Unit of Work Pattern
13:43
Просмотров 34 тыс.
What you should know about Threads in .NET
14:52
Просмотров 7 тыс.
Don't throw exceptions in C#. Do this instead
18:13
Просмотров 252 тыс.
Understanding .NET C# Heaps (Deep Dive)
28:23
Просмотров 12 тыс.