EF Core 8 Finally Fixes Value Objects

Поделиться
HTML-код
  • Опубликовано: 21 янв 2025

Комментарии • 126

  • @MilanJovanovicTech
    @MilanJovanovicTech  Год назад +3

    Want to master Clean Architecture? Go here: bit.ly/3PupkOJ
    Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt

    • @Dpaz2009
      @Dpaz2009 Год назад

      Thank you!

    • @kumailn7662
      @kumailn7662 Год назад

      why programming with C# is becoming complex. !!

  • @gianlucalocri
    @gianlucalocri Год назад +5

    Finally!!! 🤩
    Thanks Milan!

  • @antonmartyniuk
    @antonmartyniuk Год назад +3

    I like this feature much, hope collections will be supported too until EF 8 release

  • @MikyShooter
    @MikyShooter Год назад +7

    For all that says this example does not suit the feature, you may be right. It doesn't metter but the explication, that was imo exaustive.
    Here is another simple example:
    In case of an order we may have a shipping address related to it. It's not ideal to save the Id of the customer address but the information itself at time t.
    This because the address could potentially be removed from the customer entity, or even modified. Best thing is to store it separately, as this case, in a complex object, also to sepate concerns in class.
    Thank you Milan for all your videos.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      Very good example with order + shipping address ✅

    • @jeroen7362
      @jeroen7362 Год назад

      The order with shipping addres as mentioned here could be a scenario. Still i would not use it for that. Just add a few fields to the order for the address, postal code and town, country and done. probably country would be a foreign key to the country table. The actual up to date address would be stored in a address table that is referenced by the customer class. This is the address you would show for next checkout and in their profile.

    • @MikyShooter
      @MikyShooter Год назад

      @@jeroen7362 With complex objects, as explained by Milan and by the docs, are still created in the same table. No need to add in the same class. In code side they are wrapped in a class but in db they are on the same table as "Type_PropertyName", so no need to have a class with many properties that could be easly the wrapped.
      For this particular scenario I would not relate shipping address to profile address since:
      - profile address could be changed but shipping address in order not. (maybe I moved out of the parents house and update the profile address, but the shipping address should be the same as choosed in the creation contract). Any updates should be done separately, also with other kind of process (for courier). For instance one moves from europe to america, and update the profile address, but the order is shipped so the address should be the one in europe and not let the courier be in entropy.
      - by referencing profile address in order, you don't have consistency on the history side: at time t1 was with address1 and at t2 with address2, and not t1 with address2 (with reference and not value).
      - you still can create another table of shipping order address, that is 1 to 1 if you want, I thought this example suited more for the mentioned feature. A lot of people here didn't get the actual value of the feature.
      I worked on a Ecommerce aggregator project, so these were a few of the many requirements.

    • @jeroen7362
      @jeroen7362 Год назад +2

      @@MikyShooter Yes we are on the same page, the profile address is not referenced by the order. (also not via customer). The order address should be on the order and should not be updated or used outside the fullfilling of that one order. The customer has a profile with an up to date address on it. That is on a separate table (you could have 2 addresses for a customer, one is shipping default and the other is invoice) You should not need any orders table to find the latest address for a customer when he gets on the checkout page. A customer without any orders can have an address.

  • @silvertek
    @silvertek Год назад +2

    Will this avoid the exception when using stringly typed ids since we use them as both value/nonentity types for the aggregate but as entity types when we reference them from another aggregate?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Give it a try? 😁

    • @matthewrossee
      @matthewrossee Год назад

      You mean the identity paradox presented by Amichai Mantinband on his channel? I'm curious as well. Have you tried it yet?

  • @JordiC354
    @JordiC354 Год назад +1

    Hi Milan, do you achieve that complex property works with typed ID, like BookId(Guid ID)?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Does it not work for you?

    • @JordiC354
      @JordiC354 11 месяцев назад

      @@MilanJovanovicTech Not for primary key. Does it work for you?

  • @tonystoynev7969
    @tonystoynev7969 Год назад +3

    Hey, another great video! But I am not getting the point of having a collection of complex types inside the entity? How this collection will be mapped into the database?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Could be all mapped to one column as JSON

    • @ArcaneEye
      @ArcaneEye Год назад

      ​@@MilanJovanovicTechcan't you already do that with hasconversion? And wouldn't it be faster to query a relational table than having the SQL server do string magic to find your query target?

    • @aerostorm_
      @aerostorm_ 10 месяцев назад

      ​@@ArcaneEye to answer your somewhat older question, this is essentially a built-in HasConversion json implementation.
      Document databases are supported by EF now and this paradigm makes more sense if you are using EF to abstract a document DB. If you are abstracting a SQL DB then this feature is not as optimal as querying with relations.

  • @astralpowers
    @astralpowers Год назад +2

    Collections for value objects would really be useful for my use case

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      We'll see if it makes the cut

    • @ArcaneEye
      @ArcaneEye Год назад +1

      How? I'm really curious as I can't see a situation where that would be the correct approach.

  • @Sander-Brilman
    @Sander-Brilman Год назад +1

    how would collections work with complex types? shouldnt a new table be added for that since not all db providers support the array type

  • @abcde123429
    @abcde123429 13 дней назад

    I'm trying to do the second case (.complexProperty) butu it does not work . An exception raises: System.Collections.Generic.KeyNotFoundException: 'The given key 'Property: Product.amount#ProductAmount.value (int) Required' was not present in the dictionary.' .( im using .net 9 with an inmemory db! with an entity PRODUCT that have a value ProductAmount) I would appreciate if you know why it can not be recognize when querying the entity product.

  • @EzequielRegaldo
    @EzequielRegaldo Год назад +1

    Its AWESOME ! thank you so much for your content

  • @vasiliylu8054
    @vasiliylu8054 Год назад +1

    Thanks! It's very cool feature

  • @ArcaneEye
    @ArcaneEye Год назад

    Quick question re: list of complex types. If a complex type is mapped to columns in a table alongside the entity they belong to, how would you map a list of those to the table? Isn't that exactly what navigation properties are for?

  • @_IGORzysko
    @_IGORzysko Год назад +3

    Thanks for the video Milan! The possibility o fetching only value object data from the table is really great 🔥
    Are EF 8 value objects tracked the same as "entity" entities? 🤔

  • @joebyrne7343
    @joebyrne7343 Год назад

    Must the columns be author_? Can this be implemented in db first scenario where the existing columns may not follow that naming convention?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      You, configure with ComplexProperty and define column with HasColumnName("your_existing_db_col")

  • @nicolasL009
    @nicolasL009 Год назад +4

    Very informative video! Too bad it'll take a while before I get to use EFCore 8 at my current job. Stubbled upon the issues mentioned in the video quite a few times with older EF versions. Keep up the good work :)

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +2

      I think EF 8 will work on olders .NET versions, need to check though 🤔

    • @modernkennnern
      @modernkennnern Год назад

      @@MilanJovanovicTech I think they try to keep it working on the latest LTS. In this case, that is .Net 8. But I might be wrong - it might still work on .Net 6. I know that EF7 only works on 6+

  •  Год назад +3

    It's not yet full supported. Complex type collection support is not ready. Check the issue 31237 and vote!

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      There's ways around that for the time being

    •  Год назад

      @@MilanJovanovicTech You could show an example of this in your videos. I am currently following Amichai's workarround "The Identity Paradox | DDD, EF Core & Strongly Typed IDs", although I don't like the arrangement too much because there is actually a small leak in the domain model that causes difficulties with the ids if it is being used the repository pattern with specification.
      On the other hand, I have tried creating a data model and mapping it to the domain model, but it seems like too much effort to maintain synchronization between both models. Additionally, the Id collections of my domain model are of type readonly collection. So instantiating it with automapper and EF is difficult for me and I don't think using reflection is the way to go.
      I look forward to your solution. Greetings :)

  • @phugia963
    @phugia963 Год назад +1

    finally we can have a good EF features to support define & map value object to the database 😄

  • @rusektor
    @rusektor Год назад +1

    Doesn't this remind [ComplextType] attribute in EF 4.0?

  • @kennyhendricks4293
    @kennyhendricks4293 Год назад +1

    I am glad that this has been fixed

  • @wmalgoire
    @wmalgoire Год назад +2

    I feel bad for people getting obsessed by the business domain trivial example and not trying to understand this new feature.. Maybe you should post a video on active listening Milan 😂😂
    I mean, there are other great contents on value objects use cases and benefits.
    Having struggled with poor implementations and complexity of value object persistence in efcore, I'm quite excited by this new feature! Even if in the meantime we'll have to handle collections manually..but that's a good step forward and I'll use this for sure 😊
    All in all, another handy feature to keep in our toolbelt 👍
    Thanks for your hard work Milan 🙏

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      It doesn't matter, I'm glad people are getting value from this and understanding the Complex Types feature 😁

    • @wmalgoire
      @wmalgoire Год назад

      @MilanJovanovicTech Right! What matters is communicating, not trying to change people opinions 😊
      And if at least one people get value, that's a win 👍

  • @vallJune
    @vallJune 8 месяцев назад

    Hi Milan.First of all many thanks. Would you happen to know how to configure ComplexObject as an optional? Book { Author? Author}

    • @MilanJovanovicTech
      @MilanJovanovicTech  8 месяцев назад

      Not supported right now

    • @vallJune
      @vallJune 8 месяцев назад

      @@MilanJovanovicTech thank you, Milan. The OwnsOne seems to work for me

  • @xiovaii
    @xiovaii Год назад +2

    Finally!

  • @kodindoyannick5328
    @kodindoyannick5328 9 месяцев назад

    Thanks Milan

  • @gorgestv6340
    @gorgestv6340 Год назад +1

    I don't understand what is the meaning of the Owned types

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      Read the docs: learn.microsoft.com/en-us/ef/core/modeling/owned-entities

  • @vinr
    @vinr 5 месяцев назад

    Can all properties of the complex type be nullable, I don't think it is possible

    • @MilanJovanovicTech
      @MilanJovanovicTech  5 месяцев назад

      Doesn't seem like it: github.com/dotnet/efcore/issues/31376

  • @psdmaniac
    @psdmaniac Год назад +5

    I have never used that. Autors should be stored is diffrent table and reused (many to many).
    This "own" thing looks like something against database normalization.

    • @antonmartyniuk
      @antonmartyniuk Год назад

      It is indeed against database normalisation. But there are certainly cases when you need to sacrifice this in favor of performance

    • @antonofka9018
      @antonofka9018 Год назад +4

      ​It's going to be one less join when you query, which can be a big deal

    • @psdmaniac
      @psdmaniac Год назад +2

      Books and autors and you don't see why It should be normalized? Realy :)?
      What If you need to display list of autors? Will you sellect all books records and make district on autors? What if someone ask you to genereate more advance report, Daily? What about performance now :)?
      The solutuon is
      1. Always keep your db normalized
      2. Violate point 1 only if it is realy needed. For example some data redundancy is needed. And even of needed in longer period of time this bacame problematic (multiple points of truth).

    • @antonmartyniuk
      @antonmartyniuk Год назад +2

      @@psdmaniac we aren't talking specifically about this concrete example of books/authors with data normalisation.
      And besides, this data shape is completely valid in NoSQL world and apps have no problems with this approach

    • @GiovanniCostagliola
      @GiovanniCostagliola Год назад +1

      You’re right, Author is tipically modeled as Entity.

  • @fandermill
    @fandermill Год назад +4

    Changing the last name of the author and updating it in all books does not make sense, as ValueObjects should be immutable (well, at least from a DDD perspective).
    Nice feature demo though, I can't wait to clean up my dbcontext configurations.

    • @ahmedrizk106
      @ahmedrizk106 Год назад +2

      I think it makes perfect sense if you are making the change on the shared author instance, but if you are making the change on the book instance it self it should not affect any other records.

    • @Code_Bits
      @Code_Bits Год назад

      And also using the with statement means it would set the value bypassing any guard clauses if they would be there. I'm not a fan of using Records for ValueObjects.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      I needed a super simple example to showcase the features, so I had to "dumb" it down

    • @sunzhang-d9v
      @sunzhang-d9v Год назад

      class ->record ,yes?

  • @MaxwellGomesdeMorais
    @MaxwellGomesdeMorais Год назад +1

    awesome

  • @reelleer
    @reelleer Год назад +1

    Dicha funcionalidad existía en EF 6, por lo tanto ver que EF Core 8 ya la tiene me motiva a migrar mi aplicación "legacy".

  • @hanneskasel1853
    @hanneskasel1853 10 месяцев назад +1

    hmm yeah sharing instance is also not recommended whereas you shall make the complex type immutable

    • @MilanJovanovicTech
      @MilanJovanovicTech  10 месяцев назад +1

      This was more about showcasing what Complex types can do, though

  • @jeroen7362
    @jeroen7362 Год назад +1

    I do not see the point. what does this solve? Why not have two real classes and tables? In real life an author writes 1 or more books. Why would you store the author multiple times? My advise is to not use any magic in EF core. Also implicit many2many tables that can be created by EF. Just write your own many2many class, you can then for instance put the timestamp and userid on it, who added that connection. Your are then in full control of everything.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      You're missing the forest for the trees. This is about EF 8 Complex types. Not "correct" data modeling.

    • @jeroen7362
      @jeroen7362 Год назад

      @@MilanJovanovicTech Yes so why am i missing the forest? I can not think of any "complex type" scenario that i would put in a database like that. Your sample with books and author sure isnt it so what real life thing would you ever put in a database like that? Only problems need a solution, i have never encountered a problem in over 20 years of software development and database design that this would solve. Edit: the order with shipping addres as mentioned here could be a scenario. Still i would not use it for that. Just add a few fields for the address, postal code and town, country and done. probably country would be a foreign key to the country table.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      @@jeroen7362 See, there are scenarios where this is practical. You'd use an anemic model with properties for the address. Someone else prefers having a type.

  • @KevinBecker-p4u
    @KevinBecker-p4u Год назад +17

    This is a very poor example of a Value Object. I think the feature might be useful in some scenarios, but this example is not one. It would be nice to see a real-world example instead.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +9

      The point is EF Complex types and how they can be used to map Value objects. This is far better than Owned types that we had thus far.
      And if I put too much effort into the example, it moves focus away from what I wanted to showcase here.
      Can't satisfy everyone

    • @AndersBaumann
      @AndersBaumann Год назад +6

      Yes. Author is definitely not a value object. Use money, address or email as examples.

    • @KevinBecker-p4u
      @KevinBecker-p4u Год назад +2

      @@MilanJovanovicTech This is the problem I run into when learning new techniques, finding bad examples and not understanding why I would want to do such a thing. I don’t use value types and your example didn’t make me understand any better as I was wondering why anyone would want to “flatten” data in a relational situation.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +7

      @@KevinBecker-p4u Funny thing is, the people who already knew about Value objects didn't think this was a bad example. And they were my target audience with this video. I didn't want to focus at all on "why" you should use a Value object. This was simply about "how" to use EF 8 Complex types to implement Value objects (and I'm kind of assuming you already know what they are, and how they fit into DDD)

    • @Dpaz2009
      @Dpaz2009 Год назад +2

      @user-rd4cd9zj1s
      People who say this is a poor example should create a tutorial showing us better or else even better shut up and thank for sharing. 😅

  • @ahmedh2482
    @ahmedh2482 9 месяцев назад

    Doesn't this lead to redundancy in the database?!,
    I mean object values
    Why you don't use the author id instead of author object?
    Then you can use dto or anything to deal with your needs without overburden the database

    • @MilanJovanovicTech
      @MilanJovanovicTech  9 месяцев назад

      Is redundancy always bad?

    • @ahmedh2482
      @ahmedh2482 9 месяцев назад

      @@MilanJovanovicTech
      Suppose I'm trying to build a software for a distribution company (FMCG company),
      When the data entry or sales representative try to create an invoice
      He should determine the following
      The store: where the goods located,
      The salesman: the person who sold,
      The customer,...
      Each of the previous entities have at least 5 to 10 properties,
      Why I build a table in the database contains about 35 columns and most of them duplicate?

    • @skelaw
      @skelaw 7 месяцев назад

      ​​@@ahmedh2482 to preserve historical data at particullar point of time. I always store simillar info you listed in order/invoice etc. Those informations belongs to document and this is not redundant to store them, its required.

  • @nasermasri9954
    @nasermasri9954 Год назад +1

    First!😅

  • @nick_stelmakh
    @nick_stelmakh Год назад +3

    the poor example. didn’t show all wide spectrum of this update

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +2

      Now you have what to show us in your video

    • @nick_stelmakh
      @nick_stelmakh Год назад

      i like your videos but to be honest it so not this time, you could have spent more time giving end watchers a real example of why and for what it appeared@@MilanJovanovicTech

  • @techpc5453
    @techpc5453 Год назад +1

  • @greensporevalley
    @greensporevalley Год назад +1

    .NET stare

  • @damiannizio4039
    @damiannizio4039 Год назад +1

    Couldn't you just have simply used conversion before?
    IE:
    builder.Property(x => x.SomeProperty).HasConversion(x => x.Value, x => new ValueObject(x));

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      How does that work for an object instead of a value?

    • @damiannizio4039
      @damiannizio4039 Год назад

      @@MilanJovanovicTech it simply creates a column and translates the VO into primitive of value type inside value object. When the data is retrieved from database it creates a new ValueObject.

    • @damiannizio4039
      @damiannizio4039 Год назад

      @@MilanJovanovicTech I hope that's what You meant

    • @svorskemattias
      @svorskemattias Год назад

      Yes that worked for single-valued value-objects! I use it all the time

    • @ArcaneEye
      @ArcaneEye Год назад

      ​@@svorskemattiasif you don't mind me asking, I'm having some issues doing linq .Where() queries relating to these single property value types. How do you go about that?

  • @jakelanning1535
    @jakelanning1535 6 месяцев назад

    Why not have an author entity and let book have an author id column with a foreign key into the authors table
    modelBuilder.Entity()
    .HasOne()
    .WithMany(e => e.Books)
    .HasForeignKey(e => e.AuthorId)
    .IsRequired();
    Book's `public Author Author` would become `public virtual Author Author` and, as previously stated, would need a `public int AuthorId`
    and Author would need to have a `public virtual ICollection Books` as well as a `public int Id`
    I understand this is an example just to show off the feature, but i just can't imagine any case where using .OwnsOne makes more sense than to use a separate entity with a navigation property enabled by a foreign key, especially if theres a chance that the owned entity might be used in multiple of the parent entities.
    are joins evil and no one told me?

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 месяцев назад +1

      Just showcasing an EF feature here :) We're not talking about relational design 101.