How to Implement Strongly Typed IDs with EF Core and ASP.NET Core Razor Pages

Поділитися
Вставка
  • Опубліковано 7 чер 2024
  • Check out Dometrain and use code ZORAN for 15% off any course ► dometrain.com/?coupon_code=ZORAN
    Become a sponsor to access source code ► / zoranhorvat
    Join Discord server with topics on C# ► codinghelmet.com/go/discord
    Enroll course Beginning Object-Oriented Programming with C# ► codinghelmet.com/go/beginning...
    Subscribe ► / @zoran-horvat
    Related videos:
    How DDD Makes OOD More Powerful ► • This Is How Domain-Dri...
    Learn To Love DDD-Style Strongly Typed IDs ► • Learn To Love DDD-Styl...
    Validating Records in C# ► • Validating Records in ...
    The Lesson About GUID IDs I Learned the Hard Way ► • The Lesson About GUID ...
    Do you pass strings and ints around your application? Nah, that's a primitive obsession!
    Do you exchange enums between classes? Nah, that's an anemic domain model!
    Then, why would one send raw ID values between classes and to the UI? Those raw IDs look that way because the database is fine with them, not because they are good in the domain model!
    This video dissects the problem of ensuring strong typing when assigning ID values through all the application layers: in Entity Framework Core mappings, in the domain model and all the calls inside it, to Razor pages and HTML generation.
    You will learn how to work with autoincrement numbers, common GUIDs, and lexicographically sortable UUIDs, each separately wrapped into a strong ID type that preserves assignment safety, including model binding to and from HTML.
    ⌚ 00:00 Intro
    ⌚ 01:14 Implementing a strong ID
    ⌚ 06:51 Mapping to EF Core and Razor pages
    ⌚ 10:55 Strongly typed model binding
    ⌚ 12:48 Using sortable UUIDs
    ⌚ 14:48 Using autoincrement IDs
    ⌚ 15:45 Demonstrating strongly typed IDs
    ⌚ 16:56 Outro
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    ⚡️COPYRIGHT NOTICE:
    The Copyright Laws of the United States recognize a “fair use” of copyrighted content. Section 107 of the U.S. Copyright Act states: “Notwithstanding the provisions of sections 106 and 106A, the fair use of a copyrighted work, including such use by reproduction in copies or phono records or by any other means specified by that section, for purposes such as criticism, comment, news reporting, teaching (including multiple copies for classroom use), scholarship, or research, is not an infringement of copyright." This video and our youtube channel, in general, may contain certain copyrighted works that were not specifically authorized to be used by the copyright holder(s), but which we believe in good faith are protected by federal law and the Fair use doctrine for one or more of the reasons noted above.
    #csharp #dotnet #entityframeworkcore
  • Наука та технологія

КОМЕНТАРІ • 35

  • @zoran-horvat
    @zoran-horvat  2 місяці тому +5

    Check out Dometrain and use code ZORAN for 15% off any course ► dometrain.com/?coupon_code=ZORAN

  • @poloolo69
    @poloolo69 Місяць тому +1

    quality content as always, thank you

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

    Thanks for these. I always learn something, or at minimum refresh reasoning for specific design decisions.

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

    this is lovely! i got lots of work to do now, things to change ❤

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

    Keep going with great stuff Zoki! :) Cheers!

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

    Thank you

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

    Can't tell you how many times I've used the wrong type of ID and somehow it's never occurred to me to do this. Not a super proud moment for me

    • @zoran-horvat
      @zoran-horvat  2 місяці тому +2

      This practice is a native part of DDD, as it is practiced these days. The downside of that is that many programmers approach it with religious rage, causing a lot of noise that makes others think this is for lunatics only.
      But if you can just focus on the substance, strong IDs are a simple and effective tool that costs close to nothing and buys you the peace of mind in many places in code.

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

    Great video, and important techniques demonstrated. I think if an app already uses strongly typed ids, then changing the implementation details of said ids becomes trivial, without ever touching most of the related code. For instance going from int to guid, or varchar to int...etc.

    • @zoran-horvat
      @zoran-horvat  2 місяці тому

      That argument exists, but it is not likely to happen. In my opinion, the benefit is the same as in any other car where we choose to pass a domain model instead of a primitive type, such as a Book instead of a string representing its title.

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

    Hi, Zoran. Considering the implementation of strongly typed ids, could you comment on Vladimir Khorikov's blog post from 2020 on "C# 9 Records as DDD Value Objects" (I can't post a link, unfortunately)?
    He states some valid arguments against using records as DDD Value Objects, but this may be before the time of readonly record structs.
    Does that make a difference?

    • @zoran-horvat
      @zoran-horvat  2 місяці тому +1

      Khorikov was referring to other aspects of working with Value Objects. In this video, a value type is used to simplify code. It was a technical decision. Note that the Id is not a Value Object but a value type, where Value Objects need not be value types - they are reference types more often than not.

  • @IcaroFelix2023
    @IcaroFelix2023 20 днів тому

    Are you using some kind of library of functional extensions to CSharp ? If yes, could you say which is it ?

    • @zoran-horvat
      @zoran-horvat  19 днів тому

      Sometimes I use LanguageExt, mostly for Option and Result definitions. I regularly depend on language syntax alone, rather than third party libraries, because that is the only way I can preserve backward compatibility in the long run.

  • @Rick104547
    @Rick104547 Місяць тому

    In which situations would you use this? It does feel like additional complexity which might not be worth it in alot of simple scenario's.

    • @zoran-horvat
      @zoran-horvat  Місяць тому

      It is common in distributed systems, where it is inconvenient to wait for the database to communicate the identity back.

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

    Something that I have been wondering lately....
    Do you include Value Types (BookID, Address, FullName, etc.) as properties on DTOs, or should DTOs be "simplified" into strings or other primitives?
    I can see pros and cons for ether situation.
    One negative I see, having these types in the DTO might "bypass" some validations that are placed when creating/parsing these values.

    • @zoran-horvat
      @zoran-horvat  2 місяці тому

      I only include primitive types into DTOs and no behavior or validation. Their only purpose is to convey the values, including in serialization. They don't know what is valid and to whom.

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

      @@zoran-horvat thank you for the reply 🙏🏻
      This makes sense, thank you.

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

    So we can't have an Entity Base for this kind of model?

    • @zoran-horvat
      @zoran-horvat  2 місяці тому +2

      We can, but it should be a generic class receiving the ID type as a parameter.
      On the other hand, this situation outlines one immense fallacy, which you might recognize if you try to answer a tricky question: Why do you have the entity base class? Does it do anything?
      Think carefully before jumping to answer. Your thinking process will lead you to a revelation that will teach you a valuable lesson. But you need to give that thought some time to bubble.

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

      Ive used base entity class for putting on createdat and modiefiedat rows to every entity in database. Sometimes the client needs soft delete so you can have some value representing that aswell. Then overriding savechanges in ef makes it trivial to apply at every insert/update. So the base entity that all entities inherits from can have uses sometimes even when id is not part of the base entity. I do like strongly typed ids though. Especially when a calculated url slug is needed for seo.

    • @zoran-horvat
      @zoran-horvat  2 місяці тому +1

      @@elraito For those purposes, the base class does not necessarily have to know the ID type. The hidden issue is that some entities generate their ID and some have it from storage, making it harder to define an abstract base class. Not only that it is not clear how the value is initialized, but it is also unclear whether it is immutable or mutable and, if mutable, whether it is a "set once" property.
      All those questions have the potential to needlessly complicate the abstract base class.

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

      @@zoran-horvat Totally agree. I may have expressed myself wrong. Im not advocating putting id fields into base entity but advocating that there can be also other uses for base entity class besides id field.

    • @zoran-horvat
      @zoran-horvat  2 місяці тому

      @@elraito You said it right. I agree with your answer, and was afraid you could give me a generic story I hear all around, mostly from DDD circles.

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

    I thought that this was an interesting video. That said, I feel like exposing elements of your domain model directly to the API and database code might end up being a trap further down the line.
    Obviously, your approach is faster to develop than, for example, having the API DTOs, domain models and DB models isolated with the own logic and mapping between them. However, I think that such an approach has the advantage that you make the objects as “plain” as possible, and being able to rely on the tooling to handle well established types. I also believe that this approach gives us greater visibility on any such mapping, whereas the approach suggested here is powerful, I worry that it ends up obfuscating what’s going on to a less knowledgeable developer.

    • @zoran-horvat
      @zoran-horvat  2 місяці тому

      I must admit I do not follow the reasoning. How can at least two additional indirections make anything better in the sense of the design, let alone help develop the model?

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

      @@zoran-horvat my apologies, I suspect that I didn't phrase it particularly well.
      My concern is about exposing the domain models outside of the domain itself.
      As my understanding goes, within our domain, we should have (at least in theory) control of our data structure and work with it as we see fit.
      However, once we have these domain models exposed directly to the API or EF Core, we have to keep in mind any strange "gotchas" these interfaces might impose upon our models.
      Whilst exposing IDs in this manner is essentially harmless, it would not take much effort at all to expose our richer domain models, which opens us up to those sorts of issues.
      Of course, I might be mistaken and I'm just being paranoid.

    • @zoran-horvat
      @zoran-horvat  2 місяці тому +1

      @@metallixbrother There is a gross misconception about separating the domain model from an ORM which boils down to calling for a separate model that comes out of the ORM. The domain model is already the separate model! The other one is the persistence model, and ORM is mapping onto it.
      Attempting to separate an ORM from the domain model means we need three models, one mapped onto another. That is one model too many in my opinion.
      Also, there is no API in this demo application. Should there be any, I would map the domain model onto DTOs for the purpose of communicating with external systems. Doing so within the same component is yet another model too many in my opinion.

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

      @@zoran-horvat thank you for your response, it was helpful, I think that I have a better understanding now.

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

    But isn't Id a database concern? Why not hide them in Shadow properties (if using EF) and expose real business keys as "Id"s, like ISBN? If it's in the domain, shouldn't we be speaking the domain's language? Let me play devil's advocate for a second and suggest this is stored in a No-SQL data store where Ids are not generated (or required) because everything related to the aggregate is contained within it, then do these Ids still make sense? If not, should they be in the domain in the first place?

    • @zoran-horvat
      @zoran-horvat  2 місяці тому +1

      That is a good question. In my opinion, we use IDs to point to entities the same way we use memory references for in-memory objects.
      Using meaningful IDs makes sense in the UI, but only for the most prominent entities in the system. If you imagine a rich HTML interface, imposing a meaning onto all keys is not plausible. Most entities do not even have a natural key within the domain.
      The shadow property does not solve the problem because the entities would then have to reference other entities that reference other entities until you have the entire database at hand in memory. Some people refer to lazy loading, but that only addresses one aspect of the problem, while opening the same number of new problems of its own. I think that there is nothing wrong with having an ID (of this or other entity) in any entity's definition.
      Substituting an ID with a less stringent but readable field is a useful tactic which I haven't demonstrated in this video. It has a special uptake that you don't have to maintain and such value constant to eternity. That still doesn't dismiss regular IDs in the domain model definition.