Part 1 and Part 2 of the "Getting User Data" videos have really broadened my understanding of C#, APIs and applications. Seeing these projects separated, the API calls being made, the queries to the database, objects being passed around and their methods being called, that's experience that I haven't gotten from any other course I've taken. At least not at this scale, where there are multiple projects involved, and so many factors to take into consideration. Even the debugging sessions have been super informative. Your videos are (from a learning standpoint) the best videos I've seen on UA-cam. I don't know how many times I will thank you throughout this playlist, but I will continue to do so regardless of how many times I've already said it. Thank you. Thank you. Thank you.
Thank you for creating these vids. We may not be a numerous bunch viewing but we are certainly rooting for your success.soon this will be a reference for other newbs
Yup. It is drifting out of the nice, neat tutorial realm and into the "real world". The key is to make small changes, test often, and don't be afraid to step back and plan out a step if you aren't sure.
How Is Tim Switching Between Files So Quickly? At 23:00 I got a little confused as to how Tim was navigating around so quickly. So for everyone else who doesn't know if you hold ctrl and click on an element it will take to straight to the definition. Alternatively, you can put your cursor inside the element and press F12. Hopefully, someone finds this useful :)
Awesome job as always! I am learning a lot through this about building out an API as I build one for my job's data access. Just a few quick thoughts: @21:16 - AuthenticatedUser is still in the TRMDesktopUI.Models namespace (notice IAPIHelper's using statement for AuthenticatedUser). Nit-picky, I know. @29:00 - Yes, DefaultRequestHeaders.Clear() does clear everything under DefaultRequestHeaders, including Accept. @39:40 - You can Ctrl+. to get the option to 'Pull to interface' so you don't have to manually copy & paste into the file. Very handy! editted to remove answered question in video.
Good catch on the using. I'll try to remember to remove that in the next video. Thanks for verifying the clear. I wasn't positive and didn't have the time to check. As for the one line assignment, yes I could but I don't prefer that method. It is harder to read. As for the Ctrl+., I didn't realize they added that. That's awesome! I'll have to remember that.
Hi Tim, thanks for the great course, I am enjoying the journey. Two suggestions from my side: 1. one section that would help me in this journey would be a small overview of what we have done so far where we stand and what we will do in this video and what will be the next steps. Sometimes I get lost in the details and am not aware of what exactly we are doing and how it fits into the larger picture. 2.I would appreciate some kind of challenge, it should not be too hard, but it should make the users try to think through a small solution themselves. Ok that's it, keep up the great work!
Hi Tim, here a big fan. Compliments. With the issue regarding api/user in stead of /user I have fixed while the application was still running. This worked and I was very pleased with it. So you don't have to stop the application, make the fix and then start again.
Love the series! As a great way to improve and teach. It would be a fantastic idea to add a tiny bonus tip in each video for "Did you know?" types of things. For instance where to add custom code snippets, or keyboard shotcuts that could speed up your coding. I know you have a couple of videos for tips & tricks, but this I feel like could help either remind or teach people watching your videos! :)
Watching this great tutorial so far, I'd recommend adding folders for the solutions, since they're becoming many and could get confusing to some. Solutions folders names as API, UI, Database etc would be great way to organise the project even more.
No, a ViewModel is roughly equivalent to a Controller in MVC. MVC stands for Model-View-Controller. The front-end model is the M in MVC. It is (potentially) different than the back-end model in that the front-end model may have decorators or other UI-specific items in it.
@@IAmTimCorey Hmm, so if the front-end model is the M in MVC, what is the back-end model? Apologies, but this is the first time I have heard the terms 'front-end model' and 'back-end model' used within the context of MVC.
@@neilljamieson9606 I think there might have been some confusion somewhere as I think they might have thought you were asking about the ViewModel in MVVM. In MVC, if you add a view model, it acts as another layer between the view and the domain model. You may not want to expose everything (ex. sensitive data) from your domain model back to the view, so that's where the view model would come in. So in that way it would be a front-end model. The back-end model, or domain model would be the M in MVC since those tie back to the database.
Firstly Tim, thanks for the great tutorial videos. Could you in a future video explain the system architecture, in particular the API calls. Currently we have two databases, one which we use to create accounts with and another for our retail management system (which also stores our logged in user). My problem with this architecture is that we authenticate the user through the cloud but we then have to call the TRM database to retrieve the authenticated user details. Ignoring we haven't as yet resolved how we will duplicate the account to our TRM database, should the web API access our TRM database at all? I logically would think these databases would be on separate servers and we shouldn't have a dependency across the two, due to security, etc. And if you do have time, I would like to here your opinion why you have used Entity Framework to create the Web API but use Dapper for TRM database?
I'll try to answer your questions here. First, yes, the WebAPI needs access to both databases because it is only when a user is authenticated that we can then use that authentication to access the rest of the data. The authentication grants the user the rights to get the sensitive data in the main database. As for duplicating the account, it isn't really duplication. We are just using the UserId as the primary key for a table with more information about the user. We can still separate these two databases onto two servers if we want (not necessary but possible) and access them both from the API. As for using EF for the authentication system but Dapper for the main database, the reason is that I'm locked into EF for authentication. Pulling out EF and replacing it is a headache I don't need so I treat the authentication system like a plugin. I just use it. I use Dapper for the other database because it is faster at runtime than EF, it gives me more control over my database, and it can be more easily secured compared to EF.
Thanks Tim. I'm probably getting a bit ahead of myself as I've been curious how we are going to implement the registration process. Hence when I look at the AspNetUsers table definition, my thought is why not include any additional columns here instead of our TRMData.User table (is it because we don't what to modify EntityFramework code?)?
Hey Tim, quick question about your decision (at 3:27) to change GetById() to return a single UserModel rather than a list of them. By requesting the ".First()" item in the list aren't we risking a crash if it comes back empty? We're assuming that the logged in Identity/User's Id exists in the register's Users table, which it feasibly might not. What would be the best way to protect against this eventuality, if you feel it's even worthwhile? Also, a huge thank you for this resource you've put together.
Not sure if this is the right way to resolve this, but using .FirstOrDefault() instead of .First() returns null rather than causing an exception if the list is empty.
Good question. Yes, we are risking a crash. Yes, FirstOrDefault will give you null instead. The question is, do you want that? I decided I wanted the application to crash. The reason why is if the user is in the system but no record is returned for them, that is a major issue. It is one the application cannot resolve gracefully. Therefore, I want to crash the application rather than having it continue in a bad state.
@@IAmTimCorey As I also said in the previous comment, why have the original function give back a list of users at all as the Id is unique so GetById would never return more than one user, and I'd rather deal with a null than first having to check if there are any users in the list before getting the user. So IMHO the problem already exists in the original design.
Great Job Tim, I almost got lost, I managed to get back in track. FYI I couldn't use the "token" from inside our APIHelpers, instead only from LoggedInUserModel. Either way, it works.
Great video as always. Wouldn't make it sense for UserModel in DataManager.Library and LoggedInUserModel in DesktopUI.Library to share an Interface together maybe in an TRM.Shared project? So you can make sure the names of the properties match and did not get changed only on one side.
I get the idea but not really. The reason why is that then those two projects are tied together. If you update the interface, both projects have to update. Treat this like an API outside your control. Just like the other APIs we have worked with. We don't have interfaces for them. Besides, we might not want to capture every field. That's ok to do except when you have an interface enforcing implementation.
3 роки тому
@@IAmTimCorey I get your point, but if I have both client and API side of project under control and I would like to do not forget to add property in UI or in API, is it OK to create shared interfaces only for fields which has to be in both models? say ISharedUserModel (FirstName, LastName, Email, ...) and implement this interface on APIModel and UIModel, and other props which I do not want to transfer will not be part of Interface?
We're starting to have a number of projects in the solution and they all start with TMRD, some 2 of them end in library.. it's hard to identify things quickly.. I would suggest adding solution folders to the solution to at least break the API from the front end. The solution folders don't affect namespace but are great for organizing groups of projects together.
Good suggestion. Since I'm heading out on a trip, I pre-recorded a few videos but I'll add that suggestion to the list. Hopefully I can add it in around the time we upgrade to .NET Core.
Good lesson. My question is this. do you feel this entire section was right for a single git commit? Or could we have broken down the steps a bit more? Sometimes I don't think its possible because of all the things you had to change.
Each commit should be a full change. I do make larger commits in these videos simply for time's sake but you do need to be careful that you don't push part of a change as a commit.
Tim, I love you man. This is the best tutorial series on UA-cam. I am just became a Patreon supporter TODAY. I am late to the game with this series. Will I be able to download this solutions still? I read a comment in the previous video that the code was no longer available. I will be honest. I'm a lone ranger and I've been writing ASP.Net VB for 20 years. My code is horrible. I want to rewrite my web site in C# using your project as a template for "how you SHOULD do things". I know this is not a web project, but most of the framework you created here applies. Using your code as a starting point will mean I don't have to go back to vid 1 and write the code myself as you show on screen. Thanks for this. I've learned more from these videos than I could ever have learned trying to piece together a hundred how-to vids on specific items. I can't believe how awesome this series is.
The source code is no longer on Pateon because it is no longer an active series. However, you can still get the code by purchasing the course (it is currently broken up into three phases, although we will be joining it together at some point): www.iamtimcorey.com/courses?query=timco
I'm going back watching all of these and I just accidentally happened to turn on closed captioning and I'm dying...at 26:51, it thinks Tim said "stepped on ourself our own TOAST" lol. :p
~10:11 When you populate the LoggedInUserModel by copy/paste the UserModel properties. If you had an interface IUserModel in your Data Access Library, would it be okay to implement that into LoggedInUserModel and then add any additional properties as needed??
I didn't want to do that because the class is just properties and I needed to change all of them (decorate them, actually). An interface would require me to keep all of the properties, too, which might not be what I always want.
Out of interest is there any reason not to have some kind of singleton LoggedInUserHolder class which just references the LoggedInUserModel? It would allow you to just assign the new user rather than do a prop by prop update. It'd also let you set up your events and property change notifications on that one central property. Whereas now you'll either trigger events on every property update, have to pick some kind of canonical property that triggers the events or manually call some kind of method to drive the events all of which would be error prone IMO.
Hi Tim, thanks for this series. It's nice to see a programmers mind at work. I've started watching these to keep me company during the lonely lockdown home office hours. I've tagged along with my own hobby project using this as a reference. I did however start with Net 5, figuring things out myself which don't match the old 4.x Framework ways. One issue I had was the unability to access my user API with the token (401 - Unauthorized). The API is available on both http & https (via redirect), turns out that the issue only exists when I go to the API via http (I guess the token gets lost during the redirect?). I also passed the authorization header to the httpclient in a different way: _apiClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);. Anyhow just wanted to share this info for others who might end up with similar issues.
When you move the API Helper to the library, you still use bootstrapper to get the resources you need for that class. But bootstrapper lives in DesktopUI. Wouldn't that be a dependency in the opposite direction?
Yep. You always have dependencies but we inverted the dependencies (Dependency Inversion Principle) and the UI tells the libraries which items to rely on instead of the libraries forcing the UI to use certain items.
Hey Tim, great series! I've been seeing it the last week or so and it is great to see how all of this teaches so many things at once, even if it's just something "minor" like fixing typo mistakes. Now one thing I ask myself is, what would be a reason to have the separate calls for logging in and than a second call for getting user data? I mean, obviously to keep thing cleanly and structured it would make sense in some way... But why wouldn't someone call for a login request to the API and then with that call, get the user data back? Since, if your call returns a success response it'd probably mean already that the user was logged in successfully and you could then fetch the data from the response as well and you wouldn't have to keep track of two sperate models, but only the one user instance itself. I hope it makes sense :) Thx for all the great posts and hard work! Greetings from Germany
Hey Tim, great series, i was just wondering if there was an overview/preview/course introduction of the MVC add on for the tournament tracker series, just so i could see what would be achieved in the course as i am interested in seeing where we can take the design when using MVC, many thanks
That is a high priority on my todo list but it isn't done yet. I talk you through it but I don't show you. Hopefully I'll get that together in the next few months.
why am I getting back "CreatedDate": "0001-01-01T00:00:00" getutcdate() but in the database I have 4/28/2020 1:18:09 AM (the correct one) above has January first year 1
Solved this. It was a difference between CreatedDate vs CreateDate. CreatedDate was defined in the model and CreateDate was on the Database so it was null from model and getutcdate() bring back 01/01/0001\
Hi Tim! Thank you for the good job you do, you are very educational and explain everything slowly and well. I have a thought, the easiest and best way to build authentication into a WPF application, is it to create a web api that you have done in this series?
Great series so far Tim. This could possibly be another video that you do at some other time. At around 8.00 minutes you mention that you could have done the TRMDataManager and Library as one solution and the TRMDesktopUI and Library as another solution. That leads me to wondering how would you join those two solutions together? If you made a change to the TMRDataManager solution and didn't need to make changes to the TRMDesktopUI solution how would you ensure that the TRMDesktopUI solution contains the newer version of the TRMDataManager solution?
DesktopUI solution doesn't really have any connection(no dll's are referred) to the DataManger project, all it does it uses the WebApi's exposed by the DataManager. If you want you can have an android application built using Java/Kotlin in a different IDE and still use the same Api exposed by DataManager. Thats the beauty of WebApi's. When i started watching this course, I got a little worried when I saw him creating the WPF project in the same solution. But later i realised why this was done. Also, the question you have is correct. WebApi(DataManager) and WPFClient(Desktop UI) do evolve separately when in production. I mean The UI changes much more frequently in real life. But as long as the endpoints(WebAPIs don't change, its okay). If the API does change, the client has to be separately upgraded to use the new APIs. But even then the legacy APIs are still kept intact.
Thanks for a great video. I know that I am a whole year behind in this but I noticed a small thing that I thought was a bit weird. It looked to me as one of the using statements in the APIHelper refers to TRMDesktopUI.Models but it works anyway. I then realized that you forgot to change the namespace when you moved AuthenticatedUser.cs between projects.
Thanks for pointing it out. You haven't caught up to this point yet but in the future, we are using Azure Boards to track our bugs and features and we are assigning them to commits. I just entered this in as bug #29. When you get that far, you will see us fix this bug you submitted (it hasn't happened yet). Thanks for pointing it out.
Presumably it would be better to do .Single() vs .First()? We're only expecting one entry for that user ID, if there's more than 1 entry - something is wrong somewhere... .Single() would allow us to discover the issue and handle the exception accordingly? Interested on your perspective on this!
There are two items that aren't quite bugs, but I believe they are not what you intended: 1. The class authenticatedUser.cs is in the wrong namespace. It is in the TRMDesktopUI.Models namespace instead of the TRMDesktopUI.Library.Models namespace which is where I think you intended to put it. 2. The call to apiClient.GetAsync("/api/User") in APIHelper executes the method GetById in the UserController. The GetById returns an instance of UserModel. After the call to apiClient.GetAsync("/api/User") there's a call to ReadAsAsync . I believe that ReadAsAsync should return an instance of UserModel because that's what was returned from GetById, but it instead returns an instance of LoggedInUserModel. Since the UserModel class and the LoggedInUserModel are virtually identical, this is more of a semantic issue than an actual bug/error. The one caveat is that in order to correct this you need the TRMDesktopUI.Library to refer to the TRMDataManager.Library which I am not sure you want to do because it ties the libraries together. Am I correct that we should change the namespace for issue number 1 and return the UserModel from ReadAsAsync for issue number 2? Thanks
Yep, the namespace issue is on the bug list to fix. As for number 2, no, I don't want to mix the two. I am acting as though I have no control over the API, which is the right way to proceed when creating an API. What the front-end calls the models and how it interacts with them will be different than the back-end. I call the front-end model LoggedInUserModel because it is descriptive of what the model represents: the logged in user and their information.
@@IAmTimCorey Thanks for getting back to me. I wasn't aware that there is a bug list. As far as the second issue: I understand why you did this. What confused me was how ReadAsAsync was able to map the response data from the UserModel object into a LoggedInUserModel object. I think I figured it out. The mapping from UserModel into a LoggedInUserModel object ONLY happens if the field names are identical in the UserModel and LoggedInUserModel. Field names that are spelled differently return a null. I have to assume that it uses reflection to do this. Hopefully this makes sense. Thanks
Hi Tim, your videos are great and really helpful. I'm just getting started and I'm also reading the book Clean Architecture by Robert C. Martin. In your solution the API Project uses an object (for example the user object) and the DesktopUI also uses a different user object. Where should I create the entities which build the domain of the whole project? They build the core and should be reusable in all projects. Should I create for example a TRMLibrary with all these entities or do I miss something?
You could create a project type like that (maybe a new class library), but I'm often leery of doing so. If both sides rely on a model type and then the UI needs to have some UI-specific logic in the model, you have a mess. I am cautious where I use that approach for that reason.
Would it be beneficial to refactor the user properties onto an interface shared between the frontend and backend? Is that valuable if it only serves to reduce the property duplication?
I don't think that will benefit our application development. If we did that, we would need to have a library that both the front-end and back-end depended on. Then, if we changed that library, it would force both parts to update. At that point, there isn't a need for the API. We should just make it one application. Besides, the UI models are (or can be) different than the back-end models even if they appear to be identical today. They serve different purposes. Good thought though.
I noticed a container is used in this section. How does this relate to containers you mentioned in your video regarding creating containers using Docker?
Hello Tim, Thanks for the brilliant content you are putting on youtube! Great stuff! My question is about the naming conventions of class fields - is it not considered a bad practice to name fields, starting with an underscore? Now I understand that your point here might be not to use the "this" keyword in order to setup the properties for the relevant fields, but anyways when I've learnt naming conventions for C# the best practice states to name fields with camel case, the same as variables. Please let me know your opinion and does that naming come due to your experience as a developer ? Thanks in advance!
There are a couple times when the underscore is recommended. First is when it is a private backing field for a public property (full property). The second is when you bring in the value from an external location (such as dependency injection). Both indicate that you shouldn't write directly to that item or overwrite it.
I was surprised to see that TRMDesktopUI.Library APIHelper's constructor initializing ILoggedInUserModel using DI. I guess you don't need to explicitly use DI in each library and can figure out from singleton setups.
@@IAmTimCorey I have a question: Is the library class's for example TRMDataManager.Library will contain only data access related functions or is it going to contain other functions as well. If it does contain only Data access functionality wouldn't adding more library for different purposes would make the naming really confusing. Same thing for the TRMDesktopUI.Library
Hi Tim, just wondering for the model sharing. Why don't we have a single library class that supplies the most basic data (like UserModel), then the DesktopUI or TRMDataManager inherit on the class and add changes based on their needs? They having different purpose but they should also using the same model. So, we should can have the code sharing and also certain level of code separation.
I disagree with the "they should also be using the same model" concept. These are two different models with two different purposes. Inheritance isn't about just code sharing. It is about a relationship. That's where people get in trouble with inheritance. "If it has wheels and an engine, they can share the same base class" and suddenly you have an airplane, a car, and a lawn tractor sharing a base class. That sounds fine until you realize you have totally different concepts of what the engine does, what a transmission looks like, and so on. Same thing here. Just because they look similar does not mean that they should be the same or inherit from the same base. If, down the road, I decide to ask for less properties from the API, I'd be stuck. I couldn't do it. That's a design problem.
Hi Tim, thanks for the great content. About copying all the properties from the LoggedInUserModel to the singleton object. Just my thoughts: isn't it more useful to create a Clone() function in the LoggedInUserModel that returns a new LoggedInUserModel? So things stay together and it's almost impossible to forget copying properties as you're in the model itself. public LoggedInUserModel Clone() { return new LoggedInUserModel { Id = this.Id, ... }; }
@IAMTimCorey, in this video your public async Task GetLoggedInUserInfo(string token) method in the APIHelper class doesn't seem to return anything yet it still works. I can only get mine working by returning the local _loggedInUser object. What am I missing? Thanks in advance!
We don't want to create a singleton for the AuthenticatedUser because we want a clean, new instance each time. Plus, we aren't getting that model from dependency injection.
Might it be better to separate the Models that both the UI and API are using into a common model project. That way the data access is still independent of the UI but the models are still shared. The common model could be held within another object holding data that is specific to the UI. Or the UI model could inherit from the common model.
I tend not to do that because of a couple of reasons. First, it tends to make me lazy. If I have a common library, I avoid creating UI-specific models when I need them. I know I could inherit from them, but that's messy. I'm ok with a bit of duplication, especially since it keeps my UI-specific code in my UI. It doesn't wander into the shared library. Second, it reduces my complexity. If I have a shared library, I have an additional reference to maintain, an additional place to look for the code, and if I make a change in the shared library, I can affect two (or more) projects negatively. I know a lot of people use shared libraries (including Microsoft), but I have mostly stayed away from them for these reasons. These are just personal preferences, though.
Here you pass a string as a parameter from the UI to the server to get the user details. But what if we had to pass an object as a parameter? The the object definition should be available in the UI library and the server library?
When you pass something through an API, it gets converted to JSON. So, you can pass objects but you have to have that object defined at both ends (but it isn't the same object, it just has the same properties).
@Gowtham Natarajan This is what the Newtonsoft.Json library does. I would highly recommend reading the documentation, as fully understanding the concept of objects as data structures and JSON as a representation of that structure's format is IMO a milestone in your knowledge of programming. It's an interesting and really intuitive library, especially compared to options for other languages (*cough* Java).
I ran into an issue where the API call to /api/User returned a list, but it seemed that it returned a single object in this video. I just changed my code to handle the list and use the first object (in this case LoggedinUserModel) in the list if there were any. If I missed this in the video I apologize.
Where is a good place to write complex logic? The Backend library or the UI library? If we have to read data from DB and do some complex calculations before the result is shown in the UI, the server could just pass the raw data to the UI library which does the calculation or the server can do the calculation and pass just the result to the UI. Which is better?
It depends on what the "calculations" are. If they are UI-specific then the UI layer. Otherwise, move them down to the business logic layer (in the class library). Only UI-specific code should go in the UI.
Hi Tim, Great video as always! I have a question; In the GetLoggedInUserInfo method when you do the manual mapping of the result, would it not be possible to just assign result directly to "_loggedInUserModel" seeing that they have the same type? I seems to me like the mapping is unnecessary, or am I missing something?
The reason is that the _loggedInUserModel is a singleton from dependency injection. We can't overwrite it, we need to use that instance Therefore, we replace the values instead of changing the instance.
@@IAmTimCorey I see, I guess it was my unfamiliarity to Automapper that made me miss your explanation in the video. Thanks for taking the time to answer!
As fas as I understand, Data, DataManager and its library would be the server, and DesktopUI and its library would be the clients if you implement this as 'server with multiple clients'. Am I right? Would you need to do adaptations to the code to make it work that way?
The API project would be a server and it could serve multiple clients (the WPF project). We will actually set this up down the road. There is nothing you need to do in addition for this to work. If you wanted it to work across the Internet, you would just need to make sure your API project is deployed to an Internet-accessible spot (like an Azure Web App or a web host).
Hi Tim, I have to admit that using frameworks makes me confused. If I understand Calibrun.Micro, it helps to bring the different WPF parts together by relying on syntax convention. When I launch the app I get a null value for the _loggedInUser variable. The programme goes through the constructor, but I don't really understand what gives the loggedInUser value in the constructor. I believe it comes from the container of the dependency injection with .Singleton(). Could you please tell me a little bit more? I don't think I've missed any step.
Yes, this is dependency injection. It isn't really a Caliburn Micro issue at this point, it is just a DI issue. The DI system will give you an instance of LoggedInUserModel when you request an ILoggedInUserModel through the constructor. The code in your container should look like this: .Singleton() (that's a bit different than what you listed).
@@IAmTimCorey Thanks Tim for your explanation and sorry for my error. Nevertheless, I've just realised that: - result.Id = null and it is also null in swagger. - Event if I assign _loggedInUser.Id to a string, let's say "XXXX", it doesn't appear in swagger. - Id has a value in (localdb)\MSSQLocalDB\..\dbo.User. I'm not an IT developper (even if I hope to become one a day), but I'm pretty sure I'm missing something somewhere (I don't really know what separates the client to the server). Have you already encounter a similar issue? Thanks again :-)
Hello Tim, I'm sorry to bother you again, but I haven't found the issue regarding my last message yet. Do you have an idea what it comes from? Many thanks for your help!
@@ludovicwagner2656 I might be guessing but doesn't hurt to try, one of the things that work best for me in these cases is: rewind to where your code was working before, than, have the tutorial in a separate monitor/screen, and start comparing word for word against your project. in most cases, you will see that you are missing one word. I had an error just now with my LoginViewModel constructor after I extracted my APIHelper Interface.I took almost an hour debugging, and the error was consistent. Turns out I had to make my Interface class public. I hope this helps
@@ludovicwagner2656 Hi, I'm not sure if you were able to solve your issue for Id yet, but you can check the Select statement in the stored procedure which is returning the data, and add Id column in the select query. I hope this helps.
Hi Tim, was the response intentionly mapped to LoggedInUserModel even if the API return a UserModel (just because the JSON mapper can resolve it since they have relativly the same properties by returning null on token) or was it just a little unseen bug. thank you for the series, realy enjoying it.
It sounds like maybe you named your property something slightly different and so the system is not matching it up to the passed in property name. It wouldn't be a casing issue but it could be that the property is a letter off.
@@IAmTimCorey It's really weird because the letters are correct. I have: - PostAsync("/Token", data) - _loggedInUser.Token = token - GetLoggedInUserInfo(result.AcessToken) In AuthenticatedUser: - public string AcessToken { get; set; }
You have the property AcessToken but it should be Access_Token (notice the two "c" characters and the underscore). That's your problem most likely. The system doesn't know how to map "access_token" to "AcessToken" because it is more than a casing difference.
Hi Tim, really enjoying the series. I've hit a snag, when I run the solution, the web api opens in my browser. But the Login screen is not showing up. I don't see any issues in the debugger screen. Any suggestions on where to look to figure out why Login screen isn't showing?
hi tim. I'm writing right, I'm running into an error called. " The object reference is not set to an instance of an object" I get all my data but now I press continue. comes the message in text TextBlock x:Name="ErrorMassage". Is it a natural mistake or I have to look closely. One more question is the possibility of running it all together in Database instead of 2. totally delicious work you do. it teaches me a lot here in Denmark Sincerely Michael Schannong
The Object Reference is not set... message is because you are attempting to use a class that has not been instantiated. My guess is that in the API, you are trying to use a model without first instantiating it (= new ModelName()). That is why it is passing down that message. As for the database question, it is definitely possible to do this in one database. There are just a few issues with that. First, you cannot scan the database for changes and bring them into your project because it will bring in the EF changes. Second, it mixes your generated SQL with EF generated SQL. Not necessarily an issue but it makes for a messy structure where no one system has the complete design. I prefer not to mix my authentication db with my data db.
Thanks for another great video. Just wondering how string api = ConfigurationManager.AppSettings["api"]; in TRMDesktopUI.Library.Api.APIHelper InitializeClient method gets value from app.config in TRMDesktopUI? These are different projects and appSetings not even exists in TRMDesktopUI.Library.
A class library cannot be run. We run the user interface project and it uses the class library. Therefore, the class library uses the app.config of the project that runs it (the UI).
@@Tahmasib13 What was the answer here? I am having that error on my application "Error CS0103 The name 'ConfigurationManager' does not exist in the current context " and I just cant move from here!!
So what is the solution for this error ? "Error CS0103 The name 'ConfigurationManager' does not exist in the current context " I just cant move from here!!
Great video as always! I got the "Unauthorized" error! I used break point and it still can access and get the token for the login part, but then it could not access to get other information using that token (Noted that I used another url not my localhost)! What should I do Tim?
Did you include the "bearer " in front of the token when passing it in? Did you pass it in as a header value? Have you tried from Postman to see if you can get it to work?
Tim - Thanks for the content, I've been following along with the series, however, when I try to run the api I'm getting a "Parser Error", where it could not load type TRMDataManager.WebApiApplication There's a Source Error: Line 1: I've done the Googling and suggestions were to use Dapper.StrongName, or remove the Global.asax.cs file..I used the StrongName, and it worked, so there a big enough difference between Dapper and Dapper.StrongName?
Something else is going on here. You should not need to change that and no, don't get rid of global.asax.cs. My guess is that you actually have a naming issue somewhere (casing is not correct somewhere).
Hey Tim, I am getting an "Internal Server Error" as my response from HttpResponseMessage response = await apiClient.GetAsync("/api/User"). I checked my spacing around the $"Bearer {token} and my spelling of Authorization. Any idea where the error could lie?
Hi @IAmTimCorey I was wondering how we can pass the current logged in users details to another Screen, I'm thinking along the lines of passing instance of ApiHelper to the second view ("Screen") and then retrieving current logged in user through that. But the problem I'm having is how do I find the current users token to pass in to GetLoggedInUser info method without having to re-authenticate the user post log-in? Any help would be much appreciated!
We captured the logged in user's data in a Singleton class, which means wherever we ask for that class, we will get the same instance, including all of the data we put in it.
If you change databases, you will need to make sure the user is created in the new database before you can use it. I will be pushing this project to Azure at some point.
It seems worrying that in the UI Library, we are importing stuff from UI (e.g. AuthenticatedUser), whereas in UI, we are importing stuff from UI Library (IAPIHelper). Isn't this a circular dependency, or am I missing something?
First off, great video series! Being new to programming, this series has been very helpful!! I'm getting an error when I run the application at the end of this video above the username: "An error occurred while sending the request". I've been trying to figure this out for several days now and can't seem to find what I'm doing wrong. Any idea what I'm doing wrong??
So that error message isn't the "real" one. See if you can put a breakpoint to catch the error so you can see the inner exception. That message should show you the real error. That's the place to start.
@@IAmTimCorey Ok, I figured it out. So if anyone else has this problem...this might be an answer... I was getting errors from a couple different places all saying that they threw "an exception of type 'System.NullReferenceException'." ... i.e. "response.Content threw an exception of type 'System.NullReferenceException' ". By accident, while debugging, I saw that the API Project URL was different...for some reason...from the BaseAddress used in the APIHelper Class. When I changed it to the right port in the TRMDataManager Project properties and created a new Virtual Directory, that fixed the problem.
I am not sure if you changed the namespace in AuthenticatedUser class from 'SRMDesktopUI.Models' to 'SRMDesktopUI.Library.Models'. Maybe that's more logical.
When we didn't get the data (the false on GetAsync) I was still getting 404... it turned out I'd accidentally inserted a space between User and its / in that call. Is it better practice to not have the trailing slash when building an API?
Hi, I'm a big fun of those courses. Thanks Tim! I followed up to this tutorial, but in a login form I got an error "an error ocurred while sending the request", actually I do not even know where to look up in a code right now, so much behind already. Tried debug this error, could not find it, where is the problem in my side. Maybe it's kinda typo error by me, or need to look somewhere else. Anyone please could help me.
I'm afraid I can't offer help but I can offer encouragement. Debugging is a critical skill to learn for every programmer. Learning to trace the data and logic flows is very useful. If possible, back out changes to the last time it worked, then add code , testing after each piece. Alternatively, review each step you recently added, one at a time, testing each one to see the impact. Testing ands debugging are must have tools.
In the APIHelper code this returns "true" and you're authorized... apiClient.DefaultRequestHeaders.Add("Authorization", $"Bearer { token }"); and this returns "false" and you're not... apiClient.DefaultRequestHeaders.Add("Authorization", $"Bearer{ token }"); in other words check the space between Bearer and token in your code.
This is gonna be an ongoing comment I'll edit as I watch: 1) I actually thought it's smarter to interface everything, return an IEnumarble and ditch the .First(). 2) Wouldn't it be better to extract an interface from UserModel for reuse in DesktopUI.Library? What are the dangers of sharing an interface across the projects? 3) Finding the right references manually is a major headache, praise the Ctr + . 4) * Points back at 2) with a grin *
Here are my replies: 1) By returning First, we ensure we only return one login record (no need for a list or a possible issue of people misunderstanding). If we expect only one record, we should return only one record. 2) I discussed the dangers somewhat in the video. First, we don't want coupling between our front-end and our back-end. They are two separate projects and we want to treat them as such. Second, we don't want to expose the direct data access to the front-end. Otherwise, people could bypass the API. Third, and most importantly, the models are not the same. Yes, they look the same but they don't have to be. If we tie them to the same interface, we can't then change the UI model to fit the UI (less properties, more properties, etc.) 3) Yup 4) Points back to my answer.
@@IAmTimCorey 1) Wouldn't IEnumerable imply a single enumerable dataset of the user model? and in the case of feature development into the feature would be more expendable? as explained in your SOLID Principles video. Or would that actually be more prone to errors if we use it that way? 2) So basically we are not extracting an interface because we want to make sure it's clear that the front and back end are not coupled together and should never be, we are eliminating the chance of use cases of polymorphism between the two projects by design
1) IEnumerable means it can return zero or more UserModels. We are expecting only one. That would be unexpected behavior to get more than one. When you log in, you shouldn't get back two accounts. That leaves the decision of which one is "correct" in the hands of the UI and that's not secure. Besides, it should be impossible to get back two records. If we want to expand the data in our model, we can still do that without returning multiple. 2) There are two different things here. We aren't sharing an interface between UI and API for the reason I specified. We aren't extracting an interface because it typically is not beneficial to do so with a model. For everything else, we will extract interfaces to be used with Dependency Injection (which isn't in place in the API just yet).
Mine returns an error "Sequence contains no elements " at this Line - return data.GetUserById(userId).First(); in the UserController file, and a userId is returned still ?? Pls Help Tim!!
While trying to use the login page, I get the following error. I've tried troubleshooting all morning and can't seem to get the login screen to work. This is the error I receive when I click the login button: {System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. Did anyone else get this error?
In the event you are unable to add as a trusted ssl during startup of your project you will need to export your localhost cert and import it to the Trusted Root Cert Store... Had same issue!
Because I don't change anything off-screen. At the end of every video, I commit the changes and then I don't change the project until the next time I am recording.
I'm getting the Exeption"Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'RMDesktopUI.Library.Model.LoggedInUserModel' because the type requires a JSON object (e.g. {\"name\":\"value\"}) to deserialize correctly. To fix this error either change the JSON to a JSON object (e.g. {\"name\":\"value\"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array. Path '', line 1, position 1."
I litteraly have no idea how to debug this... its this line of code that doesnt work var result = await response.Content.ReadAsAsync(); But the LoggedInUserModel is correct.
Ok i figuered it out.... for some reason i have to make it a list var result = await response.Content.ReadAsAsync(); fo me its obvious now /api/User returns a List of UserModel... but why isnt't that for you the case?
And this is where I am going to disagree. The reason you have duplication, because your remote API infrastructure and your persistence are sprinkled with your domain. But great lesson anyway :)
Part 1 and Part 2 of the "Getting User Data" videos have really broadened my understanding of C#, APIs and applications. Seeing these projects separated, the API calls being made, the queries to the database, objects being passed around and their methods being called, that's experience that I haven't gotten from any other course I've taken. At least not at this scale, where there are multiple projects involved, and so many factors to take into consideration. Even the debugging sessions have been super informative. Your videos are (from a learning standpoint) the best videos I've seen on UA-cam. I don't know how many times I will thank you throughout this playlist, but I will continue to do so regardless of how many times I've already said it. Thank you. Thank you. Thank you.
You are just the type person Tim is looking to help. Thanks for trusting him to help build your skills.
Thank you for creating these vids. We may not be a numerous bunch viewing but we are certainly rooting for your success.soon this will be a reference for other newbs
You are welcome.
This is not for newbs. Jumping in and out of errors in programming and correcting before concentrating on the subject. Very confusing.
Thanks! The Series is incredibly informative. Definitely looking forward to getting through the rest.
Thank you!
quote of the year "Make it public" .Love your style by the way.
Thanks!
WOW!!! this is getting a LOT MORE COMPLEX!, probably will have to watch this video twice to understand/digest all the information.
Yup. It is drifting out of the nice, neat tutorial realm and into the "real world". The key is to make small changes, test often, and don't be afraid to step back and plan out a step if you aren't sure.
How Is Tim Switching Between Files So Quickly?
At 23:00 I got a little confused as to how Tim was navigating around so quickly. So for everyone else who doesn't know if you hold ctrl and click on an element it will take to straight to the definition. Alternatively, you can put your cursor inside the element and press F12. Hopefully, someone finds this useful :)
Yep, those are great options.
Awesome job as always! I am learning a lot through this about building out an API as I build one for my job's data access. Just a few quick thoughts:
@21:16 - AuthenticatedUser is still in the TRMDesktopUI.Models namespace (notice IAPIHelper's using statement for AuthenticatedUser). Nit-picky, I know.
@29:00 - Yes, DefaultRequestHeaders.Clear() does clear everything under DefaultRequestHeaders, including Accept.
@39:40 - You can Ctrl+. to get the option to 'Pull to interface' so you don't have to manually copy & paste into the file. Very handy!
editted to remove answered question in video.
Good catch on the using. I'll try to remember to remove that in the next video. Thanks for verifying the clear. I wasn't positive and didn't have the time to check. As for the one line assignment, yes I could but I don't prefer that method. It is harder to read. As for the Ctrl+., I didn't realize they added that. That's awesome! I'll have to remember that.
Hi Tim, thanks for the great course, I am enjoying the journey. Two suggestions from my side: 1. one section that would help me in this journey would be a small overview of what we have done so far where we stand and what we will do in this video and what will be the next steps. Sometimes I get lost in the details and am not aware of what exactly we are doing and how it fits into the larger picture. 2.I would appreciate some kind of challenge, it should not be too hard, but it should make the users try to think through a small solution themselves. Ok that's it, keep up the great work!
Thanks for the suggestions! I'll keep them in mind.
Hi Tim, here a big fan. Compliments. With the issue regarding api/user in stead of /user I have fixed while the application was still running. This worked and I was very pleased with it. So you don't have to stop the application, make the fix and then start again.
Thanks for sharing
The answer to my question in last video is the first thing in this video. Thanks for answering it Tim! :)
I think Tim can read minds. Have not proven it yet, but ...
Great episode Tim, thanks - took me 3 attempts to finish due to distractions, but finally got there this morning :)
I'm glad you stuck with it.
Love the series!
As a great way to improve and teach. It would be a fantastic idea to add a tiny bonus tip in each video for "Did you know?" types of things.
For instance where to add custom code snippets, or keyboard shotcuts that could speed up your coding.
I know you have a couple of videos for tips & tricks, but this I feel like could help either remind or teach people watching your videos! :)
I do try to call out tips when I use them but I'll see what I can do to be more intentional about it. Thanks for the suggestion.
Watching this great tutorial so far, I'd recommend adding folders for the solutions, since they're becoming many and could get confusing to some. Solutions folders names as API, UI, Database etc would be great way to organise the project even more.
Wait for it... Wait for it... (they are coming in a future video...based upon user feedback). Good suggestion.
Great part of whole tutorial. I jump to next one :)
Great!
Hi Tim. At around 12:50 you speak about MVC and use the term Front-End Model. Is this the same as a ViewModel?
No, a ViewModel is roughly equivalent to a Controller in MVC. MVC stands for Model-View-Controller. The front-end model is the M in MVC. It is (potentially) different than the back-end model in that the front-end model may have decorators or other UI-specific items in it.
@@IAmTimCorey Hmm, so if the front-end model is the M in MVC, what is the back-end model? Apologies, but this is the first time I have heard the terms 'front-end model' and 'back-end model' used within the context of MVC.
@@neilljamieson9606 I think there might have been some confusion somewhere as I think they might have thought you were asking about the ViewModel in MVVM. In MVC, if you add a view model, it acts as another layer between the view and the domain model. You may not want to expose everything (ex. sensitive data) from your domain model back to the view, so that's where the view model would come in. So in that way it would be a front-end model. The back-end model, or domain model would be the M in MVC since those tie back to the database.
Firstly Tim, thanks for the great tutorial videos. Could you in a future video explain the system architecture, in particular the API calls. Currently we have two databases, one which we use to create accounts with and another for our retail management system (which also stores our logged in user). My problem with this architecture is that we authenticate the user through the cloud but we then have to call the TRM database to retrieve the authenticated user details. Ignoring we haven't as yet resolved how we will duplicate the account to our TRM database, should the web API access our TRM database at all? I logically would think these databases would be on separate servers and we shouldn't have a dependency across the two, due to security, etc.
And if you do have time, I would like to here your opinion why you have used Entity Framework to create the Web API but use Dapper for TRM database?
I'll try to answer your questions here. First, yes, the WebAPI needs access to both databases because it is only when a user is authenticated that we can then use that authentication to access the rest of the data. The authentication grants the user the rights to get the sensitive data in the main database. As for duplicating the account, it isn't really duplication. We are just using the UserId as the primary key for a table with more information about the user. We can still separate these two databases onto two servers if we want (not necessary but possible) and access them both from the API.
As for using EF for the authentication system but Dapper for the main database, the reason is that I'm locked into EF for authentication. Pulling out EF and replacing it is a headache I don't need so I treat the authentication system like a plugin. I just use it. I use Dapper for the other database because it is faster at runtime than EF, it gives me more control over my database, and it can be more easily secured compared to EF.
Thanks Tim. I'm probably getting a bit ahead of myself as I've been curious how we are going to implement the registration process. Hence when I look at the AspNetUsers table definition, my thought is why not include any additional columns here instead of our TRMData.User table (is it because we don't what to modify EntityFramework code?)?
Hey Tim, quick question about your decision (at 3:27) to change GetById() to return a single UserModel rather than a list of them. By requesting the ".First()" item in the list aren't we risking a crash if it comes back empty? We're assuming that the logged in Identity/User's Id exists in the register's Users table, which it feasibly might not. What would be the best way to protect against this eventuality, if you feel it's even worthwhile? Also, a huge thank you for this resource you've put together.
Not sure if this is the right way to resolve this, but using .FirstOrDefault() instead of .First() returns null rather than causing an exception if the list is empty.
Good question. Yes, we are risking a crash. Yes, FirstOrDefault will give you null instead. The question is, do you want that? I decided I wanted the application to crash. The reason why is if the user is in the system but no record is returned for them, that is a major issue. It is one the application cannot resolve gracefully. Therefore, I want to crash the application rather than having it continue in a bad state.
@@IAmTimCorey As I also said in the previous comment, why have the original function give back a list of users at all as the Id is unique so GetById would never return more than one user, and I'd rather deal with a null than first having to check if there are any users in the list before getting the user. So IMHO the problem already exists in the original design.
Great Job Tim, I almost got lost, I managed to get back in track. FYI I couldn't use the "token" from inside our APIHelpers, instead only from LoggedInUserModel. Either way, it works.
I am glad you got it to work.
Great video as always.
Wouldn't make it sense for UserModel in DataManager.Library and LoggedInUserModel in DesktopUI.Library to share an Interface together maybe in an TRM.Shared project? So you can make sure the names of the properties match and did not get changed only on one side.
I get the idea but not really. The reason why is that then those two projects are tied together. If you update the interface, both projects have to update. Treat this like an API outside your control. Just like the other APIs we have worked with. We don't have interfaces for them. Besides, we might not want to capture every field. That's ok to do except when you have an interface enforcing implementation.
@@IAmTimCorey I get your point, but if I have both client and API side of project under control and I would like to do not forget to add property in UI or in API, is it OK to create shared interfaces only for fields which has to be in both models? say ISharedUserModel (FirstName, LastName, Email, ...) and implement this interface on APIModel and UIModel, and other props which I do not want to transfer will not be part of Interface?
We're starting to have a number of projects in the solution and they all start with TMRD, some 2 of them end in library.. it's hard to identify things quickly.. I would suggest adding solution folders to the solution to at least break the API from the front end. The solution folders don't affect namespace but are great for organizing groups of projects together.
Good suggestion. Since I'm heading out on a trip, I pre-recorded a few videos but I'll add that suggestion to the list. Hopefully I can add it in around the time we upgrade to .NET Core.
Good lesson. My question is this. do you feel this entire section was right for a single git commit? Or could we have broken down the steps a bit more? Sometimes I don't think its possible because of all the things you had to change.
Each commit should be a full change. I do make larger commits in these videos simply for time's sake but you do need to be careful that you don't push part of a change as a commit.
Great video and project. Thanks a lot and keep it up!
Thanks! Will do.
Tim, I love you man. This is the best tutorial series on UA-cam. I am just became a Patreon supporter TODAY. I am late to the game with this series. Will I be able to download this solutions still? I read a comment in the previous video that the code was no longer available. I will be honest. I'm a lone ranger and I've been writing ASP.Net VB for 20 years. My code is horrible. I want to rewrite my web site in C# using your project as a template for "how you SHOULD do things". I know this is not a web project, but most of the framework you created here applies. Using your code as a starting point will mean I don't have to go back to vid 1 and write the code myself as you show on screen. Thanks for this. I've learned more from these videos than I could ever have learned trying to piece together a hundred how-to vids on specific items. I can't believe how awesome this series is.
The source code is no longer on Pateon because it is no longer an active series. However, you can still get the code by purchasing the course (it is currently broken up into three phases, although we will be joining it together at some point): www.iamtimcorey.com/courses?query=timco
@@IAmTimCorey Just enrolled. Thanks for the quick reply.
I'm going back watching all of these and I just accidentally happened to turn on closed captioning and I'm dying...at 26:51, it thinks Tim said "stepped on ourself our own TOAST" lol. :p
Gotta love auto-generated closed captions.
@@IAmTimCorey indeed. I left them on. If I find any other funny ones, I’ll time stamp them ❤️
Why did you map each property seperately and not just assigned the whole result to the _loggedInUser? (38:00)
Because I didn't want to replace the instance, I wanted to update the existing instance.
~10:11 When you populate the LoggedInUserModel by copy/paste the UserModel properties. If you had an interface IUserModel in your Data Access Library, would it be okay to implement that into LoggedInUserModel and then add any additional properties as needed??
I didn't want to do that because the class is just properties and I needed to change all of them (decorate them, actually). An interface would require me to keep all of the properties, too, which might not be what I always want.
Out of interest is there any reason not to have some kind of singleton LoggedInUserHolder class which just references the LoggedInUserModel? It would allow you to just assign the new user rather than do a prop by prop update. It'd also let you set up your events and property change notifications on that one central property. Whereas now you'll either trigger events on every property update, have to pick some kind of canonical property that triggers the events or manually call some kind of method to drive the events all of which would be error prone IMO.
Hi Tim, thanks for this series. It's nice to see a programmers mind at work. I've started watching these to keep me company during the lonely lockdown home office hours. I've tagged along with my own hobby project using this as a reference. I did however start with Net 5, figuring things out myself which don't match the old 4.x Framework ways. One issue I had was the unability to access my user API with the token (401 - Unauthorized). The API is available on both http & https (via redirect), turns out that the issue only exists when I go to the API via http (I guess the token gets lost during the redirect?). I also passed the authorization header to the httpclient in a different way: _apiClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);. Anyhow just wanted to share this info for others who might end up with similar issues.
Thanks for watching and sharing your experiences.
When you move the API Helper to the library, you still use bootstrapper to get the resources you need for that class. But bootstrapper lives in DesktopUI. Wouldn't that be a dependency in the opposite direction?
Yep. You always have dependencies but we inverted the dependencies (Dependency Inversion Principle) and the UI tells the libraries which items to rely on instead of the libraries forcing the UI to use certain items.
Hey Tim, great series! I've been seeing it the last week or so and it is great to see how all of this teaches so many things at once, even if it's just something "minor" like fixing typo mistakes.
Now one thing I ask myself is, what would be a reason to have the separate calls for logging in and than a second call for getting user data?
I mean, obviously to keep thing cleanly and structured it would make sense in some way... But why wouldn't someone call for a login request to the API and then with that call, get the user data back?
Since, if your call returns a success response it'd probably mean already that the user was logged in successfully and you could then fetch the data from the response as well and you wouldn't have to keep track of two sperate models, but only the one user instance itself.
I hope it makes sense :)
Thx for all the great posts and hard work!
Greetings from Germany
Hey Tim, great series, i was just wondering if there was an overview/preview/course introduction of the MVC add on for the tournament tracker series, just so i could see what would be achieved in the course as i am interested in seeing where we can take the design when using MVC, many thanks
That is a high priority on my todo list but it isn't done yet. I talk you through it but I don't show you. Hopefully I'll get that together in the next few months.
why am I getting back "CreatedDate": "0001-01-01T00:00:00" getutcdate()
but in the database I have
4/28/2020 1:18:09 AM (the correct one) above has January first year 1
Solved this. It was a difference between CreatedDate vs CreateDate. CreatedDate was defined in the model and CreateDate was on the Database so it was null from model and getutcdate() bring back 01/01/0001\
That would do it. Glad you figured it out.
@@MrDdepass Thank you for this, I also mislabelled my CreatedDate column and didn't find my mistake until I saw your comment!
Excellent Video!
Thanks!
Hi Tim! Thank you for the good job you do, you are very educational and explain everything slowly and well. I have a thought, the easiest and best way to build authentication into a WPF application, is it to create a web api that you have done in this series?
Have you viewed this video yet? - ua-cam.com/video/_LdiqQ13NBo/v-deo.html I think it will help you.
I have viewed that video and I think it was great, that was why im asking you if that it the best solulation :) Thank you for great videos!
Great series so far Tim. This could possibly be another video that you do at some other time. At around 8.00 minutes you mention that you could have done the TRMDataManager and Library as one solution and the TRMDesktopUI and Library as another solution. That leads me to wondering how would you join those two solutions together? If you made a change to the TMRDataManager solution and didn't need to make changes to the TRMDesktopUI solution how would you ensure that the TRMDesktopUI solution contains the newer version of the TRMDataManager solution?
DesktopUI solution doesn't really have any connection(no dll's are referred) to the DataManger project, all it does it uses the WebApi's exposed by the DataManager. If you want you can have an android application built using Java/Kotlin in a different IDE and still use the same Api exposed by DataManager. Thats the beauty of WebApi's. When i started watching this course, I got a little worried when I saw him creating the WPF project in the same solution. But later i realised why this was done. Also, the question you have is correct. WebApi(DataManager) and WPFClient(Desktop UI) do evolve separately when in production. I mean The UI changes much more frequently in real life. But as long as the endpoints(WebAPIs don't change, its okay). If the API does change, the client has to be separately upgraded to use the new APIs. But even then the legacy APIs are still kept intact.
I will add it to the list. Thanks for the suggestion.
Thanks for a great video. I know that I am a whole year behind in this but I noticed a small thing that I thought was a bit weird. It looked to me as one of the using statements in the APIHelper refers to TRMDesktopUI.Models but it works anyway. I then realized that you forgot to change the namespace when you moved AuthenticatedUser.cs between projects.
Thanks for pointing it out. You haven't caught up to this point yet but in the future, we are using Azure Boards to track our bugs and features and we are assigning them to commits. I just entered this in as bug #29. When you get that far, you will see us fix this bug you submitted (it hasn't happened yet). Thanks for pointing it out.
Presumably it would be better to do .Single() vs .First()? We're only expecting one entry for that user ID, if there's more than 1 entry - something is wrong somewhere...
.Single() would allow us to discover the issue and handle the exception accordingly? Interested on your perspective on this!
It depends on what you are looking for. First is slightly more efficient but Single will throw an exception if there is more than one.
There are two items that aren't quite bugs, but I believe they are not what you intended:
1. The class authenticatedUser.cs is in the wrong namespace. It is in the TRMDesktopUI.Models namespace instead of the TRMDesktopUI.Library.Models namespace which is where I think you
intended to put it.
2. The call to apiClient.GetAsync("/api/User") in APIHelper executes the method GetById in the UserController. The GetById returns an instance of UserModel. After the call to apiClient.GetAsync("/api/User") there's a call to ReadAsAsync . I believe that ReadAsAsync should return an instance of UserModel because that's what was returned from GetById, but it instead returns an instance of LoggedInUserModel.
Since the UserModel class and the LoggedInUserModel are virtually identical, this is more of a semantic issue than an actual bug/error. The one caveat is that in order to correct this you need the TRMDesktopUI.Library to refer to the TRMDataManager.Library which I am not sure you want to do because it ties the libraries together.
Am I correct that we should change the namespace for issue number 1 and return the UserModel from ReadAsAsync for issue number 2?
Thanks
Yep, the namespace issue is on the bug list to fix. As for number 2, no, I don't want to mix the two. I am acting as though I have no control over the API, which is the right way to proceed when creating an API. What the front-end calls the models and how it interacts with them will be different than the back-end. I call the front-end model LoggedInUserModel because it is descriptive of what the model represents: the logged in user and their information.
@@IAmTimCorey Thanks for getting back to me. I wasn't aware that there is a bug list.
As far as the second issue:
I understand why you did this. What confused me was how ReadAsAsync was able to map the response data from the UserModel object into a LoggedInUserModel object.
I think I figured it out. The mapping from UserModel into a LoggedInUserModel object ONLY happens if the field names are identical in the UserModel and LoggedInUserModel. Field names that are spelled differently return a null.
I have to assume that it uses reflection to do this.
Hopefully this makes sense.
Thanks
Could we have used interfaces to help with the code duplication? I'm aware I'm late to the party here, lol. Just curios
Hi Tim, your videos are great and really helpful. I'm just getting started and I'm also reading the book Clean Architecture by Robert C. Martin. In your solution the API Project uses an object (for example the user object) and the DesktopUI also uses a different user object. Where should I create the entities which build the domain of the whole project? They build the core and should be reusable in all projects. Should I create for example a TRMLibrary with all these entities or do I miss something?
You could create a project type like that (maybe a new class library), but I'm often leery of doing so. If both sides rely on a model type and then the UI needs to have some UI-specific logic in the model, you have a mess. I am cautious where I use that approach for that reason.
Would it be beneficial to refactor the user properties onto an interface shared between the frontend and backend? Is that valuable if it only serves to reduce the property duplication?
I don't think that will benefit our application development. If we did that, we would need to have a library that both the front-end and back-end depended on. Then, if we changed that library, it would force both parts to update. At that point, there isn't a need for the API. We should just make it one application. Besides, the UI models are (or can be) different than the back-end models even if they appear to be identical today. They serve different purposes. Good thought though.
I noticed a container is used in this section. How does this relate to containers you mentioned in your video regarding creating containers using Docker?
Can you provide me a timecode? I don't remember using containers in this series.
@@IAmTimCorey 36:20
@@IAmTimCorey I see _ container
That's something different. It is referencing a Dependency Injection container (the thing that holds all of our dependencies).
Hello Tim,
Thanks for the brilliant content you are putting on youtube! Great stuff!
My question is about the naming conventions of class fields - is it not considered a bad practice to name fields, starting with an underscore? Now I understand that your point here might be not to use the "this" keyword in order to setup the properties for the relevant fields, but anyways when I've learnt naming conventions for C# the best practice states to name fields with camel case, the same as variables. Please let me know your opinion and does that naming come due to your experience as a developer ?
Thanks in advance!
There are a couple times when the underscore is recommended. First is when it is a private backing field for a public property (full property). The second is when you bring in the value from an external location (such as dependency injection). Both indicate that you shouldn't write directly to that item or overwrite it.
I was surprised to see that TRMDesktopUI.Library APIHelper's constructor initializing ILoggedInUserModel using DI. I guess you don't need to explicitly use DI in each library and can figure out from singleton setups.
Correct, you do not need to set up DI in the class library but the class library can expect DI to be in place.
@@IAmTimCorey I have a question:
Is the library class's for example TRMDataManager.Library will contain only data access related functions or is it going to contain other functions as well. If it does contain only Data access functionality wouldn't adding more library for different purposes would make the naming really confusing. Same thing for the TRMDesktopUI.Library
Hi Tim, just wondering for the model sharing.
Why don't we have a single library class that supplies the most basic data (like UserModel), then the DesktopUI or TRMDataManager inherit on the class and add changes based on their needs? They having different purpose but they should also using the same model.
So, we should can have the code sharing and also certain level of code separation.
I disagree with the "they should also be using the same model" concept. These are two different models with two different purposes. Inheritance isn't about just code sharing. It is about a relationship. That's where people get in trouble with inheritance. "If it has wheels and an engine, they can share the same base class" and suddenly you have an airplane, a car, and a lawn tractor sharing a base class. That sounds fine until you realize you have totally different concepts of what the engine does, what a transmission looks like, and so on. Same thing here. Just because they look similar does not mean that they should be the same or inherit from the same base. If, down the road, I decide to ask for less properties from the API, I'd be stuck. I couldn't do it. That's a design problem.
@@IAmTimCorey Oh okay, thanks tim! Now i understand more
Hi Tim, thanks for the great content.
About copying all the properties from the LoggedInUserModel to the singleton object.
Just my thoughts: isn't it more useful to create a Clone() function in the LoggedInUserModel that returns a new LoggedInUserModel? So things stay together and it's almost impossible to forget copying properties as you're in the model itself.
public LoggedInUserModel Clone()
{
return new LoggedInUserModel
{
Id = this.Id,
...
};
}
@IAMTimCorey, in this video your public async Task GetLoggedInUserInfo(string token) method in the APIHelper class doesn't seem to return anything yet it still works. I can only get mine working by returning the local _loggedInUser object. What am I missing? Thanks in advance!
Wouldn't it be also good to add the AuthenticatedUserModel as a singleton?
We don't want to create a singleton for the AuthenticatedUser because we want a clean, new instance each time. Plus, we aren't getting that model from dependency injection.
Might it be better to separate the Models that both the UI and API are using into a common model project. That way the data access is still independent of the UI but the models are still shared. The common model could be held within another object holding data that is specific to the UI. Or the UI model could inherit from the common model.
I tend not to do that because of a couple of reasons. First, it tends to make me lazy. If I have a common library, I avoid creating UI-specific models when I need them. I know I could inherit from them, but that's messy. I'm ok with a bit of duplication, especially since it keeps my UI-specific code in my UI. It doesn't wander into the shared library. Second, it reduces my complexity. If I have a shared library, I have an additional reference to maintain, an additional place to look for the code, and if I make a change in the shared library, I can affect two (or more) projects negatively.
I know a lot of people use shared libraries (including Microsoft), but I have mostly stayed away from them for these reasons. These are just personal preferences, though.
Here you pass a string as a parameter from the UI to the server to get the user details. But what if we had to pass an object as a parameter? The the object definition should be available in the UI library and the server library?
When you pass something through an API, it gets converted to JSON. So, you can pass objects but you have to have that object defined at both ends (but it isn't the same object, it just has the same properties).
@Gowtham Natarajan This is what the Newtonsoft.Json library does. I would highly recommend reading the documentation, as fully understanding the concept of objects as data structures and JSON as a representation of that structure's format is IMO a milestone in your knowledge of programming.
It's an interesting and really intuitive library, especially compared to options for other languages (*cough* Java).
I ran into an issue where the API call to /api/User returned a list, but it seemed that it returned a single object in this video. I just changed my code to handle the list and use the first object (in this case LoggedinUserModel) in the list if there were any. If I missed this in the video I apologize.
Where is a good place to write complex logic? The Backend library or the UI library? If we have to read data from DB and do some complex calculations before the result is shown in the UI, the server could just pass the raw data to the UI library which does the calculation or the server can do the calculation and pass just the result to the UI. Which is better?
It depends on what the "calculations" are. If they are UI-specific then the UI layer. Otherwise, move them down to the business logic layer (in the class library). Only UI-specific code should go in the UI.
Hi Tim,
Great video as always!
I have a question; In the GetLoggedInUserInfo method when you do the manual mapping of the result, would it not be possible to just assign result directly to "_loggedInUserModel" seeing that they have the same type? I seems to me like the mapping is unnecessary, or am I missing something?
The reason is that the _loggedInUserModel is a singleton from dependency injection. We can't overwrite it, we need to use that instance Therefore, we replace the values instead of changing the instance.
@@IAmTimCorey I see, I guess it was my unfamiliarity to Automapper that made me miss your explanation in the video. Thanks for taking the time to answer!
As fas as I understand, Data, DataManager and its library would be the server, and DesktopUI and its library would be the clients if you implement this as 'server with multiple clients'. Am I right? Would you need to do adaptations to the code to make it work that way?
I mean in an intranet
The API project would be a server and it could serve multiple clients (the WPF project). We will actually set this up down the road. There is nothing you need to do in addition for this to work. If you wanted it to work across the Internet, you would just need to make sure your API project is deployed to an Internet-accessible spot (like an Azure Web App or a web host).
Hi Tim, I have to admit that using frameworks makes me confused. If I understand Calibrun.Micro, it helps to bring the different WPF parts together by relying on syntax convention. When I launch the app I get a null value for the _loggedInUser variable. The programme goes through the constructor, but I don't really understand what gives the loggedInUser value in the constructor. I believe it comes from the container of the dependency injection with .Singleton(). Could you please tell me a little bit more? I don't think I've missed any step.
Yes, this is dependency injection. It isn't really a Caliburn Micro issue at this point, it is just a DI issue. The DI system will give you an instance of LoggedInUserModel when you request an ILoggedInUserModel through the constructor. The code in your container should look like this: .Singleton() (that's a bit different than what you listed).
@@IAmTimCorey Thanks Tim for your explanation and sorry for my error. Nevertheless, I've just realised that:
- result.Id = null and it is also null in swagger.
- Event if I assign _loggedInUser.Id to a string, let's say "XXXX", it doesn't appear in swagger.
- Id has a value in (localdb)\MSSQLocalDB\..\dbo.User.
I'm not an IT developper (even if I hope to become one a day), but I'm pretty sure I'm missing something somewhere (I don't really know what separates the client to the server). Have you already encounter a similar issue? Thanks again :-)
Hello Tim, I'm sorry to bother you again, but I haven't found the issue regarding my last message yet. Do you have an idea what it comes from? Many thanks for your help!
@@ludovicwagner2656 I might be guessing but doesn't hurt to try, one of the things that work best for me in these cases is: rewind to where your code was working before, than, have the tutorial in a separate monitor/screen, and start comparing word for word against your project. in most cases, you will see that you are missing one word.
I had an error just now with my LoginViewModel constructor after I extracted my APIHelper Interface.I took almost an hour debugging, and the error was consistent. Turns out I had to make my Interface class public.
I hope this helps
@@ludovicwagner2656 Hi, I'm not sure if you were able to solve your issue for Id yet, but you can check the Select statement in the stored procedure which is returning the data, and add Id column in the select query.
I hope this helps.
Hi Tim, was the response intentionly mapped to LoggedInUserModel even if the API return a UserModel (just because the JSON mapper can resolve it since they have relativly the same properties by returning null on token) or was it just a little unseen bug.
thank you for the series, realy enjoying it.
It was intentional because that is the model we wanted it in. That's the benefit of using JSON - you can put it into any class that matches.
My AcessToken is getting null with status code 401 Unauthorized after updating api/user. Why it might be?
Your access token expires/changes when you change your user. Best practice is to get a new one every time you start the application.
@@IAmTimCorey I am referirng to when I click log in in form and in the debugger it shows null
It sounds like maybe you named your property something slightly different and so the system is not matching it up to the passed in property name. It wouldn't be a casing issue but it could be that the property is a letter off.
@@IAmTimCorey It's really weird because the letters are correct. I have:
- PostAsync("/Token", data)
- _loggedInUser.Token = token
- GetLoggedInUserInfo(result.AcessToken)
In AuthenticatedUser:
- public string AcessToken { get; set; }
You have the property AcessToken but it should be Access_Token (notice the two "c" characters and the underscore). That's your problem most likely. The system doesn't know how to map "access_token" to "AcessToken" because it is more than a casing difference.
Hi Tim, really enjoying the series. I've hit a snag, when I run the solution, the web api opens in my browser. But the Login screen is not showing up. I don't see any issues in the debugger screen. Any suggestions on where to look to figure out why Login screen isn't showing?
It sounds like you don't have the WPF project set as startup as well as the API.
hi tim. I'm writing right, I'm running into an error called. " The object reference is not set to an instance of an object" I get all my data but now I press continue. comes the message in text TextBlock x:Name="ErrorMassage". Is it a natural mistake or I have to look closely.
One more question is the possibility of running it all together in Database instead of 2.
totally delicious work you do. it teaches me a lot here in Denmark
Sincerely
Michael Schannong
Is your TextBlock x:Name="ErrorMassage" vs. the property name ErrorMessage? Massage vs Message?
The Object Reference is not set... message is because you are attempting to use a class that has not been instantiated. My guess is that in the API, you are trying to use a model without first instantiating it (= new ModelName()). That is why it is passing down that message.
As for the database question, it is definitely possible to do this in one database. There are just a few issues with that. First, you cannot scan the database for changes and bring them into your project because it will bring in the EF changes. Second, it mixes your generated SQL with EF generated SQL. Not necessarily an issue but it makes for a messy structure where no one system has the complete design. I prefer not to mix my authentication db with my data db.
@@jimlynch8313 yes spelling mistakes are dyslexic :)
@@IAmTimCorey
thanks i try to look through the error
@@IAmTimCorey I found it was a logging ... that was not an interface
thanks
Thanks for another great video. Just wondering how string api = ConfigurationManager.AppSettings["api"]; in TRMDesktopUI.Library.Api.APIHelper InitializeClient method gets value from app.config in TRMDesktopUI? These are different projects and appSetings not even exists in TRMDesktopUI.Library.
A class library cannot be run. We run the user interface project and it uses the class library. Therefore, the class library uses the app.config of the project that runs it (the UI).
@@IAmTimCorey Thx!
@@Tahmasib13 What was the answer here? I am having that error on my application "Error CS0103 The name 'ConfigurationManager' does not exist in the current context
" and I just cant move from here!!
So what is the solution for this error ? "Error CS0103 The name 'ConfigurationManager' does not exist in the current context " I just cant move from here!!
found the problem ... little details .... daaaammnn!!
Great video as always! I got the "Unauthorized" error! I used break point and it still can access and get the token for the login part, but then it could not access to get other information using that token (Noted that I used another url not my localhost)! What should I do Tim?
Did you include the "bearer " in front of the token when passing it in? Did you pass it in as a header value? Have you tried from Postman to see if you can get it to work?
@@IAmTimCorey Thanks a lot for your advice Tim. I did it! I forgot the space between Bearer and token ^^!
@@IAmTimCorey I got another error Tim! Which telling that, they require a JSON object, so cannot paste the value into LogInUserModel
Tim -
Thanks for the content, I've been following along with the series, however, when I try to run the api I'm getting a "Parser Error", where it could not load type TRMDataManager.WebApiApplication
There's a Source Error:
Line 1:
I've done the Googling and suggestions were to use Dapper.StrongName, or remove the Global.asax.cs file..I used the StrongName, and it worked, so there a big enough difference between Dapper and Dapper.StrongName?
Something else is going on here. You should not need to change that and no, don't get rid of global.asax.cs. My guess is that you actually have a naming issue somewhere (casing is not correct somewhere).
@@IAmTimCorey Sounds good thank you senpai 🙏🏽
Hey Tim, I am getting an "Internal Server Error" as my response from HttpResponseMessage response = await apiClient.GetAsync("/api/User"). I checked my spacing around the $"Bearer {token} and my spelling of Authorization. Any idea where the error could lie?
I'm not sure, sorry. Try some debugging. Maybe it is hitting the endpoint but having an issue?
Hi @IAmTimCorey I was wondering how we can pass the current logged in users details to another Screen, I'm thinking along the lines of passing instance of ApiHelper to the second view ("Screen") and then retrieving current logged in user through that. But the problem I'm having is how do I find the current users token to pass in to GetLoggedInUser info method without having to re-authenticate the user post log-in?
Any help would be much appreciated!
We captured the logged in user's data in a Singleton class, which means wherever we ask for that class, we will get the same instance, including all of the data we put in it.
@@IAmTimCorey Ah yes, I got it.. Nice one!
Can you show web api punished to iis and consumer from the ui. Token api itself has failed when I tried.
If you change databases, you will need to make sure the user is created in the new database before you can use it. I will be pushing this project to Azure at some point.
It seems worrying that in the UI Library, we are importing stuff from UI (e.g. AuthenticatedUser), whereas in UI, we are importing stuff from UI Library (IAPIHelper). Isn't this a circular dependency, or am I missing something?
Oh, you actually talk about it at 20:05. Never mind!
First off, great video series! Being new to programming, this series has been very helpful!! I'm getting an error when I run the application at the end of this video above the username: "An error occurred while sending the request". I've been trying to figure this out for several days now and can't seem to find what I'm doing wrong. Any idea what I'm doing wrong??
So that error message isn't the "real" one. See if you can put a breakpoint to catch the error so you can see the inner exception. That message should show you the real error. That's the place to start.
@@IAmTimCorey Ok, I figured it out. So if anyone else has this problem...this might be an answer... I was getting errors from a couple different places all saying that they threw "an exception of type 'System.NullReferenceException'." ... i.e. "response.Content threw an exception of type 'System.NullReferenceException' ". By accident, while debugging, I saw that the API Project URL was different...for some reason...from the BaseAddress used in the APIHelper Class. When I changed it to the right port in the TRMDataManager Project properties and created a new Virtual Directory, that fixed the problem.
I am not sure if you changed the namespace in AuthenticatedUser class from 'SRMDesktopUI.Models' to 'SRMDesktopUI.Library.Models'. Maybe that's more logical.
Yep, we change that later in the series.
When we didn't get the data (the false on GetAsync) I was still getting 404... it turned out I'd accidentally inserted a space between User and its / in that call. Is it better practice to not have the trailing slash when building an API?
I don't know if there is a best practice on the trailing slash but spaces are definitely an issue.
Hi, I'm a big fun of those courses. Thanks Tim!
I followed up to this tutorial, but in a login form I got an error "an error ocurred while sending the request", actually I do not even know where to look up in a code right now, so much behind already. Tried debug this error, could not find it, where is the problem in my side. Maybe it's kinda typo error by me, or need to look somewhere else. Anyone please could help me.
I'm afraid I can't offer help but I can offer encouragement. Debugging is a critical skill to learn for every programmer. Learning to trace the data and logic flows is very useful. If possible, back out changes to the last time it worked, then add code , testing after each piece. Alternatively, review each step you recently added, one at a time, testing each one to see the impact. Testing ands debugging are must have tools.
In the APIHelper code
this returns "true" and you're authorized...
apiClient.DefaultRequestHeaders.Add("Authorization", $"Bearer { token }");
and this returns "false" and you're not...
apiClient.DefaultRequestHeaders.Add("Authorization", $"Bearer{ token }");
in other words check the space between Bearer and token in your code.
Yep, that space is important.
This is gonna be an ongoing comment I'll edit as I watch:
1) I actually thought it's smarter to interface everything, return an IEnumarble and ditch the .First().
2) Wouldn't it be better to extract an interface from UserModel for reuse in DesktopUI.Library? What are the dangers of sharing an interface across the projects?
3) Finding the right references manually is a major headache, praise the Ctr + .
4) * Points back at 2) with a grin *
Here are my replies:
1) By returning First, we ensure we only return one login record (no need for a list or a possible issue of people misunderstanding). If we expect only one record, we should return only one record.
2) I discussed the dangers somewhat in the video. First, we don't want coupling between our front-end and our back-end. They are two separate projects and we want to treat them as such. Second, we don't want to expose the direct data access to the front-end. Otherwise, people could bypass the API. Third, and most importantly, the models are not the same. Yes, they look the same but they don't have to be. If we tie them to the same interface, we can't then change the UI model to fit the UI (less properties, more properties, etc.)
3) Yup
4) Points back to my answer.
@@IAmTimCorey
1) Wouldn't IEnumerable imply a single enumerable dataset of the user model? and in the case of feature development into the feature would be more expendable? as explained in your SOLID Principles video. Or would that actually be more prone to errors if we use it that way?
2) So basically we are not extracting an interface because we want to make sure it's clear that the front and back end are not coupled together and should never be, we are eliminating the chance of use cases of polymorphism between the two projects by design
1) IEnumerable means it can return zero or more UserModels. We are expecting only one. That would be unexpected behavior to get more than one. When you log in, you shouldn't get back two accounts. That leaves the decision of which one is "correct" in the hands of the UI and that's not secure. Besides, it should be impossible to get back two records. If we want to expand the data in our model, we can still do that without returning multiple.
2) There are two different things here. We aren't sharing an interface between UI and API for the reason I specified. We aren't extracting an interface because it typically is not beneficial to do so with a model. For everything else, we will extract interfaces to be used with Dependency Injection (which isn't in place in the API just yet).
Why I'm i getting this error cannot deserialize the current JSON array (e.g. [1,2,3]) into type...?
It sounds like you are trying to deserialize it into an incorrect type. An array of [1,2,3] would deseriealize into a List or int[].
var jsonString = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject(jsonString);
This one worked for me
The problem with this you are stack to one instance. Mapping is the problem
Mine returns an error "Sequence contains no elements " at this Line - return data.GetUserById(userId).First(); in the UserController file, and a userId is returned still ?? Pls Help Tim!!
It sounds like you don't have any records in the table. Remember that I added a row manually. Might want to check to see if you have that row added.
@@IAmTimCorey Yes, this is the problem. sorted!. Thanks for responding swiftly Tim.
While trying to use the login page, I get the following error. I've tried troubleshooting all morning and can't seem to get the login screen to work. This is the error I receive when I click the login button: {System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. Did anyone else get this error?
It sounds like you need to trust your development SSL certificate.
In the event you are unable to add as a trusted ssl during startup of your project you will need to export your localhost cert and import it to the Trusted Root Cert Store... Had same issue!
Someone got a "bad request" mess after giving the credentials?
If you don't pass the type as bearer token or if you use http instead of https or if you use GET instead of POST, it can happen.
why every video start with Zero Git Changes?
Because I don't change anything off-screen. At the end of every video, I commit the changes and then I don't change the project until the next time I am recording.
@@IAmTimCorey Not all videos include You do commit , that what i want to say , like the video prev to this.
Sure. I do commits off screen mostly because it doesn’t add anything of value to continually do so.
I'm getting the Exeption"Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'RMDesktopUI.Library.Model.LoggedInUserModel' because the type requires a JSON object (e.g. {\"name\":\"value\"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {\"name\":\"value\"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
Path '', line 1, position 1."
When i use the ReadAsStringAsync() function i see the correct data, but somehow i cannot map it to the object;
I litteraly have no idea how to debug this...
its this line of code that doesnt work var result = await response.Content.ReadAsAsync();
But the LoggedInUserModel is correct.
Ok i figuered it out.... for some reason i have to make it a list var result = await response.Content.ReadAsAsync();
fo me its obvious now /api/User returns a List of UserModel... but why isnt't that for you the case?
OHHHHHHHHHHHHH SHIT I missed the beginning Thats a way of wasting an hour
I'm glad you figured it out.
Copy, paste, errors, correct, very confused now.
I'm not sure what you are saying.
And this is where I am going to disagree. The reason you have duplication, because your remote API infrastructure and your persistence are sprinkled with your domain. But great lesson anyway :)
Thanks for the comment. I appreciate the kind disagreement (I'm being serious, not sarcastic).
@@IAmTimCorey And, as many many others, appreciate your effort to put this together. This is awesome. Thank you!