>> Entity framework friendly. Azure Cosmos DB. >> Count down commencing. T minus two minutes. [MUSIC] >> 10, 9, 8, 7, 6, 5, 4, 3, 2, 1. Lift off. We are live for.NET
Data Community Standup. >> We're back. It's been a long few months where we
haven't had one of these. I would say we took a
summer break to have fun, but it was more, took a summer
break to do other things, including trying to
get EF Core 8 and.NET 8 out the door which has
been quite challenging. Anyway, we think we have
something we can ship. It's got a few limitations, but we're back here now and we're going to talk a little bit about some of the stuff today. As is always the case for these things, we're
completely unprepared. I made a mistake of decided to have a nap this
afternoon and then didn't wake up, and so I was going to prepare
all this a lot better, including the state of the unicorn, but I didn't, so it is what it is. Anyway. Most of the time we're
just going to spend walking through code and talking
about complex types. Hopefully that will be
interesting and educational. Anyway, without a lot of prep. Without any further ado. Then let us do the
state of the unicorn, which is very hastily
cobble together today and apparently has nothing
on it that will animate. Good to start there. What
I was going to say was this EF Core 8 is out now
let's see if we find it. >> Wow also good your
showmanship is impending. >> Here we go. EF Core 8. It's not out now, I missed RC1. EF8 RC1 is out now. It's on you get. It
basically feature complete, meaning it has all of the features
that we're going to ship in it. It does have some missing bits
and quite a few bugs in it, Yes. Sure. Try LC1, it's
better than nothing, but it's actually really easy
to use the daily builds. All you need to do is find a file. Put this file, a NuGet.config. Drop it down in the same
directory as your application, and put in the path, it still says.NET 7 here, put in the path for.NET 8. All of this is on the EF Core
repo,.NET EF repo front page. There's a link that says daily builds that tells
you how to do this. It's literally copy this file, put it in this place, go to, you get packages and select the
latest daily build of EF Core, and then you'll get all of the
bug fixes and everything that we've done since we
shipped or we built RC1. This is also going
to be true for RC2. What I'm going to use
today is actually the daily build rather
than the RC1 release, just because it works generally better and got
some bug fixes in it. >> What you're mentioning, so some people get cold feet when
they hear the word daily build. There's no reason to be stressed, especially now the
daily builds basically represent a more stable
version compared to RC1. All we've done since RC1 came out
was to fix bugs and stabilize, so there's nothing
experimental or untested, or unsure about the daily builds
quite at the current library. They really do approximate
at this point. They are basically the same
thing that's going to be released as RC2 in a few weeks. You're really better off. RC1 I think is actually very usable as well even
though there's bugs. But why would you want to use less current and
less stable version? >> Exactly. What we're going to
talk about specifically today is complex types and complex
types used as value objects. What do I mean by that? Firstly, what we're talking about
here are types with structure, so types that have multiple
properties that you actually want to map to the
database with multiple- >> We'll come to Jason later. But if we're not using Jason, then you want to map them
to multiple columns. We're not talking about a simple
property with a simple value, in which case it just
is a simple property, maybe with a value converter that goes to a single column
in the database, and conceptually on both
sides is just a single value. What we're talking about here
are types that have a structure. Like typically an
address, for example, where it has two lines, a postcode, a city,
country, whatever. It's got structure
inside and you want that structure to be
mapped to the database. >> But Arthur, we already
have entity types, which are types that have
multiple properties, which get mapped to
multiple columns, so what's new here? >> The difference
between entity types, including owned entity
types, and complex types, is that entity types have a
defined identity, a defined key. You typically have a property
called ID, or something else, or maybe two properties, but those properties define
that object's identity. Whereas in a complex type, it doesn't have any identity. It doesn't have any identity
other than the thing that it is. Let's take an address, for example. If I change the city in an address, that's not the same address
in a different city, that doesn't make any sense, it's a different address now. It's entirely different address
and that's what I mean. It's like you change some part of the address and
it's a different address. But if Shay gives me an address
with all the same elements, the same street number, same house, same zip
code, or whatever, everything exactly the same as mine, those are the same address. It wouldn't make any
sense most of the time. Now we'll come back to when
this does to give this one an ID of 10 and this one an ID of
13, even though they're the same. Basically, that's what
we're talking about here. >> Does it make sense to
make the comparison with our.Net structs and classes now
just to drive the point home? >> We will come back to that, but that is an important
thing because that very much ties in to
all of these concepts. >> Conceptually, I'm going
to say two words about that because I think it might
help drive this point home. In.Net as most of you probably know. You've got a class, a class is
a reference type, and so on. When you have a class in
your code in C Sharp, now we're not in the database anymore, we're just in C Sharp code. So when you have a class, it's a reference type. Which means that if
you have a variable, that is like a class, that means it actually
points to something. The class has a location,
it has a pointer, it has a reference somewhere
in memory and you can have multiple variables pointing
to the same place in memory. We can say that the
identity in the case of.Net objects or classes
is basically the reference. There's something that identifies that thing and you can refer to it. You can reference it
from another class. You can have pointers and so on. Whereas structs or value types in.Net do not have
that thing in general. They can be embedded in a class, for example, and they
can be on the stack. Of course, they are
somewhere in memory, everything is somewhere in memory. That's always the case, but conceptually, structs
don't have an identity. This is also why, by default, if you compare two
classes to each other, you're going to get true only if
they refer to the same thing. We don't look at the contents of what's inside the
class by default, what we do by default is
reference comparison, whereas with structs in.Net, if you compare those two
things for equality, what you'll get by default, once again, is a member
wise comparison. Only if the address field is the same as here and
there in the city, and so on and so forth. I gloss over a lot there I think. >> It's a good analogy, but bear in mind that you can use.Net reference types
and.Net value types, both for complex types, both for these types that don't have any identity in F.
We'll get to that. >> Right. >> Let's look at some code, stop talking, and just look at some code because we've
given an overview. What we're going to do is
we're actually going to take address and we're going to model address into a
small entity model. I have an entity model
here, customer orders, and it's, why is that
flashing though? Is that supposed to be doing
that in presentation mode now? >> The cursor? >> Up here, there's dots flashing
across the top here. Anyway. >> Oh, yeah, I see. Yeah,
that's really weird. >> Anyway, okay, so
we have a customer in order and we're going to put
an address into this model. Let's get our address here. Immediately we can
see that a customer, put in order both have an ID. If we go back to our analogy, if the customer calls and says, I've got married and
my name's changed, that doesn't make
them a new customer. We can mutate this and give
it a new name and they still retain the identity of
being that same customer, and that's another way of
conceptualizing this stuff. Where if somebody says, I've
moved to a new address, well it's a new address,
it's not the same address. Now so we're going to give, as we just said, a
customer an address. We're also going to say when
a customer makes an order, we want to put two
addresses into the order. We're going to put in a shipping
address and a billing address. We've now got three
addresses in the model. Let's go and get our context type. I may have already
prepared from earlier. Let me see here, explorer. A simple DB context type
for customers and orders. Now we want to map these
things as complex types. If I run this thing right now, it's going to tell me you
can't do that because we don't ever detect complex
types by convention. The reason being is that from
a convention perspective, we can't determine the
difference between this and an entity type other than the fact it doesn't have
a key that we can find, oh it finished, interesting. >> The whole time. >> This is a great comedy stand up. >> What was I saying? We
can't distinguish it, by convention, from an entity type, other than the fact that
it doesn't have a key, and not having a
property that we can find as a key is a common error. If we were suddenly to discover
all these as complex types, it would become very
confusing because you'd never get any warning out of it. We chose to make it so that you have to discover them by convention, although it appears that doesn't work because it was
supposed to crash and it didn't, which I'm now very interested in understanding but I
probably should just move on and put in the
configuration here. Actually it didn't run
right. Oh, you know why? >> Why? >> It just says appear
console right line. >> Of course. >> I wasn't going to
run it initially, that's not in my script. See, I'm going off script. Now I'll run it and it
will give an error. >> That looks better. >> It's saying, here
if I can get it, requires a prior
entity type address. It's discovered as
a dress like I was describing and saying
it right private. We probably should
change that actually. We should let that message, could you make a note of that, Shay to update message to say
it might be a complex type? >> Sure. >> You can configure
them as you might expect inside your dB
context our model creating, and it's going to look like this. You do entity, and then you say that entity has a complex property and
you put it to address. Complex property think of it as
property but for complex types. Likewise, we can do two
calls for it in order. Now when we run this, we should see it creating a table
for the customer in the order. Let's look at what's
in those tables. You can see that what
we've done by default, and this is the same as what
happens for own types by default, we've split out the
complex type into columns in the entity
types, main table. City county, and so
far have a column. Where we have two of the same type. This one is prefixed
by the property name, in this case it's also
prefixed by the property name, and that distinguishes
the billing address from the shipping address. Of course, we could choose
not to do this when it's only one but then if
you add another one, it causes a migration to change
these names and everything so we just be consistent from the
beginning and name them like this. We want to make it possible to map these two
complex type columns like you can with own types as well, but that's a limitation
in EF core eight. For now, they only map to additional columns in
the main entity type table. There is an attribute, we'll
get to that in a second. There's a comment here. Yes,
we're going to look at that. You can not own types, you can split off
into another table so you don't have to have their
own type share a table with its parent entity type that's not
something we plan to support for complex types because
we'd end up with a table with rows that had
no identification in them, so it doesn't really work. >> Should we discuss
a little bit complex versus own types and
what's the difference? >> We'll get to it. Coming
back to the question, if we don't want to put
all of this in here, we can just go to customer and put complex type attribute on that and that will
produce the same thing. >> Not on the customer
though I think. >> Not on the customer,
you're right. That won't produce the
same thing at all. That will produce a crash
as we see down below. Two things about the
complex type attribute. This is the old complex type
attributes in data annotations, namespace. There's a bug. I believe it's not in the daily
build but it's still in even RC2, I think, where if you apply this to a nested
complex type it doesn't work. For nested complex types until GA, you'll have to use the fluent API. Also, you can't put this on a struct because in our wisdom when we
created this thing back in 2012, 11, whenever we said, well, it's only ever going to go on an F6 complex type which
could not be a struct. Currently, we're not planning
to do anything about that. If you think we should, there
is an issue you can vote for. There is finally one other way. If you don't want to
use the attribute, but you don't want to configure every property usage
as a complex type, then you can override
configure conventions, this is where you do both configuration before
the EF conventions of run. You can say complex
properties address. In that case, anytime
we find a user address, it will be used as
a complex property. If we run that again, we should see that that now creates the same tables
as we had to start with. Indeed it does. Any questions
we should answer Shay? >> There is a question here, why is the attribute on the class level? Which I wrote an answer to but you
probably can give a better one. >> I don't know, we created complex
type attribute a long time ago before EF 41 was released. Old EF 41. This was like 2012 or
something like that. This was before that, 2010. >> My answer would be so typically, when you have a.net type, and you want to use it as a
complex type in one place, you probably you want to use it as a complex type in
another place as well. Like imagine this address type. It's very contrived to
say I want address to be a complex to be treated as a
complex type here, but not there. There I wanted to have an ID, like be an actual entity
type with its own table. That's very strange in general. That's not how the attribute works. We just made it much simpler. I think it's the right thing, that you slap it on the type
once and then you can use that type anywhere and it's going to be recognized as a complex type. I think makes a lot of sense. >> It's interesting.
I'm wondering whether I could map it as a different type in other places where I probably could but I'm not
going to for now anyway. We have our model
with complex types. Let's switch it back to
this just because it's a little cleaner about later. Let's save a customer
with an address. We'll create a database and we're going to create
a customer with a name, and it's got an address, so we're just creating
a new address. We add that customer, we call say changes. There should be nothing
particularly unusual there. Indeed, we can see that we're inserting into customers
with the address. Let's say we now want
to create an order. We've got a customer
and we're going to create an order from that customer. Now, we'll forget about everything
here associated with this other than the stuff around the address. Well soon I can just
create a new order. When we create the
order, I'm going to copy the customer address into the billing address and the
shipping address by default. Obviously, we would let the
customer change those things, say ship to my grandma, for example, and bill to my
mother or whatever it is, but by default, we'll copy
the customer's address into those things and we
will save changes. Let's see what happens
here when we do that. You can see that we now have
insert into the orders with the shipping address values and
the customer address values copied from the original customer. At this point, you probably
could have been saying, we could have done all
of this with own types. Actually that last step there
would have failed with own types. It'll be interesting to see
if you've read the novel, I'm not going to say that because
this is in the blog post. Has anybody read the blog post
or the watch new and can say why this fails with own types? Anyway, it does fail with own types. Read the blog post or the
watch new to find out why. In a nutshell, it's
because you can't share the same complex type instance between three different usage of it. That's because they're entity
types and they have identity. What you're saying is
this complex type here, this has the same identity as all of these three other places
when you're doing own types. Now you could give them
all different ID's, and you could change
the ID or generate a new ID when you copied it
over and that will be fine. You could model address as an entity type and then share that address in multiple
places and we would track the references like Shy
was saying before because it's a C# reference type and
everything, and it would be fine. But you have to give it identity
if you're going to do that. If you don't give it identity, then it's just an
object with values. We don't care whether it's a copy or not when it's a complex
type but with an own type, we definitely do care whether
it's the same instance or not. That's one of the big difference
between complex type. There's a lot of other things
like this which are nuanced and which appear to be small
semantic differences but in reality have a big impact on the experience you have
when you're trying to use these scenes because it just
doesn't meet the principle of expectation where
it behaves like you expect it to to given the semantics
you think it should have. We're hoping that complex types have a lot more closer semantics to what you expect for this kind of object. >> It's maybe worth
mentioning so obviously we introduced on types a long
time ago relatively speaking, and complex types are new way. There's quite a bit of
overlap and we expect a lot of people to
get a bit confused. Which one should I use
and why and so on. Which is fair enough and we need to document this a little
bit and explain it. It's a little bit subtle. But it is worth saying, I think the way I see it in my head, I hope Arthur agrees. We've had a lot of discussions
about this in the team. Basically, if what you want to do is represent something that's
inside your entity, it is inside, it's contained
inside your entity, then that is exactly what
a complex type is for. We made, I think I can say this, we made a mistake basically by saying we're going to take
the owned entity concept, which has its uses, and we're going to apply that, we're going to use that
for this containment idea. Containment by the way, you can think about it in many ways, like Arthur just showed
one type of containment, which is to flatten
that thing out into your table so you basically add
more columns into your table, prefixed by the name. That's one way of
something being contained. It's not somewhere else and we're pointing to it
via a foreign key. There's no identity, so
there's no foreign key. It really is inside that thing, inside that same role. Another conceptual way to
contain something is via J-SON. You can have a single column, doesn't even matter
if it's J-SON or not. You have a column in
the database and it somehow contains that
complex thing inside it. It could be J-SON inside it, It could be some other format, It could be [inaudible]
conceptually, even though nobody likes
to do that anymore. All these concepts of
containment, basically, this is what we're
targeting here with this new feature
called complex types. It doesn't have an
independent existence at all. It is really inside
that JSON document. It is inside that table, whatever you want to say. Up to now, a recommendation. The way to do this with
EF Core has been to use own types and that is
not the ideal way. There are various, as Arthur said, there are various mismatches there. It doesn't work the right
way because conceptually, own entity types still
have the idea of an identity and an ID
behind the scenes, even if you don't
necessarily see it. Yeah, that was the
thing I wanted to say. >> Absolutely. Let's
go and look at what happens when we try and change
these things and use that to look at different kinds of objects that can be
used as complex types. By different kinds of objects, I'm going back to the different
kinds of CSharp.net objects, value types and reference
types and then we'll also look at records as part of that. The first thing I'm going to do is make what looks like
an innocuous change. Now, conceptually you might be
thinking this doesn't change, nobody just moves down the street and then updates just the
line one address or whatever. That's fine, you
normally would create a complete new instance of an address when somebody
gives you address, they'll type in all the bits and
you'll create a new instance, you wouldn't mutate
certain parts of it. But for the sake of demoing the different kinds
of objects and how to behave, we're going to be mutating
here and then show how not to. I'm going to change the
address line one which was Barking Gate and will
now be Peacock Lodge. Then I'm going to call say, changes. This is tracked, should detect the change and save
it to the database. We indeed do do that, but we actually update
customers address line 1, orders billing address line 1, and shipping address line 1, all to Peacock Lodge. Which may certainly not
be what we intended here. When we copied the shipping
address and the billing address, that was a starting point
for people to edit. We weren't expecting that if
then the customer says, oh, I've now moved, that we should then update the address automatically. We might want to do that, and that would be a
different scenario. That's when using address as an
entity type might make more sense because you want to keep track of that particular address as identity. But in this case, when
we change line 1, we don't want them
all to change but, address is a reference type. When I did billing
address, customer address, and shipping addresses,
customer address, I wasn't actually making
copies of these things. I was just assigning a reference to that complex type in
the customer address, to these things which
is fine because EF doesn't care about its identity, which is different from own
types like we talked about. and it will save it, but it's problematic if you then mutate
it and you're sharing it. Now, this isn't really
an EF problem as such. This is really a problem with mutable types and
reference types in general. >> I think Shay I
would agree with this, the best way to deal with this
when you're using complex types, because of the semantics
and their nature, is to make them immutable. Let's do that. We're going to do
it in a really trivial way first and then we're going to
look a record and show how records make it look like nicer. I'm going to change
the address type here. We don't need that anymore. What I've done here is actually let me just do it in
line so you can see, I'm just going to change
all the setters init. Init can only be set when
the object is being created. This is now immutable. It's not immutable if
you use reflection, you can mutate it with reflection, but if you're using the C-Sharp
language, this is immutable. Once I've created an
instance of address, I can't change any
of its properties. All of its properties
are immutable too, which is important for
immutability remember, because if I could
mutate a string here, then I could mutate
this, but I can't. This whole thing is now immutable. That of course means that
I can't do this anymore. I can't now say, mutate that line because
that's what we just did. I can't change it. What
I can do is do this. Conceptually I can
create a copy of it. I'll create a new address. I'll put in all the values from the old address as they
were before but with just this line change
now I've set a customer to a new address instance
with these lines change. Notice here that I'm still
sharing the instance where I can. It's fine to share an instance
because it's now immutable, so that instance will never
change where it's been shared to. It's never going to
have a different value and if it wants to change somewhere, it has to create a new instance
as leaving the other ones fine. That's the real like advantage
of your immutable types here. There's other advantage
in other scenarios, but in terms of what we're talking about here, that's the advantage. When I run this, it
should only update the address on the customer
and not on the order now. You can see it does. Interesting thing here is
notice that we actually set all the values and a
completely new object. This is a new instance of address. But the EF change tracker
still understands the structure of this
and it still detected changes between these two things
and said the only thing that's actually changed that the
database cares about is Line 1. All I'm going to do
is send an update for Line 1 and change it and
leave the rest the same. You are now using these immutable
objects in your entity types, but the change tracker
is still doing fully optimized partial updates to the database based on
what's actually changed. Now some of you hopefully
will be yelling, use a record for disks and that is
indeed what we are going to do. C-sharp record is a class, in this case you can
have a struct as well, but basically with additional
semantics over it. It's not really anything
particularly special. From a conceptual standpoint, I have an address and I've
given it a record type. I can be explicit about this, call it class record
or is it record class? I don't know. Record class I
can be explicit about that now to imply that this definitely is a reference
type, but I don't need that. It's got a constructor that takes
all the values and then it's got the property getters and
setters like we had before, but it's conceptually very similar. Now the thing is we
can actually make this into a really concise syntax
with primary constructors. If I say convert this
thing to primary scripts, we get this single line of code
which defines an address object. This is exactly the same. When I just did that "Alt"
enter there and refactored it. I didn't change the
semantics of this at all. This has all of those properties. It's got that constructor, it also has a deconstruct as well, but that's not super
important for this. We can now use that in our types. Now we have to start
calling the constructor so we're going to need to
change our calling code here. Let me just do that real quick. We'll call that null for line 2, city, country, and the postcode. We have to use the constructor here. But rather than using the constructor
and doing all this manually, we actually have this really
nice simple syntax called with, which means that we don't
have to do all this manually. We can have the compiler
basically generate this for us. >> Now this is getting really nice. Everybody pay attention. >> Now when I want to update line 1, But this is immutable, I can just say the customer address but with this one property
or multiple properties, you can add multiple properties
in here but with this property changed and it's doing the same thing that we were doing
with the class under the covers. It's extracting the values out of the existing
instance and then it's calling the primary constructor on the new instance to create a
new instance and setting it. But it's just all nicely
sugared underneath so that all you have to do, do that. We should be able to run that will work the same as the class example and we don't
have a sharing issue anymore. I have to say, I don't know
if you agree with me on this, but I would say record types are
the best fit for complex types. If you're doing complex types, then pretty much use
a record type for it. It's pretty much perfect fit. >> I'm sure there are scenarios
where a class is a better fit. But I think, by default it's definitely a good
option to start with. I have to say again, because I like to speak and
say a lot of long things. But this question of EF and
immutability has existed, I think since EF has existed and we have a big
issue where people tell us, please support immutability
better and records have appeared at some point and people have asked how do I use
records with EF Core? And the truth is that there wasn't necessarily a good story
for that and by the way, we still have not
resolved this completely. This is definitely one step. It's one area where
suddenly records fit extremely well in the EF
ecosystem. It's really nice. I personally absolutely love
what Arthur just showed, where you can just,use
records like this and everything works
precisely as it should. Everything aligns conceptually like complex types and records and so on. That works really nicely. We still, of course, need to think about how to do immutability outside
of complex types. For example, some people want
entities to be immutable as well. Especially if you're
coming, say, from F sharp, from a more functional
background than you want this. That's a whole different problem, which we don't have a
good answer for right now because it involves
change tracking. How do you change
track something that's immutable in one that's difficult. But I love that in EF eight, we could at least do
this pretty major step I think for stuff that's contained in matching that as
an immutable record thing. Which is I think super cool. I'm really looking forward to seeing
people doing stuff with this. >> Let's talk about struct now, I was talking earlier about C-Sharp, reference types and value types. With records we can
create a record struct. I guess it's a struct record record. >> I just added the
keyword struct in there, but everything else is the same. If I run this, we should see that it works
exactly the same as before. This is a key aspect of complex types that is again
different from own types. Own types because we need to be able to track their reference
as Shay was describing at the beginning have identity and
they have to be reference types. It doesn't make sense to
try and map a value type, a C Sharp value type, as a entity type. However, complex types don't
have that characteristic, we actually don't care about whether it's an instance or
it's the same instance or not. Actually, structs or value types
are a reasonable fit for this. Records being immutable and having that nice syntax have the
same advantages they do here as they have when
using them with classes. In addition, you end up using a
read only struct and we'll look in a minute why it's
important to use read only struct as opposed
to mutable structs. The bottom line when it comes to
whether or not you should use a record struct or a record class as a complex type is purely the
same as anything else in.NET. That is whether or not you
think it's better to allocate this thing on the heap or whether you think it's better
to allocate on the stack. For something like a coordinate, for example, which just has x and y. A struct seems like
an obvious choice. There's a lot of overhead
allocating that on the heap when it's just
two simple numeric values. On the other hand, if
you had a very complex, no pun intended, complex type with a lot
of structure in it, a lot of values and
a lot of properties, it might actually make more
sense to have that still be a immutable reference
type share instances and not just copy it
around all over the place. But those are the same questions
that you would tackle when dealing with value types versus reference types
elsewhere in C Sharp. Let's look at non-record
value types real quickly. What I have here is a value
type and it's mutable. This is a mutable struct. Let me find the code
there that I had. Go back to initializing it because it doesn't have
a constructor anymore. I could add a constructor, but for the sake of this
demo I'm not going to. Initialize it that way. Now when we get down to updating it, we can actually still use with here. With actually doesn't only
work with value types, with record types, it will pattern match anything that has the appropriate
stuff it will do. What I actually wanted to show here is that we've actually
created an immutable struct. What I can do here is say this. It's immutable struct,
why can't I do that? Well, that's part of the issue
with why mutable structs. Eric Lipper always said, don't use mutable structs.
Make them immutable. It's because when I do this access this calls
the getter for address, calls it getter, the
getter for address will return a copy of
this address object. Because it's a struct, it's
creating a copy of it. It's not returning a reference
to it that then you can change, it's creating a copy of it. C# in this case actually
is trying to help us. It's trying to stop us from
shooting ourselves in the foot. Because what I've
actually said here is, take me a copy of
the address object, on that copy change Line
1 to be Peacock Lodge. Well, that's a no,
that does nothing, because the address on customer hasn't been changed because
I created a copy of it here. C# is basically saying
you can't do that. We're not going to allow
you to mutate the thing on that side because we know it's a copy and it's just
getting thrown away. That's invalid, but
it doesn't always. What you'd actually
have to do, by the way, is you actually have to do something like this
if you did want to do this. Let's extract this into a variable. We'll just replace one
instance and we'll say it. That's our current address. We can then update the line
on the current address. Then we can say address
equals customer address. It's looking very similar to
the immutable type, in fact. It's one of the
reasons why it doesn't make sense we would
not be immutable. This works, and the compiler is
saying this is okay as well. We've created a copy, we change the address on the copy, then we copy the copy
back into the original. Now this one is changed and if we run this, this should be working. But it's not intuitive. Also the compiler doesn't always give you that nice red line saying what you're doing is stupid. You can do lots of things
that the compiler will say, that's fine, go ahead and do that. Then you end up saying, the value is not what I expected it and it's because you
copied the struct somewhere, but you didn't realize
that you were saving it on a copy and it
gets very confusing. Basically, don't use mutable
struct is the upshot of that. Just make them immutable. You can make an immutable struct
that is not a record type, but you may as well basically go to a record type once you've got there. Again, we end up going with either
immutable struct records or immutable struct glasses and that's the good pattern for complex types. I'm going to go to nested
complex types next. Is there any questions
we should answer? >> There's lots of
lively conversation, I'm typing my fingers
off to be honest. I will say one thing because
it came up several times. First of all, for
those who missed it, nested complex types work
very well. You can use that. So inside your address you can have yet another thing that's
nested, that works fine. EF will handle that. That's not a problem regarding JSON because that came
up several times. This wonderful complex
type feature for eight, this is the first iteration. In eight, we will not be supporting JSON mapping via complex types. You can still do that
with own entities, which is a feature we
introduced in seven. That's the right way with EF
to do JSON at the moment. However, I think it's a very
high priority item for us, I think in nine and of course nine to also allow mapping
JSON via complex types. At that point I think
complex types becomes the preferred way to do
JSON mapping, basically. You're going to have to hang
tight until we do that, but for now, JSON is
still via owned entities. I think that was the main thing that I saw in the repetitive questions. >> Sounds good. What we're
going to do is look at, as Shay said, nest complex types. What do I mean by
nested complex types? Well, before we had an order
which just had addresses. But let's say I actually want
to expand that concept and give the customer a contact information, which is both the customer's address but then also the
different phone numbers. I've got three phone numbers here. We can do that by creating
actually three complex types. We can create the address complex
type that we already had, a phone number complex types with a country code
and then the number, and then a complex type which has all of the nested
complex types in it. This is a complex type containing only complex type properties itself. An entity type or a complex type
can create any combination of complex properties or
just normal properties. You'll notice I didn't create
a primary constructor on this. That's because EF doesn't yet support injecting
complex type properties into a outer complex type property when materializing these entities. That's actually, I think, relatively easy to do with complex
types compared to own types. We hope to put that in, but it didn't make it past eight. Once we have that, then
you'll be able to use the nice compact syntax with the primary constructor
for all of these. As it is, we'll put a
contact on the customer, and we'll put two addresses and
a contact phone on the order. You can see that
we're using these at different levels at different
places and that's fine. At this point, phone number
is at the top level in order, but it's also contained
in contact itself. You can mix them at
different levels. We do need to configure them. Let's go to the configuration here. I'm just going to say,
wherever phone number is used and wherever contact is used, that's always going
to be a complex type. Let's delete some of
this code that was for the old stuff and let's
create that database and look at what the structure of it is. >> Structure of the database
as you might expect. Why did orders not get created? Did delete the orders in that promp? Customer references orders. That book. >> Sorry. I've been doing
questions on the chat, I've not been following closely. >> Well, so I read it and my orders table is not being
created and I'm not sure why. Great table customers
and then we just ended. That's so weird. Let me try again. Just run it again. It will be different.
It is different. Go figure. I'm not sure
what happened there, but we have a customer's table and you can see that
we've again split it out by the property so
a contact_address city, so on home phone. When you have a nested complex tape, then by default we flatten it all out as we
were just talking about. As I said we would like this to be a single column with adjacent
document in that's something we will implement after
eight and then the same here for orders table
you can see it's spread out. Let's do some queries.
Should we do some queries? >> Of course. There's
so much to see. >> Yes. Probably need
to add some data in. Let's see if I can ignore the person behind the screen for the moment
because I'm not fully prepared. This is all on Github, by the way. This is what's new in F8 and in here we have one called
nested complex type sample and it has a seed method
that will at least create some data on the database so
that we can see some queries. Let's put that into
our context here. Go back here and seed. Now we've got some
data in the database. Let's just be clean and clear the change check
out at the end of it. Lazy, really should create
in context instance. But now that that's
there, I'm just lazy. Simple query. Copy. I guess we need a customer ID is one. What we're doing here is a
query for the entity type. There's nothing specific
to complex types in this, and I want to show you what
happens when we run this. Because complex properties, just like any other property, any other normal
non-navigation property are always loaded when
you load the entity. We have an issue on the backlog
for lazy or deferred loading of property values which can be useful
if you've got a property value like a blob which has a lot of data in it and you want
to not load it straight away, but at the moment, EF
doesn't support that for any property and that's also
included for complex properties. I'm avoiding saying you
don't need to use include here because as we all know include works on entity types
and through navigations, and the complex properties
aren't navigations so include doesn't even
make sense here to use. But it's a similar. Again, we try to simulate this with own types by doing
auto-include on them and that try to make it
look semantically the same as it being a property. But yeah, we wouldn't even be having this conversation if we hadn't done that. Their properties, they get loaded as properties and you can see
here that the select we did on the database is
bringing back all of the different columns as
we would expect there. That's not that interesting. What happens if we want to
do something like project? Objection is a bit more interesting. Create order ID. >> I know which query
I'm waiting for. There's a very exciting
query coming up. >> There is, okay. I'll
try to get through it. As you might expect, let me actually put a break
point on here so that we can. What was that trick that
somebody showed us to do that? >> Yeah. >> I used to use console right line and now I just do that. That's nice. >> Yeah. Nice trick. >> Yeah. We're projecting out
just the shipping address here. Very common thing that you would
want to do so get an order, give me the shipping address. We'll run this again
as you can expect it's not particularly
interesting translation. >> Much interesting, but was not easy to implement. I
will just say that. >> Yes. >> The amount of magic that's
going on behind the hood here, that's why we're here and you
guys are the users seriously, so that you don't have to contend
with all this complexity. >> We've got an exception,
contains the stains no element. You know why? >> You forgot to see. >> There's no orders
in the database. Because I copied the c-thing
and then the code was supposed to add the order
afterwards. Bear with me. We'll find the chunk of code
here that creates an order. It's going to be in here somewhere. You know what, I'll just go back
to my right opening. Not there. Here. Let's go to the top here. You will create an
order, see changes. Maybe I should show this. This is
actually interesting, isn't it? Update. Basically,
rather than showing it, and you see what's
on the screen here, that you can update, you can modify bits of a complex
type even if you're doing the immutable mechanism and we'll still do the change tracking
down into the nested structure. If the only thing you change
is work phone country, which would be weird, then
we'll only update that. I forgot to mention that, but I think it's fairly
straightforward. Let's add an order here. Do it my seed method here. Sure. If this is needed, but I'm going to do it anyway. In the wrong place.
It together here. That's why we needed address
but we can just do this. No contact. >> Now we've got some
data. Let's try. Let's see if we can run this
non-interesting query finally. We're projecting the shipping
address and as you can see, we're selecting only the
shipping address that we need from there and projecting
it out as you would expect. Not a particularly
interesting query, but we got there
eventually. What's next? How about we query into
the complex types? Let's use it in a predicate. This isn't the
interesting query yet. Here, what we're doing is find me all things that
are shipping to this city. Again, I'm drilling down into the complex type and finding
everything that's nested, which we should see is fairly,
again, self-explanatory query. Select all of this stuff where the
shipping address city is that. Maybe this is the interesting one. You can also use the complex type
instance itself in predicates. >> Now it's getting interesting. >> Let's see. Somebody gave
me their phone number. It actually really doesn't
make sense to search for the individual parts of the
phone number separately. You rarely want to find all the phone numbers in any
country of a certain number. I suppose you could want to find all of the phone
numbers in a country, so that part makes sense, but usually, they've
given me a phone number, let me find all of the
customers that have that phone number and
I want you to find all the customers that
have that phone number, either as their mobile
work or home phone. I'll run it and then
I guess I'll let Shay talk about the translation and why that makes sense especially when we're using
record types for this >> Should I? >> Yeah, go for it. >> Once again, this is equality
between the whole type and another whole type
which is different from a specific property or field on
that type, that's very different. The behavior here, what
you're seeing is that EF expands that very
simple equality. It interprets it to mean. Every single column, in this case, must be exactly the
same on both sides and what's interesting here is on
the left side of the equality, we have something in
the database that's mapped to our columns
in the database. On the right side, we have a
parameter in the link query above, if you look at it, which means
that when we expand this equality, if you look at the workclass
that Arthur just highlighted, each time, on the left, we see a property on the table
that contains that thing, and on the right side
we have a parameter. We're going to basically send
a parameter for each and every property of that
parameter instance, so we took this.NET instance and we actually
converted it to a bunch of parameters that we now send to SQL server or whatever
database you use and we just construct comparisons
between each and every one of them, which is pretty cool. This is the first time
you can do something like this in EF and it's important to compare this
with what happens if you did the exact same thing with something
that isn't a complex type. If you had an own entity
or just a normal entity, where this equals that, then you get something
very different in general because entity types are
defined by identity. Remember, again, this discussion about.NET types and classes
and all that kind of stuff. When you do equality
between two things, what that should translate to is equality between their identities. You wouldn't, like in this case, compare their actual values
inside structural equality, but rather their references. That doesn't really work
in the same way at all. If you have, let's say, two addresses and you're asking, give me all the roles
where the shipping address is the same as the billing address, if it's an entity type and it's
going to actually check that it is the exact same instance
so that the IDs are the same rather than the
contents are the same, which is usually not what
you want with addresses. Once again, this is another
great example in my mind why complex types are the more
correct way to model this. What is important
about a complex type, about an address, is its contents
and when you want to compare, what you want is value symatics, you basically want to compare the
contents and not the identity, not the reference or the ID. Complex types allow us to do this. People have been asking,
by the way, in the chat, what is the exact advantage
of complex types compared to, say, own entity types? This is yet another small corner
where identity interferes. It gets in the way when
you're using own entity types whereas complex types
are the correct way to model this, in my opinion. >> We should write
something about this in the docs, like you said earlier. But you really need to ask yourself, the thing that I'm modeling, does it have identity? If it has identity that lasts longer than its characteristics
so when I change my name, my identity doesn't change. I'm an entity type. Or is the identity made up by
its characteristics? Coordinate 23 is different
from the Coordinate 56. You can't mutate 2, 3, and 5, 6 and say, that's the same coordinate
just with a different value. It's a different coordinate. >> It also doesn't make sense
to have the same coordinate twice and then distinguish
between the two. That doesn't make any sense. >> That's entity types
and complex types. Then you throw own types in there. When should I use own types? Well, you should only use own types when you want it to have a key, but you want that key to
be hidden and it treated as a entity type with a hidden key, which is basically
pretty much never. In essence, you never
want to use own types. That's not strictly
true if you think of own types in terms of
aggregates where you could define a key but still have
it be an own type and have the advantage of being
auto included and those advantages of an aggregate, but it being an entity
type in the aggregate, but that's not what most people
do with own types right now. In doing that, you're still very much modeling
it as an entity type, but we don't think it
really makes sense to model something without a
key as an entity type. Own types with keys in shadow state, like they exist today, will probably not be recommended
for anything going forward. We may automatically
make the default to be complex types in some situations
when we do certain things, we haven't really decided that yet, but at the same time we're not going to deprecate own
types immediately, so even if we change the defaults, you'll still be able
to go back to all of that behavior and
will still exist. Years down the road, we have dreams of being able
to deprecate it entirely, but that rarely happens as
you guys are well aware. For now they're both going to exist, but you should basically use
complex types pretty much in every case where you think there's
even a question essentially. >> I'll maybe add one last note, if we go back to this equality
property by property equality, going back to our analogy from the beginning with.net classes
and structs that once again aligns with the
struct behavior and record behavior in.net so
when you compare two records, the idea of a record, by the way, is basically value semantics. That's what records are, some people think records
are about immutability. That's not quite right, records can be just as mutable as classes are. That's not the point, the point it really is about value semantics so if
you compare two records, what you get is a comparison
of their properties. Similarly with structures or
value types, if you compare them, what you get is a comparison of
their property so we're very much mapping concepts now in the same way and interpreting
equality in the same way, which I think is great,
it's the right thing. >> There's a comment here that
I want to bring up because it's important and I've been meaning
to mention it throughout. Own types are not, despite what this comment says
are not great for collections. Own types are absolutely
horrendous for collections. This whole problem with keys is massively greater when you have
a collection of these things. If you have a single instance of a complex of our own
type on an entity, then the own type inherits
the key from the entity, that's how it gets its hidden key. If you have a collection, you can't obviously inherit the
key value because you've got lots of things so we have to
synthesize key values for that. Those key values, in
the case of JSON, aren't even stored in the database
like actually in relational. They're not stored in the
database either so you just end up with this huge mess of when you use collections of
own types and unfortunately, we couldn't do collections
of complex types for eight, we just ran out of time basically. It's unfortunate that that's
not shipping in eight, but ultimately that is probably the best usage is
going to be for collections. >> First, just a shout
out to Victor who says, I'm using it all wrong so, don't feel bad at all. >> There's no other way
to do it right now. It's just a bad design. >> Also, this is a subject of very heated discussions inside the team that we used to have about. This is all very complex
and has a history, but I would like to ask people. When you say own types are great for collections, that
can mean two things, you can use them as collections for JSON collections,
that's one thing. >> Absolutely. That's fine. >> That's the only way,
as Arthur said you cannot do JSON it any other way. Again, if nine, we'll fix
this, we'll make this work. But there's also the maybe
lesser known option of doing owned types for normal collections which are not JSON mapped
to a different table. Anybody who's using it I'd really
like them to think and then tell me what exactly it is that
they're getting out of it, as opposed to not
using own collection. It could just be a normal
collection so I'm just interested genuinely
asking, and interested. What does that owed give you? It does give you, for example, the fact that it's automatically
load it when you do. >> That why I think
most people use it. >> Absolutely. But we also have
something called auto include. We have a feature that
does the same thing and so this own concept again, which has a long history and a lot of hidden
discussions around it. There's this question of
what it really is good for modeling and I don't think, by the way, that we should kill
the own concept in general. Everywhere it does have its uses. But this is not an easy question to answer
let's say it like this, It's a very complex topic. >> Indeed. I think
that's all we had on the querying side so let's
show this really quickly. You can access, track
complex properties. In this case, we've queried
for customer with number, so we can do context
thoughts entry here. Let's just take first one, there is one and this is going to give me an entity
entry for the tract entry. I can then go into this and I can use complex property in the same
way I can use property to say, let me manipulate something
on this complex property. Contact being the context property and I can now drill in further so I could say drill down and
go to the mobile phone. Then I could just
say find a property on that so let's find
the country code. Country code and now I can do all the normal things I
can do on here so I could say, I could mark the country
code as modified. Then if I call context save changes here that yes, now when we run this, we should see that it's going
to mark the country code as modified and it will save
that to the database. All of this the normal API here that you can expect to change the current
value, original values. That all works as you
would expect and indeed, we have updated the country code because we marked it as
modified as we'd expect. It's a little interesting as well in that remember how
these are immutable? Well, they're not really. For example, let's say if I go on to address here and go to line one. I can now say value equals hello and this will actually work
and it will actually mutate it. We are using M-class struck here, even though they're immutable from
a perspective of the language, when you actually go and
mess with their internals, you can still mutate them. EF does that as it
normally does when dealing with these things because
it's writing to and from fields and all of that stuff, not saying that a
recommended way of doing it. But it does mean that you
can in many ways treat complex types similarly to entity types in auditing
and things like that. Where you can just read
the current values, compare with you do updates, whatever you need to do there.. >> I think we should talk about current limitations and then I think that's about all we
have. Right Shay? >> Yes. >> I'm looking here on
the what's new here. We've listed out here some of the important current
limitation of complex types, which are basically because we just didn't get time to do
it in this release. Some of these things are things
that we intend to support. Let me just go through
each one of them. Support collections
of complex types, we talked about that,
so that's important. One of the things we
haven't mentioned is that complex type properties have
to be non null for now, we don't allow you
to have an optional. This is a little bit like the optional versus
required owned entities. We will fix this past eight and it's a difficult area to get
right from a query perspective, because things that are not null, then not appearing on one
side of a query can end up null and then you
end up trying to deal with a not null thing,
with a null thing, for a non null thing,
and vice versa and so the whole nullability thing
inquiry is complicated, no pun intended again, but we'll get to it. I don't know if you want to
say any more about that Shay. It's more your area
than mine, I think. >> Specifically, nullability. Sorry. >> Yeah. No, that's fine. >> Yeah. There's a whole complex thing about what happens when a complex
type is optional or nullable. For example, just to give people like a nice riddle to think about, how do we represent
that in the database? If something is optional and all its properties
are optional as well, then what is our way
of distinguishing whether the complex type is there, but all its properties are null. >> Exactly. >> Or it's not there at all? That's quite a riddle. That's also something we've struggled with in the
past with own entities. I'm not sure we ended
up in the best place in the world and I hope
we'll do this correctly, but it gives you an idea
of the type of problems. Again, distinguish between
null and an empty instance. >> Yeah. >> Basically, it's a big problem. We need some other column in the database just to tell us
whether that thing is there or not. That gives you a taste of the problems that you have to
deal with if you want to do this. For now, we just don't
support it, unfortunately. >> There are issues for
each of these things. Go vote on the issues. Thumbs up on the issue if
it's important to you. I know a lot of people are
going to yell at us about the null stuff because they did
before about [inaudible] types, but it's really is difficult. JSON columns, we talked about, we want to support mapping
them in JSON columns. Constructor injection,
I also mentioned, so you should be able to create an instance of a phone number type, a constructor that takes a
phone number type and we should be able to call that constructor
when we create the instance. If you're using C data, think carefully about
whether it's a good idea, but if you are using it then that's not supported currently
in complex types yet either. We don't support complex
type properties in Cosmos, but we definitely intend to do
that and probably make them the default for the adjacent
documents in Cosmos. But that's not done yet and they're not implemented
in memory provider. Well, parts of them are, the part for model building
and things like that, that we needed them to be
in the memory provider. Those queries do not work. If you try and run a query
against complex type in memory database, it won't work. This we're very
unlikely to implement because we're not
really promoting the in memory provider as something that people should be using
for their own work as opposed to us using it when we're developing EF core for
which is very useful. But there's an issue for it, so go vote for it if you want. I doubt it will change
anything in this case, but it's always good
to get the feedback. I think that's everything we had. Just very quickly. Also new in the EF Core 8
that you might want to try. Primitive collections, we had
a stand up on that before, although things have
changed quite a lot. We've made a lot of improvements there and I think it's
working pretty well now. That is, primitive collections
mapped into adjacent column, and primitive collections in
embedded in adjacent document. >> Even non primitive collections
in adjacent column. What's that? Exactly. If you have
a JSON document, there own entities and you
have a collection in there, you can now query them. You can add arbitrary, super cool. Everything is queryable. >> Adjacent column,
they work on SQL light. I think there's some
other stuff in there. We've got hierarchy ID, we just stand up on that. Unmapped types, you can write raw SQL queries for unmapped
types in more cases now. Lazy loading for
disconnected entities and lazy loading can be switched on or off per property and maybe
a couple of other things there. Changes some improvements
to the API for tracking entities and how you
can look things up by key. Easier, for example, some model building and then a whole bunch
of new math translations. This isn't finished yet, by the way, we're still working
on adding to this thing. I think price has a PR
already yesterday for that. More documentation is coming. But grab the daily build, give it a try, give
us your feedback. Because if you report a serious
book in the next couple of weeks, probably then we can fix it and it will be in the
GA release after that, December at the earliest is
when you're going to get it. It could end up being February
like it was last year. It's really important to try it now and give us the feedback
as soon as you can. Try the daily bills if you can really helps us if you're
on the latest daily bill. Otherwise, the first thing
we have to do when you file a book is to see whether it
repos on the daily bill. If you file it on RC1, for example. I think that's it.
Anything else, Shay? >> I don't think so. There's been, again,
a ton of engagement, which really is great. I think this has been
talking to a lot of people. This feature, which is
really nice to see. I think a lot of people dealt
with the problems around this, so it's nice to see. But yeah, I'm actually super
excited about this release. We're delivering
quite a lot of stuff. We just have to document it all
so that people are aware of it. It's a pre cool release. >> Yeah. Well, we will let you-all go then and hopefully it won't be three
months before the next one. We will see you-all again soon. Of course, I have to
find the thing to say. Here we go. Thanks everyone. Bye. [MUSIC]