Object Mapping - Mapster | ASP.NET 6 REST API Following CLEAN ARCHITECTURE & DDD Tutorial | Part 7

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
today's video is all about object mapping using Master okay so what is object mapping well it's basically when you have two objects and you want to map the properties of one object to the other object you might do something like this this is called manual mapping right where you basically say the ID of the user is the idea of the user responds and so on and what happens in many systems which are built using clean architecture or domain driven design or other modular approaches is that you have all these different modules using their own entities and when you pass information from one module to another module then you might have this kind of mapping so this might be if you're talking about a web API modeling here the user to the user response but it could also be taking the user and mapping it to the object which will actually store in your database or perhaps mapping a request to a query or request to a command and so on you get the idea and that's why there are multiple libraries for object mapping So today we're going to look at a specific Library called mapster the most common library is auto mapper this library is four times faster so if you have something similar to this in your code base then make sure to watch until the end and see if this is right for you if it is this has many features which you might be interested in so we're going to cover now some of the basic functionalities of this library and we'll see how you actually use it in a project built using clean architecture how you may structure it how you can use assembly scanning and so on so if this sounds intriguing make sure to watch until the end and let's get started hi everyone amihai here I do have a quick disclaimer if you're new to the channel so I am a Microsoft employee and these are Microsoft Technologies but I'm not talking on behalf of Microsoft these are my personal opinions right so just to see how this behaves so if I run it as you would expect it simply Maps the properties from the user to the user response okay now let's add the master Library so dotnet at package that's very great and now what we can do is we can basically get rid of this manual mapping and instead say user.adapt to a user response so if we run this now then without doing anything it knows how to map the various properties okay but many times this isn't the situation sometimes you might have in the response one property which is based on two properties in the source an easy example for this instead of first name and lasting over here we have the full name so if we manually mapped it then we would simply take the first name add a space put the last name and that's it how do you do it with mapster so if we look at the adapt method as you can see this is the method that we're calling but there's another overload which gets a configuration okay so let's look at how we can use this type adapter config to configure the mapping so what we can do is Define here a new config object then we can pass this configuration to the that method now we can use it to actually configure things so the way you use it is it's falling so we say config dot new config then you say from which object to Which object so in our case it's from the user to the user response and we want to say that in the destination right exactly okay so the way you read this is we're mapping from a user to the user response and we're saying that for this property populated by this manipulation on the source object okay so let's look at how this works so now if we run it then we can see it's used both of them okay if we didn't have this at all and we try running it then this doesn't throw an exception or something it simply ignores this property and it doesn't put a value in it and another thing to notice is that when you have configurations on only one of the properties then it doesn't affect the rest of the mappings right so you can see that one is still mapped over here to the response okay if you wanted to ignore anything that's not defined over here then you can say ignore non-mapped and now if we run it then you can see we have the full name but the ID wasn't mapped now you might be looking at this and thinking to yourself am I actually supposed to pass this config every time I want to map two objects then of course not so if we look at the original adapt method that we called then we can see it calls the same method that we call but with the global settings okay if we look at this property then we can see it's a type adapter config it's public and static which means we can just access it and manipulate it as we want so instead of creating our own config object what we can do is simply say type adapter config dot global settings right so this will give us that property which is passed by default over here and we don't need to pass anything anymore and it'll still work like before okay and another way to write the same thing is we can say type adapter config say from the user to the user response we want to have a new config and over here to have whatever we want right so this can be the same thing as this okay and if we run this then it works the same thing like before okay now one thing to notice here is that as the name suggests then this does create a new configuration for the mapping between the user to the user response which means that if you map over here something else let's say that the destination ID is the source ID plus one right so something like this then this will take effect but it will override the previous configuration so we can see that this did work but we're losing the last name if you don't want to override previous configurations then you can use the or type and then this will simply append so you can see it keeps both of the rules if over here you want to overwrite a previous configuration so for example you want to put stars around everything then this doesn't work so it basically takes only the first one that was configured okay another thing you can do is you can add conditions so let me just wrap this so you can say that this is true only when let's say first name I don't know starts with a right something like this okay so now if we run this then let's look at the first name it's not a and we don't get the last name another common scenario is a scenario in which let's say we have here some I don't know Trace ID and over here we have the trace ID value let's say yeah some new GUI okay so basically what we have over here is we have two objects which we want to turn into one object so what you can do is you can say Okay I want to map the user and the trace ID to a user response right and the way you can use it over here as you can say user and go in and to make this a bit more readable let's say user user guide price ID and what you can do now is you can say Okay I want in the destination the trace ID to be yes the source.trace ID remember that's the source now is this Tuple over here and right so this is one option we can simply say okay the ID in the destination comes from the source which is this Tuple dot user that ID but a more concise way to do this is simply say okay in the destination all the rest simply use whatever properties from the user that match right so if we run this now then we still get the same thing and over here we mapped two objects to a single object and another cool and useful feature is that you can say over here after mapping and here you can do whatever manipulation you want on the destination so for example console.writeline and over here we can simply print it now we can get rid of this so if we run this now then this line over here comes from this line over here okay so as you would expect there's also before mapping so I encourage you to take a look generally speaking when you adapt one object to another object then it checks if you have some specific configuration for these types if yes then great uses it otherwise it'll fall back to some default okay so there are three main types of configurations one is what we're doing over here where we're basically saying one type two another type two is global settings that you have for all your mappings and the third type is four destination types so you can have for example config dot or Destination type okay let's imagine over here that you have some interface public interface I validatable which has a single method void validate yes and let's give it a default implementation where it basically says validating and we have some of our objects implementing this interface so let's imagine that every time we map an object and it implements this interface we want to invoke this validate method and the way this looks is you can say when Destination type is I validatable then after mapping what you want to do is for example call validate so you can say simply this and now if we run the project then because we're mapping to the user response as you would expect it invokes this method last thing I want to talk about while we're getting familiar with this package is that we don't have to use the adapt extension method but another thing that we can do is use their mapper so what we can do is Define here a new mapper and instead of using this then we can say mapper.map we want to map it to the user response and we pass it the user object okay so if we run this then it works same thing like before and one thing to notice is that when you look at the Constructor then you can see that when you call it without a configuration then again it simply uses the global settings that means that any manipulation that we do to the global settings then it will apply also to the mapper instance so if this is the first video that you're watching out of my series of building a rest API completely from scratch following clean architecture and domain-driven design principles what we have at the moment is a system that supports registering and logging in so what we have is the register and login requests which arrive to the authentication controller in the authentication controller we're currently manually mapping them to a register command or a login query then we send them and they're handled by the corresponding handlers and on the way back so we have the authentication result over here going back to the controller and then we manually map it to the authentication response which eventually returns to the client okay so let's go ahead and use mapster to map these objects for us okay so I opened our project I want us to look at our controller and see what we have at the moment so over here we're getting the register request which we are manually mapping to the register command and over here we have the login request which we're manually mapping to the login query then on the way back both over here and over here we take whatever authentication result that we receive from the application layer and we map it over here to the authentication result okay so first of all let's add mapper to our project so.net add to the API project the packaged master and because we're also going to need dependency injection stuff then also let's add the dependency injection one great now that we have both of those let's imagine that our mapper is already configured and it works as we expect so over here let's say we already have our mapper let's add it to our Constructor as following then over here we'll no longer manually map it but instead what we'll have is mapper dot map to the register command and we pass it the request right same thing over here so mapper plot map to the login query and we pass it the request then on the way back then we no longer need to do this so let's actually replace map of result this entire thing with mapper that's map to the authentication response the auth result right now we can get rid of this method over here okay so I want us to look at the various mappings that we did and see if they currently will work without doing anything so let's open over here the various mappings that we're doing so we have from the register request two the register command right so looking at both of these objects then they're exactly the same they have the same property names so this line over here will already work out of the box without doing anything looking at the mapping from the login requests to the login query so login requests and here let's put the login query then we can see also here same properties exactly so also this will work out of the box okay and over here we're taking the authentication result and turning it into the authentication response so as a result and auto response and we can see that here it's not exactly the same right so over here the token is simply mapped to the Token but all the rest of the properties are coming from within the user object okay so from the first part of this video then you should be already familiar with how to actually do this kind of mapping but let's see how we would actually structure it in a project so let's get rid of all of these okay so let's look at how to set everything up so over here in the comment folder in the API project I want to create a new folder it's called mapping and inside here let's create a new file let's call it authentication mapping config and what we'll have over here are all the various configurations that have to do with authentication so the way this works is we need to implement the I register interface and this interface has a single method called register and over here we can have whatever configurations that we want and they'll be applied later in our controller so over here the only configuration that we need at the moment is between the authentication results to the authentication response so let's put it on the side so let's just split this over here let's open the authentication result on the top and the response on the bottom okay so as we're already familiar then we say config dot new config and over here we want to say the mapping between the authentication results to the authentication response let's make this a bit bigger to the authentication response right so as it's suggesting the token in the destination comes from the token in the source and like we said the rest of the properties are coming from the user object so the way we can configure this as we did before we can say in the destination simply fill the properties from the user property in the source so we simply say Source dot user and that's it okay so let's close this over here and all the folders just so it's a bit more organized so again just to recap all we did was to add the mapster and the master.dependency injection packages to the API project then over here we injected the imapper which comes from the mapster library and we simply changed all our manual mappings to use this library right and over here and we have a single file over here inside the authentication mapping config which defines the mapping between the authentication result to the authentication response okay now if we try running the service now then of course this won't behave as we expect and that's because we didn't wire anything together yet so let's go ahead and do that so what I want to do is to have this mapping component over here be in charge of registering its own dependencies so let's create here a new file it's called dependency injection so public traffic class dependency injection something similar to what was suggested so this will be add mappings it will return the iservice collection over here it will be in charge of wiring some stuff together so first of all let's return the services so the first thing we want to do is to make sure we're scanning the assembly finding all those I register interfaces and registering the different configurations for that let's get a hold of our config so this comes from the type adapterconfig dot global settings and then all we need to do is config.scan and here pass it one or more assemblies that we want to scan so in our case the only assembly that we want to scan is the currently executing assembly so we can say assembly Dot get executing assembly okay now that we have that let's register it so Services Dot at Singleton and let's add our configure and lastly let's add our mapper so Services dot at scoped and here let's have our mapper be the service mapper if you're curious what's this service mapper because up until now we only talked about the mapper then you can see it's basically the mapper with some other stuff that are needed for dependency injection and this allows for some other features as well okay now that we have this if we try running it then it's still not going to work and that's because we're not calling this method from anywhere so let's go ahead and add it so going back to the program CS if you remember we have over here each layer registering its own dependencies so we have the application layer adding its dependencies and same goes for the infrastructure layer let's add the layer that we're in now which is the presentation layer as well so over here let's add presentation and let's implement this method so similar to the dependency injection file we have in the application layer and in the infrastructure layer then we're going to have something similar for our layer let's simply copy and paste this file over here and let's get rid of whatever we don't need let's change this to presentation and let's fix the namespace and over here let's simply add our logic which is adding the mappings let's add the easy statement and now we can move this logic over here as well let's just replace Builder dot capital S with non-capital s 6 indentation include what we need to include and back over here then we can get rid of what we aren't using anymore and that's it okay so again all we have over here is we're adding all the dependencies of the presentation layer right so this is implemented over here and over here we're calling the add mapping which is in charge of scanning and adding all the various mapping configurations that we have in our system right so it scans all the I register interfaces which have the actual mapping logic and that's it let's run the application and make sure that this works as we expect okay let's make the register request but first let's attach the debugger so Uber dinner and I want us to put a breakpoint in the authentication controller let's put it over here and let's make the request okay we see we arrived at our endpoints we have a request with the various details we step over and over here we can see that it was mapped as we expect okay stepping over this this gave us the authentication result which has inside the token and the user with the other details okay and if we continue then we get the response and we can see that it's successfully extracted the user details to the appropriate properties and the token to the Token property can if you remember we said that if the properties have the same name then you don't need to Define it so we can actually go over here to the mapping and get rid of this real net mapping let's disconnect the debugger and stop the program okay so as we said this component will be in charge of all the authentication mappings now I like to be aware of all the various mappings we have in our system and that if someone needs to configure something special then he knows exactly where to look so now if anyone wants to change something in the mapping between the authentication results to the authentication response then it knows where to look I want it to be true for the other mappings as well so I'm going to add here another config for the register requests to the register command and same goes four the login request to the login query okay now even though these two are redundant I do like this because we're also aware of the various mappings and also if someone needs to configure anything he knows exactly where to look last thing I want to do is fix a bug from the previous video so this is called login query Handler but we actually call the fast login command Handler so let's change this to query and another thing that's bugging me is this warning over here which comes from the fact that we are generating a state machine but we're not doing anything as synchronous so to get rid of this that's just a weight a completed task yes to get rid of this warning both here and in the register command Handler and we'll get rid of it when we actually have here asynchronous logic okay so that's it for this video I really hope you have a better understanding of how to use mapster in your application if you think this adds a layer of complexity that you don't want to buy into then feel free to continue manually mapping your objects there is no right and wrong over here it's really up to you and what you prefer okay don't worry if the project is starting to look a bit intimidating we have a lot of files a lot of projects we're using more and more libraries and as the series continues we'll introduce even more libraries but as you'll see in the next few videos then everything you're starting to get a better structure will do the same things again and again using the same libraries and every piece of logic will have its defined place in which it's going to be added so you don't need to try to remember everything that we did up until now because I think in the following few videos then you'll start getting a good intuition about how to use it and it'll be easier for you to understand how you can actually use it in your systems if at the moment it's feeling a bit intimidating in the next video we're going to look at model validation which is a very interesting topic so if that sounds interesting make sure to subscribe and I'll see you in the next one
Info
Channel: Amichai Mantinband
Views: 38,911
Rating: undefined out of 5
Keywords: Dotnet, .net, C#, Mapster
Id: vBs6naPD6RE
Channel Id: undefined
Length: 23min 47sec (1427 seconds)
Published: Mon Jul 25 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.