Simple C# Data Access with Dapper and SQL - Minimal API Project Part 1

Поділитися
Вставка
  • Опубліковано 15 жов 2024
  • Minimal APIs are now the default in .NET 6. I thought that we should take some time and look at what they are, how they work, and what they might look like in the real world. To that end, we are going to spend two videos covering this topic. This video will cover setting up a SQL database and configuring Dapper for easy data access. We will set up a simple but full CRUD data layer so that when we build our minimal API in part two, we can concentrate on the API itself and not the setup. The great thing is that this lesson will work with almost any user interface, not just a minimal API.
    Part 2: • Minimal API in .NET 6 ...
    Full Training Courses: IAmTimCorey.com
    Source Code: leadmagnets.ap...
    Mailing List: signup.iamtimc...

КОМЕНТАРІ • 554

  • @RobertLenior
    @RobertLenior 2 роки тому +12

    Switched over to Dapper thanks to you Tim! You have been a great help to me. The way you explain things is incredible. Soo much easier than so many others and also your voice helps a lot. No funny accents and very human. Even the tiny errors and correcting them helps.

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +2

      I am glad my content has been helpful.

  • @manuelgamezz
    @manuelgamezz Рік тому +1

    Right now, I understand dapper, it's really cool to use to any database, SQL server + stored procedure is magic. Thanks Tim.

  • @c130daddy
    @c130daddy 2 роки тому +14

    Another great video sir, please keep them coming. For anyone watching wondering wether his courses are worth it, they totally are! I've been a developer for quite some time and I wish I'd found you and your work a long time ago sir. Can't recommend you to other developers enough! Thank you!

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +1

      I appreciate the kind words.

  • @deltaphilip8611
    @deltaphilip8611 Рік тому

    When I want to learn something new, Corey is my go to trainer. To show my appreciation I will purchase one of his classes. I learned working with SQL DB's with EF. I like EF, but it made my SQL programming weak. Learning Dapper my alleviate that handicap.

    • @IAmTimCorey
      @IAmTimCorey  Рік тому

      I’m glad my content has been helpful.

  • @amirhosseinerambue1214
    @amirhosseinerambue1214 2 роки тому +3

    OH MY GOD , thank u so much , this is very handy , i think every beginner dev should watch this video , thanks bro

  • @sainsoftsoftwares2302
    @sainsoftsoftwares2302 2 роки тому

    I have been searching .net core web api from the last 2 months and my search end here
    It help me a lot, Thanku you very much for this greate informational videos.

  • @waynehawkins654
    @waynehawkins654 Рік тому

    Thanks Tim. Amazing video. It's changing my workflow and now do a mix of EF and Dapper. EF to build my database tables, Visual Studio database project to build my Stored Procedures and Dapper to do all the CRUD. The best of both worlds, or maybe good and evil being nice to each other.

  • @aythienutties6223
    @aythienutties6223 Рік тому +1

    Hello Corey your videos ready amazing.. since I've been watching these videos I have broadly understanding in programming

  • @Mche72
    @Mche72 2 роки тому +6

    That was such a great help for me, thanks tim! I am really new in c# and worked on a project for my company with ef core but now I am thinking to use dapper instead of ef core because of the advantages you mentioned. I just love this simplicity and the separation of concerns with dapper, also it seems to me much easier to understand than ef core.

  • @girornsveinsson7970
    @girornsveinsson7970 2 роки тому +5

    I really like the Contrib extension of Dapper. It allows us to write generic CRUD methods in the case of this video to the SqlDataAccess class and then we don't have to write CRUD stored procedures for every table unless there are some specific side effects that are needed in the sprocs. I think it saves a lot of coding time but of course there might be some reasons such as access control and maybe as you mentioned, performance which would require stored procedures. In my experience though, the Contrib methods are just fine.

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +2

      I haven't used that extension before in one of my videos. I'll have to change that. Since it does not use pre-written procedures, it will make it a bit tougher to debug slow queries in SQL, but that seems like a bit of a non-issue with simple queries.

    • @JasonTira
      @JasonTira 2 роки тому

      @@IAmTimCorey I would like to see this, count this as a +1

    • @girornsveinsson7970
      @girornsveinsson7970 2 роки тому +3

      This is an example of how I implemented generic GetAll and Insert methods using Contrib:
      public async Task GetAllAsync() where T : class
      {
      using IDbConnection connection = new SqlConnection(_connectionString);
      var rows = await connection.GetAllAsync();
      return rows;
      }
      public async Task InsertAsync(T entity) where T : class
      {
      using IDbConnection connection = new SqlConnection(_connectionString);
      var entityId = await connection.InsertAsync(entity);
      return entityId;
      }
      In this case I would then use this in the UserData class like so:
      public async Task GetUsers()
      {
      var users = await _sqlDataAccess.GetAllAsync();
      return users;
      }
      Note that the UserModel class need to have the table decorator from Contrib:
      [Table("User")]
      public class UserModel
      {
      //...
      }

  • @hamzaanushath
    @hamzaanushath Рік тому

    This is my 10th hour of watching your explanation. Mind-blowing ❣

    • @IAmTimCorey
      @IAmTimCorey  Рік тому +1

      I’m glad they have been helpful.

  • @soroushsaghafi8473
    @soroushsaghafi8473 2 роки тому +1

    That was a great video. Thanks for the good content, organized presentation, clear voice and accent. Also the last but not least, thanks for NOT using Entity Framework.

  • @IAm-MB
    @IAm-MB 2 роки тому +4

    Hey Tim... I was watching your videos since sometime but never commented before. I can't resist myself this time. This is a really good video, easy to go along the flow. Nicely explained (as always). I am pretty sure second part will be as interesting to learn as this one.

  • @hutchm92
    @hutchm92 9 місяців тому

    Outstanding work here. This has help me design a clearer roadmap to migrate framework project to core replace linqtosql Orm. Thanks again!

  • @turn1210
    @turn1210 2 роки тому +2

    Your videos always seem to pop up just at the right time for me, I’m in two minds about using EF for my latest project, the cons are outweighing the pros at the minute on a technical level, I tend to lean towards stored procedures as I find all but the simplest db schema just bloats the code/queries when using EF, but, the team all use It exclusively, so this will help give another option.

  • @Steph-jb2yk
    @Steph-jb2yk Рік тому

    Great video, helped me a lot in my work!
    One thing I didn't understand: the "Save data" method in the "ISqlDataAccess" interface has the following signature: SaveData(string ..., T ..., string ... = "Default"). When we use this method in the UserData class, we don't always "adhere" to this signature. For example: _db.SaveData("dbo.spUser_Update", user). It appears we don't pass in / specify the argument. I would have expected something like: _db.SaveData("dbo.spUser_Update", user). Why is the generic parameter not required to call this method? And am I right to assume that the string argument can be dropped because a default value has been defined in the SaveData method?

  • @marmazza1
    @marmazza1 Рік тому

    Hi Tim, can't find the words to describe my thoughts now. Your contents are awesome, that's it, thank you very much!

  • @family--A
    @family--A 2 роки тому +7

    Thank you very much, it was really helpful!
    Could you please add dynamic sorting, paging and filtering on part 2?
    Or at least point me to some example of how this could be achieved.

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +4

      That isn't in the plan for the next video (which is already recorded). However, I'll keep that in mind for the future.

  • @niangel2704
    @niangel2704 2 роки тому

    Excellent video Tim!! The only weakness that I could think of, of this approach, is the lack of a proper way to get back the Id of the newly created object, by hitting the DB once with the Insert stored proc!! I really value your opinion about this!! Thanx again Tim, for enlightening us with your in depth knowledge of what we all love!!
    I look forward to hearing from you...

    • @rolfherbert2051
      @rolfherbert2051 2 роки тому +1

      If you are using an SP you can return an integer value or if you switch on NOCOUNT you can return any data such as SELECT SCOPE_IDENTITY(), or SELECT FIRSTNAME, LASTNAME FROM USERS WHERE ID = SCOPE_IDENTITY() to get back the ID. If you were writing your own SQL code you can combine multiple statements so "SET NOCOUNT ON; INSERT INTO USER (FIRSTNAME, LASTNAME) VALUES (@0, @1); SELECT SCOPE_IDENTITY()

  • @rolfherbert2051
    @rolfherbert2051 2 роки тому

    Hi there,
    Thanks for all the resources. I know alot more about MSSQL than I do about .NET so thanks for getting me learning new skills.
    Few comments about stored procedures; stored procedures are not inherently faster than ad-hoc statements, all plans are compiled and cached ad-hoc or otherwise. MSSQL will consider dynamic SQL to be a different query and so will create a new plan for every execution - but if you use parameters (please god use parameters) then the query plan is reused for each different parameter.
    If you wrote this sql as a dapper query 'SELECT FIRSTNAME, LASTNAME FROM USERS WHERE FIRSTNAME = @0' and substituted in the parameter at run time then the query plan would be reused regardless of which parameter is used.
    You get parameter sniffing issues with both adhoc and SPs, where the same plan is grossly wrong for different parameters but you are definitely using the same query plan.
    If you write dynamic sql eg "SELECT FIRSTNAME, LASTNAME FROM USERS WHERE FIRSTNAME = '" + firstName + "'" then you would produce a different query plan for every query.
    Parameter sniffing is where the plan is optimised for the first parameter used but maybe grossly inefficient for other parameters..you get weird effects where you run the query in SSMS and it runs quick but your app runs slow. Its a whole different topic.
    Stored procedures are not faster than parameterized queries...its the same to the engine. Replicating simple SQL statements (even update ones) in a SP is no faster and if you know SQL is probably slower to create. What stored procedures definitely do is act as a gatekeeper to your database and prevent unstructured data access.
    Thanks again for producing so many quality learning resources

    • @rolfherbert2051
      @rolfherbert2051 2 роки тому

      for the pre-script reasoning....the EXISTS keyword is used here rather than say IF (SELECT COUNT(*) FROM [USER]) > 0 because EXIST will stop retrieving rows as soon as it finds one...so IF NOT EXISTS (SELECT 1 FROM [USER]) will only ever return one row regardless how many rows there are. Its the exist keyword which is making all the speed difference not the 1 or * which if you used in an EXIST statement would be an unmeasurable difference. In normal selects * would be slower cos it has to enumerate the columns first. COUNT(*) I believe always produces an index or table scan so it could be much slower if the table was large even with an exist (it is only returning a single row anyways)

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому

      Thanks for sharing. I agree on the stored procedure vs ad-hoc caching. What I believe I was trying to communicate is that stored procs that are used are always cached (after the plan is created), whereas ad-hoc isn't always cached (if the signature changed) so you get a 100% vs a possible 100% cache reuse.
      Also, you are right about exists. 👍

  • @RussWilsonII
    @RussWilsonII 2 роки тому +27

    Wonderful first video, Tim! Thank you! For the insert operations, isn't in best practice to return (at a minimum) the ID of the object that was created, if not the entire new UserModel itself? I'm just wondering if practices have changed or if I have misinterpreted things along the way. Maybe I'm conflating the behavior of this w/ a POST operation for a RESTful API, which would typically return the object being created, but I'm assuming that's what this DataAccessLayer would be used for in part 2 (a RESTful API implementation?). Thanks for all you do for the community!

    • @RaMz00z
      @RaMz00z Рік тому

      In a Restful API, a Post doesn't return an object, it returns the URI for the newly created object.
      Often the caller just wanted to create somthing, why bother them with a (potentially big) new object in return that will take place on the network ?
      As for inserts in DBb, that is what SQL does, and what EF does as well, so I would find that logical. Particurlarly Updates actually, more than Inserts.

  • @crispaul27
    @crispaul27 2 роки тому

    Wow... this tool is soooo awesome!!! How did I survive for so long without it!

  • @ZRide72
    @ZRide72 2 роки тому +1

    This is amazing so far. Thank you Tim. Glad I found your videos.

  • @princeonukwili996
    @princeonukwili996 Рік тому

    Thank you so much sir...I never really liked the idea of using ORM's in my .NET apps, but from what I've seen in this tutorial, I thing I'm gonna start implementing Dapper in any new application I'll develop😌.

  • @haihai2999
    @haihai2999 2 роки тому +1

    FINALLY. My most waiting Topic this month. Thanks Tim

  • @SpiritVector
    @SpiritVector 2 роки тому

    Just wanted to say thank you for helping me learn about web development in general. I will soon contribute to this awesome cause!

  • @thomstunes6485
    @thomstunes6485 2 роки тому +1

    Exactly what I've done after seeing your older tutorial on dapper as my data access layer. I'm curious to see if my approach is close than yours then!

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому

      Sounds great!

    • @thomstunes6485
      @thomstunes6485 2 роки тому

      @@IAmTimCorey The main difference is that I did not isolated DataAccess into its own DLL. I've put all objects and calls through Dapper into the API's models. Which I guess is fine for me. Small DB, only for our entreprise using...
      The other big difference is the technical level but I cannot compete with God himself :)

  • @thomasdewitt6719
    @thomasdewitt6719 2 роки тому

    Tim, this was very informative and will make me go back and refactor one of my sample projects to get a firm grip on this subject to fully understand it

  • @MrMarkbad
    @MrMarkbad 2 роки тому

    Wow! Powerful way to setup data access. I am sold.

  • @Benexdrake
    @Benexdrake 2 роки тому

    I love working with SQL and this Video was Awsome, thank you.

  • @LoriPerch
    @LoriPerch Рік тому +2

    Tim, Great video! I love the idea of using generic data access with dapper. But, I don't understand how I can use generic data access with complex models (i.e. models that have some number of sub-models within them). For example, a customer model where the prefix isn't just a string... It is a prefix model. ...and a suffix where the suffix is a suffix model. Using Dapper, is it possible to use just one generic data retrieval function regardless of how many sub-models there are within my model? Thank you so much for your videos. Looking forward to your reply. :)

    • @IAmTimCorey
      @IAmTimCorey  Рік тому +1

      That takes developing a bit of logic, but that's normal. Think about how SQL stores data. It stores data in rows. How do you get a complex object back in one row? You can't. What you need to do is either create a query that puts all of the data in a row (which would be a model without sub-models) or you need to do more than one query.
      Let's talk through an example of more than one query. Let's say you have a set of people and those people have one or more addresses per person. OK, so the first query would be "get all the people you want". That gives you the "main" model's data but leaves the List empty in each PersonModel. Now, do another query that is selects all of the addresses for all of the people selected in the original query. Then in C#, use LINQ to put those AddressModels into the correct PersonModels.
      That seems like a lot of work, but that is extremely efficient compared to what EF is doing. Also, when you do that, you start to rethink if you need all of that data. Odds are you don't actually need it all. That makes your application even more efficient. For instance, maybe you don't look at each person's list of addresses but you do want to know how many addresses they have. Cool. You can return that number as a column in the first query. Then, if you do want to actually look at the addresses a person has, you can load just those addresses up when requested.

  • @integrsolutions7064
    @integrsolutions7064 2 роки тому

    Hey Tim, I am a big fan of you - and even suggest your videos to newbies.
    I am applying Dapper to my main project, I did a basic dbcontext myself with MySQLConnector (that I could type Data.Table(procedureQuery, parsObjectArray). It worked well by now but everytime I need to add something is still painful.
    With your videos I have learned coding patterns like nowhere. Keep it up! ♥

  • @scotttct
    @scotttct Рік тому +1

    Tim Love using your videos to learn proper coding patterns and new technique, after this I wonder what you would recommend for data security, already have an Encryption Library, so any help with a proper pattern to secure the api other than that would be awesome.

  • @scotttct
    @scotttct Рік тому

    Great Tutorial, Thanks. Looking to speed up my web that utilize EF6, Wow, all in an hour. Tim you are awesome!

  • @JasonTira
    @JasonTira 2 роки тому

    Its like you are reading my mind on what I want/need

  • @Ronaldos267
    @Ronaldos267 2 роки тому +3

    Hello Tim, thanks again for a wonderful tutorial. I generally avoid to use stored procedures as I do not want to encapsulate business logic in the database. Also it is also recommended not to encapsulate business logic at database level. I try to use SPs for creating reports etc. My simple question is - what is your recommendation I mean when to use stored procedures?

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +5

      I disagree with the "no stored procedures" or "no business logic in the database" recommendations. We need security in depth. If the database layer is fully permissive then you are relying on applications to do all of your security. That's fine as long as those applications all have the same business logic. Also, don't forget that there is a security issue here - your connection to the database. If a user has access to it, they can bypass the business logic and insert data directly that might be harmful/malformed. Plus, if you use stored procedures, you can lock down your database much more than if you use ad hoc queries.

    • @easy-e4472
      @easy-e4472 2 роки тому

      ​@@IAmTimCorey Tightly coupling the API to a specific DB is generally frowned upon to my understanding. In this example, wouldn't your business logic become dependent on a SQL Server backend? BL in SPs are also more difficult to unit test, can be more difficult to maintain when complexity is involved, etc.... Always exceptions of course and tradeoffs to be made.

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +2

      No because you don’t rely on the logic in the stored procedures. Also, the SP logic is limited.

    • @Ronaldos267
      @Ronaldos267 2 роки тому +1

      @@IAmTimCorey Ok thank you for your advise. I understand the security point of view. I asked this question because we recently feel pain when we had to change our backend database from SQL Server to Oracle. The product that I am working on has a SQL server database. But recently a new client wanted Oracle as the backend instead of SQL server. Then we had to do a lot of work in converting SQL SPs into Oracle SPs. That was a real headache. But almost all the EF queries ran successfully without doing any changes with Oracle as the backend. This was my main concern.

    • @rolfherbert2051
      @rolfherbert2051 2 роки тому +2

      Stored procedures are not faster or better than ad hoc, and some logic is always better in your app (unless you are an expert on creating set based queries then loops and the like should not be in your SPs) but store procedures most definitely keep your developers and app away from the data. If your app only uses stored procedures no one is ever going to be able to accidentally write a DELETE FROM USERS WHERE NAME=NAME type query, its just not possible if that user does not have access to the data but only to the SPs.

  • @eloyrolandocanchanyabalbin4692
    @eloyrolandocanchanyabalbin4692 2 роки тому +1

    Thanks a lot sensei Tim Corey, regards from some place in Perú.

  • @delw1138
    @delw1138 2 роки тому +1

    Thanks Tim, looking forward to part two!

  • @michlef774
    @michlef774 2 роки тому

    Very nice presentation. Thanks for sharing your Knowledge. Keep going........

  • @Eamo-21
    @Eamo-21 2 роки тому

    Beautifully simple solution. Thanks Tim!

  • @bipinchandrakant3085
    @bipinchandrakant3085 2 роки тому +1

    Wow! This is simple and clean. can you please recommend an optimal way to implement retry logic along with Dapper? Let's say one of the DB operations fails due to a deadlock/intermediate connectivity issue, is there a direct way to retry like Polly for HTTP? As of now, I use a recursive function to retry one more time and before failing, but I am sure there should be a better way to handle this.

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +1

      I would probably look at Polly for retry logic.

  • @MaddCoder
    @MaddCoder Рік тому

    Awesome video, this has really helped me with my new project. Thanks so much Tim.

  • @swordblaster2596
    @swordblaster2596 2 роки тому

    Excellent "starting from scratch" video.

  • @joshme8157
    @joshme8157 2 місяці тому

    pretty cool video, really like that you used a sql database project! One question, for your get user method, is there a reason you used FirstOrDefault over SingleOrDefault? In the database since the id is the primary key if it returns more than one record wouldn't you want it to throw an exception?

    • @IAmTimCorey
      @IAmTimCorey  2 місяці тому +1

      The database enforces uniqueness, so Single vs First does not matter. We don't need to check to be sure there aren't two entries with the same primary key. So the difference comes down to performance. FirstOrDefault is more performant than SingleOrDefault. So in this case, I chose FirstOrDefault.

  • @ianwanjala8621
    @ianwanjala8621 11 місяців тому

    hey tim, very very nice course. So my question is, is there a way to log what dapper is doing while executing those procs? i.e what if in the update proc I had a method that checked whether a user with Id 2 exists and I found out he/she doesn't, in such a case I might be returning 31 from the proc. Is there a way to check for such a value being returned from the .net application?

  • @AliAktepee
    @AliAktepee 2 роки тому +1

    Hey Tim,
    Another great tutorial thank you. But I have one hesitation, if I recall correctly, PostgreSQL does not use dbo prefix? So I will have to re-write a separate UserData for it? Or is there a shorter way?
    I can probably do something in DataAccess class like if connectionID == "Postgres" then replace "dbo." with "" but I don't think this is the right way to do it. Maybe you mentioned this in Part-2 , moving to that one now. Thanks again for this great tutorial.

  • @1kuruvchi
    @1kuruvchi Рік тому

    An absolutely great tutorial, thank you, Tim!

  • @tresaidh3y90
    @tresaidh3y90 2 роки тому +1

    Hey Tim once again another beautiful video. Do you have a video explaining Lambdas? The process seems similar to Java however your tutorials are so great I would love to see one on Lambdas expressions

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому

      Thanks for the suggestion. Please add it to the list on the suggestion site so others can vote on it as well: suggestions.iamtimcorey.com/

  • @andysmith872
    @andysmith872 2 роки тому

    Firstly - first video I've seen of yours Tim - awesome - I'm now a subscriber / fan!
    Q - LoadData is only implemented with IEnumerable. Why not one also for returning one row? Is this out of simplicity for the demo/video or do you *always* do it this way? Thanks.

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому

      I always do it this way because an IEnumerable can return 0, 1, or many records. I don't usually have a need to create a separate method with a different name (since you cannot overload a method by only changing the return type) just to limit the results to one record.

  • @Dimitris.Christoforidis
    @Dimitris.Christoforidis 3 місяці тому

    Amazing vidoes Tim!! I wonder if is good practise to use sqlconnection as DI? And if yes could you please create a seperate video for this?

    • @IAmTimCorey
      @IAmTimCorey  3 місяці тому +1

      No, because you want to instantiate it just long enough to do the work and then you want to shut it down. That's why we have it in a using statement. If we added it to DI, we would need to either have it open for the lifetime of the class instance (transient dependency) or we would have to actually inject a factory that would then create instances as needed. That factory would essentially do the same thing as what I am doing here, but with more complexity.

  • @JasonTira
    @JasonTira 2 роки тому +7

    After watching the whole video, and following along of course, it is very similar to your older video with a ton of wonderful little tidbits thrown in for the updated versions, and looks like a bit cleaner code as well. I still need to take your dapper course to get a better understanding of what is available. The quick intro to the stored procedures covered more than I thought it would in such a short time.
    I would like to see a 10 min video on how to change this DataAccess layer over to a different db like you mention.
    My current work process is using docker with a restored db from a backup from your video ( ua-cam.com/video/Yj69cWEG_Yo/v-deo.html ), setting up in the appsettings.json and appsettings.development.json so when developing/updating the application ( also from your videos, I believe the paid course - worth every penny ). I just have to make sure I start the container, then when I publish it out it just moves to the live db. This gives a great way for me to just spin up a backup and replicate any issues the users are seeing in a safe environment for me to break.

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +4

      Awesome! Thanks for the suggestion. It sounds like you are getting a lot of value out of my content.

    • @JasonTira
      @JasonTira 2 роки тому

      @@IAmTimCorey very much so. I can't thank you enough.

  • @PaulSebastianM
    @PaulSebastianM 2 роки тому

    If I'm not mistaken, the first time you new up a SqlConnection, it creates an internal connection pool so later new ups just return a free connection from the pool.

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому

      It does (unless you turn it off, which you should not). Closing the connection releases the connection to the pool. The system will close active connections if they aren't being used. It will also open new connections if you clog things up and don't properly close out connections.

  • @diligencehumility6971
    @diligencehumility6971 2 роки тому +1

    Hey Tim. Maybe you could do a video on how to do a scalable app? Now you have covered most aspect of real-world-applications, and lots of us do have real world app's out there, customers use. Now scalability becomes a problem, since one server instance is not enough. Should we do Azure Functions and let Azure scale our app? Should we do CosmosDB instead of MS SQL, because it's easily scalable? What about the serverless options? Serverless Functions? Serverless CosmosDB? There is even a serverless SignalR service. Like to hear your thoughts on this

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +3

      I will add it to the list. Thanks for the suggestion.

  • @adisakyfx
    @adisakyfx 2 роки тому

    I really need part 2. Waiting for it. Good job.

  • @Astral100
    @Astral100 Рік тому +1

    I wonder if it is possible to unit test db layer with this approach same as you can with Entity Framework by using memory implementation of EF?
    That is one benefit I think EF has over every other approach (as far as I know) is that you can easily test all the operations that manipulate the database.

  • @philipvandenheever2084
    @philipvandenheever2084 Рік тому

    Brilliant as always Tim.

  • @bobclemens8065
    @bobclemens8065 2 роки тому +3

    Would it make sense to move the stuff that's in the 'DBAccess' folder, namely the SqlDataAccess class, to a separate project, so that it could be reused with other projects that use other SQL databases? It knows nothing about specific tables, columns, etc., and it encapsulates all the Dapper stuff, so as you point out it's the only part of the application that deals with either Dapper or Sql.

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +1

      Yep, that would be ideal, and it is something I've been meaning to do for a while. I would like to abstract it to a NuGet package. Basically a Dapper helper.

  • @ralphgoyarts8930
    @ralphgoyarts8930 2 роки тому +1

    Hi Tim, I brushed up my knowledge of SQL and SQL server in your “SQL Databases From Start to Finish” course and I learned how to do most of the SQL Server development in the “System.data.SqlClient”. This helped me a lot!
    The SqlClient draws the database into the application development environment and offers a single source development here. I am fully adapting the Only Stored Procedure approach to protect my data. I consider the relation ships between tables as an important part in that as it guarantees the relational characteristics of the database against programmer mistakes.
    But I am missing the relationship definitions in the SqlClient. Is there a way to overcome this?
    I Appreciate your videos, keep them coming.

  • @roncoy887
    @roncoy887 2 роки тому +1

    Hi Tim, Thanks for your great tutorials, I am an enthusiast rather than active developer, you have advised the seperation of identity context from data contextand I understand why, do you have anything on how to implement that in an .net core app, i.e. do you have 2 active contexts at the same time.
    Thanks again Ron Coy

  • @ommadon84
    @ommadon84 Рік тому

    really good video explaining how to build the api, made a different project with various stored procedures and it all worked fine testing it in swagger. It says in the video that it can be used with blazor server and loads of other stuff, but then there is nothing explaining how to link it all up, and its not much use having the api if you don't know how to link it to the project

    • @IAmTimCorey
      @IAmTimCorey  Рік тому +1

      That's a different topic, though. This video (actually 2 videos) is about creating an API. Consuming that API is outside the scope of the video. However, I have multiple videos that cover consuming APIs, including this one: ua-cam.com/video/cwgck1k0YKU/v-deo.html and this one: ua-cam.com/video/HH8drNbai8w/v-deo.html In the TimCo Retail Manager course here on UA-cam, we also build an API and consume it from multiple clients.

  • @BinaryNexus
    @BinaryNexus Рік тому

    This is awesome! Great video! I never take enough advantage of generics to be honest.

  • @mikebreeden6071
    @mikebreeden6071 2 роки тому

    Fantastic... I'm going on to the next one... I'll have to pay for another class of some sort. Cash is almost as flattering as imitation...

  • @therealrolanddeschain9253
    @therealrolanddeschain9253 2 роки тому +6

    Ok, probably I'm just weird for asking, but could you not somehow default the parameters to null if you don't need one, or use a single parameter (e.g. 'ID') directly? I assume this is a non-issue, but the new-ing up of dynamic objects makes me a bit uneasy. For multiple params it makes perfect sense to me.

  • @bulelf
    @bulelf 2 роки тому +2

    Great video! What are your thoughts on the scalability of this approach? Is a small query string here and there considered a bad practice? My only concern with stored procedures is that when looking at the code, you can't actually tell how you got that result, you'll have to go and find the stored procedure.

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +7

      Well, since we built the stored procedure in the database project, it is all right there in our solution. As far as writing T-SQL in C#, I prefer not to do that because we are mixing our two systems. We are relying on C# to provide the SQL statement, which means SQL cannot optimize the statement until we pass it over. That's not ideal. The issue isn't as much scalability as it is one of being easy to maintain. If you are evaluating long-running or expensive queries, knowing where they are located is a real time-saver. If you think finding the stored procedure is inconvenient, try having a SQL statement that was dynamically generated that you have to figure out where it came from in your source code.

  • @Mercino311
    @Mercino311 2 роки тому +2

    Hi Tim, do you have any resources for understanding how to work with Dapper with Identity? Seems to me like a complete overhaul of their interfaces may be required but I'm afraid I might leave vulnerabilities. I really like the idea of using dapper and SQL Data Tools but can't seem to commit to it without some understanding of how to work without EF and identity.
    Small Edit: Just to add some context. For me this would include working in MVC with EF and Identity.

  • @torrvic1156
    @torrvic1156 11 місяців тому

    Thank you so much for enlightening us Mr. Corey!
    Can you tell me if SqlDataAccess class methods can be used with MySql instead of Microsoft SQL without significant modification? I am not sure if StoredProcedures will function the same way. And as far as I understand Identity is an Auto_Increment in MySql.

  • @claytonmyers6211
    @claytonmyers6211 Рік тому +2

    Hi Tim,
    In your advanced Dapper tutorial, you showed how to combine multiple datasets with Dapper. Do you know of a good way to write a generic implementation as done in this video, but capable of joining multiple datasets? If not, what would be your preferred way to handle such an issue (IE: Loading a user model which may have a phone model as a property)

  • @abhishekjadhav9289
    @abhishekjadhav9289 Рік тому

    Firstly, thanks for the fantastic video as always. I have a question. In the one-liner method, "public TaskGetUsers() => ", I see that even the method has "Task" however it did not give compile time error to force you to write "async" before the Task keyword and same for "await" keyword too. Why is that so ? Though in the other method "GetUser" you wrote async keyword. So a bit lost.

    • @IAmTimCorey
      @IAmTimCorey  Рік тому +1

      If you are not awaiting anything, you don't mark the method as async even if you are returning a Task.

    • @abhishekjadhav9289
      @abhishekjadhav9289 Рік тому

      @@IAmTimCorey Thank you so much for the reply.

  • @Dimitris.Christoforidis
    @Dimitris.Christoforidis 6 місяців тому

    Hello Tim!! I read in a article that is good practise to use .ConfigureAwait(false). Example await conn.ExecuteAsync(sql, param)
    .ConfigureAwait(false); Is it good practise ?

  • @petek5781
    @petek5781 2 роки тому

    Hi Tim, is this really best practice? In my opinion, an ORM would be cleaner. For example, if you change FirstName varchar 50 --> 60, you have to change it on several places. In an ORM, you would just change the model an run a migration. What is your opinion about a clean best practice approach? (not talking about speed, since C# is already much faster than python).

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +1

      Yep, for a number of reasons. You have to remember that typically your application is not the sole reason your database exists. It also exists to issue reports, for additional applications to access, and more. Also, the database is much more easily optimized in the database than in an application. Just like we put our appsettings outside of our C# code so we can change them without recompiling our application, we can keep our SQL commands outside of the C# application so that we can optimize them without redeploying our application. For instance, one of the things you should do when an application hits production is to start evaluating what your most expensive queries are. Then, you should look into how to optimize them in order to improve your application's performance (and your server's performance).

  • @kawthooleidevelopers
    @kawthooleidevelopers 2 роки тому

    Wow... love this workflow. I honestly have been looking for something that is easy to work with. I've tried FluentMigrator and the DB initializer technique and I think this is a best approach for me now, since I don't want to use EF Core. Every other course just use EF core but I want my codes to be optimized, thus Dapper. However, I find it clumsy to work with it when I need to make changes to the models or just simply seed more data. Thanks for the great content. I've canceled my every other subscriptions. I will sign up to your course soon.

  • @MegaChaien
    @MegaChaien Рік тому

    Hi Corey, first your tutorial is very easy to follow with reach explanation. I have followed you step by step that able to bring up the demo app.
    But then I am trying a personal project which connect to a current DB with data the return of API is 0 for int value and null for string value. I have follow your video C# tool sql to import the database then write a Store Procedure for that then publish but cannot get any data return
    Do I miss any step of db config? thanks Huy

    • @MegaChaien
      @MegaChaien Рік тому

      I have figured out after long time researching, the Dapper will automap column name from db with object name it passed to and if they are different they will return null as dont know how to map. Great thank a lot Correy

    • @IAmTimCorey
      @IAmTimCorey  Рік тому +1

      I'm glad you figured it out.

  • @leriosindane720
    @leriosindane720 Рік тому

    Excelent. Tim.
    You change my way of thinking about database and layer implementation.
    Just one question.
    How can i return one or more output parameters?

    • @leriosindane720
      @leriosindane720 Рік тому

      public async Task ProcessarPedidoCreditoTeste(PedidoModel pedido)
      {
      int Msg = 0;//OUTPUT PARAMETER
      await _db.SaveData("sp_IbCPanel_PedidoCredito", new { pedido.iduser, pedido.idempresa, pedido.idavalista, pedido.idgestor, pedido.idpedido, Msg });

      return Msg;
      }

  • @bernardlogador5364
    @bernardlogador5364 2 роки тому

    Thanks Tim for this! Exactly what i need at the moment

  • @Kevin-mb4xf
    @Kevin-mb4xf 2 роки тому +2

    Hey Tim, thanks for making this video. I was looking for a up-to-date tutorial on dapper.
    Could you tell us when you will release part 2?

  • @kevingates5851
    @kevingates5851 2 роки тому

    You always have timely videos.

  • @JeetKhinde
    @JeetKhinde 2 роки тому

    Thanks for a great video. At 1:08:00, I guess it might be better in the long run if you return inserted record after inserting it in DB and the same is true for the update and delete method.

  • @TheBKTaco
    @TheBKTaco 2 роки тому +1

    Hi Tim. Great video as always. I just finished part 2 and decided to try to implement another class that implements the DataAccess interface and use MySQL. I've entered a second connection string in the appsettings.json file, but it always tries to grab the "Default" one instead of the new one I've added for MySQL. I put in a breakpoint and inspected the value for the connectionId and found it to be "Default" even though my new MySqlDataAccess class is setting it to something else. I think it's doing this because it's being assigned in the interface itself. If I remove it from the interface then I don't have the correct number of arguments when the UserData class calls the new implementation. If I add it in the UserData class when it calls the DataAccess then every method needs to know the connection string name and changing it in the future would need it to be changed everywhere. Is there a way to get this to work the way it was intended, or am I just doing something wrong?

  • @muhammedeminokcu1327
    @muhammedeminokcu1327 2 роки тому

    Awesome content for beginner to be a LEO ;). Thank you.

  • @RC-uq4lw
    @RC-uq4lw Рік тому

    Great video. If I wanted to write integration tests for this database, what approach would you suggest for keeping the test DB schema synced with the prod version (or db project) and for resetting the DB to a known state between tests. With EF, this is fairly straightforward, but coming up with an approach to use without EF is a little tricky. Any ideas?

  • @techsamurai11
    @techsamurai11 Рік тому

    You did a great job explaining this but I'm not happy with the state of the C# language.
    I like the File Scoped Namespaces (it's unbelievable that they weren't added 20 years as it destroys the code visually and indents all files for no reason at all so thank you for pointing that out but alas it doesn't work in VS2019) and I also liked the one line Using although that was jarring if you didn't know that you could do that. It's akin to an "If End If" statement missing the End If all of a sudden even though it spans many lines. Thank you for explaining it.
    Here's my biggest issue - the code while short and efficient is more complex than C++ ever was. In the beginning, there were braces and semi-colons in C#. In the end, there are (){}=> ();. It's a cluster . Then there is the ridiculous syntactic implementation of Asynchronous Tasks. There were so many better ways to implement that but the C# team clearly is not fluent in linguistic constructs. Task this, await this, async that, generic this, generic that.
    I think it's fair to say that C# has gone from C# where it tried to become a friendly version of C and C++ to compete with VB.net to C## or . Perhaps Microsoft may change the name to the latter to reflect that and it would be entertaining to see how folks pronounce it (Generic C#, less than C# greater than C#, less/more C#).
    Was it C#10 that finally added Global Using? There isn't a single file where I'm not greeted by using after using statement. Oh no, it uses Serilog, it uses IO, it uses System, it uses System.System.System.System (the C# should have done that just for fun) ... Imagine if SQL Server required a library declaration in every stored proc?😁

  • @Reaper7mk
    @Reaper7mk 2 роки тому +2

    Is this the repository pattern or some version of it? I would love a video on that at some point! Thank you Tim

    • @torrvic1156
      @torrvic1156 11 місяців тому

      Looks like that. SqlDataAccess is like a repository and UserData is like a service of some sort tied with SqlDataAccess.

  • @Dimitris.Christoforidis
    @Dimitris.Christoforidis 9 місяців тому

    Hello Tim. After that video i need to optimize my whole project!! 😅 Thank you. I am using winforms. What if create SqlDataAccess.LoadData as static? Instead of creating a new instance each time ? Is it good practice??

    • @IAmTimCorey
      @IAmTimCorey  9 місяців тому

      Static doesn't allow for dependency injection. You can make it a singleton with DI, which acts in a similar manner but gives you more flexibility. That is what we do in part 2 of this application: ua-cam.com/video/5tYSO5mAjXs/v-deo.htmlsi=bYCBjVgeFopQUabT&t=786

    • @Dimitris.Christoforidis
      @Dimitris.Christoforidis 9 місяців тому

      @@IAmTimCorey Thank you Tim, can I also use singleton in winforms project?? Does dependency injection tutorial from your website describes how can I do this?

    • @IAmTimCorey
      @IAmTimCorey  9 місяців тому

      Yes, you can. As for seeing this work with dependency injection, the link I provided for part 2 shows you how to implement a Singleton in Dependency Injection. That will be the same for any project type that uses DI.

    • @Dimitris.Christoforidis
      @Dimitris.Christoforidis 9 місяців тому

      Do i need to add library? Microsoft.Extensions.DependencyInjection.Abstractions?
      @@IAmTimCorey Please make a video for this. How to add DI in winforms. Or please let me know if there is already in youtube or your website

    • @Dimitris.Christoforidis
      @Dimitris.Christoforidis 9 місяців тому

      Could you please provide me what library for DI do you suggest for Net framework projects? Thank you

  • @LiamGlanfield
    @LiamGlanfield 2 роки тому +1

    Question with the single line methods on the UserData class, why don't you need 'await'? For example getUsers doesn't have await? On a side note thanks for the videos, they have been very helpful and educational.

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +2

      You only need to await a call when you need to do something with the results. If the rest of the code doesn't rely on the results of your async call, you can skip the await. The caller will await the call, since it needs to do something with the results, but by not adding an await where you aren't waiting on the results, you lower the overhead of that call (every await adds overhead).

    • @LiamGlanfield
      @LiamGlanfield 2 роки тому

      @@IAmTimCorey I'm not a developer but trying to broaden my knowledge. A lightbulb has just gone off and now have a new understanding. When I set a method to return Task but I am returning an IEnumerable I didn't stop to think I could also return the task! The API call I assume will then await and get the result from the Task. Makes far more sense now. I can see some improvements to make in my own code now. Thanks for the reply do appreciate the time taken to do that.

  • @techsamurai11
    @techsamurai11 Рік тому

    One thing that wasn't apparent was how Dapper functioned because I saw the using Dapper statement but I didn't see a single instantiation of a Dapper object or helper. I sort of figured out that it extends the DBConnection object adding those methods but I don't think everyone will infer that. Still, I'm not sure that I prefer that over an explicit Dapper (ala SQLHelper) object that encapsulates that functionality.

  • @victorbouffier3580
    @victorbouffier3580 2 роки тому

    Hi Tim. Kudos on your page. You have great insight and your explanations are fantastic
    I am using a Mac M1 with Visual Studio 17 for Mac. I cannot seem to find the SQL project. My understanding is that SQL Server is not available for Mac but the underlying DB is SQLite. Should I just ignore the project setup? How do you proceed?

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому

      The data project won't work, but you could still create a database and then just connect to it. If you know how to create a SQLite database then use that. You won't be able to create stored procedures, but you can just call queries to do the same thing.

  • @gazmendhalili6575
    @gazmendhalili6575 11 місяців тому

    First thing first you are awesome, I like your teaching and I respect your knowledge, I have a question at 1:05:53 can we make the Task InsertUser async would that make any difference in the performance of the website.
    I hope you see it, thank you anyway for everything.

    • @IAmTimCorey
      @IAmTimCorey  11 місяців тому

      That method is asynchronous. We don't mark it as async, though, since we aren't awaiting anything (which is actually faster). So, we are returning the Task and the caller can await it.

  • @jmiedreich
    @jmiedreich 2 роки тому

    Hey Tim, great video as always. I enjoy using Dapper myself and more recently repodb . I'm looking for some best practices on rolling out sql databases changes to production. EF does the migrations, what's the best approach to move examples in your video to production?

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому

      I use the database project I showed in this video. It allows for migrations too.

    • @jmiedreich
      @jmiedreich 2 роки тому

      @@IAmTimCorey Thank you, I will look at that again.

  • @Otonium
    @Otonium 2 роки тому

    Great video, Thank you!
    Q: Why dynamic? and Why are we obligated to pass an empty object in GetUsers 1:01 ?

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +2

      It needs the parameters. I want to pass in an empty anonymous class instead of creating a value that I don't use. That's why I use dynamic and an empty class.

  • @SteffenHansen-vp2fu
    @SteffenHansen-vp2fu Рік тому

    Hi Tim,
    Thanks for a great demonstration! May I ask why you did not move the IUserData interface out of the DataAccess project, i.e. into the project that expects this data and then made the UserData implementation depend on that instead of the other way around, that is instead of the project expecting this data depending on the project that is able to provide it. In my opinion, when the interface resides in the implementation project, it is not as easy to change the database technology as you suggested.

    • @IAmTimCorey
      @IAmTimCorey  Рік тому +2

      If you put the IUserData interface in the UI then the library will need to know about the UI and the UI will need to know about the library (a circular reference, which is not allowed). You can still easily change the data access layer, but you need to do it in the data access library.

    • @SteffenHansen-vp2fu
      @SteffenHansen-vp2fu Рік тому

      @@IAmTimCorey I see. Thanks!

  • @mads3564
    @mads3564 2 роки тому +1

    Hi Tim, what are the benefits of creating the SqlDataAccess and UserData classes as non-static classes as opposed to static classes?

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +2

      I can control their scope and distribute them as needed via dependency injection. That allows me to replace them with mocked versions for unit testing, and it allows me to decide if I want different versions used in different places or if I want to use the same instance (Transient vs Singleton).

    • @mads3564
      @mads3564 2 роки тому

      @@IAmTimCorey Thank you, that makes sense.

  • @rogerswetnam1416
    @rogerswetnam1416 2 роки тому

    Hi Tim - thanks for the excellent video. I have been used to using EF to scaffold my Identity Management - Register Login, etc. I feel a lot more secure than trying to do this on my own. However, I really like the way you have used Dapper for the Db Sql project. Can you suggest how I might combine MS Identity Management with classes like UserManager with the Dapper approach that you are demonstrating in your video. Learned a lot and registered in my first course from you.

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому

      I don't have any content on that because I don't mess with Identity. I use Entity Framework for that because that is what is built-in. Authentication is something you NEED to get right. I try not to make major changes to it. However, if you want an easier system, try out Azure Active Directory B2C. It is easier, integrates with C# well, and is free for the first 50,000 active users per month. I've got a course on it and I'll be doing some UA-cam content on it soon.

    • @rogerswetnam1416
      @rogerswetnam1416 2 роки тому

      @@IAmTimCorey Hi Tim - thanks - that's my point - I don't want to mess with Identity either. I am guessing that to use MS identity with a solution that also includes Dapper as you have demonstrated in this demo, I would create another separate project called something like IdentityDataAccess which uses EF - but not migrations and then import a scaffolded identity database and access the identity related tables (aspUser etc.) using the MS classes such as UserManager via the IdentityDataAccess project? Any suggestions here?

  • @Spirittism
    @Spirittism 2 роки тому +1

    How much does Microsoft pay you? because your literally creating future .Net programmers, good job Tim.

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +2

      They don't pay me anything. I do this to help the community, and now the community is supporting me in return, which allows me to provide even more free content.

    • @Spirittism
      @Spirittism 2 роки тому

      @@IAmTimCorey whatever the reason, you have my thanks, as should Microsoft.

  • @BaiLani
    @BaiLani 2 роки тому

    Real thanks to Tim and community for helping out in comments!

  • @joaomacuacua9268
    @joaomacuacua9268 2 роки тому

    Great Video. I like the Load and SaveData. Just one Question. How Can i Return an OutputParameter on the SaveData Method

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому

      You can send it back in the parameters list.

  • @Marko-the-Beast-Master
    @Marko-the-Beast-Master 2 роки тому

    Hi Tim, how would it be if you were updating multiple rows at once. Would it be via DataTable and on SQL as a tablespace or how? Is Dapper suitable for bulk records? And I would also ask for a more in depth video on how to compose the result into multiple objects. For example if one main object contains a property which is itself a class.

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +2

      With the save data, you can pass in a list of parameter objects and Dapper will do the multiple inserts for you.

  • @tarekhajj8809
    @tarekhajj8809 2 роки тому

    Extremely interesting video Tim !!! thanks a lot, I was just wondering about the exceptions handling in the data access layer, you didn't add any try catch block, can you please elaborate on this matter, how are we supposed to handle database operations exceptions ? (on which level ? data access layer, business layer ?)

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому +1

      I typically handle them in the UI layer unless there is a specific reason to handle them lower down. That way, we can alert the user to the issue and let them make a decision (retry, etc.)

  • @alfonsdeda8912
    @alfonsdeda8912 2 роки тому

    Hi Tim. If I want to connect to different data providers, for example SQL connection and oledbconnection, i create two repository sqlserverrepository and Oracle repository that inherit from generic repository interface.
    Should I change in every repository the IDBconnection for each different method or create a factory method that choose the idbconnection and create only once repository that get the IDBconnection from this factory?

  • @MADCookieTube
    @MADCookieTube 2 роки тому +1

    Why not add "Async" to the name of the async methods? (LoadData & SaveData)

  • @bobclemens8065
    @bobclemens8065 2 роки тому

    You discuss the possibility of "abstracting out" the 'using' statements that are in both LoadData and SaveData. How about one way to make it a little simpler would be to put the connection string lookup in the class constructor right after _config is assigned. You could also instantiate the SqlConnection object itself in the constructor, but I guess the problem with that is you don't guarantee its properly disposed like it is in the 'using' blocks.

    • @IAmTimCorey
      @IAmTimCorey  2 роки тому

      The connection string lookup is already one line (a call to a method). We've already abstracted it away. Putting it in the constructor would save us a call, but only if the database never changed. That's not a guarantee, so I didn't add the overhead of trying to handle that situation. As for instantiating the SqlConnection in the constructor, that would not be good because it would mean that the connection would stay open for as long as the class instance lived. That's not a good thing, so you are correct that it would cause an issue with our using scope.