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.
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.
@@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.
@@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.
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?
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?
@@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?
@@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.
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.
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?
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? 🤔
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 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 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 :)
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 🙏
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.
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).
@@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
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.
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.
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.
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 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.
@@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.
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.
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
@@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.
@@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)
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 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?
@@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.
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
@@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.
@@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?
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?
Want to master Clean Architecture? Go here: bit.ly/3PupkOJ
Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt
Thank you!
why programming with C# is becoming complex. !!
Finally!!! 🤩
Thanks Milan!
Sure thing :)
I like this feature much, hope collections will be supported too until EF 8 release
I don't think we get that in EF 8 :/
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.
Very good example with order + shipping address ✅
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.
@@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.
@@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.
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?
Give it a try? 😁
You mean the identity paradox presented by Amichai Mantinband on his channel? I'm curious as well. Have you tried it yet?
Hi Milan, do you achieve that complex property works with typed ID, like BookId(Guid ID)?
Does it not work for you?
@@MilanJovanovicTech Not for primary key. Does it work for you?
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?
Could be all mapped to one column as JSON
@@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?
@@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.
Collections for value objects would really be useful for my use case
We'll see if it makes the cut
How? I'm really curious as I can't see a situation where that would be the correct approach.
how would collections work with complex types? shouldnt a new table be added for that since not all db providers support the array type
Or JSON into one column?
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.
In memory DB is not a DB, that's your problem
Its AWESOME ! thank you so much for your content
Glad you enjoy it!
Thanks! It's very cool feature
You're welcome :)
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?
Lists not supported yet
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? 🤔
Yes, they're tracked by EF
Must the columns be author_? Can this be implemented in db first scenario where the existing columns may not follow that naming convention?
You, configure with ComplexProperty and define column with HasColumnName("your_existing_db_col")
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 :)
I think EF 8 will work on olders .NET versions, need to check though 🤔
@@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+
It's not yet full supported. Complex type collection support is not ready. Check the issue 31237 and vote!
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 :)
finally we can have a good EF features to support define & map value object to the database 😄
Yes!
Doesn't this remind [ComplextType] attribute in EF 4.0?
Yes, these are old EF features ported to EF Core
I am glad that this has been fixed
We're getting there :)
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 🙏
It doesn't matter, I'm glad people are getting value from this and understanding the Complex Types feature 😁
@MilanJovanovicTech Right! What matters is communicating, not trying to change people opinions 😊
And if at least one people get value, that's a win 👍
Hi Milan.First of all many thanks. Would you happen to know how to configure ComplexObject as an optional? Book { Author? Author}
Not supported right now
@@MilanJovanovicTech thank you, Milan. The OwnsOne seems to work for me
Finally!
✅
Thanks Milan
Any time
I don't understand what is the meaning of the Owned types
Read the docs: learn.microsoft.com/en-us/ef/core/modeling/owned-entities
Can all properties of the complex type be nullable, I don't think it is possible
Doesn't seem like it: github.com/dotnet/efcore/issues/31376
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.
It is indeed against database normalisation. But there are certainly cases when you need to sacrifice this in favor of performance
It's going to be one less join when you query, which can be a big deal
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).
@@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
You’re right, Author is tipically modeled as Entity.
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.
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.
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.
I needed a super simple example to showcase the features, so I had to "dumb" it down
class ->record ,yes?
awesome
Thanks a lot!
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".
Yep, old EF features coming to EF Core
hmm yeah sharing instance is also not recommended whereas you shall make the complex type immutable
This was more about showcasing what Complex types can do, though
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.
You're missing the forest for the trees. This is about EF 8 Complex types. Not "correct" data modeling.
@@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.
@@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.
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.
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
Yes. Author is definitely not a value object. Use money, address or email as examples.
@@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.
@@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)
@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. 😅
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
Is redundancy always bad?
@@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?
@@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.
First!😅
Blazing speed!
the poor example. didn’t show all wide spectrum of this update
Now you have what to show us in your video
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
👋
.NET stare
👀
Couldn't you just have simply used conversion before?
IE:
builder.Property(x => x.SomeProperty).HasConversion(x => x.Value, x => new ValueObject(x));
How does that work for an object instead of a value?
@@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.
@@MilanJovanovicTech I hope that's what You meant
Yes that worked for single-valued value-objects! I use it all the time
@@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?
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?
Just showcasing an EF feature here :) We're not talking about relational design 101.