Stop using Entity Framework as a DTO provider! - Chris Klug - NDC Oslo 2023

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
okay so I guess it's time to get started I did not expect this many people at an Entity framework torque I I've the scary thing is based on how people react when I'm doing an Entity framework talk I can only come up with three reasons that you'd be here one is you have really strong opinions that want to boo me throughout the entire hook two you're here hoping for my demos to explode so that you can tell your friends that there's another proof that you shouldn't use Entity framework or three that you actually genuinely care about using Entity framework and I really hope it's the last one because the other two suck for me anyway we're going to talk about Entity framework um and I'll get into what I want to talk about in just a second I just want to go ahead and start off by saying that my name is Chris kloge I work as a software software developer slash architect slash public speaker slash whatever you pay me to do in the tech sector at a company called activesolution in Stockholm and I've been on the Microsoft platform since about 1999 professionally since 2000 uh I I've been lucky enough to be Microsoft MVP for the last 11 or 12 years or something as well like that as well so I I've got to say The Good the Bad and the Ugly for for NET Framework and I do agree with people having opinions about Entity framework I'm going to talk about Entity framework core which is better give it a chance I tell you but before we get started I just want to go back to this slide here it's like stop using Entity framework as a dto provider and I guess that might need a bit of explanation to some people and what do I mean by a dto provider so dto stands for data transfer objects they are very specific type of objects and to explain what I mean by a detail provider I went online and I googled the data transfer object and I ended up on Wikipedia and it says in the field of programming a data program a data transfer object is an object that carries data between processes okay details are simple objects that should not contain any business logic but may contain serialization and deserialization mechanisms for transferring data over the wire okay this pattern is often incorrectly used outside of remote interfaces this has triggered a response from its author where he reiterates the whole purpose of details is to shift data in expensive remote calls so what the heck has that got to do with it with Entity Framework honestly nothing the only reason I bring it up is because a big portion of people in this room today probably use them together with Entity framework because you honestly been told so you end up with objects that look like this this is what you map with Entity framework right a lot of you probably do yes a couple of hands coming up I am sorry if I'm going to say that you're wrong but I'm going to say that you're wrong but I'm an opinionated so I might be wrong as well but what you end up with is this obviously has no functionality whatsoever and they have no encapsulation their public get sets for everything no functionality they are stupid dtos and I guess the question is then what's wrong with that right I mean you some of us have been using the this way this type of objects for years to access the database and I'm gonna I'm gonna come back to what's wrong with it but this answer here with I've been using the this types of updates for years yes you probably have a lot of us have probably mapped that type of objects with Entity framework and you know why because if you go to the getting started with ef core on internet on the Microsoft website and then you scroll down it says create a new project install Entity framework core create the model uh creating a DB context and then create your classes that you want to map and look at that how unexpected that they are using really ugly dtos and they do really stupid mappings and they map both IDs both blog ID and blog and a bunch of anti-patterns according to me when it comes to Entity framework but that's the way that we've all been taught and to the framework that's where you start but I think we can go further than that and we can use it better but why well then brings us back to what's wrong with that well where do you place the business logic if all of your data comes out into these stupid data objects that only have public get sets where do you put the business logic well obviously you put those in Services right or commands depending on if you want to do mediator or whatever or services and sure I've been doing this for years as well it's what we've been taught to do in a lot of cases but then the question is or just write the comment is so you spread all your business logic out across a bunch of different classes because the business logic is no not connected to the data in any way because the data is in your dtos but you take all your business logic and you spread that across a bunch of different commands and a bunch of different services and things like that so there's no way where you can go in and look at the class and go and say what's the business logic behind this object what am I allowed to do what am I not allowed to do because it's spread across your your entire base or code base and where's the data validation well once again the answer is I put that in my service or I put that in my command Yeah so basically you're spreading your data validation all across your application as well it's in different places different things all over and you may need to make sure that you have the same same validation in different places for certain things it makes it really hard to come up with a conclusive way of saying what's the validation logic around this property here and then yes somebody's gonna say no no no no I'm better than that I use a validator class or a validator service that validates all of the things that we can share among our services so we we take that piece of the stuff and we put that into one thing and then we couple everything else in our solution to that one class coupling is good right nope so that's not a great solution Heller so you instead either so you're still forcing the services and commands to remember to call it as well so yes you can put your logic and your validation into that class but unless the people building the services remember to call it it's never going to validate anything and then we have the people that answer that's why we have setters we put validation logic insetters I honestly heard another fellow speaker say that to me today down here and I was about to explode and then I realized that I actually liked him so I didn't uh because it's not so once again Microsoft has actually often told us that in this Setters you can do validation logic to validate that the data you're setting is correct right well if you go online and you go to the design property design for the microsoft.net framework architecture stuff and you scroll down to the that part there it actually says property Getters should be simple operations that should not have any preconditions if a getter can throw an exception it should be probably be redesigned to be a method we should not put validation logic in our Setters because the only way to notify a user that you're setting it to the incorrect value is by throwing exception we should not throw exceptions out of our setters so no but if we just ignore the fact that I just told that all this is wrong I want to show you what it looks like if you build it like this and once again I am truly sorry but not sorry if you're building it like this now hopefully it will have a discussion about it and you can see that there are better ways to do it so what I've got is a simple little project here I've got a a simple object here here on my my dto objects or my data objects as we tend to call them and there's like an address in here you can see I'm using attribute based things for Entity framework which I dislike because attributes on my domain classes has nothing to do with my domain shouldn't be a part of my my domain classes but that's a different story and then all of them are these public get set things they're they're stupid objects no logic at all and instead in my I have a raw thing here I call it raw uh I have my personal let's take the vehicle service actually let's take the personal service in the face so I have a personal service in here this is the thing where I interact with my my entities my objects my data and I make changes so in my personal service I have like a get person by ID and in here I do a bunch of I get the context I pull out the people I do an include to get the addresses blah blah blah but the thing that gets interesting is when you get down to things like set here is a bit of validation in my code to validate that it's not null then I go and pull out my person once again here's a bunch of database logic that I have have in every single one of my methods I have to run this code and then in here I pull out the to check to see if there is an address that is current if there is a current address then reset that to not be is current anymore so it's current equals false then create a new address add the address to the address list oh yeah there's a requirement here to only save the last three addresses as well so if there are more than three addresses remove the extra one so they're only three business logic spread out across my services really hard to find and then down here I do a save so we got everything in here we get our validation stuff we got our business logic we get everything in this class here which I guess is kind of okay but it gets complicated when you start doing more classes and or more services and more functionality because your business logic and your validation as I said gets spread out across everything I got another one in here which is the vehicle service it also has a bunch of stuff with vehicles so if we do Set current owner it pulls out the current owner if there isn't one or if it's the same owner it throws an exception it finds the new owner if that exists then if there is a current owner it has to set the the current owners to date so when you stop being the current owner to today once again business logic spread out across a bunch of different things kind of annoying and most of the reason that we do this I think is because Microsoft is showing that in our samples this is the way your map thinks about the framework doesn't have to be so what I've been working on is this very simple domain here it has only kind of four things Earth yeah so you got an owner which is a person depending on who you ask so if I'm talking to the people working with vehicles it's an owner but if I'm talking to the people managing the people's addresses it's a person but it's the same thing right owner person can do the same thing but from a domain-driven perspective it's a little bit different Vehicles has one or more owners owners have the one or more Vehicles so there's a many-to-many relationship an owner has multiple addresses and the addresses can be of type delivery address or invoice address that's kind of the model I'm working on very very basic I know and that we can kind of probably agree that that turns into a database diagram that looks something like this so we have a Vehicles table it has a vehicle's owner's table that keeps track of the many-to-many relationship between the vehicle and the owners we have a people table that has the owners slash people in there and we have a an address table that has a type on there that defines whether or not it's a delivery or an invoice address this I think we can mostly agree on right so I kind of showed you that that was what I just showed you I don't know why I have an extra slide on that sorry um so the question is can we make this cleaner can we make this better than what I'm what I've showed you before where we spread everything out and and things like that and we can we could make it uh a bit more Orient object oriented and more domain driven design so I want to highlight the fact in here that I do like domain driven design I'm not a hardcore designer and Define the design person I don't do all of it but I do like the idea of object orienting I do like the fact that functionality and business logic and validation comes together with the entity that I'm working with so that everything is in one place if I'm working with a vehicle everything about that vehicle should be in the vehicle class so it basically encapsulates its own data and its own logic so that I can work with it so a solution to that would be well we can we can wrap the Entity framework objects and make them more entry-like okay so so what would that look like well imagine the vehicle class I just showed you the vehicle that was stupid and had public ad sets we could make this out of it so we could turn that vehicle class I just showed you that's public gets a dto and may call it vehicle dto instead and map that with empty framework and then we inject that into a separate entity which is called vehicle in this case and then we add the actual functionality and the validation and the the manipulation and all of this functionality to that vehicle class and then you end up with things like exposing Vin then goes and exposes vehicle.vin and getting the current owner goes vehicle.owners or first or default and so on so we we instead of having it in our service we put it into this extra wrapper class and that kind of works it ends up looking like this so I have a wrapper here we end up with a personal service that looks more like this set person address so I go and pull out a person with IDs I haven't I had an extension method here that does the The Entity framework work for me so it pulls out just the the query to the context and it then returns a person.create that returns a person object and inside the person object I I kind of wrap this whole person thing so I wrap the uh the object that is being used to talk to the database and then I can keep all of my logic in one place so in here we have like a delivery addresses and a delivery an invoice address gets pulled out wrapping it in once again another class in here that has the validation logic and all of that for for address and it means that things like with things like setting a person's address everything is in here I just pass in the personal ID the address type and the properties and then I can put everything in here so it goes in here like we did before and instead of having all of the business logic in this class it just gets forwarded to person.set delivery address person.set invoice address and inside those we have encapsulated all of the functionality so now I'm not spreading my functionality across things what I do is I basically go ahead so that uses a helper function down here I put my validation logic in here I I do my my business logic with resetting the is current back to false and I limit the number of addresses and everything all of that is now nicely done inside of this class except for the fact that it looks like crap except for the fact that everything is basically just piggybacking on this person dot object so I have this internal representation of person that I'm wrapping and everything has to manipulate that everything goes person.addresses person.firstname person.id person.person blah blah blah it looks a little bit better because it means that our services don't include all of the validation logic and all of the business logic all the service really does it it pulls out the person and then asks the person to set the new address and all of it is in one place so it's getting a little bit better and I'm a little bit more happy but why why am I using Entity framework to map to an object that I'm then wrapping with another object to expose the object why am I not just mapping straight to the object that I want why why are we not using Entity framework like an object relational mapper Entity framework can map ways in ways that we can make it work for us Entity framework can map private Fields it can map Constructor parameters it can map actually it can even map properties that don't exist on your objects you can make Entity framework work for you instead of going and basically surrendering and saying oh the easiest way to use Entity framework is to map it to public ad sets all over the place and then spread my stuff across so I actually had to go in and get the definition of an orm because I kind of didn't know how to formulate that in best way I ended up in this place I have no idea what Prisma i o is but I like the definition so an orm or object relational mapper is a piece of software designed to translate between the data representation used by the database and those used in the object-oriented programming that's the important part design to translate between the data representation used by databases and those used in object oriented programming we're not doing that in the previous example we are not we are not translating anything at all we are mapping one to one table goes into class column goes into property and then we're good to go that's not translating orms are way more complicated than that and can do way more so we can map to the objects directly yes we can so before I want to show you that I want to go back to this thing here because another thing I kind of like about day-to-day is what they call bounded contexts we work with classes or entities in different contexts and in different contexts different entities mean or different things or have different functionality or have different properties so for example in this case we've got vehicle and then I have owner slash person because the people working with the addresses they talk about people they talk about this person has this address but over in the vehicle side they're basically saying oh this vehicle has this owner the owner and person object is mapped towards the same table in the database but they are different things so on the people management side of things I want to be able to change their names set their addresses and things like that while on the vehicle side I really only want to have an owner's first name and last name to basically be able to say that this owner is owned by whoever because I care about owner things I want to map them like a bit differently so you end up with this in the main driven design you get your different domains subdomains so in the vehicle management subdomain we do vehicle and owner those are the two objects that I want to work with and then on the people management side we get person and address and then I can inherit our address to delivery address and invoice address and they they are separate and they don't need to be interconnected all over the place so there's a problem with that and that's the fact that Entity framework has a strict entity to table-ish I love the fact that I went strict and then ish okay so it is you can only map one table to one class there are inheritance things that made me go ish but basically if you have a table it can only be mapped to one one class you cannot go and say oh the people table I want to map that to the owner class and I want to map that to the person class it doesn't work can only map one has anybody ever told you that you're not allowed to have more than one DB context in the.net framework application no you know why why nobody's ever told you that because it's not true you can put as many DB contexts as you want into your application and you can map however you want to whatever classes you want in multiple different DB contexts that's not a problem at all and we can utilize that to do subdomain based architecture and on top of that if you actually map this like this and you have smaller DB context that has more precise objects you probably don't want to turn on lazy loading instead you pull up one object and you Auto and eager load the things that you need onto it because you don't end up loading your whole database because your domain isn't that big because you have a smaller DB context and also please don't turn on and lazy loading it has a whole set of problems as well EF core didn't even have lazy loading until quite recently there is one problem that I can't really fix for you well I can but you're not going to like it that's the fact that Entity framework cannot generate a database based on multiple DB contexts so if you start doing One DB context for the people management style and you do One DB context for a vehicle management side there's no way to go dotnet EF migration blah blah blah just doesn't work so the solution to that is that you have to write your own migrations having that said right in your migrations is not hard if you think writing your own migrations to a database is hard and complicated and you don't understand it you probably need to go back and read a book about database design because you need to understand that part as well I don't want to be an ass but you need to kind of understand that you need to understand what an index is and what data types you want to use and things like that and by doing it manually on your own you're going to get more knowledge about that area and it's going to help you a bit more so if I take all of those things and we move in and look at mapping this in a more day-to-day Style I have a a project here if they collapse that we go to The Domain so in my domain here I put everything in one one class Pro one project because that's all I need I have a vehicle management and I have a people management folder those are my sub domains those are my sub contacts that I work with and I work with things individually there so things in the vehicle management don't bleed over to the people management and vice versa I have a migration folder that has a migration context which is just a dummy DB context I will explain that later on why I've got that but it's in there it's not going to be used for anything really and then in my infrastructure stuff I have a service result class that helps me to return stuff from Services that's it so that's kind of it so I'm going to start out with the Vehicle Management side of it and I've got this vehicle service that looks identical to the one that I showed you before except for the fact that it's throw new not implemented exception all over the place so we're going to go ahead and make some changes here so first of all I have oh yeah I also have details in here so my dtos are the things that's going to be returned by the the service so that my API can then return that to the client and they are stupid objects but they are record objects instead so they're actually immutable but it just makes for very small and simple dto objects that we can return from our interface or from a service so what I'm going to do is I'm going to go ahead and I'm going to create my first entity here and it's going to be called vehicle I'm going to add a new class and I'm going to call it vehicle like that I'm going to replace this clap start and we're going to go back up and explain what I did so vehicle here it's a very simple object so far but it is an object I like it's an object that I the way that I want to have it so my vehicle has a list of owners I can create a VIN or a vehicle by passing in a VIN this is private though so there's going to be a static Constructor method for it in a little while but basically I expose a VIN once again it's a VIN object as well so it's not a string it's an actual value object that allows me to put in my validation logic around my VIN which is that it has to be 17 characters that follows that regex and I can then use that VIN to have my validation logic around my VIN so I don't put validation logic into my vehicle class and say that this is a string and add validation instead I accept Vin the object as such and the object in here has things like when you create one it validates it and throws an exception if it doesn't work it has a try create method that first validates it and then creates it so we we encapsulate the logic around what a VIN is instead of just doing a string and then I have a list of owners and instead of having logic to figure out what is the current owner and the previous owner in some service somewhere that is built in here so current owner means that I take the owner's list and I find the first one where the two property is set to null and all the previous owners are basically the ones where null is not set to the two is not set to null and the actual owner object well that is an ID a first name last name to and from this is kind of the object that I want to have this is what my domain is going to look like when it comes to this system here so I'm going to move that now I don't have a database so I'm going to have to do some migration stuff so I'm going to go ahead and I'm going to create a migration in here so I'm going to go add class Vehicle Management context migration I think was supposed to be called Vehicle Management contact migration that looks decent let's do that I'm gonna do like that and then I'm going to replace this and I'm going to run a small Snippets function I have in vs code the error in Visual Studio it allows me to create a migration so that's going to go one it's going to be called V Yoko context migration it and DB context migration context so I mentioned that I have this this dummy context called migration context all migrations needs to be mapped to some DB context for me to be able to run the migrations I don't want to map this migration to the DB context that I'm putting into the vehicle management system or the one that I put into the people management instead I take all of my migration I map them and connect them to this bogus fake migration context that allows me to basically just have a way for me to run the tools that's why it's in there it is only used for tooling and then in here my migrations they're actually not that hard to do so to put in my migration I just do something that looks like this migration Builder create table I won't have a table called Vehicles it should have two columns ID and Vin I tell it to be int and N varchar 17 and add a primary key PK Vehicles down here which points towards the ID for the uh people table once again just a create table create the table it's not that hard to figure out yes I do agree that sucks that sucks really bad I know but you Google how to do it and it will tell you and then you're good to go it won't be that hard and then for my many to many table I create a vehicle owner's table vehicle ID personal ID from to I also love that in SQL you should use date time 2 not daytime because daytime 2 is better because it has a two in it um and then it has a primary key but I also set up my foreign keycaps here in here so there's a foreign key to the people table there's the foreign key to the vehicles table and I put up something called referential action saying that if you delete one of these things then this entry should be deleted as well so we get a lot more control in here and then finally we need some indexes in here that basically says oh there's an index on the person ID column of the vehicle owners because that is what I'm very likely going to be joining on and then for it down well I just dropped my tables in the right order I'm done good too full control over what's in there and it's still a migration yes I didn't go dotnet EF migration ad blah blah blah which I always forget anyway I had to do it manually but I personally don't find it that complicated so now I have my migration that adds the tables that I need I have my um vehicle uh here it is so there is one little problem I really like this idea of having a list of owners but the problem is that if we go into the owner class it has a first name and a last name and an ID and a two in the from the astute onlooker now will go well to and from comes out of the person vehicle table while the ID first name and last name is actually part of the person that is in the other table so yes you can't map everything perfectly I I do agree with that so what we're going to do is I'm going to go ahead I am going to have to have a class called person in here as well so I'm going to create one of these really dumb simple dto-like objects except for it being private set because inside this domain sub context I'm not going to modify the first name and last name of a person so it's private sets I'm going to move that into its own class and then I'm gonna go up here and we're going to add actually I think I'm gonna do this I think this is what I'm supposed to do there it is so what I'm going to do is I'm going to add an internal private person object here and then I'm going to expose ID first name and last name by going person dot last name person.firstname person.id but to and from is going to be mapped here so I'm I'm kind of Faking the object that I want because because of Entity framework I do need the person class in there unfortunately but it is private so from a from external point of view if I go vehicle dot current owner dot I'm gonna see first name last name ID to and from I'm not gonna have to go vehicle.owner.person.firstname am I the only one that dislikes that way of going like owner.person.firstname it looks weird but I'm gonna I'm gonna make that concession for Entity framework I'm gonna go and say that's just the way it is we're gonna have to accept that now let's close those down so inside of my vehicle management context I have a DB context as well as you can see it is fairly much empty it's a base thing I've overridden the on model creating because this is where we do our Entity framework mapping and what I'm going to do is I'm going to go and uh let's see I'm at 09 this is what we do for mappings instead of adding attributes to it or just trusting EF to do magical stuff it's not that complicated you go hey I want to map my vehicle class it should be mapped to the table called vehicles I have a property of type A so this is weird Okay that is weird because if I go and look at vehicle it doesn't have an ID field right there's nothing in here that says ID there's no integer in here but I do know that in the database there is a column called ID right that has an integer which I use only for things in the database I never query a vehicle based on the ID I query it based on the VIN so why in the world would I expose ID inside my domain when it's only there for the database to work so what Entity framework can do is that if you go and say x property of type and you do of int and then add a string in here it is actually going to go and say oh I can't find anything called ID so I'm going to create what's called a shadow of property which is basically Entity framework keeping track of that property on the side so when you ask for a vehicle it's going to give you a vehicle it's not going to have an ID but you can go and ask enter the framework to say hey by the way the ID that you pulled out for that thing over there can you give that to me so Shadow properties are nice because they allow us to put things in there that we don't want to expose then I map my VIN and the reason that I explicitly map that instead of just trusting it to map the VIN property to the VIN column in the database is because this is of type Vin and ended framework has no idea what a VIN is it only knows strings and instant and things like that so I'll get back to that in a second then I do a one-to-many mapping here with Entity framework I do agree that the Syntax for that is a bit weird but once again I go with a type here and a string definition and by using a string definition that actually corresponds to a private field it will map to the private field of the class so I don't have to map public things I can map private Fields as well so if you want to hide something and make it a private thing then use this property the property of or has many of and then use a string instead and that will go and look for internal implementations as well or internal Properties or fields then I also go ahead and I go and say hey that that owners thing that you just created can you please Auto include that so whenever somebody asks for a vehicle can you please join the the owners straight away and have that pre-populated so I don't have to go and say owners dot include and things like that to get it included when I do the query and then I tell it to ignore two properties because those are calculated properties so those are the ones that I'm calculating myself so those shouldn't be mapped and finally I tell it that it has an ID of type and then passing the string ID so I mapped the ID column once again as a shadow property but it's also used as a key even though it's not even part of my class and for the VIN if I run this now it's going to complain and say I have no idea what a VIN is I don't know how to handle that well Entity framework has has conversion like that and when you want to convert it to the database thingy then just return x dot value and when you want to turn it from a string to a VIN you just do Vin dot create X like that and it allows me to run queries on Vin in the uh when I do my SQL queries and everything like that as well so it does have conversion capabilities and if you don't want to have like an inline conversion like this because you have a type that is being used in a lot of places it can be created as a class and added to your Entity framework so it recognizes wins in all places so that mapping that wasn't too hard in my opinion and then if we do that we map the vehicle owners thing it's kind of the same thing map to table vehicle owners add a shadow property called vehicle ID because I don't want to expose that map the internal person field to to the type person or to include that thing for me and ignore these three properties because I've already I calculate those on my own however I do not go in and specifically map to and from because they happen to have the same column name as their property names and that means that they just magically work without any configuration and finally I tell it that it has a compound key so basically the primary key for this this object is both vehicle ID and person ID so mapping once again not overly complicated in my opinion once you get used to it and then finally the person thing mapping a simple object like person which is just your regular get set dto thingy you just tell it what table to use and what key to use and then it will just map column to uh to fit it properly and you're good to go so with that mapping it's not overly complicated in my opinion with that mapping we can then go to the vehicle service and we can start implementing things but before I can Implement anything we need a way to get hold of entities right I need a way to pull out the entities from the database and talk to Entity framework and I'm going to do that in a very simple way I'm going to go in here I'm going to add a Constructor that takes a vehicle management context and now somebody is going there going oh no Chris you don't have an a repository pattern and you don't have a unit of work and you don't have I don't care honestly entity Frameworks DB context is a unit of work because when you call Save changes on it that is your unit work that is what it's going to save and for the the red the repository thing it works as a repository it gets things from the database it's a bit complicated to work with which is why I have a very simple way of handling that in my folder here with my DB context I'm adding a new class called Vehicle Management argument context extensions like that and I think I can replace that with this nope there it is and it gives me this vehicle with win method as an extension to my DB context it takes a VIN so I can query on Vin as you can see down here even if that is a type that EF core doesn't know about I can query using it because I added that conversion thing and then I also make it possible to pass in Arsenal tracking so that if I don't want Entity framework to track my object which is if I only query I don't want it to keep any references to it so I can just pass that in as as no tracking and it means that it's a little bit better performance so now in get vehicle by VIN we can go ahead and do something like that and then to get my vehicle all I do is context Dot a vehicle with vin and I pass in the exclamation mark like that because I do a try create so I do if not win dot try create basically you passed an invalid win then I return an invalid win exception then I pull out the vehicle based on my win and then down here this needs to return a dto and my solution to returning dtos is kind of the same as I have for my uh context thing so I'm going to go actually sorry that needs to be a weight like that um so I'm going to go ahead and add a new class in here dto extensions like that let's see if it's there it is so these are just stupid extension methods so I extend the vehicle with a two model that basically creates my my dto object I extend the owner class with the two model that creates me an owner dto very very simple way of doing it which means I can go in here and say vehicle.2 model and somebody's going to say but you could use automapper for that yes I could but this is as simple basically so I kind of like that so this this becomes quite nice I don't have to do like oh yeah and we also as I said for querying as no tracking true so because I'm just reading it and returning it to your client straight away I don't need to store it for my unit of work which means that I can just not do ask no tracking the next one is get vehicle by ID that's a little bit more complicated because if we look at this thing here and we do that thing yes so to get this to work I need to implement vehicle with ID so if I go over to my in my vehicle management extension thingy and we add ah that so here's an interesting thing then so I have this extension method called vehicle with ID I need to query my vehicle by ID this is just because I need it for external things I don't want it in my domain but the problem is as I mentioned before ID doesn't exist on my vehicle because it's not part of my domain I still want to be able to query it but it doesn't exist because it's a shadow property now EF has a solution to that you just go EF dot property of type int that is found on object X and called ID that thing should be same as ID so I can query my shadow properties as well so even if they're not defined on my object and I can do the same thing here with private Fields so if you've mapped it as a private field I can use EF property and use the private field to do queries even if it's not publicly available which is a bit weird but it actually works pretty well um so we go back here that seems to work to detail that seems to work nice once again looks pretty clean get current owner by VIN once again not very complicated to implement we do something like this first we go and ask did the VIN that you passed in is that a valid win otherwise return invalid win exception then we go and look for the vehicle that you you are the VIN that you passed in is supposed to be for if there is no such vehicle return the vehicle not found exception look at the vehicle and see if there's a current owner if not return an owner not found exception and also notice that I don't throw exceptions I return a result I return a service result of that type and then finally I just returned vehicle.currentowner.2 model not a bunch of logic in here more than more more than the obvious things of what I need to return so invalid then vehicle not found owner not found the exception stuff and then other than that there's nothing in here validation of my VIN is being done inside win.create try create if we go with I think the next one that I'm supposed to implement is ADD vehicle in my code here so we'll do it with our vehicle fairly simple as well it becomes these pretty clean things fairly easy to read look at the VIN If it's incorrect throw it return an exception go and find the vehicle with the VIN if it doesn't exist or sorry go find a vehicle with that VIN if it exactly exists sorry throw an exception because you can't create a class with or a vehicle with the same win twice so duplicate win exception go and try to pull out the person that is supposed to be the new owner to make sure that that person exists if it doesn't exist then go personal sound exception then we go into a try catch we go vehicle.create I'm going to create that method in a second with my owner and then I just go context ad vehicle and save changes no logic in here whatsoever and if we go into this thing here we can go and create nope sorry I need two things that was the wrong one first of all person with ID doesn't exist so we need to add that sorry that was this one so that's just another one of these queries that I needed to use oh yeah one thing I forgot to mention I my context never has it doesn't expose any DB set of X or of T instead of exposing stuff from my my DB context so this thing is really clean it doesn't have any properties on it like you normally do with DB set of T instead I use the set method to create those DB sets on the Fly instead which removes it property from that thing and then in here vehicle.create well looks like that create a new vehicle with this Vin go to the owners add owner how do you create owner we go owner dot create inside owner.create we can have validation logic if we want that but right now there's nothing in here because it's very simple uh oh yeah there is actually logic in here because what happens is that it automatically sets the from date for example in here uh by default so there is a bit of logic in there as well but not a whole lot and the last method to implement is Set current owner here was also a bunch of logic in here as such and all that turns into is this once again try create on the VIN to verify that it's a valid win that you've passed in get the vehicle with the vein return an error if it wasn't found find a new owner return an error if that owner doesn't exist or that person doesn't exist then call and here we go ahead and we call set owner so we take that logic of what needs to be done when you set a new owner and we move it in here which is much nicer actually I wanna well no that actually works so in here we now move that logic into the class saying set owner means that if there is a current owner and it is the same that you're trying to set that's an exception so we're going to throw an exception and throwing exceptions are fine from methods but if it if there is an owner and it's not the same owner then we need to end the ownership that is actually handled in a separate method in here so I keep that logic in here so together with this ending ownership is actually just setting the two thingy but once again I get that as a part of my owner object so the logic around the owner object is in the owner object which is where I think it should be and then finally I just go and create a new owner I add it to my owners and I'm good to go and the service out here then becomes very nice and simple vehicle set owner inside the try catch and then save changes async no validation logic except for finding objects to see if they exist no business logic because it's all in your entity which makes it a lot easier to work with so if I close this down now it's going to be very repetitive in the in the other domain because it's it's kind of the same thing but there are some different objects in here but I still want to show you when you go into the second domain and the first thing we're going to need is we're going to need a new a new class in here so we're going to need a new migration so I'm going to create a people management context migration
Info
Channel: NDC Conferences
Views: 25,208
Rating: undefined out of 5
Keywords: Database, .NET, Architecture, Chris Klug, NDC, Conferences, 2023, Live, Fun, Oslo
Id: ZYfdjszs8sU
Channel Id: undefined
Length: 46min 46sec (2806 seconds)
Published: Fri Jun 23 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.