From 41455aeec24a17ebb5d47def115311729f5fad51 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Wed, 6 Jan 2021 09:06:46 -0800 Subject: [PATCH] Change tracker docs (#2972) Fixes #2725 Fixes #2707 Fixes #2525 Fixes #2524 Fixes #2331 Fixes #2105 Fixes #2076 Fixes #1541 Fixes #1489 Fixes #1453 Fixes #1398 Fixes #1339 Fixes #1235 Fixes #1007 Fixes #863 Fixes #815 Fixes #786 Fixes #531 Fixes #505 --- .../change-tracking/_static/debug-view.png | Bin 0 -> 290351 bytes .../core/change-tracking/change-detection.md | 423 ++++++ .../core/change-tracking/debug-views.md | 327 +++++ .../core/change-tracking/entity-entries.md | 598 +++++++++ .../core/change-tracking/entity-state.md.stub | 0 .../core/change-tracking/explicit-tracking.md | 990 ++++++++++++++ .../find-local-queries.md.stub | 0 .../change-tracking/identity-resolution.md | 665 ++++++++++ .../core/change-tracking/index.md | 271 ++++ .../core/change-tracking/index.md.stub | 0 .../core/change-tracking/miscellaneous.md | 509 +++++++ .../notification-entities.md.stub | 0 .../change-tracking/property-changes.md.stub | 0 .../core/change-tracking/proxies.md.stub | 0 .../change-tracking/relationship-changes.md | 1181 +++++++++++++++++ .../relationship-changes.md.stub | 0 entity-framework/toc.yml | 27 +- .../AccessingTrackedEntities.csproj | 14 + .../AccessingTrackedEntities/Program.cs | 30 + .../AccessingTrackedEntities/Samples.cs | 634 +++++++++ .../AdditionalChangeTrackingFeatures.csproj | 14 + .../DefaultValueSamples.cs | 272 ++++ .../Program.cs | 22 + .../Samples.cs | 261 ++++ .../ChangeDetectionAndNotifications.csproj | 15 + .../ChangeTrackingProxiesSamples.cs | 155 +++ .../NotificationEntitiesSamples.cs | 233 ++++ .../NotificationWithBaseSamples.cs | 210 +++ .../Program.cs | 22 + .../SnapshotSamples.cs | 207 +++ .../ChangeTrackerDebugging.csproj | 14 + .../ChangeTrackerDebugging/Program.cs | 13 + .../ChangeTrackerDebugging/Samples.cs | 252 ++++ .../ChangeTrackingInEFCore.csproj | 14 + .../ExplicitKeysRequiredSamples.cs | 138 ++ .../ExplicitKeysSamples.cs | 461 +++++++ .../GeneratedKeysSamples.cs | 386 ++++++ .../ChangeTrackingInEFCore/Program.cs | 37 + .../ChangingFKsAndNavigations.csproj | 14 + .../ExplicitJoinEntityAndSkipsSamples.cs | 225 ++++ .../ExplicitJoinEntitySamples.cs | 195 +++ .../ExplicitJoinEntityWithPayloadSamples.cs | 178 +++ ...licitJoinEntityWithStringPayloadSamples.cs | 230 ++++ .../OptionalRelationshipsSamples.cs | 437 ++++++ .../ChangingFKsAndNavigations/Program.cs | 46 + .../RequiredRelationshipsSamples.cs | 291 ++++ .../IdentityResolutionInEFCore.csproj | 15 + .../IdentityResolutionSamples.cs | 367 +++++ .../IdentityResolutionInEFCore/Program.cs | 28 + .../ReferenceEqualityComparer.cs | 17 + .../SerializedGraphExamples.cs | 337 +++++ samples/core/Samples.sln | 51 + 52 files changed, 10817 insertions(+), 9 deletions(-) create mode 100644 entity-framework/core/change-tracking/_static/debug-view.png create mode 100644 entity-framework/core/change-tracking/change-detection.md create mode 100644 entity-framework/core/change-tracking/debug-views.md create mode 100644 entity-framework/core/change-tracking/entity-entries.md delete mode 100644 entity-framework/core/change-tracking/entity-state.md.stub create mode 100644 entity-framework/core/change-tracking/explicit-tracking.md delete mode 100644 entity-framework/core/change-tracking/find-local-queries.md.stub create mode 100644 entity-framework/core/change-tracking/identity-resolution.md create mode 100644 entity-framework/core/change-tracking/index.md delete mode 100644 entity-framework/core/change-tracking/index.md.stub create mode 100644 entity-framework/core/change-tracking/miscellaneous.md delete mode 100644 entity-framework/core/change-tracking/notification-entities.md.stub delete mode 100644 entity-framework/core/change-tracking/property-changes.md.stub delete mode 100644 entity-framework/core/change-tracking/proxies.md.stub create mode 100644 entity-framework/core/change-tracking/relationship-changes.md delete mode 100644 entity-framework/core/change-tracking/relationship-changes.md.stub create mode 100644 samples/core/ChangeTracking/AccessingTrackedEntities/AccessingTrackedEntities.csproj create mode 100644 samples/core/ChangeTracking/AccessingTrackedEntities/Program.cs create mode 100644 samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs create mode 100644 samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/AdditionalChangeTrackingFeatures.csproj create mode 100644 samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs create mode 100644 samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/Program.cs create mode 100644 samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/Samples.cs create mode 100644 samples/core/ChangeTracking/ChangeDetectionAndNotifications/ChangeDetectionAndNotifications.csproj create mode 100644 samples/core/ChangeTracking/ChangeDetectionAndNotifications/ChangeTrackingProxiesSamples.cs create mode 100644 samples/core/ChangeTracking/ChangeDetectionAndNotifications/NotificationEntitiesSamples.cs create mode 100644 samples/core/ChangeTracking/ChangeDetectionAndNotifications/NotificationWithBaseSamples.cs create mode 100644 samples/core/ChangeTracking/ChangeDetectionAndNotifications/Program.cs create mode 100644 samples/core/ChangeTracking/ChangeDetectionAndNotifications/SnapshotSamples.cs create mode 100644 samples/core/ChangeTracking/ChangeTrackerDebugging/ChangeTrackerDebugging.csproj create mode 100644 samples/core/ChangeTracking/ChangeTrackerDebugging/Program.cs create mode 100644 samples/core/ChangeTracking/ChangeTrackerDebugging/Samples.cs create mode 100644 samples/core/ChangeTracking/ChangeTrackingInEFCore/ChangeTrackingInEFCore.csproj create mode 100644 samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysRequiredSamples.cs create mode 100644 samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysSamples.cs create mode 100644 samples/core/ChangeTracking/ChangeTrackingInEFCore/GeneratedKeysSamples.cs create mode 100644 samples/core/ChangeTracking/ChangeTrackingInEFCore/Program.cs create mode 100644 samples/core/ChangeTracking/ChangingFKsAndNavigations/ChangingFKsAndNavigations.csproj create mode 100644 samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityAndSkipsSamples.cs create mode 100644 samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntitySamples.cs create mode 100644 samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityWithPayloadSamples.cs create mode 100644 samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityWithStringPayloadSamples.cs create mode 100644 samples/core/ChangeTracking/ChangingFKsAndNavigations/OptionalRelationshipsSamples.cs create mode 100644 samples/core/ChangeTracking/ChangingFKsAndNavigations/Program.cs create mode 100644 samples/core/ChangeTracking/ChangingFKsAndNavigations/RequiredRelationshipsSamples.cs create mode 100644 samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionInEFCore.csproj create mode 100644 samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionSamples.cs create mode 100644 samples/core/ChangeTracking/IdentityResolutionInEFCore/Program.cs create mode 100644 samples/core/ChangeTracking/IdentityResolutionInEFCore/ReferenceEqualityComparer.cs create mode 100644 samples/core/ChangeTracking/IdentityResolutionInEFCore/SerializedGraphExamples.cs diff --git a/entity-framework/core/change-tracking/_static/debug-view.png b/entity-framework/core/change-tracking/_static/debug-view.png new file mode 100644 index 0000000000000000000000000000000000000000..9170eac0d4b4fd71a2d1bc5ebf74e45627fd15ab GIT binary patch literal 290351 zcmce-cT|&2_b!a0AR;27^r9d|q)4v;3xY@&0qN39=tv8Y*l1Fs^p1k`5&`KYAYDqN zcLIdoLVyrjk}vo?&s)B=&N{#I$2qfBR&w8&+?m<4XYYOOeNDKImMYC?!6P+z4vylce=LL&1)qC-G~h6r%D4KGn4O#`fPfv zAH5-eGgI%I{mmv41f_QCsB??zBp>T-ok?v!={C$h+Lmo5AL9zdF<8g(5QBG@@3pUh zcGuk$!}-uj!DxW<70-II>eBifA}K@h5U+`=ru0peyCtvZV^rNIqKyNMBIoMoxHTX| za9K&`0v@OI`*8?Tx~YuJExz%sg~AE-CfL53V;$Y$C~ej8f!Pm?+sijG32nV9a|LJW zP7B`64%j)gh74Q#%MevgOAnl}g%jY2dIVx27ik`WA>v(3@p~npaeiq>j%~-n zw%lNg$Cj=!;WfJ%^6nUQ-+5{zkX&G^taH($u}wBTRQKckki*1 znTj-SaJ3R=11a;e06S_hZ_D z$8QYH{q^Bo|FoG~ZW4XtO|#W-@4773r{J%V@1nQl~J-^01BD z1RE>hXm^GCaA@>TCCFl?==OIHKW}uApAW7g$^7Q9HP7A+jzD)t0iavk5$F=#O@`#8 z7$`SX;XI?+zU&MA^oaicJj{0e$<72qtFy{wYG8n_H3JZ^Z^3jGu&1*ewk4rEP_1>~| z$rHcetzVp+X}&$>2BW9Z1#P32l?!XOIS(ECBT`9)o8; zQ2J&>O}1x_Jbr#8$RIE5C!(nIMQP8MU?p0bq-v$H^17Hx3X0h0Oc^Rm4Z8<922Oh| z-RarCXf*Uoj(rT-J?WT$aOrvFOVEUOpi`ROphT}SN_Tre&Wp+mVWrf3Yx&HM=ufvZ z{F4@69t4!8Iao+{OKK$(-h2$#aMn!Hi53BS2rKS>w%(Z{nn(3(YJY9qf|vQC>ucuT z?S1#(GjJaN{ng3Kh#hi@2~UBm{ir5VKo!bW;CrK^2f)+aMA}tf?ucoMP);xty@~Qa z?S0%6PinuzS)a|r7woQ$InI zl9f*GR2gk@q65@!C9Q{)uC3atPwh6!dkAJ`s}|L+Q5&~Idl1wUO28 zC3`87*~V()ry64iy$RC%#6_VNrPhEXHXR4zkp>j`dFcf zxOsGGI~6UMI+J(`XMyHuTRz)@4Lx(`>H5|`raR$p7$`^08F&{n0T?SpG`+$L4$Wvw z-afq8@Z*KZ$_n~X-pfwqcik^T3e3!V=Bf^sA+0NnAGbc3`MG~(vyj$Is&KLLIA#3( z8sMI13+l88q;xIU((WIt0uw)0(U-QjcWxjG9J*Lc>@$NHuF9?6QXq`S`eE7=em_POqHLIDdcsS?gLBzv< zQgYtVqAyLB+5G<4qwBWDzm=87z@S@b;)>;AiccglL2G;(Kq(({kaJO^X}P|}=2YF! zDt&-2v08PfQQl`RrXKqmesTKt$yD8rT_7+}K4H4iwoO9S4;ch+`*>5Svj{eMqWc-W zu^*m&Mv4_pyP>V}s*rd7Cs4%*>p!iTz{ z!V@#X_^R&gp@*uxdA2ns6y}vpWpU15_Jm2s%LSD5{eWQ@P(!ch#Mllr%#mW=`uR1vK{i+AgUARrJE#`>r zV0Bx_)K*69Tm0$+zhFaSU^0lltcR;3O~T0k2Qf z2M+w;cbcHlXSPjyTkY!dSakuea}5WR8;5u_XUpwSwvULkle=ty1Act6N8rg4eoLSQ zD?+^flSn`#zk~6hQxqq$Ig_xT7#Z_?stN$jB)q<84w+fqL)Wg5#}W_Rvdx$lOTG1jr;wusSNd7=rg6%R7bFLSQ+!u1a*m^h*S}9#shlKD zz(hX5ph>Z;gdsDEZ`eR?+0f5s>pc*Lr|{YIqZ%{w1z43u9moEZTAsPi=G7(Y=SB<& za}8fuGt;&vx$gKmIqPZW!n;2KCkJ0{kG>)8Y11hCo7afli=$N~2`S2sqChD#pc|pz zj?ui_-mH9|-<9yQI4< z0UM+=Q=A$eaDg@RM?cIRyT5;nEh7|8z`B8+&Ijx*RqKG1??%h4UOGLBS3>2PDd|&{ z(jG@Oxy}aSG^fik49=TVtIwR(R5?8*v{(aJq@2UnZZp3Opib~vpVT-93+e4`(-C%V zMZlzvKN?TUl6;s^ta<`MD(DKMG(o25f?-~clE4P{KwM@L0&<$lvTPpmddV}@#;Rsn zy;XH|E{3X9)twZyEa!K5SR3s0(J4!Zw`LJF{88@(r>C z;qt}43uD@K;CFSE-s8V3&Y{6`rJ5eUliu&F(JMwF0vE*(CwAlg&;Z|((tU-GsfmE# zx4$ZM3qqNy=2u))DuQvQj%tNz5N;L`MGiBUqVGI2L9T-e(>AFcvR3AG$pFlR)r$Q& zj1dguvqf*k98Ekg9N`-$NoQzIyqNZ?9_%K#fnh|xZLhjosTk27;msv5A@+?FDv5fT zWb~Nf{5w7KHgu%zxnfPqX@&m@h`D}g%u6L;eoqg8**ATlEL9uOm=Bh=o~eE#Zzl(I>CU~%xWG1Y zs?RdSeEawWSH#<3qa+z@v(OeRx)K9v&p;i_&CiB@$6`wazDmvwG$a+5cnr6w&KE<| zgV)0)4^fkRJ4F^%1-!pv$!h2am3xIu2kqU7>}_gk9;1DM81%25L{7_anjs2#O0KXE!& zy)!sHCF(syb4V-t`7p_e9Wm;5=Oi#Nu?1>e^~~8bI&oRbLj+gd?n(SzHSLsEkp9vS z`^(QtqufOa&>J8>{Wij)9IATc+q!@*LN3s%1+^)R{Cx+GAwua=DohvhSySND|2Xic;~26G$rS$Tf<3defJ}-Uzzi2 zuFTPZ113|y`IWZQw+{Q34TR$jXHRI@KW+D2cgcC3u5O{VFz)E)-emEeH7})Ld?@6N z;Vk5$JZ`(K{sh@x7RnX@Wj_@S+;4_j;nZQ-TW>9__m+hUfX;Nf{WSKT>c?HG(Jf6u zoUafW)omV8;X?n`Bd91z-8#9BD|<-pG&e*2*&C8KqFzrM0Sx%| zy$f7X!KhDAEu@1C(AE+oH7^D>b`V&!ODD zJzdBwP>X%p2?E$SbmwAcgK%cv8nHbO|FMe7akRy?`ec_Iml^&gVmdnAuTSM`u?JHD znAw2M$9FkNk?borpYg96uK&@AF6WbvXT&bF``?q{;>ubaeD+_R=x)Bdj@S6t-;qAN z*ohgP{@47^J_y_?uKkytKI_U&5&LhrLAsW(y8nN_!v!rReB?R&r0wQ7YX9E92beYN zg9huvcRPUyQpETFAE}D5WVM{4R}qN!-@n*>jy=Q;d+|zE4>Bi)b%je-|D#W5?+$IR zJR|<|hRPM*|F<1NCZKkiVQ+ct0~ALg*c%u2{Yb0wp}XW>>%tqjyMh2V;9qmedW2i9 zKN<0AtutH45DABcpiFA4cxOAk?XI_2a-{rVTENV+AJc>`Kvt< zCV{CYm`GRKsJ**ckHEA-0ONK+?Rm+BPJlKo*#faa4#(=?=-6zeJvOy$=7kWq8eQ&f z7n6eh^j=x7R*v(vRy-bnKG_E*KE5ie0l+1I4FCbMmV+KOh5O{EJ~OTg*`YLf6^b4T zP425xFEMQ<_pgrBdabRglD{Q04>}@hoIBj2y$Mr<7%mYrvRmIeB_Fe5X`p_asyFUa(9857HIhZR4{D zWJR9td(>>b!xS>aIA6uo1I@(CZQ{a0MDX_5Y0vPU&yW!^XYL7s=GWIiS)>1V=6wPfOSt zla3l&TA8!3*Z7%{$I%wsAI$bGn{cGi%ZBrevF+`x@eq3}`-X&T5lmk^&SuSRDHChL zEVKGXl$5^-7_Wd?(2D*~#qmdIU9bJ*Y-n<6EYgdeq;o(|#Vq5fAAg>X;g~o!P-wC3 z-%HIra!@Gqo>^y%qO16{v1o9VW6`;k=iCn92rqHA$(TO2PvcZ%Mfm`tkP68*^=w#e z8OVn_)g6&k`4?IUcoj}J-m@jLymN*2ELGTaQ@S%6pat*^TsFXNJ~^nZToXqx6h)9C z5>Qg&z4nK5_OV#jCCyB@>=jMwd+sx$$0a{%8Q(6$BN#)!>IPQ*-V=YHf+&N%avHk& z+Bno`W$Q52naxLsY8I*V2G`d5@qeTXe{0Np3@ zPHb(zw=%DAjMe;*(vILvO#!!~>i6z#KYjrq0I&@KxT1Ag=Do=ZVoiBw5k>E8<#|aZ zkSVRY3e?TT1+#YZQUqMGda8D;q-_Mn(V>YX+yo7u=r7XxPZuS zx&8^+D?&y?8bV&{wql1+L!UL5nxkTS=N<<^`tLw*KvNI|#*tDhYTfY#4k05aKe%)M zOwV=lTSLDfO03I3;aW06pv&j*hwIC4BsfNX#Pr9gkzA? z`j<>#Q#~usD1tZ9a^s0g0#cK=HEpj-!|j@)9_T^`6*QNC? zZKKPPE|DERdqtyIh9w>pHgh{kfx(a8F1^|o^nPc#p7T{^qg~q1{}ZFq;^tS?dm&N0 z|-_aLww7m5^xqI0u_uPlp6WiN_fA$H@stIeG_w4t+DboOT)e=E;9b2GF z(#ce!PkM(AW_7@8v@xSxJ;NtxIP~o~95m!i^Us9hny~Xy+3jxYqLPpG;N$Hoy(J_8 zdN_3??b?chr+OtNIdeQV(hDo*Z>qQZ8)a4y@59 z2Ttz)1H1Zfl@Vjlh{~Pd>xfl+zZLsSnW;WJ>sBtGu8IN#d7#U9AHaup>C?+1SGtU~lcm*H@6c;V zBM3=;STO?;!qsia!>gxU;6r@G&aNlS7HiIHosrq%Eo693ZM4i(THOyZWIyjDYGEWV zh~Jv``TdZx034AP#PYYzJiI2Mz6#qL@x|V=JPmb!_L00s!xSr0(99^PNe}j}Ap9Ph z$_K`v*djMdWm(D9+qeUZ34&hs(ef8i(J~L<>i=6;^KX&JOn+M2aqe0~KnaIV3UqO_ z;*h3`;@||oQ*e)o-w`KzDx|xU8ipQ%d}g0OB#C)I2s# z`rLy(yBXx@4D8j^qSzHdHuO-WrDrD_i0mrhw;49^|HQgJ&TXyWiB+=W;^&lV*rA<5 zEdkI)pS`?OfOM-%leBe;TP|8ou8O z6#zg{(nS^>%{3k}4{1F-1c$yHup13+WZ1u{C?!QExrv*5UUP3LzkX}OPyJaKm7hBb zB$aXI{k?u)La4ae8-jkKwufwonn>T~pQUU#ZX-?D1a`z`WrT60XFW>d8&%Vn8)ts^<95f=lH0&R6FCx zuJcbFY09u^DRqbEkb9}rSBQKZ_x2nTe!C@unOZjA)LsEG`<6V2Qsp#>nGpQSLY#K{ zva~KNYsHV6(UZ>&L(aUO+d?4&Nf~BU%r-S2j24$mvik`d z^x))Y+Ziz1Swq(|nTLM6DX>T6ADRIBe&5qRxxX{XS_AzLq{DL2=H0VOXC97%82#kP zR9^u6XEoZN0%a%tW$8eq&dEJJV8*59#noX=rVRZfW=T^yoSdqXq0!NmQs5Cq{|I`a zA5T%LNIhP849Cjd#e%YdFOBGR7rfAu`khwT7%|gtNHtpg^4;F; z>mnV{&IF+1N41Vu&H)XqN-|YH4fJ|Qg85^|oHsB(eVhKhCaof4lGP#4`hl+PYb;u& zHu0shJTUi-l;_L*|+XLh)73pYb{~ID^(Q%igkRYLiOz}DOzXjhOO~O?U`Da6rxR5l>m1X7myArW# z*t*eJ-#-ld132=poqhe7CwnnL;rBJVDBe&`f+gO`{ZIFwi+^EWe@hY`3%PtITQySm zIHgW@d#L!wnIaei!lPNkmB!F>?F%rIfk;96*;x~~Pm=tA&fNjT*-#`RXTc{zSSIOh zKkTi4Cg*mB6XPxbUFnvfMqV3H(jVoUWY+mnGBJhA2^!97^n3>SD`xdRg)5KR&L&1` zi@|^9^PPmm2QEq7p9_He{B-Y|?d^XanYGYP>iJ^$s?w=@Z}J{2Nd{vRmzao}@kO>Q z_nBaZ^|Y<=$21^8NU)h#k4#`s+Vfl2bYP(oq|y?DrC(yO%GJ^7@*dT`5)_m(v;xsz z>NA8S9I#cnK_0>@)b+hK_A=p5;hwRbb3!In>%J3~eSeUB)=!BAC)Z$>ftyt48~gGz zDz40c+#g-iG{f=(OFPnXQnB~B1TK&yqC0S$2svs1b$v@ntOOF@oYghp_$?zqTZwzD z2|54WySi#TDA7PFVevAa6%kH69&g1@ZyLuEYvObC)U_Mcmh}YL63&;AANp+tJ7%GJ z$5VXPe}x^;Camwdkr4o12+%m5L4)}?XxB)i`wK+p-;Ww!Ot=>D!pibR;|~(Dð3 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 Dy(V(2Fh-!?kr01Pmy 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�AcC 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Ν;StX(bC7uk=0`5@_j9!lD!qw?3~r-=}_t?4T2F1Ozhlsn1m2?3ArjC0D4= zdSpdJ5_Ds={AE-(&7Q;GOfEY1xx~Ml494r~K)7U%w?5; z;aqQ|*Mpuz*RNsb6^f#{yUTJfNfGT1!GOwXA;ip%n1fd zlM3RWTE(~DMV2I$*cw)&h4|)$FF9}!mEK>l zQTd*D3~EzUR+e_i1);W5VTk`3m?V8FNl5}t|9ZR*7k&h~)`Fv>;diBIqD8cwZ$xB6 zOUK7$H$BH1AG@_4zzq%M$dIe$@Ra?K0)v*}>4x_304d{tn&AuWGygWj>kNxezj)z! zNj<2DYyenAIT#)bNRSSQQd}DPn)%CCje=Da_n0SU31MV8=1de>>xZU-gm^(w_L?(c zG4^+|iChYgyK!T(;x3n*ssVX)EWgc}5hw#j(pZe*F)#j8^kS<3deMtSj6iAuC1(#f zLcXm72}#{K)}Vu4?W7ve2piENn-#b!T%3=^JP#H6tXN`LCF$ofHQE<@dV!pBu&qv3eM3c?+%9<{UWIajo!||2a zb4|6X<}WI7kN>~<|(2y_QR&kZdR+wM@11{WuWN9(*z!cs8~R5EoE^E$K+6xX_xjhmrBxGU4(?w z(LXkMo4;)GIcd{s$?JmpNUD3^`yF>Z%P<7Lwap7-6ct?z)1BDnnqI+saJ-;GLwugt zuml^4OAS7Ihpct|6`^vUgD4KuHK8a;tSkIno)w{n> zzIG(waHG5oei;HJ1W0@A$vdxyGf!zc{MAJ?7`P_KcxhDOc7`I9xRiRblxFTM&i*6mAbF+F8TiY1fvJZ7B;E zxvt65bLZ`I%_}Gw#1poh5Mb>iGb7pGmK##(I%)@kXb>D!g5`Q+d<(7-*L=b!3m^O` zI(z|0g&wQycBc7-SaDRdfm=3%E=H3RNUJFMAP5oevea92!ZY0naH`B))P-u?I6hl|f)5&-qPq&XStN5w(=SYqqRBd%DV6YV0*`$Of z)M{SurjP(qEd1B!y5#a74N(ybxLyx7L!&EkJ>37mI;SKWe+yZ)9Tud; z$Cw$YU0(lc0A`E_@w^lc1~fA=ucGB$plM8if7Kz4jTCVMMQ-QHguHROnpyJ%M^dyb z6g1|>3Z3MdMTzMCqK^RUOr>vD1qrK56%O7By^#!D(j z5`28g8c&wHVU~B9t-h(0>E6i);}HxmP%^+2&u=>G&#$9cl13S98%h%&nH6C*@1RZv zl-{H_ETB)rcFUZ17JjIXFL2qY)#5)r0s=uC-1)+c3|BpRXc1#_b1M)WmKLU9b5ZNZ zdSl!-6O=5gUSC9mWFRzByRNZHmR=YS;FS~)MP6S7+f4W(Dkl+s{zp#o*RsYw2*P6W zx*!=W;yhhv5oy!BOF47+e~U=2^!aB*@{qlS()*U6U$l7jY_QY97OGFh^&{D$>sR}tNWc=*L0;i0<*c6jSB1^?)Sfi^ez=$ ztiP4?1lE_juo^%AHSKAPkj~5Bsvi1%XkjDpJF0~gMuVl(KD$Z>xnwFSOYUSZuQeY6Il$qsSLqi0@)-EMb<>{!}m?CE_rE-1ES;3q4U@P zk$RK)Bqgd`ii)_6-o1CVO^`0H*-lhf zDOcA>8(tU+o5%kXLqX$&4@7)_2nDGEh)T*#S@(jmZZcKQ?CUBjgxQul_`^qZ+Q|=k z)?Qt%jsAsx;d2xk1BBx%b0qo_woG+Be=EvxyBG+Rau>r-jtoWrbXy1ALrhoR5OLV+ z#!u0zR!)bSG4~+I?Lj+KPXCb!CE)&Ff=s3_xX4~6gdHEcqm!`Io=#g#C0Sn5O|L4e za>a~kYt${su_APW)`$-m`{)b!g4$g8A}N8HjROaLU?C^$?W_$)&E1~L2Y?cP+m-t# zL!JgqWt_HNB5VAETw%?PKxBWdhr?y$(+;c+&;?h_nfj-VVm=ByF>^;3V%(9z*V{tD z>vyR1WRFfj<=lkByZrjc4?uQ7FS0|Js%8x~VAu!4GaEkvwMD|bSIFuw;Ylz6itkq zqoWpQ@Z2OM?kPf{kfF=7=0FW5o}=4SIvCAY;TkEKd4Oiiu0GlvY=#Ows<$NZ64`;F zQazL|5qUjx#Rt7?AAep+(_LgX;vd|1#kGnueayhjW=eS7+?p5aPvrq&JnZc4lp&M- z+WwtSdFo4ss-TCathrE)0q35f*N&WN?P`)6{8D0S(wt}rtq1#;-5AQP1?vl4qVKH3 z_M3I|h8aLYJQN)7V6}j4`DMpwbl`DCUBTm{<+)#<1!>o zcL4HK^y@u;R0KwO>&JEOr&u6EKcn|oz1RKu4UqjC`cin8unrVC=H3x~$rafPOzL(u z4|PoM(@+sT0=mo&0g zD(T{qvWMJ!E#Ntqt!;uiP!s)W&q-8>$e80lmrK31 zOqQD$H!n_5e^UcUwG||CF#A{wjAPeZwaZ@Z&yCOupqUBhx{RW849|<%wH2${Uywau z;cz&2PFnIA$!byJ*WVbMP|RjSC{uW(u*)&8bFPFKi^|9uK)GIu}AloaY17O+PVgZEc+#RC|TR61RtL zXFN-zp|O<6fvfJOzj(~X$8H^FJMIkayVYc_UB8376&kaIkGR+l8unnYK|wI;kN*xY z!0XUiUQW-1LI-kZrG&$>{+V2$Xv=T1DzQDFwIN7b_;p5*0%k+1Zv&j!y7!X@&*%7ma$1e=fTZ+9jNn@jEf+4 z|HCkw={;Lr+KLHQ?BeglTxXYd&?UdEClWW!q^t`rmtUe5m#=s}v$i@r*NSalVb`}S zyCOs&VKGxR5ApJiEJ|({P1!lTK&`x3VlrP4^kP;D!d~p$*eCsqH}V0FsXOLM+o&MT zR2raNTuMZheoVKEdXd%<98zrNocX5`X~^oz*5g2&GI5R!+ww=*4e{R>o9hMu+n4{JNdmdYIGvYHpLtct*SMRu7!g{x}6<(nN zYGsJ4hqtPwtUxoO%E9HBdKPl2S#xxmKg#E2-@FJL^}YfRX4B{eE6~yY5dnKOj%f0l zIFYZu8=@^01^vkH-PfydJOAvNN^Heqjh7db z?V&R>O2QNra^}~pV$cPJaRpCKi_a^%Ax+}N0 z=%_uni%Oc0IV-Z<%}{6Wnl7 zt0s~~A(V<<0n+np&LrAN23~(2QFR&a3{KAoPQd4+HH(CV3D00*EwpC z&2r7m2wtM>hH`jJO75kLAve1#V>%P_Zpm%sIivL%He#Ixw&nEwS)uw3N~Wq69FD4DEA$5uO@5< zLWw|w>y2)o$|t)y5544X2AJYtg$=qPIem=0T3F9ER25{2lynfN{5PL>>!LDl>Ep=Ilw+O}n@} zY3RW%1G|l`H42L5O2A?=fk4+v0_QCf!Mzd06zFXXdW%uZabxCNGoRh_RSL%b&55zu zafPj-bhNOjusJ5MkCn_Mmg<|XO6T7+5B3Is=-R^>2Ck!E{>nIDHRl)6HI5i0s>Yl_ z;mn^&yx{54f7=;vBk6NyZg%vgt)`@vRpeU7t=grmVpy88;x3m-I%kyhIDs!yh6D-1 zANWB*NZ!*}>vFy|h6QSNBG%~)fL^0E^t@kO>FAHm{{~bDw4H|go&d2Bol8qAoiC!Z zVR2L2Svy3W$)w3vmc6VPRGFVfn#7 ztn;{x&+ra~qRuja(%khJyFnDd+hNE-Xf06nRQf+O^*Lt!K{xBe&I(acvHT!O{JL!l z^42Y{59pJ_8vYzV-!m*>j&-$6ww|h+Ts2vfl(&Qg@Stm;;trq0+oi~xYs;tkuB;Pn zQ2MlRMH<*Gcp+Y!Nfq|6nCljojwBOXm#_G_s6d;|@pfcZZgE`^Qypt{3?EQSM9lZ} zVeH~Gc-~HYOpU-O+2?}JZ&K>K__AENiCvao#zsBR<)En@AFQc`Vl;l~QrPA$QxtJW z8knlNT3b!D+Mt1XDGedX^2I@gb!!!MwoAB2dbO9^dL~*d8cpk$6z;;-mNq_ocPs18 zA~N4iTP{A^2e%lrw8!E~c-rxZ#V)C>4^jL2T|o;VgAKgeVpu~IL>d+mI&ukud1zlu zY_o3+S^PBl(~F0!NvR^Q*geW{V+t`J&;(rwh@h6I>Y)y(c2w6~qkZ3oNfUCiWODeN z9Z8`$LJ|{g*p7f*m*(IRc$w~Y1%%sX{Y0>x7haRniyJiDX z(+C!^@GNki{r1D~fw}q~&M4zj-%uqZU(E1pKK>7r5XxuFx%*oF z6MAsR|(j%65|13PFEgXG6^d%2-BjtZKGa%>#TQ=}*VR33-Sr zghStL@e`H*ah8nzh)(axoI>QSidhgHDVBCG+9jCDP6=wGZq15h-7(N+iZ2P{6PTn0nv!TK8LGjJ{#T58aOEDyF(`dyj zI3->hD1vv`@;h8E^nrF4fw5t?rcBJvtR&mX8D4x(2sje;6Q(mJ$75=m)v5Q4<9EXAMQxhCG}utt4%4bz(H!!F~IcbYTX zK&`%CI^_ESLcHoZubSNe8Tm9F_epn=|*TU)6hFIBBaj)SCD47nP#3 z^%zR%ER!Im$$#iHmqxU|${p^N3#uC5#K8$A8cqG?dnDP`=%c^t&K7u=;Cr!(wBakm z0q;n~#_+!LG}m5RC8*{HqAXdYONT}|{V8@W*k$IMl*NIMIx5iHl!CZ?eztlaTfZR( ziWi~BecqG7=&kzbT(VK6-HzCM*M0R_xqRk0DNV7b@`n%IQFq&@Q7U)ufVahy40P!i z#eZ(Bzz;#uk`LQTM6n;cDg~V#%r%-Fp*5XX;Rb63mmFp_%r|E|u1hED9rm3$D%g)( zVjqv8KlhF^Gqd{FkbjSdk{G4t+s_7d!IUEnq*ddr0jZ6a3*Dl2narF>Ols0?+ap#4w{DO6D27>p;(D(+SAiuOu zI;_sSQ;((#9T9_02uw6@2Uxk;RNvwS!Ya;|5d_3MngB$0Wbc)qK^Qs+U0ixV`f_ zy&mx%t~pFlg-2Mo!ap8<@xjyl+}-H=i6=^yG#{uEG6z;CRhf$EWuYtFnX0*7n9`vCQpZvfkEOP%ESp-XmYaR z<^)W<;Jc%9rN|Bs4ooGal&X?;ZM!o>U3ue&H*So?;g9O({xKUv+Asmr8rElw_l->_ z>F-)72zBtb`n>T+cS78`ag>8^d(2dt`FbVWUK1N{F7u$;og|&3#d{aP&U=r0ZV(I* z1*!5tAE^*NvQ`PgO#9O@PqU7SPFL*0pL5a?L;iGAE~X^Fo21S}gcT9I%0;A$b<VKo`H{W+?Z zxNHggM|!M`mh>AVqXF~saIe?v+#bknP%GX>=~Am2YK@q65$@1Os4EFR_C}mj`-l^g zW4%NYh%}kRsRNx;=xQ!qb~jy@f-OiLCupO)YjM|v9w>PP#9{;qe$n~{2r8TM_zbG@ zxM8&f+BC=lN0}U;;Eu1K(Hn6Jh#(L#(|!CG(b0b3#)F`69H}7qdtkdp_c)taSgs32`&?Tg-LYX93PyX}fg2hvk1ge%%4jGV&l>n!VXznjYCNR<_Z7pqm_7mw z?>(?3GizP`UQseghTVRI6tk6rW2`{siKX*Kv~U*=dy3wE>-EoSjbqRX+gq*M0WVsM zT-uN2UmL(fyOknXXd7YG)L=Ro{2`{!kFj3e@WO9{v4S(1`y!5k=jXI=zJYGYH=vdn zsD8+^JJ+kT;522SKT(&i{ndz4zc^BB@g`jyODRdzS;|gs>{!d3`h=-I?pb_6)BHMB z+yLPUhe2x+-E(R0kQh5+Jp$K<)mbbh$@cX=KO`9Gk=3?4D%MvR7X8f)O=DJPFdVK= z+sk~9=&>82>irLJTbN33l9W)yuG8xq@CnjbTtwN+wbE$p4D5@Kel^*v*`|ySP`%eM zoHc9x=sFIu)3Qw$tf(J%ELK+DbRnL87Jl3Nlj`QEV(`c1@mA;iUolZDBdLq8hZ_AY zZV2|4a1pWC<&#_-5G9Up-rNk~wjKPM!}o;fQv4^UP(+cMnd36q)9{{ zSGE9K`ciBjaZ9^obQpdkhJC(a10_U+P*Evmm(`WIZ6MxOx+!7LdGlcclfspyLr-R5 z4NEQfa5U>?vxiX*C7i=L!MbS{!Ge0Q*<&O%}$$qSk>c+D2bfwh+RN~m&V^#{w z?lU-M1juVJ>wFW)59iDL_1F~lkmjY?^$d2@xQ*ldczJxz{kH4r@gC^t=3h(2R!bgV zO7sQb6AG)?=F5J*(IOZT;vO9x7E8M^BR3^pVKxYUyP4n_j*V)08rK5Y8U_|wh~&KT z5A=OkGyL0gFgHItto!kXzV;c(M;#paCS#8n)JGrs-;L1DopR>0!C&&|jM61cBiuAA zV(XdPuB=|Y_SF|+jSt~vP5jC?dFpEEy=^28HQqxa1508zFg(LNiGKms-ka*&z`~s6 z>zuxva1Q5xQC4$qt0DQl%VOSzeEqPcT7E;DtOWMRbp!nHC-E;wc>f7|pIw}bZ#Z=2 z!s9D0{tIJe5nO%9Ect~{dHhvlpp}n$l%t_+h|LZV%lS8ybsG8mF%;~TE422FUk*+l zAUkQduQE0rww4^j&lc?!?y%F@^b;!s`c8WUBwVHsN_#@wo=g7zz(x)h>tQ2sxnb<= z>~y7BN;EJZ%=}(Td;oP%mkTyuvgeLd!qjyWMNvO0f^se6X;-;UR52?`Pdt;R;ITy) z!oP!9?gPymi`>x3fuIktf=QY2SauX)DiOBr4OHZ$jP^sU&kt@0e6%S62!R_K2e3K24;b&mPO*#}4MgJ9`xz?_rBWu)no4M<&F&O_^i# zb7U}$LGVI{G(fo$J}EmO;YfRR+lSV)-&vcDuC2eUylD zGOL-%H%BuS-^M+E6R%tFg>d?a16=F1rl=`h1fvAi70Zd|JX1MvLh_1W0hm%?@GF1U& zAx@A}*y`Nt!OBsU{+a|1o<^>@=~(bsu{crEZG(1RPIh^o(Uy$a5?H8-z#FnW*v-C1 zEPT=QNV3^_NV#0XW{3}axG=mJ$oO{xm^at?J-=vei)4dDV!#)L`yM0U$>Q0Q5G$VN zXp>SW7lhsk4VXfz_e@>S>#KLw3ueAi6`7fkYgg?%M~-eR7=)nz`vS@+(32BwE5Wrq zb7c%UZ^o|!iAX~oAM@l$f%%m+K>h=bL--HhT<-VVpUjd{27;LTYfbPCQi30o3w|r1VhEznzyFq9D2mm~j3Y%22ErmTRBglJ0G?b& zsUHKBGW=ltQ76ktztZoHxE7$^Z}jh~T*J{^i)bM}yUD-57^cugCYbXysLT|PMi%>>ktzun^zwYn(U&};Lub9uff*4LkPynk_cGgi z{v+L5OzLuNBjjUebenfC(%AVDcf7qZi zHPi&Qt;Xq-4*Y$fM}LqtFCJ2dJhxD+O<BUuIIZrVlzXc-mjW9cSSNJ9bON;8P)5hi`VVXY<6L!rm5uF zAYeR$vJ?k=mu338w4EKX0uavZEs;n9XViOQ+DT&k^bkMJ8K>ML_cm?*FZSLtD6X$v z`wR*0?(Xi^xP%ab2Y0vNEN|I~S(b7tmLz4Ol0 z%$NDrySsK*_pY_qUhBGl*L90TVZd}_l@aHk*sTqtz1Vw+<7cww=7hz~^U)M46R0W7 zGiDU@H>tHDRsg9Bijc^rrG$mHITuUr!3AC#!A`LZ~P zl=GQJqy9`<$3QeTKFSuVC9$=#e@|#p1RqvlT+Tz4Y9ZCr-YMuM=(;@XC`hvPa!lY{ z;qS^gstAgg`v<=DZYOKQfct2zg9?*EgucDfu(s`b0swx~-oS0xPyd-C0l0dvLcDY? zu1g%p1-g_2We?t{5x2uDH3CY~$#KBW)<{aDW+ck1^p6IrcvnTf5EXr`MmJ4KH1S^| zoIG6@aV-DR#QrYEiEfUUkOtzZBAS{2ASUv;DkC-tgn`X?NzBX32FM20zb0B4cc*{O z!6BSWt-bJm_QiQYYTW()dHs66WBdghd$_3&I#wxXOF|wob0Q2c>7kFQrcz-vv5+3x z*W2jk#NywTWAJJ+A4NDVMhp6Z9wsL(!^yg@B*YLijPBNPFTRCh{oocBMQE9?!tdL4 zY!5YOX5Ia_e0LV?=7bQBs#yBZ_3Re%8zbk))oFbs7aoZaE8Iv)k8m?rU#(_Fhfxyv zEcnLUZqbp;I_K%->;u#KrYsHA7y)yJL8Bl9Yzif5H#uRjErCJX>JAJ4hFXK0!@n;_ z-^Y*BW0fELP;&Cb3~Q0<)KobCnMcwH66p*3BYsNe>OkJYerTe;G@A~7aM#72f^W&s zIAr<~Yv1quiX=ZK;nCIO*bO)v30`UUPxSJ<|5<4AkN)`2#s^eb@uep0YS2ip1ov(R z^$wKs12)8&hh>2bn*Z7v{l|D=C(_jIUe|*&L$OP$<0bfs633`1`LwUv1$HzEe}mMH z8G%J*{vvta8x(>ScoyogQJ*BE{a?T0Uq__`+V96I&nOJHY6@brbws^9q@A6){GTs# zBR-$D5yAZHoHs8K5?3@nFmn8AlWp5W77Rrl+-t9n z>sot`hx?O#U`+7utMku2@XbmsSwrB`UfSyDVn#M=^bMQ+{{~ln#?lDvpMdY(0t^k&|Nb|JIt^*Vklan;wIojP3S4SuWS~ROP{IBs0o1yoM@r6dedEa zy3p+th zkr7CdhEj0~(WXl@Y><=PX_C1grEblSsKQk@lV@lCggYf%kvY-Y({P0(ecf|W1 ze;;ln=bandVpFjpwiIhQ$B4Ak-Uw5blwuCvxxdmc!3vpX?Ht+#ckiWIE$gNg7{RLh zrZ2KVuht%MI&EGaTWUVHL5vV=*_m;`Ik6}Rhtk0IdSjngK-6}4WOkNZ8k5n za5H=F?fSYKT|p!Vvk=gR*0Hmk6IB(QN$R#2R)CdAj6ROJu~L{%7efMcz~c8Tq!|b( z6D&Qg77?XyPmj$|5?spyX`S}^Q66iNH8h>4vk5`L85f39q6@NWm5U%M9NbdZU)SEymU4kzTHBur0Q;F^$(0nt?l zGxt|uHBb^vXym1s8~yYz^KWe{Jy3lHD*iF7Q6}6Y3{RZnAIzab>}fx0!uO^z9-#F`{AOJd}}IT zVp4NK`ZCtu$LO)c8Y$`z_H?FkSXvi8{k&h@ev={T{aJq1#FjaIW-xH6frbw^BJZQc zYU6<_p;?M_*Q6y5B7MV>lGAYgc04jYUjyibH=&}!nN2Le!1hQb0GRs9u&WP0ZV8rutJz94Ir@h^Rg);hZ(ewK=2RK48T$gX}|%`2HnC zx|Bp*j%7=urW{LvQv|-QXFHxK$3gKNT3^ed`1;U$aAB_xY}&zBPko=0GbZji+g_toL9a^A^E*P;pe;#``eq&Hz)Hu$VO>#t~0rv09e~_oafsG?LV*A zXmEmKU+(nIn=a`4i89F8;^Xjw`pKDY7&CetmUQ>5 zF7Pd!X!YT{`UT$<*Zd3Z@)*1A{fRJd68WSRsb^>4Yf&EA-D{3FEogpeF66D{Sf8)p zm3-V}e0!rE6YmYn9reI$@D@Euj?lQ>pI{F8tb9Ik3kV^ogE(>P`}#vpZ=<^Rz4mBT z#04|{Y z18_`%IF6MMHUc$r@9{^+#=3>t&xu?f|1?00(x1uD(aG`UeeDL5&xRunCMK@6Mtbl4 z_Edh!tjNXV-HcAUpf>NYuxF*&?@_D$q{#{A6u#Rd!;T$>-Zb)A=0V{dri0`GQTou% zqWL_7T!vj0v`g2t%grWcrrFMK$zXSkR?kJ<9KpYG9=IzqE1EmVTvYO;Ng=}@^JUE| zr1E1HC3eTalNCqLz-NhTvCjEl7wAfxRT}KxI4cWVtIWUF1whQj_WC?jL;}N)Zz|wQ zTJ2(8Z5>NP_d)3~HQ~m27X>kpI{}jVAOb-nNdGP9nA*_i&*9l3W%*&mfiK^qlxW=W z!x@~{>rWcM|ROle~7^_MZ3;#1bI&P!pVyz5LKb3}X(s#MnAvG4ClG1D<;r_5#BK#4PR#i6!SR+(*aU>s?+2gyr50;qVtcAoJ zDe7BPvk9JeBM;LPb|@q`Tmxmv<1!!6T7KkItNAXF9rlCzwRgzAFR1U0L+MuV9I?;M z-;v;aT@US^Il^{#*}i{QUJjad0MnI%hX$^ zkt}WJxDOo|rg8RIwsa6pOHW^WBT@Ll6X(=-A!B_)Cb%(7ap-35M_fkFX-1d(z@5RD zbP6ojPefcT4QR0TbNfCwWGOkWe(|^cE=gIJWT18{>09e59$z+18|jS#8gQZ@?3)vhZuI>rE2Fn6}`v^NLe7*eCL}9xr;|)9rxu-|X)#L5ItJU8?YcuVxvb^qZiNo2>NAkw!xnd*1IqA}4jPM<5*(GKT%4PM$XPaN98g z-St`8Ia}bqCerLHcB57DwMZ7!3AfCQ0lqO)YlI3~uJQweT(w za%P>!6`U_V;R0{!a-p6l zUnK8;*qFMU`3k$NA42wfAj~pd=+%Ys+ND|X%+{w%tcTXjn=T7+YZYkcMLf<(j z>{xYS+|xWzz+*v7YCB|CjG>Ydhwa0tRv?;+zcTCi7Mm{Ny6;%XUh?*(0`~yc@a7-D zLW*$8eC=@f&>%gZr5l$6MH_27cJf(2|93}?(c0Qba*2=aWo+^^;%&&~>P}4HQ@&#= zI+N6s6n(6faqq5g(MZs;A39;@EvP>{5(s(Sr`o$+jCwvb`fR|D3RS=bKxW%Bfk-p{ z21*@aDuZ{>WzX)aAH2V0VnxBs5or*XjPViAEzD^RqMZ3*%Q>2IC7%ERB`Qm|$wM}A zjA*PDATh_=GLeH4awi(H^Ti-efN9PZ$@MAA(_=3^w@JxR83`kX%jwg`4^wMl6uuWl z|1$lHgirQhj+K%LUlpi$t++C56D>%km@maDX#WA`5Rl4cIKt%O!o2D8j&pfmBQzHp z0aVe8>Be)!d0Fu&#Q6Sos+zOs`Xd$npxJcqVK+6>{&S(S6V!C3TAA>-BPN~%K)4Pq zKK&Xg{`jvVRK~|aZLtIjDc6{&`#GdZ)Z`(_e~UVhouO1f~ z80kNZ2QL2Gam|kB`+A^o zTcA2OJi)(u{_&3tyP`oUY_ko64=#6p=L5iArz+jy7-YRBqmMTY z9l&HJRcTl#jQ=JX5)>Li72ed;5fOBk{_vvsQz%`1fHs{k6cZ z5tl<5!XvqUccVt2O+Pi9I3%P)K z=WelzaDgLg=!C92)dODoEf>`KhKO;%!NM}u1Iw_(e?LrF9%Ih)Z>gl*%&+qVa)CLo zp_4;owIRY)GyJxiE4ps)zE#sif>}%cDWylb`fO1z*=4yZ_MQpINZu(@L4J7_ZNEXc zS#q{XC44xTlrSLetcp}~dxvXqB{Z{FIr2=xZsUR7Z&ROu{Lu$%nmV1)Vi1KJx$fg$ z1OPRdjw>NqhFIySP=Xq66U}W9xRk)cw9_4mLi`OI^k*S_yYFyfDY>|5>4^C=9Tqdz z7wy$16v)t#(`&|I)Te}q!*md5fvQy2t(5^Tj_xh>Q*LPa{RNOXX79EzbsEEOJztH~ z=krme!m+n5k?c_~xgOzB>YRD)nK9yVtkKW4NWifWeRM4ICHWOqy(INy4`$F0716XS zIt=`c<*?`&ig-EOBx(%&IEBY|$?o@JpE&A!km+wV5VqE1W?NFl<2#JPdMzNV6%A~R zGgAPuTSLTm8!AvH-_^#F64Dj3$YBAoI21s zZ7LKjTq85i&CT_&`}uyb;9_#MqJBSf@5lS`QCSH2s==7QGyAhL=2*eewe0qpq1e0GL3~gB8YaF`8D{rrKGWPm5FO>nIq}E_bie_7I?95%_;-ECdjFvp;}x? z)oyHMWFF)J_vKioQ0_dUoZ`cMt?V+1(K6=#HQP~w#uKTJShYzFM&b$kx0Bd0gVI!( zuHSkq;&2-o;K=5l#E38Id^PJul0F%4MglQM(V&vs-KNRTj_e}T>$ftLz@CbrNfoFr zs(=Uf!xu|vk)G;4Xnv*;g}fTLq?PL@Ew;c>-M+REsuMT#uDF$JPCShGp=ZYsTK=5@ zbuSZZilYAmnqzbe@Xv@076Iaj(VWlf2U9Rm=c}1P_5hZpLCpD^2DmW`Kp?y z)w%^^>El^z;1K1kW3(34G8Bx!29Ntt-U)^Y=^c23rSRjkb|$f>&BNkoZ8^dhYdn;V z&)=Cczg7pY&&c!Ov@FQjq0?Q9s(`s;&g16fGY3S)2EOs7ovUC6=mFRhIW%VwVI<<; z(4oQ&SmA0{@&)iWbm**)O9F-J`7DAa^U*^1^UVXd$UQDPR(z+8#3hMt$LeP;!8_E- zFF0mvq_61;t!Z0Mpov2Io#=*8Qq z-VWDOa>Syn)m|(V-^mW}(UePp;be{2HNo=C>9C4U+Tdkjmki#gG3H?;?`U$m?tKCp zBNME1<#Ho(c`yI{x3Wa&9_q%Gp{533Jb>g5(FN$+&=MuTXST9-W+sColos;Bp!D}I z#VzPhvY9#|?|TN|oK{e+r?{o2a_~PXqn~BjZX=zqO)yajc*!{ltV|AMxHGuc^P{t#7$acW!Gq*fV>(}1rLLS|? zUej^vtZj~fKKxBY!=i|8K8ABd_u9@z~}mZaPyG zLTE5spIxE`IBdp<&JgC*26Q}dKbHC z(h$5K3aoDk!%NGs+#EuNL+SVf5SM_i)|Ez$gbRF>CzbdWwGi`kU>OET71=nqdwDi> zCA{^EpVyq+_?5&5uM5{bWy9&V$X1P}I_3vlLMxB4g|xh}=zA$Z5YX{_ z_B78izg2~fv8|fFbxmQNxEO1IyJC&SMz2M6Dkmqw*de$}TzbD6IXe1YbS~*jm!{g? zuHij1X|=(`Lr#FT!C?2tmc{P%0a1M_tpgvXgimZJ6^Ao<3xwKgOa%2^QLauLK#S;% z+>&jKSFy3NF85bK0KkZf&?cCW!T{z!}&1+n6eZQHy` z_9UcU9^%aRo&6$?>+*M)Azw*Oll=+(me`xn$v1rE|6r`wmE-Tc3i|vj*Xdqbw;^TR zb1>FkGY+@OHMOlT8-hC;ni<5u!lP|M?6TtO7U;vtg@$084)O)Yg~n?sc`s}{b7qrWmSzP85Nhj4y`1EZ?t-_5zUlED+S{ z+(ree>5@KO&8VhvucFvj;8d(^ff3?8esNf6A|HNQ;=X2wAtS#ggveinm8X~(dk>&S zPHYyeO6p7IeJfyi$uvF(#dV8bD@TLJ@B|ha^JOq;?$x|q#y4WBh_@>Yd#c)z&xEfZ zFdz81$b7%ShTNsK!TYDUEDrQ-U*iRtK2&R11%fwIi8_v>yWHY%vEKfy8!bY&iesay z<;0w=;iSbmryV9##t!FruMR!mh9_?BWo2c{EE24LKrG%R%Db=>bOKfEn% zh6RCE{8h<>LZ2%}qWGEZAs19eTX<=3U+$LDY<_!>cr|qxk6aXH2w>dsm}=w9lu%N0s0mpUJaKW z<_n>;xDCw#bCo!Q6XAnV*Ct;>k=JBNee^3gCk}>>WS^!ee<}!G3CY`ph1CC!8WbD9 zVB@A@uhc{fYGNF}1KxyPiA6c~kaJFCx*Sc^EMV>*S1O6(|MBfpe1#hH2Lu;*+*)5f zH^5!Av#kHjI~HedH2$k|ogGV03F=xNIngg_7AqhF%9<^<@psGaz7Ry7Z!($$%q_;l?WsR2#Nq@2TV)A{HHTNqzS)z<#TCarYah-#7uUN)-mL zJ>Sj;B+_v(=Xg3084s&7Hb0ZZsT!+3*6~?^=ldmt&vV;RO~Ow*`BY2JI4hSi4CLDp zZ=;>HVr6x?z6oz%qNtSf(TaIUhf}`q9fpg_I=Syp53rIevy_)x1$Mb+%u;@`bC25P z*oc?^!M`~FRR(4>af(klT*PIQ{_xu@Sse8VznT4>{#Pou*WBs^Uj*b_#--L&7>I_~ ztZ^_*Y1Ds3SS8nQg`O`=dy9>w%-rqPtRy(ub8rlhn4AI)fA?9AD>8DZ=<#wT4%(el zM`DlWcCnY0b+6&Z%KzS&lIX0@h}n zZ9s6Je5IUclD^mBdr5Df(f;hmyc2h(3CkHYdSm}xn=(!b8s_k(8+7RuQ2(cni=Sjk zHt@owctdfP_I|QgV)rcYyK8lX8`Fj&)hRyXFHpEm3yU_^P*YQdfptR-2f|eh!#Iu! zYvc{CS~FcuY7caQkZoiQTr;>cG6(*8uXg%%)jZf zCQ1f&O5JQsPF?1pR5}%%S6jb=+4^2iWQ%x85Tm<3kkIQRgcbvk)6fT3qLv0goXRpQl zrN&x-_k4^)L`_L|G%&dak-L&TqU~6afgW6&hVc0N|)#UuPy!`sYaO1 zqgC^Mcvl4k6TahCxaEV)YpI4Np8M%y+hmvVZAND+X6d1aYri9~IvHp>a)cTXGThOY z&J<>v6jKWQ5Us5PQTeK!LiQvW zW3ls~tQBv8P%m78btDkP#qq&Xl-KX_Vp|b}AKO3WPGb8Oxw&|uj*ojIbcY_!ZKMM3 z*wYRZ#0?YEMoeEo7ThIF24kJ?sht_3QBIfjGA>ekkq!lC-?-x3^rs|uVd+0J6UHf3 z83YEFASKs7meEn`xvX@R$n5P6eZX?{c`{DyT;joxfpvK@gyf$Bk?HMrU}Vt`7B`=_ zX11!MD}nM!3Hf28ljK(defdllIq#&F7yIy1Oz4p2YicVyCM{hNt<_U zgxD4+DP@X|8gkS_eZEm3iBCT;Gwl{?mfx`#dYz+$^jefAJ|h~V&&6-wn-QhvXLOp! zo5kjD$u04xHoafED#R*v_OFL)JCCV7OA`9QzH1Dz1#Go0<*(u%t99LTKwJB8yk4Ik zbEAG=A>Fs|Cs1uR{^G#3otH0Ba->pi=|eKCH>*h*ymUt!;0fR@mN-b5YgFj}hfn)M z^7URpxnGfKJ1^m&Yf7hNQDosmb1B1PwGI0w#ocnNDo6EA2&MfWP*%n1!1;UEC738& zz@^9f6iM%U^3!mw3YV$3r?-aVWD}{DLxz>o;Yn7{8Kjw z<0v4-Xi&eSj?3!Q+X24@xP4GKCl3PCF8rgg79EV>M^TqPY%v9`X+=oFmUAckzEchOC;;aJ}&WU6k^z`tE1qTXx2|OA%aj9oZfq+SJ z7rkP*I$c^b@L+q>YyXWd#1SF-zUqN4m)1yC+@&rH=Mm5e!h&(>=`U6M#_&npfzv;$ zuVYLXDks`NhVMDmGH{O(>SYGrov+?d^<0SHckwMyvfGpDf6oP$t*EG<)=m7_-g2cD z5w^#C3Eg#%`cY+8&Nsh#SJ+VLXA~!t)1XVDWX0(cq}#En(c`DWtV~$zPb{;l8L{Xa zJxqMPIDw&#I7|Tb75J`svWGpY;;Zn;4*b$1!adaTzJ=I?`#^d?nXuUXp-~9SZF=mG z6Rkyp$WgY;?CS!n>Kq!8(G`$N+`i{xtY%!WhLR{8O_&#FzM1B(K|q!Y;7F0Tc|H@a zl4mTsjpSAt|JuDxx@peAuw&A_=J<)FjoAp4GqU2*e1d8;Pk^EEvbkUi)Wu&nJ z<{r#DK(#HKN*{yL^^x1EEiGJvN>6AUZ{k6yHoCRf=d^k{xqGAoq+_=fE8MrDX$eQI z$xit7a36CBj(=E+p4y;m6pmH!dWksmkdS%VVjm}fWlG*g4MUXU!yG@mkUtFY>TDAJ zT*K{$XB0j33sNV}&Iws5xOmKNDE+Pwprf{1zocy801Y!l6mvBbznqyDfd*9MFX+09 z+cqEZuAWEQENwulD#Dx%vK`unydD766D7kC!k|rL`KXlnCsO^6O`D^W@Wv8~HMYeF z^`&2uRAl#u%oe$ppAYjdcgK4SjPE_vg9w6lIA(OXf;mzKM4?OFuSFC#ACaOC)hlb9 z*Cd1kAB9JM|Be)g)_6H$a}i|uKAsPl1wIpIp}8+wR;=|D7V9{v%OUPVXrK3U1N+A8 zivBZgiv_b~QAFg!w^bJ5^aYm+@bKX(bn|?WVx#&=ad;yoWk%KStskf%g3s#Yggips zZa<$}B>ZF^|Bk+iDq@dZBL$IOK7Z$%*w#nZw-scFKRgo-B~kTYxj)0A@ObWUTlIMf z-0a%TdR~!TDVOy0qN}VOZeP0wm&1OXjH<+@h;`);L4?a`He-X)OMYqD`0OyXwJT5t zTUui>dO3w5RSraZna%O2?;xc6Vb|6iSkHoXEfAKh)dO^I^BSEc@ji+@GKx=li+WAXevuSS zsa?Q#b-)qTtO-;ER1=-GYHUKh?RFUg3e}Pj+^eUe-smfb4V?5yd^oCPr)5$YaxC^h zU3gWF9I)`O32jVmL5cS}kOfuoL_f$ff}Vs+S{w;;_Km?lyG~8%8gD;*jLc>Zu8m9& zsr{e2EOQ5FA6WQ+a?&!Oi<9m@jjoL5c>e!fXf;4PFGZSfBZXDMk&x)hi|n&Mct^ zSVZp#jUe1PU>zO2m&K&2PuHNh6f&WKXt65B+z2`^1mqyhhJVTxTWp|TY{=~Ull**xrk!Ao%Wh##fepT7+eA0T1{x^RFi68A-et2Gsp_94H|%8 zTA8QqgfJd8)I=Mqw;i3(#a4L$ni1_!?)OWLjuf&>KW`9U)T zsiE2WDG~1hV0_fR;dMkq0#}?0yMRmIN3v*LY~b~FTmx*m^}RGy@k!ym*8Jg5RC(rk zne1))ub7_Yk;x%G_OiDYWK)!6wjZP190hm=2*uy)9B$z&^WJ1!VrFQq2%Hxby{@_SmtRAN_y(eOr+0nt#jfX*d>Xa!f_Q+1LVBiLj~&040GfrTn?WERD|BHy-+N> zMqCiE!Yq3XYOqi!ienDJ^I13p%<`nk4dAJZkA{vqfv!ajQlfp{&iO<*``y4FviZg} z(zT9*&d=^c`2>a?HN9IPlTZDDWk9#%u)M>$Wz?U_v*sug*%T##|`0EN6)Qttj- zkmOj}op8zIi`;MB8>{a0&1UU&e@4l+}V6I$;JQEd;jJt`)c!xa|}|ia7E6aA5OExGUied|0@Ui zA4fqmzucomlUYyKr~E(tEzh!rg6+n@MPAMSi7EUazB)B_9c?My?qNV9e+j|ejKqHr zj55ln|8rK9+mMX20TztVxYHy*(5VdZQ4L?XD#{}r&O5^zawJGLP+M&LnAba1UURaM zFdS_(^3C*t{y+UGh#n1~q1)TVuDov0x86dY2o}oL4HLC}8*f)XL;or!FF|{&poHdKo8ZWv!sCys4{DZRW=X4>45`g+c2sQITZrRC@R z_I6UM3cy9#NtB){`;-xb!fce;@^fAp9R}b; zv0geLXm)|-yv?E4vi5E@rqYjT?$Wk;vGI3ohhY;+!?x{iA3SJDJC=9MMq}cK^33GB zWY01OGl0b#6>olN=Y=>No>kZEL4xGPQukp6^;qF}tvkYqbcXzoLy2e-O`~&2@w^%E7{fu_Bn3 z-K+Jj^z$MDZ6wsAB-Z7-(-Unou|=*}*~VoTbEiiBOfGBo>5cxJM#E5u&EIX{F!)@u z$%OnDd1viECXm|DoqvKbf_`J-pVJicwIOqavb?Xlo-=oA;=h8*8s!i5z2|*{*5E8_ zO&=#{e8iqirt>sJ8v}zqzp;0J+49O7Qf>l2;sjgn2+kQHCZ%3G0!y$G>B-5WUQ2+G zB;XRo)2&~a!gn&-SH|c;h|!>5UbcNn-a+s6Tn-6(gtmqk3KEZ^%WMdNn?t7bH8fNVIB$trTEIvFuo5 z2ecbe4YY!_g`u0majjsRw^M6Pulj#4T$Xk~(istL0%BtEyVN0s-*}c}4y;o+G*f#1 zALf~LvKFC(McEPdp9WHY+Nf+h2JbS?W}$a=dhTk@D?_;jz`=Hpm$g-vlE>zNU`O-M z`1<+<*s)Gwv1C-;$6Tg?OXaYNaRB|0A&t);Ssj4pk$GxD;&Q7J_1Fq0NZ;C3s8I9Q2HGoI-tV@uA0?QW)78{apD5Jn z;cbXI&F<)o#R*Ewf9oE7XXfIMj9Vl@neySkuzZ^>Zw~Qfxp_)vnVj5Z{VUX|f4RV7 zF}7Z`nQQR)JmlASYg+PbPzJhM?rY>#JHwcmQsi49gk|IPi)mIZGTegf3xp>=RiBpJ zevybw^MyO&JVfItoS?L?Da4R@mN@qvMd<~-;gg<}hJ=m-1=BY9oPRIK*bEY4)`sFo zP5sTk@LmfLdW)n(vFtv+0!vEsm-64mcZ21+Wo!Ea+|hP+;q<9Ib3slq&LQExVk-W- zhOl%C*v)jP?(;cw93Iw=jZ*@J^c0AA4l=K?*T3#B-DES5zjTc{_>wJ&l;?R*EnN9J z+uw;T##paE){14ZFUb}2M)C^j>^Bvh|IK&@TFZR&%IpXFkWCGVA(Whe zu`;`Wh}%vJ4e=d|%6zFpY(xr+Eqqnk5(dk+t>h&t5-{Hx>L$Z~3#2V?zoQPpLe;4A z&&xR^OGRA#zk{PC%23IiNxUkAju`^ zTvz(x_sFOf`Ii(LKoWn%yEu9?lLC>V<26hg&P|Gj=DNi%ZvCXtMdfl8x1|k33>CjL zPB9hqME_4j69wtLy@ONX(%=O`#V3&=NF^7~PRwcN zwHXYtfT2CL%Y!9Jv-ussGkdfJi1ZXvfPub&F0yZ{MxYM3)giwCtovV}@L%iyM=1Q# zDU8!F1JQN`WhD-Dx(Ho3E;oDb#^M-*xg=o@lbkQw8R5aZ{s;}4nmoHNSH6F6{K?yY z)qO~JCSbrS16vyiqoC&O;;+&zO~dB|*j<9~Q47uMim%d)VDTKk^QC-zU2C}7-NR%< z+CY?I>>k7B_}^`#|1|6*3i{A~=x9ctR}4s?j!?#{Mhhh`d`K2-0*0!02O~r%Y#nbm#K+yN&H-~ zPQ7VkvW6iXy;f$w-0|7*QnQB5l2ObnHCS6zjb;yLL*w+9fGUY@d-Dk=!OvAIOK-3m zO|s}b^hEmAgI@27J)X(`S+g;KqWMR>g%^C@wdvbsV2lr4g@u@$UNV69LPh-!Yl9FS zrT!tR7#{!kMAbhLCL0O=5LH4&um}_C2*XB4uGDWfOorAZ6=o%sQW&wu3uOOBAVklky=n^Gr(iu-lU1`b`C1c(U&m>~f9AxG*RgHM&p!zcfF ztrNYyb(m--I75h|AVoYk_7l-oGS<#a5w!xol93cgWrd zDx|yMe;`(JtuN0H_n&^o2OL=(1(BjAZkB8AWrsmad4B*JC9cal=~W=Vs$xeJJ!Dek8 z>Y#3iD%(bvGHltm;N|0h_IYq5CzDtFsCH|dBeaI;U9u)3JG#0C)fNOk6-nOq9vzIf zIjY~{!cl7@wrs$nYX$1#MAINM&&6()05+AgwP5(dT_06Mf8P&C5zj?$i?nYRO7v#+ zCbTl?){SLC-VG=*hSx?+9FMXl{&i`-B$m~~ReaA-N)A`AoYFOT3}}5JFBJVo-hHTt zGv;ByEETk+LA#6rj^3u#l=?6Bm4t@lygYTn!yFG&uH z5jAX>V4f3ReB6r2`KwPs+3(#rw~wJ{Sj1OIDLTLb3m>8}c>Y#68BxWeK4Jv``# z-otuxC&?Z*Ooc=SrY@>#_iWu81FB`2dUPFLe6Jb;rLqP{*b^7D_uSoF z-fS@!20JyVbP6V0z>3{`0lDG9ZM#Zi1vf-kQio#CV;k=?{GG_MTkl-={Jf%x2a!wq zGW@ge9*^bQF)JHXx^GI3c13tPNShrf^W3`Jzb9!YeO-_s&43jLLknS4VKen_g_(}%}o z+HQui7c9IaRCo5}n{zY_5J3S*X1Az7JT=3$OI-&Dwd4KVBU92mNTHnp0T6X%QaF6 zs)u0N0kwb)(adcKqm)x1qr%q?MBh(-OB!36eUd%G3#ldPYu2lHPu2`qk@;t)4#^S0 z0PS%9uARP@Rr&qW#it5=hE@D@owqBE+WFu}tPK)3m;4X6zOF7>aGYLWM<6W}q?8Si ze5pkP-CenY+aHK&tJ*sBoPwQEl7AuT;CQowL-l?|jBAvghBG~<@n`Ejj*OPr?{R7G zB*m|j8DhMe+2yM8NFRr)&H)3U7HubEK* z?u%-1LvT?!)O0s<8d;GRlPstS5l6Nmn}HKy<#eV8-x}d5b>0m9xJB2 z2TSaNc_RV`eiMdYh?#7IYh0?Dit2z-i>7_T4X{7}@^2RQw>7cqcx&bY2gHp)P( zbCSb3-C0XuQy-y0nZ6 zrlUY4$gFwF-o`IvMN^2iuV<6s+nLvdZ5=un>w+BdWV`~tK zP+bQ%z?zKCzVP$<{k9^LgUF|vs=w8HeT?ZhUIS^~jo+IOTAA_YeS@_a{Q~r|<>q&b z?k}YOWeoBI&X0Wy+w^jrk!0bAzM3=J&WJEE#G@jS1X8_p>dCXpnP)S0*24vcx5aMr z58lc-z!wwl`0Tyb7noQ>qEJo=1Fzfi2w#XPEkn>T=Ay9=37xHPr0WQOh6F0+RBu}P zGE6cG6x6hbmApE}X#4hdP+0jq=ymumC9e$LPRW>*6A#=&($1MP&Wt-@LEOfo#)dU$ zjX+Go-QW{ZfPM=y_3qq9+WLIPa_9=lwJ$nKt+XlVm=Qx0(+rdEp=W6(bz0aQj@9-2^Hrigpn$ z3k)Bp_?$b22rmK$76=g^#7y3R&umLz6tO0ZBJPAy!~t7$RAr48(SPp7gD_9qA2|Kb zqgA{AHvWCj_(DAP{YfGwt{uLohwdzM+#hkS`j!%Ge?g!RN7vmB3juv=Pht2=glyTi zm55X(1Gy=wgT?5DHO8rE!g1<=PUEPyyT*N%S?Aq=slKuAfcF1J-CIYs)#eM^ZE@EI zheB~L6t|+q3&q`wCAfQm;00Q=xKoN-C=S7)NGTp%iWiq+{dS*c=FBtm&YYP!XWq5m z?>{Uni?#PovhVx)T^E;8Xm$RvUCQ`MW3FoTSg5|ur`K5b`>5ppFEaQa?;6_YozQn> ziHiNiJ8j+NLc=*t9c3evkcb5q>L2W}u_-c$upV<_$99#St0|>xh&mDkijo(8X_F%+ z45@YUEijx;*2Q>fhnv2qzEwPC^@S<7JhZy@Jj^&(vWd5IAb5qCQ+Z3Zgw59tIHI3G zWGU5Zk&j*Qj>bai*o%iOiNwklv(|_>s7m)D`}TF`P{d_7m_3^9DO-Z92v8j$Ha55* zxz&xKF1C%G_n@hd1>AY&P~C|PuC6)-$A(&1WVSRU9bA832&M)iGNv@hlfc`0qd)GN z4CnknX)H52L)*T|5n4smXm9FI8x{UoLy#5`ku_5?c%_>n>@<_R=O`{G2q&rqL|l;ocJ9byy4^p z>lr+5;9#%$QB%*6cSm&1hA+1Oo#FnB>ws+-c)_rf!};jeJ@8w7I0O5{!DTIUBi7Rp zY&J;*tcwF%7XREsySb)*p@_;E5pRRnO>>{4VBux#!t9?gbqXgU?{wy}tdxgpdxm^i zo`*JU2XnTK<2?e)hWO+5^GG5`Zi)5-=wc+efHt_l)Dqmp8(F@-sw&#_93l%6Y9(j> zM;cl=ZPe;w&6`Ap(ZCIDlZSK@F6g(-5lu(L8v;blZ1Agvu`|6O1BojUZjO0+Gw12DvrJz8e?Cj&9K(h9g*ky4T8E3N^x_`yp2mJGcw^PCq}Oi zAI)*>doySExKP!EvyHou?MZlGe>=4tA|u6EFSmTMk|~0v(f01rbfL)S5hr{x)rIF$ za)_n}{&v89X!PLJv?f`?9hn}hLgZ2tok~n^n-;fniH*=pBrNzSHX&vxkcj7jLYmOP zS*J}DCAB)~9Fc2we-u5fG{*aUp)LC;=@-3b!xaY2Uctxq>&xWYurq-)_K_TRMW3)T z{0a!Ot?~9N21J+ihs0YLuO0r2+O-^OADWHzj6^%WZ0H21xMaK+HeXHXiokB4%WYNk zpqjF2bzfDl+HoEvZP2g);xmpmA<#A#wCE+L=QWV7p6tp6uA_8W+*fmq^!Q+Uo9wi> ziZ1f{!h;kA=$)NDX^Ubq;(7*4aT!y~*6=9S;c3C%D6?aY>)7{u+tx%$ra|K_Z$D4J z2J_h>WxMfHzAa!}<1WZxV)Ck?c?HTplwooFaw+u@P01wFZr74+J1_aLJqx()U3WbZ zuWy;d@+n*CbEs*f>3iG*iH^>SY_lx!iIji0SuuFF&+05Cs{E&q*>nW}7pA-0s1L^K z3Wu1PZ4FXoP2PKJ`7!Ejx?}PAEVS%QlWBo|I{USFZYK@%Eclpbd2^?DH1bh26`ny;gD!DT4`KJJsA|k-finL=WJgejH$Fh#jJE%GKq+~kfA}Knz%5r1*iiGo`i-zWgr>yOaO7OB~vi{6j zmr9v`Rt>PgRTWIee}4)mEwh7%Kla<1rR1+zr8`FNWesy#+ z?x^Um6#L?VhI6|2v2jzta~d`0@Bplk!mFvbwer}j3prIB zSM}8n*W;I4&m32WgG+pB#a@!h>M|rMwZ=y`j>yQ9(oav*PI6>sx5nB@`(8KZutz0H zD)tV!g^L%6e7+FiM~eO+vet!*xpLpAmu}!G#DRwsJGCfhjPG6Du)O-LoYCYv&yWsIj`wI8mu_lVdIn`$eb>XavJ7=??XS zB-m?S@AvgCD(#^rX)`@962ALgPm0wBdU7%1CC}y9k~?4G)qSCVt4<_3k7Jfx84k0Z zM1urB>v;EF;O*jg`W@M*qiF46`eopycZXugz)T#KPrZ!Dx>`F=)@by;$}Um;jp#P+ zn!s^^S(z_-p4)zaS#7Ae98n^o=M8&CpRb8bNg*CuJW+Rv$>hQxq=fPFl>y{VyfyCl=rdy0FpI;D{9vo^vI=_T)fd9?^o{4>zEsq# z)CS^ZXn&NG1IotCctErNH4KnFK7MW-I+VgCE&da$nxx7ix-0v3HsGr^VS;1F)3UIz zFxza}{R+5^4>^cN+!W*C3@#T4gQ~xa)}xbU2(#>*ABmZ1<+pJgw?aT&9AdL1#2(jxIICO6N*y@ zSlf6i(*5VEd0@q9Pm%d*#Vc3KXi>@kfFn7Z_UZG}`#FrX)6)b-7qYQ+Ic`(cMqVt> z-)~)63yLfJY{C?7h&g8+n&XZ@sM7)oIUG#k9x*~#) zg6FUeT^!77?zXST>EpR-&aTqBs7FT4ckDgeB)(7S25tpqHdFJSJ`%#@)A=5v3~-1? z13c4##5GwoTO}>P4lT(uV=PRJF7v{4s}v`S7QkD0%oa`>^I1i3XJl^zy<3U$z=nr^ z#I8SewJ;?Pwu3!-Q!p(suzla_zr_%Rm0yt&z3p7Cu%386a7JN^=!@kQz~eiYfL0iJAvV*EZ+R5A5X>(NFzL7t z%a&e_?4pPsObDJE&3P-(uq0Y`rexUk+%IQ@TU?mYFq~_H-X;{R^J@r0!9iM~ZF|4J z3t4{vGm?$MiQoJ*+KtHKd*V1u(=n26m7<-CxLj@S_e?v``Q*HvP{~z0&WmH= zh~zc>9)Vz~)lb@C$j6UG=sOfV^*d;7r#A3m#U zBG5?_N9XWedM!reHPV#O>%DHluIAO+nh;$jJ70S?7wi5dfONDWGO*X)p+oU&EblFK zT(g@g5zSuI+EaNCpvJMb?+{X4MvH8;&ha&X9t{b&pPaCnM=CD7(K%s)be9t^?QRdrPf5a!tzZQlsj1g z0r4X&I}XNMOjH{mEFx-S(-6=KUz8#BW%AXGp&oi_h%@$Gztt zQ%u@D6(kByUIyBM8`P{tmKjzi$FKCw7it4h@=aPW!tX8_Zvv))v7)2C9+P0RpLe%2 z+3oldDkVn8@#aFdgn~Iee<4PZaGM;{bzb~{F6!@5h9qw~9~zfn(96zR`ZBv=?Z^q? zIrF{vGeH_pY7(^a$y-)a9D&p%nElJesA}l-`F_Y+UmVbn$(SL+wVy^k(DeHog!-4< z&}N+XgCYdtPnzZZjqGSRCfy>MdA70egU~#sGGHwharvp(zx2b}CFnlDVu&4H?JjKw zqN8uj9|wCm1tVh*yG!$!f96)(+gHa_zb9&~(wH!FBUyx!l?@=}Lyw4oY1~cyocO0- za)tUoToR)&kB6m+(6-Jz2zj@7qQG+gR=pK({ON!o=I*(hv~n|7)WGN!lsrvyNF$&^ zydTQ_x?n(9`04$=cTdhxy>KLJ_GM=6^2yU8Xd-p;A*C6al`72E59g$#tco%W?ymrR z=VxZb%U!khgzf;g8sODQqVOT`qRk6t|qgJF8;5>%soS#GiU?U1{VNf{h3+ZgWB0-AvuOYQc5!~5_ zsdp(k?Trn4qQ+72V-{8Rji9jQR3A8-)LZ`0HRT3FPW3^nCpsIB5yk7uQEoDAqgS(w13 z@Eo`1l4Zf!ui%^uD1uOvt{G*;{G3Jx9m_yXDsl2fDEw`XvmGzuZ})cRCZ|Zx%v) z+86;E@`F)=_?kH=mg&&3$1$teOCdTZy4nYGz90p%JFE)sbBk;6Fp*s*p|T13ssj^4@$Woima zLG!?6{^fuYUE2)fZ{(hnuQ8QKMF$%s@~P z@euBxzW6gt3ze!iR0B>vb?4LHAH(iiGlrN7`H3nm{xJMXtMt)gf|=_0-_o!B(yVQm zyPUWsEi?6H7_b+=nBPAWu$gHd4qD?%TfYvgE zfov#cK+{>PTD1RM%HN-FP}dbuM7^>2am;72QlBECt=SB33*FXxRqe5EQ_UeTfj=Rl zWpA*hSF$*9Fv04*ca4~s>A$y|Z|6R4iRJjA*yy)2_8-H=bfR^Cmr~`@S=YdCNSbWY zx=@S)JMy0X8nfNw?xr(mdW~R0@toXc zKE*EN>h5E$^s4V{#DFDWpfu1@E!(V(-&rC0=@%{iq7Zx61=tA-o`IAaZz@nntKMQH z^WM2Q7vJMwc6&~An!Ha+utwDvY51gMzfd%ap~JguutBhe9bbm!6Kh1$-Oy(k zvRyM&dfv~mSDP0mdn(d7{{rN*3;p^T_I^L=+z;%IOmRi=?6q3haHa+KT0#3DNE^Qn z+BzX#osk9kv*8jj_Lg{SzIrL|s{bWt6-YA@gAwOezsy-qmxr|HKZ#!4FkLRAN!Yl! z{W{yt8RA0>oA89wSVe5woN+3ne@^c`BcL%C{u({TlN!9P@94n?=%s1IC%%{>REXbN zqW-F@eU>*2G%GZ8Fg#<5<{r#hwy8l!dWV283T(_BKSzG|9--?=Ca*o!@GwhU=A9Fm z8S}E6`Vv{@+%WAnn>5Yh%0ncfo1DFOd@lDPU<2WEe`caz0R_mWFjr3Lm235}r+7?XtHrNJdWd zH>lM?1mBKK1aEnI)QD}nYMBQ91A!%F`jHh@NxC{rO&ynweiKw5?_?cTH>tPYGFP3C z<>TwSa+3X1bRJL?J53vTR{xYZ&Bf~kzAL6NVJS}k_2R(#0&vd@G0iOdIN&l8*0Ix~ z`8R)LmXV^zAS;ilQ`xh`>(%t|Um{fI2mC%IUADSlvQSNz@@CmWG{#%wZ>A2w4$}6r zp~T+DkJBf_&=tq~=LMQGm_3A&xeh)xZC3~KE)rv>?pJE)x8moJ1zF)uE3@z$LW5Zs~R4>FhI4?K*g-`LfjFt15%l=G8WKX25C z*3QCJ!Ip%(KQm{1w}*Nh$VNO9NhVzBNH$|MRvWsuY~(+a&twWuAR2sZ433`I)Q1e! z7}1haB!jU$MY$vp@FAmqn9MuhU|<(?O>SY^;7POj)KZ7Cnj8N4vTN>}jmyu?F_4wz zPZ9=#{nR550jQH;q}(e*-~ZDHJ%mB~#b_XOI9;H2lt)8D<8a~N&htn`x_tRq@%k-g z%h?peeC)I4nPRp6UvY~D)q~e^M@H)Yb?p(5PY-yS4oWQ!8-7~c6W38$u>Wg9Gx>Y; z-y<~ZT7ZG1;sboAM&&q+a}|t@jDsN3T6@^_yOt zw9d}?ygrl|(Xvi@0+nQ4wqXT*eBRVnD!zQ=vj45pB+Rf0Da~)gDMC_Um~P|B^r;({ z90BV;(FBS=AsvW$_A)A+A`%BFJER30Xu!7PqYsaW49tw{q=Y8K^kL398o%OPb19@m z@F0KbM*jSz+XhTEq4fi+>~0@_m1ohpRHPJ#*}9CNVxew_zJX(8a=E~I8@|?!KTzy( zpTkE%IfflVYMBnHo~TTSS-2uhV5bBJft$a;ys8(nMhpl?fD^igqii3UhdQG3FmVzV zquBs?`;Qn+2OUNPeBo%P0odU)2R^%I1zg#Jmsf!K60}K@wlP#Z(ZhS!>}z{74?dlY zi1c7qJgB2tQKiBof`eB{tiI+GtlFl-2HU*Gp#ap?GY1&u zcVGkou=rhVGvWM*=)7Wsp^^0jpZ=dP_>!CgNW8ofR*1{&c`jW2k#*Y1DX>A)i`!uieKS@C25uSyi>}V5D>=HI6YDo!g0BdhRe?2Q4JM^3Xwgw@3Y^4T zZ%#_Kyh;jA_~2JS9P>+OJ^HV4CO0>?hyzrISPoV!uDBISHhF0hzxuIvg}q``wOW1^ zEK|}g?tBv}?Pkt)^osp1r(u9UqZd;0{B>7aahJ)UpiaZQUe7sKk*70(L=UuUVg|M7XmqIQ-ECd;1~M#iU){zwQtD|F0l$MEdP95A4@f0EF7SrY zZ&xC!>*IS-TpwTIpw`ipnY{K~IY*=O(VTHvG&4VKtjI^u3vL`2Tb{6tdm-Fx(16r@ ze8Jrw+E8M+sX3F70?{9dNE_VOorBTSxeZJzt_`#5=W^6))c49;T#2d@W?TL(8j_Ic zo#=nTkm-qkfgy=p=N#oh6zboEwj}HVpXG&9-N|Koux~#n|04{U2DKn;|FWJT9;dIA zX@~soy5xb2Q}52gU{>D}r_|~b@WUy@Ln6Cv$}C?_q{q(1hY2l4&!liAO6svh^{-!t zurn5{*$xQ`jzld{m?cw)_!EF@I%sTxa0P7%&h|yv^yVg$Q5q{@%?H0a0egrK;}!Eb z?}-y@_(d}B5=C@iLP#yM@2#59P29}4`PFqxZwE0C$dil_X)il{Yu_I4t45mZ#j!iP(LK{Tbr=@PYTF;I~$Wy&Fih{-iJUPV@&A)oheacQgu*V(VYjPgjTFIEiw- zv@A0d1|4Y{OL*`iy4?GGObRF(0|e8OvRaMK*a1M>FaAHwK?WIuxBpEHlE5sU>URus z&@2)F>-hn&{(mvnAu?v%cKrE2FxN3eh8-r;lps|ec|U-t{H6m~m0jMc{IFrRgWbvj z#Y1Q)MC`6~vb6L)p~hQadF6A(uB?F6F3*nfSYbZ{XxFSq2qB}g9ax+LTE`Vx`g5i? zoi6mKzNoY8j_+4^96GdoBD|c82Jph|4~kt9J5`s zi0r*)NkEpS^txRkjx!j^-~!3~5#gJetc(0x4cuwf&^w6+Yybj@DXjbfjtP{o?ZhRdL)u z5`4jt8yIl!r4o|YNs{Zed1R>jEi?S1-wvZo0x(#gKZ6iial8299!Q{V$1;W=`e=vp zE`Tt{XnrK3dxq@Osd)3pJ{(nF8NkhrDec8Y@mU1)#7z;=k)2M1z%#sL}+6VTl#iX7S%2JtsV;S?G+pRjT2j zf>!|3z2EnE!O_>3uffq)YRf#yY!t5yf`z$ukTz}}!g_StLtxNj;Ww$L;(|4w2XsHf z4g;88TcnCpc2xxwXISlvD|qJJ8vO62aBuUs%NrIkdpc!vQFJPeIoim8u0T6MHulp# zP_@H?@OeAQ|740}FvU?eKb|x#s4<38$57CQFmBsXh}?!Pdj0;N3wRWl>TMy9%zLlt zv!RRpN}NdHUW)_)NSAx8P@#?`>iy#M0Q@QFiQ(pZis=uO3@&S|zR^O}45?_mKW&;U z%G$V>o7UEE4h@ZgBh6_sjWUY65rLxtUn7Xvt$b?V7+x?I`h%EKl}jp`!BO8h6k$fh za2ccVYM@xl}W$o}-*mEx_e9m@Qgoz6z7X>r)2^zo z%E~%Wk}4Yyt>dWd{iwC7;_MCB>FgicrC&bEqD3S5=$1`_rUO7%;h$n!0grIYUR!C@Q$0g86@X+lJBD-vs#VFe#B9gz`F94!#0F+CZW9ripB-{GBt3ap^Z`=6>R=E|GeibsIDBGWKbxE- zXwbMe4D-qkKb85DzO95b^2k|!!YKI8GHJ_^w(6it;-uznF-Gf>kRuE_aqwvtu9mf# z1aKFo0Cy2hsm5hdOpyFfdk9v@ObP$@heh0D75-lz7D?T3NM@pS(-V<^s9q5XZmxt& zz<-J(Fin<4fMJaz2?;iFovdy_l^}bh8*E>S(ydy#C;yHp?y1?bpq{Zpox&UDGOiVt zXTH*vj08NY8aLTND~;hXzzJT$8*iZ8A?}Bn(k!bOF)THqZs@Bx&wkz<+-L2ew4e9F zp5*@eyuM-q|3rb9W&(K2F?IwU#XkMvTkot59aC{#{Sxi_Tqr~Ty(vx$X~U)@e59Ie z=83pLtGC?E%)NGx#W|f_i`18{4qE9kw+U1|OAVvD_ zRMtE}6Mr|(YFN=u!Bt2Fw@Q&$7;U*_H0ZykvNj;yJMqJrf#+wwGSIDe{K@<=>EYTu z-D2QRV2N+CPyGDJxrEsLgnxB(L2`<|#bp)r)XjgAr@D>0iKtx7^UGSOnCnv@*5Qpg zRJ1`ZjGkyGU!04jDt?73lkf6L>^nA@jo^L6>5_VEp0=RVHs98i^+4lSxL?M`jTh&j z0$H-9X6x;rnm!nViHaDw>+uL?EexQMSew>}rQsLz-OPVV!1 zFq+4FbJ}v^PHV=^jAE$|7UHzBQrTtiTUJ5yadY?(X?Y`On)YtsnYEO{Ez~dyf^zTJJBwt9 zz6}1_mk*3sp>){`v0vnb^A=v+F?RfvK7%yEaCc00>xxL~%$gf^UdXR+2C|3`h$6RE z{$%M`q9R!;B@M4KKFxt|5@l#x%Xn=CV~fQ{{6^rNz2UkZjCwf`8`Xjpvc_F1HZlH< zc+{YqCQi<_u95)Sz05w(>8t&s44H*5T;-hkRAZ}#@Fdg5aqGSGo)@j49(M*Dim|}r zZWYIA;e$0zx5fivb(Z-~OP!_`y5aU7oEJWcaP`Y8v`7*=`b4GFJfj=Fzsh|#G6#G5 zAX%35Q+;l(kS=Lt3Kw2wVyU&hZs|LCMASobb=NlRuZ*n=BXf)axb={lN!0ZjDLrK` zXK>`5XpVrL!l)i*bB>D_ftE%1O;!UwTy?#o6KAOP$jLH^{@1+a7O6of5Ip6keEF&C zQ9R_CpvPi)B`EM5%4p%+|AKl%6R_ONNTkhdQZA12em_(DF5|bomI)8#pE?8!^z6_I z38c6W?SWv0L0Q%7LaRy)UyL6pdLqMI1Vs_%RzDCR&}_>f7)yw<6)Hk}mOaz%RvK>c zuS)OK|0aE46#1Ch1-t-sxWI^}vuN|m%&;)j%h}O@u<1smyREpa*PWf4D;=*Zd5cMI zr|iipESdLA*-~FRR%2Y7z$4c-yJO*X5&*jGkQoGF?E6eFYJDY+gXBBWYt;{i{j`{~C^DP=gt zS1*{#lc7OImrMB{MHQ6L(^I-KxyH(+_8?1FL+pxN*$b`;mHAFC*m% zc@pD=(n8vb1Wzr?zM@C`_oyLM>g$+6zE%5v40-q0J;7gh20}tih5EzRwDr+1@ad_g z->}Yo%V}XAji2I(#r6CbBetmF{M3e-DDaa}abG z;o3swk`^jW8+_~}Vp34a`Mh`HIn_A1z`&>SER}Q6JLf`1Y<=T>6tSs8V8CZ7Ztd+VtVecK#-v8`ib&yjUaPUqh(R?XJkrcy z{+C=mR8w^3p1qh(G{o$(ih~{7sIiL|)e~$BE40%=YW_EzdcSUEf#vJ)Z!>ePA{Z3> zVEn)*Cjj69Qq2-YIXgQ$eB($N8cq{HD$?>VWEI%{Dyx81`1fWDP?-)oRMJkVopfj6 z%T}MeXmtMo|@t=#NTR^}LK=j)$UbPPuvi+1ii7 zrNpab%53DKRsNIpR=3+&@tjrtmA+MJj^u`tflvI+;?Oj<6Ui`!)AGmGZ%MnGCCv;@ z?JXA03y;3MUhJ+7g$|y}TM?Tjsd1i3aefmfqBV;*Gzr&ia0@sW{)n@`zZqP^tc}X@ zMf5Ax6|kbRv^0awp7%WlFXcS3bSQO$ae31{!C4_9k_QtjAzB>c$h;OpP-dTuVD3Au zz}f9!b=>rLeHNtSa$LMs^K6#Py>GXnz_^P};Cv?9h2^3}77@P^8Q%{VqAeR=y2lrR z6DP9!TM?m5+E&k#nH}pOkI_`{8|38@rb-ZYjeveNcCt^-8^*|(wB;2C^jbf$I+ap! z8zSTf$!_hDr^$<_7g=19(53F5U86|axbF|@CNk@LhU`PGz4;z~bo}}$> znQ$x?r11;wFR;=XPM@UZ%yT+eu81yP(aC?=+t2^VM#SVpC2UX^Nc2`~dh(O4{ap7m>r05S21O61UMU#$x=3@OFd;G%;2J$9 zoCa}GxzoUPThx@}0mk{cOLgMaH&;Afo-75-`X=Lxp zi>Uh(S!3e4!O^mg^8IjF|5~^?f5*pmV@QK1Vw??7i*z{EbNHgVBj9)6yI{+ekNla@ z5p>3{h+wj8#ce`!$#(Fv_Scu!4`dGV{X|bZ_*1O0GJ8d?o`{2KubZD6*pMG2BA~?e z(8-E~sEyD6(a{S<$|=yQ;$4y3Z7FwCl2n62X`FKP5N6kiR=yz_Rfw(k29-gp8$oaE zV5yN}_-2@J50E;@T|wnU{)vjZLCCw&LkN^xb#0`E$z*>lJG`}!5p1O^*xjXVv#pO_ z>yPEiwAl)N%{f@=jt|C)uZRzUs3+!%{>IKTOpaKxq-|liuksd zf*s`sLvB^XAuF@aq>BArBh0um<&p2-$`i(_KhXb*u0`pKa4AED`jal%{!rOg`|zoe zmObRM!YD9#^)yW&4-A0s<}v;nu_(P5(OG+ zRy{skql8_Q!PGi?r2QjF8e8rgBc;2whaK`NL#*>tG74)x%a$kc;?f50Vb%fTyXotv z_e9Z%L4Nz?^ESPMV z3;P?*h>UhF8KhFiYGG)x1N^gxzH>$_xAl*aa zxat-k5}9xc4)-+}Jz-c7Chch#iC%DP2|$-{?23dWS9xaiexF{poED`7&!QzvDS+PY z7E9tC$TcRl;o4VGzEG#8cTPoxCWuVB6QT0kn4Q=#^egb2sd^{4b$f3X`nq>(2YS@` z!osp^CsB+Orn7&f!IpOWW_R_(J*Wc2@1fB)>FCRTD{Of#)5g=-!g(gyN8Mr35N)x4 zHsqWDz=F5r>NC+7R!Nz$n#nYxX(5>c(6a4CkHt`k36j^{#A7GP_U9_?FyC>Ek6lQ* z;bDueGtM*Aus7weHIT}=4{|DF-8UnOiV?sk#YBaPl=>_(P9ByhX|a^TeCnYU9~@`L zsVjb}1GQI(C%8)x!K9>xuaCJG&WK|aQO0zdE0%DUZcDj3uZun4DaPNF8m7dqb-1s3 z(w(-jketrG|DK>eN9(z}y6Pbxw$FrCW!`o(q8};TeEjuBtRdo0+px zc%*oBYyypaK6_eAeG<2wN%7eDm5H@veeN7n+&M?&IpkHf{)Qo~h{;#W+;Ht{#$GeC zPr=^A_Q~V>MZx*tA;o9Ao{SIrUmF_z(y_XN(fl0EK8c36am55!`++L$T`mER><##)920 z5CYEycM^j}_a*<`Q^!b=(_s}=+I>tX2Bfbi(PLLzpz1C*sLN(q zxYw@Thx7oX5urvA;C`XJ&QIC^61F*h^5+SRp*Sv6p-yc+b)&k2*O}!F%j?b;E$SP! z)O0xK3^xoRSFW!&^rUWC++I2xN?YQI&Rr>eihs!VmD!JW!PgJxX{ywo^DU+_6XOLO zb)qq>Lc?k9xM*7nBPA~OQG4jb!A|OZmBF4Zd zl{S_$kBQ>)hB=vGB9^5LG$koFRi1}=^vS9PpsX+hndSdT{fg+G9^Mvj6N(0 z8Lr*pZb+3D+zsB2a9yIku7Mik!?;@%)O#?^fW33zVA-}WwD}vT_f5Th%mv>VN~xCS z75?NUiW_)teXU}kpxpMW%}P#Qd}t0hiM&+P-G2; z%hm{0X@SIZ*n|Qe3bU(5=1GPoPCE6IrN^!{%dMXEb5evnz@6Yxkfx=HFfDH*2`saN z2OTXXKyd|Jc&q9&en+4Z|ETCIBF9N^qPq-dY`p^P~@eU@NH6z zT|>#8eZJL>A?8O@3LqpbXf0W7k$5DR-eVK+atI-h2@DoZ4GvuR31{w&;5xhB#|GE0;b@s=Oijs!je| z5VcAdyb(c^aGg`Q@kqIWI{x^8^4_H+!RCD-6n=0B(HKf`L5mo%d0DAdr4X?DG>>c6 ze`xrkez_6UfEcMCCgdE=RPa)DeK~eu0_GYcQ2fzeu;1e`E4c<$nDHI+Q?b5uE^Wwm z6O0uJK>xOk0j`QTtKLWctmNMA+J=Rzik9JO@4{6_*>Vq$CjP`B_b%pr*Jq=F8~kq+ z+DNI|AjONUhEH_#nS$O5B9$hY+^$w`rQesE=tb0QtWrWaYj^7xky~w0+!kHmceC;g zdNNGw7+o=JR{2@2<>lx8#iuVZF41E7%#=brQrMFPt#Kav^XD7lSH=s-Uq$ZjiJydX zi3q|Xvf)mnKSO^W?$xd~=heu2W6uOF1u^c0F?0Hx=HHNFbH#M2Vn$U^Q+}i*&{)(N z$xDg~=eNY@A){!-X>eQ$!``U*u!Gs_tE8TuD0nKi)Q7O`okyEoXB0)6AZl|`EyJT5gbEbJ%D|51%fW=@Ma)(DJJ(z-WGk#8 zR5k}Ey29TMt;|UI-;=o~UhG0iaJxhf3`u+i0Z+2Yj&~z58&X4y&R?MJP1zfor(uIo%yN!7r6R| z=fA0?gA<}c|2^Ip8ct{UCc8)r0DkH2{rA8xU#kn7jXd`p@{Yt!*Qjn*+57jDfSZTT z^<|?Fz<=NLWdmb0h>P~0O)lW_dlfMbP#g+e*!H975`=BHpZ6VG9RR`DErwZkH<(qf2bxv+qKZ@({~n?2$^ zeldURTx%_~f*yf4Hc#*WW@5fCX~P3ni0PVeqO{_;Q$uwdG4jWQ2MFltqUU2{#&SmE zXW+Uyq|HY&GCrz>zZxELaoEOdOgeCs`ifQ_C~NeIs*W=P<0;h;GVVYgnU%Vh2lI!k zhY14?Z2Dr`CQznFC$q#c)H@+{ufVo+n*+CFnK4j_?O#26t#%Ht1ZQZb0T7{(!;{RW zwUoy$GWBY3uTc%a2u>j7ntnppD-ui8X#Udo$U8N2j_PBARBhm-KqNU`o&@Vcca;;U(z5g#ytYGo@o6>*`nY__s7YtXLo+WeT_4 zu!eh%Rz;k!5x{$ea}=Olg)%Ad)sTEqm9=`rN9 zL&l0pux7lC1U?RrY?Gu5ph4IrF`&dIMPM>N_22$)=u{K?s{;!K0%WO zm`q9L0h=K1qAZ&_A@|Av#s&cwOm@e$9JbNxLRHWc#(;oRjZQike1Cy`1>^WFxlX0e z_hB-#?O`q3t6QAU$N*@DCr&b_8#~&<2dtma{u$&2Ugklbj9P7Pt9qm7fo+O1?71Q# zMDpBr(XX=$lPGfeGVz6r)OXf-QxU$)0)M|rR$T^|R|{HAEN`3__+y_R_{E?fJCqwg zaFdZvr74Raltb|4TsGm}SIYpTsk*;vb>Ts4`GuBhG$5pg zSM}g%kyO>iH6Zka1FK0OA$GtZR~UL7oJ8Z?uMv06kjQ-n~4-mS5`@@p4sQ zs~krqdn_#4IM2a@!Q>PmD+*$J#7@iiWl%5ykiX+N{WtRWOV6$^g}+XoB;~B0XT=J> zu9_Hrx~}L4Z})A3N80Ls=whP0tk%=;0lc;pRVgHB*Q+AW1?dlGj?soZbhi?Fm_@&6 zOVORnQ-~52nj}3L^T271uN&Z?i~&IHyvAWOI1`$+$I4O7-E#HqaBKXN;m^eJ)%&`g zlPp_$la?Q&4V!4ve4!S&yVnZmFAvC4YbI4dy2Nk0#pQ*)S&lku!h0;^q|E(5Mc^k0 z?`G}=5~=9}QjXP(Q>kiSHVm2?d>&(A%q_BWbycEhYY+81&9T!h&y?&bhRsPESc&ezE_K0AS0^o#HvABR+pId5 zCP_R|Q5NT?PiO~_cFiL)Qy&inw<_o_h2k^}sUkH*S zRQYj=0oC9esf=`|x7BOa=qiK-w01x>xt2PX<$=OEt1a$`ZmYrU$t6NoUNo#6?$c~8 z#gVy&k@fta29vqChUb0{CX1BLhn@O%?Vg+@aFRuS>yq-Vohq`U1k*o<_IsyvP-&eJQF z52$%%KDy$esNo=Pp4X)Vdh0m9f{WC}eF8h*q-@Kv|6_wL4c!a&|2t%L{@y=8R&z1_ z5waRD=}mrz@|$xqXw+`RX~7InnM{Zt!#Gf|XDwNzho59)BOI%7B{xL{&iu$?nxBDs zzHTWe0}|c`s|s(Kf6wx@i&N=to(J96{HcOf2OOX8OfptQ&D$xoXwzRX@va>1NyB$QKCv6&B z57aI)#UFI;_}r_<zfar23f3odg zMclyghm>(i4u_&=RGaf3%#%^4jR8*E`8F*Mk_VpW?;*5UO8a(LAv6az{+cCgna@5B z2ZON|hJxKQ$)6(NA1P^AKE8jr^2Q=xp~B>g&Akw|aOB#XlTr(DREw;=fs_?0l^P2tA~+0Nox5V^6>fn=@-f z>@(j-v9kHGNbjpWIk*NUMURS^@xEc#PM+9q8SnIte?n8O)b!rH;A|uMbWpEkD4xRk z1(Czc&3mE&d-HSPb~VT9*?+>jXyi5Op7qK3&7eA+Kt)YMDv?33H}^?gkx1_j7E*ImNGhE?vpeI5_0n!k7#5&9e#@x*D|*}+}gvNqN0D*08c#17#qiObUZW?C!zbc&Am zYs1~ZBqjF{&|=?{I1;_oIsEI;9I#xq1Bh)r%;BCrqxjjLq(SEt+{dSl3!_?*XdKo; zUaB1_(6jMSU7$ki17tPCD~!_c!?@ONLDfIzT}jnUiej{yPs8~6Fvc@YT`G}dqH(HL zM@K09-Ipy=w|o+v&z=b>3-^J{`Yws-qep5gx*)cvUo|B3;9g8+)pAuH2?&tr_!n4= zI&E1gq-&zKJ9JUvHv)Eo=?Sei-hqn&nTA=^9Ej~ZjN!66!_43T&0|h`gw(BS5PdJC z^hhBvGpyuaCtu}trGmKpx0PsxKY>1nJjI z0F`T4)F@w%PeemSSktxuyK1?I*B4=o?}HqI)o&;*j$tH>(Lx|dUDsPBA-w5{ST?QE zj6uYMQ5Dpd&ptiyzm0KJ3OCJn@K=jra&V~o144ny6Qjc|_^;Jwb#!_ty$FKnB3u4#E^lR&PnYXa8@0t|F2be0)K zmlF58T=B?qF-|nBA2p-~`75|yHlXhaVW~fN-e+;HojJgy&TizWj}|XCLXLN}XL&oP zmT<17Qj^;*B>b4T45BUb%{$rT^$$}vV&mA28!2BnV`UX2wF^a9ecs85t zvKWU&R0)Irn+;-CU<~=}4!rV36|mZV0IasJ0f^M+V!>0|&1tez>|!2==CT8=+f$Xc z50unno0P)aU>ZQ3bEbYlw%dEjXp`Ev*6lf>MY=HefC5GhZAdv;$K?q;X}YBUWjE~Z zi|VYfoutT*t&9*G>0|kbTka<9>&$<`+`|qRHCLYBFN+hyHLH-uHL~_ z6{Xa_PYMT`nYfT@mPF{SWiDI9o-fT3#KCy--x0>TPJcOQYZ_2|FXFbUmH=i6Cy8az z5xp%#pCaN`YRz|Uf8GD(A!9%`-9$Ok(c8pg7GHvLl!9CP~3T#@aL z$I~$X&)|=6R7UtH(HXACCB0eRkUm19tN0LBOx>^VT-~OnUU>Vk^%fLEsU~A6?;xEf z*O8w53B0JfPi_TFK*)=0S-Q&O``D=QDob;aHO(JWI``-Y1ze0O-e2k%*d9FnUayBjAMnOWy>fw1#sy|j*3>U8rYo(-h*Nu95T{dzL8pme`q z-`0L$p|3U8FzwQYABV5qjTac_LUMP%tj#7mbK5ArVMUX^s~a`Da8ga?3MTO zE+m#OV{Hko>s9g{ScxU^8pORz;YR;ln;0h793ar7Wf~1O7=%!K^bX%zpB=ukLH2iF zkKZ8SoXBH4f%`^@O<)L-Kj*qv{O@hZCv^`IAvWaKAAfdREMT~*Wlp?)s_hcP!G8%_ z(C@giTXe7qopm_Y%}_mvO~+mNsq;{0F8!_?rK+ z=91nZST>T_wDX5fr+b^=Z4aS@lV>e4W>mf0q4k1KQE8lyE$fs^iqP!dOnAtzhok5u z`kp2_=~$7mWcRuBb>~>q`5n+wtIM+Cun>V3Idk91l?>!Ww1s9x!T_I>k)xMVxT1uV z_7lZ2Lt)YTsF>jz%{f6nm!Rn+*Yi)i<^5d;j7ekKuH+}}xiQ(a_}BMi?hl(W7wzMq zF6;-SuuoRc`2PIggZSpVLr0>;Y)-1McNNauBS0kG=p?^_9EH{L z+Mz8_@QGaKSSKZZWvch6-ur2IuO+m)2@iA@ClZ6B}F}xyw6>S_Zt_eAXm`GY4h)uWZ4rjGY z#HQSWnGsj-5KFnudFMMc3!-Mt|8A%ozSbQ=UwW?S z&bcA||1+$@3L+q+mqS8N^qlBUQSb(!Z@dAYKw6rxw-wGB)9_1`tD3h|^0XpUtGnl=RYdymrFio`SkK~S zK1x>D#!bUq5d1t@`Gl+ygMHdnYM4C&&v5(2NPl+NBF7Bh;>f}{%_)bgt8gZ5EXB4{(l(Jm+CAx zTkqEbvYvOSF5X@Mfc`NNV>_qn$*~;prDmWhmg!ly~-Zkk4apM#D9hMj9-JH_X zWubfrn4&);Qt5#ey|PnP^a<1)(LmTvpMGxB+;!$E5uNfaTeauR;!y#Q=Q#fuGjb=p z$&fW7sMNAJg|Xnq8vf*3yCJ^5woN@VK!PJ=R<|vNr&4>V3ui=|h81(K^Q5Huw5y2* zNE*r}>_2XTw;^QHomSy|vX|$RX%;H*>=yYeM@(bDao$u}bFV@8+W-n3k}j`OdX@hh z5>OynFX6J8X58FynIbD2ne%ikM0bhJ%~5T5#+R`Yao5HKjCJN*Qvrby1g6;|C!B?p zTfEx)P&)k)GL?q%637}(39HNb#MRYXF(>zjq=KAk{P8ezlKb?x9mnUS1Z;yn>U&mV zTYjFKGWW4I^_a<}4J& z%K7)4f2AKeK?gW@WLc+v+Jv>j_U^Ox5H(u9fZpb7;%LiH)CYbb`OxVxT_J}OcxSSW zVzO-M@Ekv@!@0b=@!R8Ut%o z-xxk1X>%@!ZXqIazsU|B(1IHSht}p10tvD%JH|N%C;xlNL@itCrp|n<>#JQ8XD02a z4mQ&6s)ha2pUCov7?XiRy}%&+g+Ri{hloz;0nPXBguGP!kSqwG8&7X>6K}gRtPI2> z0Z)Wjz8>hVhG@AU6yJemTh*;tsQ*>ZIRW*OlvAOmbUH?lCVms~a;!-hr02ZLhwWc_&J~ie4%@EOag3{e zrlXB8Dqhhnh}|b7e=-*iaG=G2aCmBoa=iI+0@8$#l;JHdaruqMD`Y|B2XI*h;thXucU@?-NTAIzWTNMG`{T_z{@dlInCcQU zK&2O=&!WLfm4ip*v?B@ai8c90p~DDyo`EL{lwrBLbF-fH4 zH_4X}nHve03ayl*aL#x9E_{(v4(Of{q~#+ogUv!W=opb{i> z=%UzZUrm3sTBQL6&H#B#p3B)3P$MYYmhQwcI{=+k?5vD#cwa^#r>EZvzRFbvB6-82 z?Q5*Vh^9C2$xP&gV&mZGI-D-pTz@@%t3L199k#7`n=iZG>}!wngTF2T9+#Y6e0xSU zYjA!Sodv(sQF2W1RcABD$9Uw?%Cfu5(Pe#tt?HbXA^_NUQAyGxmX`6`~D7 zmz8Ktq$bcye)`tf6WQ{kFP}Gl;RnN6Pmbq|iV?&Nzq@Kc8)nlPgn+E6hM>#C40s3n zCDF?~KTX~ugv6nERxx#8!2@*9d5P&VunTgk`u=r~BVsr3B*HK3N zLYRzYLz9R@69bKZfj<$riqe~hY?VM9uo+~(ASpCC8zAzV%wPySq+Y9VIT1_;X$&?2uIxy6R|FP0v)ayl*$^F~N+zauB z8?&T*BhU;~?Vqc&oZo(82PGup~D`~RDsUn@wSC=KO*VCWYs zZL)WQ`zIvsVn3zD4W~J{_@|5m9VvJ=&0xX|9Y%K&#;yFz-f{^ls5tB>J0l(XFCt!A zH^B#eOfrR{xA0+4O>CCzKLg@p91b3nt^M=g0%&@O<|{g!3>+A(zGYY*@EAMVNfQZ% z86WuX!&A6w7i!VRC7sIQKz@x4(9MWGTDO<4LP>f%rrErD|vv z4KZ+tsh?(-ti@*)H>fk{uuH7vV-)M4{+46YX0MaHWE4Lve81&=Wrpa{m@(_+<%wv! z8Zi?LrNF0S{-1%=>|%9h{m;YB zR;W(V&F5|SMD9DNRsGR>3my=n!D=2nSC7;Qsg2V8scHVRiUV;E7 zY2drtXI@w+AGsd)Z!&&}%Ny^VI%#NZ$G^dxnJ>b}U(vN6t~@7OYsTP>A@I#B(z})S zo`M{$mworqLNFmsZPV!(H(>1LsRG|idqz85w@Iue)B?v9Qb zw;ME(z?c11;TPQaod~V}U5Z*Gh2TGN)D3urdTd%wwcogn8h<0rx2GW!`$`G>^Rhu<~efIZtV7( z{~V0|3emJKdt9vR4HRt2wOe5Jc(RO0Heo1<;9|UzG?p1S!2iHkg>(PjtE#`+OY$4L zIg!2dPVztg3Sgz;^jO&+LQSV)d`_DVS)VKPs*vluWm{}#tCus^c0+EESzclbD#TYT z0M?ZUv$-H2J*NI;j8Wgaf3I`yT0j8fK)EOL#2b=mJ04!gB62j8NE8DR;NI*SQ5$a> zG4~e+3YL-$E6Ce*eBj(>cQ;0q5Z&qzupBCc`-?9Kv>vUHds0MD>fOF3pV{X5I;&Qf ze@%p4)G4f}5XZHQ_OhtAr_*g4qW8k`C@j4S{@rw<)6~f^enWz8H}Fc)V*z7R2+VdZ zp8Gh#D%~1Ob53mWo9}MY0)fo$R=$qMyjq=eMzJXSt2#4xz5m9`y*sPV?(c0TKE9Ez z&-n^8TuujelbD)hWMrhzf84>x?XQ-}sY9VT7eUkQN1@?bMsn}siO>su)2 zl0tb|@@CbIQIx8yCL~uhq02)StHqua5KlVplc|QDoePcuYE}Fu6G?z3-x^Gjzkler z<#_qTc;-|$1OtH@)+QkjR7P@m^vwjJ&PijEHdxpsd{k*Gp+*`$baNOTXL+~k(V18g zDh%(?9z8b~lygc%Lj4gXBhNoYWQg2nQguS!fZ0Wo%T}u%^18>h$g(HO;E{pqqVMWS zIT_gK<2IVgLtI%wO31p6!}VM@Em}rsDEM|wIiEd-T^bU2*3><^YWjt?wfemxx`*RT z2T#S2Umm}NVm=aC`6_ZJ^?iI#XSmq_S|?sFTpKQ1V*)URfYtw3HNK(&;eFG*(s(fLD2eYRTN$6IggyimO% zIb%1ZwFvoN8t2krsQ;k^rYjF`KY2>jBTw-QTCOKyMKdh{1#I*w1FHYQT~kIdeQ8`Z zaZoI$2Z(-2X^e7C@-s5_*4#W4A^+SToDFpt-9g1sy+3vG)-ypcUB8?{KBvc=IEO?N z%ePegipx%;WI17Er3mRrHq)F?vDdw4VCK5EO-7BFs_w=WDS&; zZW+k;l*93nX*A}DFkyE*aCj2kVk=LqAoCMg4dD%E4Kfi^I^Z%i;efKi*u34P=EK|xODpGS9Y#r30FT5vW4ct z2EZY1)wJGDaD!ml>X{Ji8D*z#IE)}z{=?e4NWH}pe7My~1n-FP?vojtkPr`#VGztz z{?nL<059C&7~9Ck0aZA%athpNuBoRA@EM)yOc@Ifej(T+-tO!*NuR3`R9a`1N=IoE zQ#h*(Eo=+sxE=jwu*E?~wVr9}-SJv=x?&jHm3F;m$#pBT+dbs9_>xW@VUi%mz6iWz zT1qpi>GM7!Xf8v5;TL_5d1`sG4J!f&h$F$rekRCTXK9Xz5&6251nX-A3C&AwpQ|`WDu1Vf zU#4^Ah9~s-(?CQ6SM#4Tj;9)zG+adgDdhNhwDB*f3MZzfraCmV{`k)T&6(f4?wi1y z$hU-@6@yS%!%8H)$J`fS74FMuA0(9kj@e`ep$ZbE;RT6!n{O}lpq!k`;Pr>Yf`l7* zoLdkEXd*igNVo!!`!k1@Yv6e<)xdgWa3YSzrBf*AnDpb#(hFSDcb~3k%q-6WaHpYk z;$uX!2Lj^@;kQTYsmVVoGWqZ7KP3LcOw4ceTg(E-U@G#%p^+l7o;PuTvtk^Cdkt_q zu{k~oPK%`9pB?f{S%vuqH=I2y#>V`qNT1ohYYpWjAbmKHC+pe9Z{M`w#8}@9dCpCz z|1(d43v=kHL4CW6K2LqWryn_vD z0>jW@J~at($uaui{F2ROlB2AM*@{IZcdiP|Q4`Gc8?=4?fc;q+I=gsDP(30zSB&pm zNsrNEfaS35@&mZXF)#L{<(M2`=CUT)m)N!xo^+K+x}C|Dh?^X_I<85SX{|{;M(@YA z#>}fyTD{?IVZo)+u}M;{IMKMf+0u5(yjjrl;ihwr%A>czwSA0!@uQwS%w%0$x+X&R z$Az2DfQ)@LW550-D_;Qq@nMTfAOq%^6#RjR->aVz8}lz3I~~9Q2UNwP_v&!NAD(p0 zcDvcUsn08oyYnWSZUyN5ym^asG23zeV`XSg3V2C@<~2MMCXIqwwEHS@)cgM}4kJg0 z>lM2|B2A5k@uQE%NeNW&nI{jLqf# zk&)|E*6_<=F@7t!PWS@|POhn+KbEE)8+41dbCoog`$spTbB>hNWb2b-kWc97m%+B%6R_G&fe(#Iqq8v4X>;T28i_Zj;}Fu;$r>j0fmV$A=bQW3 znhW^YUVsuC{mze*ZP76i1Kr$hzJ$!jp%68XwmIB>@)*gh7K)(}Lz<%s!0 z@CZ0_fVEA))N}1sYUq5`Bfznr)*&FEx7t5QoGRWytA^b7YFW$0}E zNf2C(Slj3oFHhK&CW!9UF|lneY~PMNhQhp&fq6geTS~2V62hk1kDLeufjm}e@@<|5 z_H=n+&T3)?%lE7*Sz4l^Y|*#j5r=S(Qj$O?`GfhZ*1CIxfJq&MI|5cP8&`B|-d8l= zTTq0ldksYZRGQsMDfcNi!Fn@pUZV$Ek(tCnYb$Nfqr?l(v|0P=PZ5jo^CbnVwUXuU zs**dZbg%}eR^v9y7E03i_7WIl=a;Q&z_HQlxemFfxx?ONDkCe1Waa=11y>2lM2l9J z*Pdm@wY*jLGg;PT=!^TghjWdhT+@8DU=L`a)WoXt41QL=R}W13$~RDLXtUnhKRQ0( zZ6C0(pRrKMdetYjCOY^`ge*-vEWvLyeaInyS9M(L-!fT})b7G2Xn3~&lq6d8dtQK> zT9bR(NS{C8ALwW{TZ>QMow&@3oU8F2eME==Z(L<9~9dVX6Yy*pH7zVIG zg|SD9hW0ztfQqyPw=8m0u&~#_N*l05Y}l2$*2dtm@7Y-W2cWEMZHeXHAwNdGI(?DR zmbbUbd1+qyj+dhE92+daWt#&DGcQ6!*{vRn%*_#+ zr(R!?xpudTz$j?pjVVJaeGaT3auBFO0J+h6y^?D`jrx)>&RF8k5nYFqdtGgPigbl^j$g# z^gd7CEAtEpaq1UfD)N}nUHc-jF%$%6n8qFR>*@*U2;%|@JC9^GHrUFr&EDS@y~3~K zOX#CU{#>XVKFrWOl9_#HJ{Js^g8S>7MB9}&Z->Ax;sFZ>pWF*6bf3>Q!Sddc!4QsO z)6pvZT3Ts&Y=XN(ohX|%8rdM=kCS%z81z_w(1;~&{!jbFbu;J| zj?y^x%JYTzt1EEa3Y7;FMR_aPReGL_{SLRKNCDk(q9x+6bKNpGE#W-HRV9kMw!ux+ z-k801T)3h7`xipG2Y)V8T*K@i$J`Or+>rak_|-~Jgyb~;U9cd zNS5TKnkLy`jcX*1U9e~1o&6@k)1f9Kk^mJTNI9Gzc6bAR^?WB3{7MDPyq+UQNlHb! z1=~+J2&E`01rr)~-#RuCB%CgwT4XJMx$>0Skv6D^6!@HXJk*7~$t|YVD9OJo-PV`j zfJe0pQ)g*P?mf-@Vv!O7CYLjx^4^rrJ2nZ)i!ME zs*@A@h76i`F|ekwL{gz>yrnP_oE|j&qZIR)GrV87=CE@=-I6Dd!nL*{87fDqiQt-@FSWiAgc^6s^@G_twPY z*|PR$t9a_bTvs@l{Q_%~cuW0<3-s0{i9nw(fY=uX&~1EL)mE^Kl^Tbpx2(4;!Y=jZ zn0srEMcc6T9-p|4Zp2h1g1uTjfg(E7)wOgRU#`fD0`B*(9eYvo8w8a~ z#BYTAzWhKFitc@1(Xs2v^2T7gylc$e&8iFTFO%Ed-0ohLgs}03?~!47u^O18k@L1R z3|kxKA#p?B!$}-tst13nU*>asa;QSt!G_T%jp~5xN zuz10;ynlwrJzKUo@sK*zmSf%L&hjo2;79IA)=zl{ks= z8z1P=C3O}z$j&)*sYV?fgk59Jmre_&EpHHR)$zLQeYogG7MZ^O^G%e^XHceOJlKXn zW45B@W2+9q&q*W#KF<|1G@&`Xg-RkRQ|*tsVm;?a(;_RmgC<)?(Hg`FqVh`RfXhar zno&xo9n;;%@mu{zI~L+Zj4zyFRA2TgX8S2xV~)3NKMB}~uZwKvfA&Nc2pRKJD8+PD zn4`@mkRMYfz^taONdGd+9wpG;^C+5l{qAzLk`w!Yfqx%(dUq{%0OshqmK)kH0la7OpJA41 z8G3ZU z%sG}K;X@Ur^p*+h_)SOJX|l56T&DEtgm0~fl#8&y6W)1;F_!X@)p7Nr)^nnu{ZeH` ztNqf>+EXnPK~qG_6!LHz;rI1k0`AFi1ray=@0lzOl$D+d_r|j-UO_x=7hl@0GtQ4l z&@JtHhjmxO&dwNq6>rXOhn8LqrA@B3pt*Y_ixaQ3t-;?sdm>SNSGc<$(4XAPH7riaFDIJ?U`4>~KcByHF+VdIVb46zC= zj8h~_u4dQv>8qRgPp_f#a>FT$k){fxp+~yW*jCj^5|$Q-oVSLrUpdRxrIky>K7xAM z7Vu^Dq=?5AmD0W?I?e`ZSN98Y=Irq$9C#T{?uU)l+bvbuh_jmR^m!nCA9oj6s?uoB zKyf6N;CM7=GcAS<4nn<1{Qco2Jv z{f#N-?uiw546$WKzBs81uHIh>Ek>0~gYvdFg za=FM3DgrM!WCmftg^$0%&KJ6G^3O_4bOyYfIbON#bndj1eDe3MW~;ZA|dkS+^uRgGHf3JYULNz^Lx z9V_L;;U~i48CKbFUg(?*-h`L|hDtxQcnzD>s)6;=JC>G4j$$UnpN})W(E3F(xatig ze)#+@C3bT?>+cNYxkOzCf!+zM_GFeZ=7=W0NEGc2lrGODw=W}(J8j!gih;YRdADrP zrD&IW6r``P-I@&T<|$3K_CYZrI`Cg%jjzIf?t9G44Eahxb5yW_z3qM}2CFPiIto$E zK}O!$v+lIiqF@~_ojWOGr$6VkUm?K(%wL^ziJGzL3K~Rl2FAdV!^;*{P;Ydemua7 zbNefDb_;Hu#tvgqZ(C@AU^K~x`>D|r5r)_6iDL3`5$1YZv}?k20ohPz&z~o5br#ip zPYjCCp=w6x{E9~CqGcky2Ss06Z}rn;`Z?GCtZ`Z@H+=XW#W!$nUtO0POu4)NC=@*_ zve_btkIr=t5qxFZ7=!mDVmzVVeQj|IrD>GrlUz0Z(3Q9S&d~(h@eqp^Ti4verNE89GaSl`%3(%*g+ig_Inus2t+v z`O|jCZTLWqtPP#w!@xM>H=$0%hRtBvh#Tkk5m|jPJ~w!6m#QAs%!OwCIbw?bR#0qXl_WZGS=87D81;=EnSjO-*3E4?D{%w54}<~M z*C1uJtHLy8G_Y~_8snXHep)Ij&pczDl_obpd6vXqeVKNz4TlanF?vsV@fTo*v93aQ zRUYABtScUMslVykA|?BDJNKaDkCjb+%?A7h;@xmNK~>Ate-Y$M{sTn@Cnf(j;q`BT~i-?t7a=T0~7sYCcG(g z*j;KY!EtXukbP(1Deu5Je$_cdsoX5G(^*Am6o;JErP*aBE>V5H&efJScBLoaHeqD? zs%SFkKx>Io+xH@6QDffp>o|vzqb>%W8<{_2$9=#UtkKJ_d+E&@6K108YcegvqE0N1 z_1iYs%8z=6Jb@wRLteFQlG)NloZ3Bf)%+1Leo>qF$Ukp{I75Ee+$TQbW3|(jexUh? zcZ<~zYC%-6TFa0xC{^cdI|nA<%+Ng@;neULoBwlb9nYsP`&2{ODdTci{LFm2)P1+n zx0}Y)3uvdw7T#`nyU!-A49o@6W^EdLkXi5Eu?Eq{m$8Dhlbp!|fMxfIvYGS)uBb=Q zIP{!1$_t<1Le&yW5!yL7$JryOtU4Vg5-Rwb0Ocz(Eoaae^wA5~ICP(L!h?VCryM8D!Nv0eff!z-WHGVXFEA`g*?EHog!z{p|HRw|CjqcVjRljkcQ{Dzf)fghj@XD73A}fYEoiVIXcrDX{ zH(?pv%ml*arE;&^+I@oQR3k?;PZchKc)my4o9#3)qMORbXT$0j0DoVCB1lH-3E!$r z@KZ*0Q6q5e%c09OFXxG<<_a>Lkq@Wg`6b4UB}rPUymjUBy>`(F+tMkpK6S0}EjarV zZ}hn{J#Rmf1~0s9WMfRXBjWiszJh}rvt>pYH$Y$iAZe#+5dV04p?T}0)^WNHfbqom z=l-Kr!s(y;pc<`cj(@wHgJ3F(P+QX%dGoLzwHs8VP2YY5rg=(Me}^-8BjyWkby)NG z`E#pb|7msQ*=Xc^@6WD&U!6xQdeNmhTYH%QD77p9_n41i86g&AV>BL1LSuiQ;Vjot zA<5^R{96mgW2%V~iwQ>Vr4b+1FX|+z#7h;$Fwd8Rxh=8sX7-#*rOL ziG~Dhe!JJ4ZX0q@-`U@sQjLowdh@+o=6=}$A?ltlsLE$4Zu95&(T6?uIX_G5wUzI7 zK8(fm+|!J3ahe*<7%p~cNYO7&e!1iBa3Rc#nZ0|-D~R$y3N<9AVqJF&eWY}R%t2rq zpQoJ3oRM&>M=Yi6wTO-Vkln|4K4+yNl{XITue={4Do zT1k<^j#!fuMkDF&-ZP5FyGa1y)=ci{0-gTirlx|6E_lX`A=-+bbZ*Cs5!UfPb;?Ab z9eEOVRjjO+6Vt!-nTI4IZru7NdQx%F^`7VS=-Ax2p-X%~tNik>_=t$!pWgRaR(U;? zufsDFEtqn0km7uTQm#&n@v$5k7JFNHgo#3`4jbXG zY&2b@oEdxo9ys+Zn=>ckc~=kYL(4-ZX0`3))LHh$=OyYiecJ?Ej6^nP36GX}FFZAV zO>c4d3Yk@XU>8uHE7J_2*8~+Ba*7#pxXOjM_V*(Co{q`2sFJLegft`znpW`trhYsg zHFh2VD6L?K^0fdbf4DTX*Xkrh0apm3Jp;T#R7U%rnxN<_*y~NV=?n5?x3!6b$pe&z!#M#+d?aap0 z>bXzF8yq}izumMn{KcUx0I#C;E_JA%(dQEWMTQ`zLK8U$MxFYmQS|2JdZQP0X0|0q zBCyD(f%Iu*>P6I9GtQ=;j;*yFPrEt`UqV;Gq+MroXU$c)$BE%I_pyZcbb7%3@E+sM zkQ7<^laXl$x*5W##Aq|k9}64zk{i?B2cve*J)ZqR*yF@!HccOXlItakM4D;?&3upX zXRjV7kCzcXUQnLU$MYVi^7BT;Woa4PyK&5?Qm{{*4R%fzO=Ct>{v&C=qZN!Jp|w$^ z(7j)79L0J0aZy3N&A6Nn;kD}Wc$1fD3d#;kQx8k2F`EXb`d2{%fze&}tu&&tu;GOf z_nwjXk$SE3P;XMMdh^Ia5Xm|Wo1Zq zs5MYk2a_@DcFVEvQv|`v1*hwAySZCNpX4e*U`_MJn$BgWg;*|BqsXaGi5c%*g2^9C z$jZP;zc1;a0D)I**#L$W!#+$(@)j6Lo4!qC52z(@5;qz)qTomG9w4AW>@m2JZuQz* ztCPK6M?UU9!aSDbZzh)8(m|Nkw)}^i0N8!mPDMrs4ZeAwXT%r zTbo+`K)7{^%-tbem%9Kt$XB!7++DzpANQcplh4AMOqHeGfVk)}4LkrqhFgw^vj1u= z3SJ4Rw9TWRAs;y9BMuokH1gS3F<5UiapE-28DkMRD655KHhMHi_>xog{?US`3h?DB zI{>H8V=Q32`unU={z7P`a2CaZGiEiv0X`v1y?(>b_g9d`+;D1iN(o$NOBcnDmc=dN zZ^qSg>%MDqsM1>EdDlA%dpWx_23Z5X3W`vq=$ohPa{hfskNT|Ex%le7%DLwaV>ONDsoDb!%!$;FP2*uu_QIT}EWx@idyC5D>wWO(+sXkYCd=MET z;wQ*_MI~+YZkEkTZi2loX=Es~I>pRU~qr4wI%a*BmYSNp2yR*vP zvFxl+_1#@LSt)@jYFm$ZnV4KZsO(W_RL0TgR9)*MPcAW zV55X->shK9(@9axe%FCX4AaSIM=sYh0u5t9$s7-u2*~YdDKC1FNd<5Y>~Lot zYfitNXR!nGbaC-GckUlbANgHA>%c*{osR*1Ux-3F(Bt; zKhK@vo?VrUt8WGW+Ak#!L##Kqr5{&k`zoEhG57pW@w0> z|1-rugm^8q(Q$w4;WBFj0viezt3U=Z-s;lWs;A6^Ru|=;INkI8l_icUYp9~fyJH@8 zHU{+S0{y+YtldTF1?#_gF)XYdT{Xs=ogVc2x(Z+wNzfHcP!W5mHqKbdNAlg&*ORae z8|a+AY;|AvDix6j5SUn_WvK94czni%JqA=Ex|&WHE0HcrmLzwF8Jn7sZ5<^QjD!m3ox2ff0=Wyi%!^v z+d0OKI4K4p^+nZ6z#DvFkvqY1{dq?e=UV8>%0t?z#Uh1e$AQWEl}h$ZnjhJio(P-! z^hTI=FBuJgt9RmVmgp$jqElMl-LJCK>V=t>zM{-j9w#-+wW1{{3Ay@djb#dV@pU=r zVsqgd^9IPCZo;i zHU!OHdeVPC@aiE;o+Wj4>=b_VG;1AmnzpS$kWqJW8z}eh7RT&L(@`-lkj*f7l4t?4 z06J^w)#XjVuW&GztBt^4cJK+Qqgn445o(2+_x?`T{;06HM8X&8aT&T_ogZsJDeN66 zRrry3{3zEydav&$zt394Eq5^nK);x+5Vo!t0*A$-so1rHX}(r|4bP+#tTHJsxZWx4 zd3WAyW;@-$||GH_M{{H zQ47FmYR}@b45Of1GdVSop09>StlraB{`SG`lw^SI6$7J;-U0AQUdzPt570^~0^}{sqq`0G_{_~hD z~>*ZBA;<8T+AAe&-jiT6>qR=wJ;FJ<%;1s|I6o|u``Mo~HG zEq`UTqY_pZb;!N({_X$@`NLL!oJ%)g`{GL})y}U>#7qf5)F_pGw7%xZr_rfqB^Zf? z4)$WM@QVv|#~eq!o{I0C^Pg>UiGF(KXkS=+D*Kf9+D>PlwvP8FOagw+s+Ms&gIQZe zGH2LHd_zJ3IuoN8G;_z4<~ws-Pm0sg7mdNGzF7V75r3JveroAFky_9>@rCy9DCG|o zeB~@%ZK-Dk8HPR$dX)B_7dkz<3qQM*FmySCC6t&K>|R5HaUGv6v9LPp-*MvhSbVt{ zCAhiTrXupG-<~Cpw+|yPvTq>7xt(I18sP0nJDGQ%MF*&!GO-9fx6AoIn0xD}s@k_* zR7F7%LApVak`UDX#IRXx&2^ zt1iuX#rHSOK;GUBJ(d%CWaq|mb1`wTjfoCI2_z(}&N76m7Ac)`8zUtABGMKRizxF$ z7K|0sZmg1i$g3$^=DT;3Dbn5>HzrcQ)_8Rwl{3a{a51u_939XE?%GNU~ocUyTj^XE_GfJ?7(@LKq@=+QMkZOaD-(g3)RlI-t z#qm*=WKsaGhHJCqD?H zWDbFXUq+2qB7d$dS4R`1nQ=ko4mf(p!c6lAJH3x_Cn z&%@{npxbpnc@0WtV%E`K7EDT8O~{ULLo>+0P)jx#nyIxDLxrxT30S#gVP^G}c+3iS z1N(^EN5S8X#nxo3uC9R>txqPz7D z?I+Jiv1p5hu-M?2*ssz{@^>1z2AT_?wNPU)Itv z8kkDK>3&}(4W8_cY{DeTpDzX+VKqTp^NM*u_bO&frL=DqH5PW@&AJ};y+;6f061Bm z6jYi8W))o8 zH>DYhEZ6LC29`Nb8|Gt=C^A14+tT}(e=p(0RM;pn<{_uv%DC-^%|g<@8y0m1rFaS` z*AiZbUJhh;vsYTo6~<#0Kp<@}Na!3eqsnA&}==I$di&xvrDB zb*^1sZ_wr%4(Ve>{c78{fm98!Bj9J$S#f1p&{dNj+cQJssL`YnLrPg~&r85w0aWRy zxG=&2*NkYw)tWDAaSq7dsgdea~cXGnm~S zc|r;{l#xWUPGO?aW*o2wq!=0)I~zCyXTvCH2DL+z<0)d9sWiwDEB!k2a4YnBiW?e+ zVY=!D2E5?@ymtvCYKIen?@WU)I*f<7N3iK> zJ^+J~5mv5Em0YW2!On}A&SM%|j|}PGLK*Ftl2vg3(ONkr?K9~|Eq!0oG}_JL8JmVo z|1Z{6d>P7_gXa$JcIOovNylQ0E(@3;_mr4cg6>{YdreRrbCCmI=Hu#r zCaMXUWRkPkjSw)Bn1D<^uDX*+6K87Hzl<0u4@#aHb{1WJ3$aT&>%>^#qylRb{R%ZL z(}%YgT5k12iCXJ6X4qBYA#~&4bdYviY^%>^+2ngw1DvC@cdLo4$jD1sufzJ zQF_|fB*8k@t?nuugj=DjI+2|nZu+a3lqUmv+Bbe?mOSY?B30r&`v>__T)8ywivi)_ZsOzxVM}kG?<6H1a=KBDRf?O zdMf?;9W*I-Q?bQxXR>0v-i7`4q+MS9ZRI*g#;?up$8^2)DxAKN;N0dnTQlij`mt zU21s$3M9}w55{-2dQ#&*&_%hF{s{V$DtsRN!plu>OU7XYk8a$R2=c^Yp40;Gi zV|7&7?{S2uy33sLtrP}xw&lvU3mNd-Q?a zB%Ncqw@R@d)~IV#DCJry{3xriEr|R9$Ve$HP3ROPfccqq-WO zhmEbIXiXkoBFhN&Usv zsRgHxIyH%RTaOG=yPc&8N5(gEUH;Fo$NB|))qZ}44>lofKPJRv9OQjoBg)8!^gUXv za7WOW8bZ5Ni2Nq7k^Vh_y!W+98xRp%y6);v4=RK{%6bMpj|HsJ z=Vz&XKighq=b)H~F^4S_U`Ix6bVg5X^Y{<9`-N4;keOr#^9k>ydH$?ObI*^!bmQ)9 z>IHpx+#Ac9CRxXORIiuW{oRp%9I%a<=~)Y{Ur@dt>fQzRm)Z|(j5#7Nk&H)m+?u%q5Op)F`;q9=~5 zCvKNXp}*bz{N>&2Fr@sL0y@|icJ>n0-=V{=t62)UrwCE?POxaeAI_3!!B+a5rq0lC zrtNyR;G4GLOS>*CD3}rH?8h*|kP!U3Cbv>WY%F6oQ#h2-j2GOO?5$E}TV~F6?uuK)rHm%hAHACzVHuGRkHnP8-R(XGCwX>88@J`>Z7P zKUV!G=+g)1x;7FAj2|z)it0-g1Ozm;09?uFoFWn$NvlJ{p&GBQ_On7qfUzH%nW2zn zy>ccBS5S9iu1QcLlR_I_Uz($AXlW@9Ev(5#ox6FDZBBsTt28k;TGB^r=|uwWE60xd zrEsmmN!q&>1(9R5BM0@E3m9ah5Xd)SfC!v8^$er8v{n_aWNJ?FP{C`P=;i^IVn&`E zpg^OsH?3i+4~giP?Kv87GecI-mfiX6Ek%C1Wxf{TmRGUrueFtRk=^sTs!XF|Mey}ZSb z#FKe$L+)^rfFByczi1eqo4(tRNLnAtnCV37u>Kj6=UM=q?r1GDJN@k*fwc9MWL@0k zC<&X>%H<&5W9_9xR_Xlc$2ONj>+clcs^#RD!zW>TY$T>PtrW)YEAI47@i=T{Sivaf ztk?}0r|#0jNTo+%xunl1plSN#zpXmcSnn)}ep%QB9kiYmeuK^M_~0*{up)-hqt56* zQ(=D8cBU8Fm`NGgl7;WU?;Q`iEIzhT;BZ=zuRB|OdXY4u3^gaNG@W4T<(FI+f>_|d z9KhPYm7@o^i}lF+>b%l5r$jzKuN9~AUMnj)AT>BB(&-3@AWjw=cQcL5?DHnhnD3wk zL}0#YbP3&k7Y-?nceEHaEhr*83ovtpAPu z!|Io%)VC=i@zbyrjW1un5?Z!AOTpRLdW^## zOnSY}>bg{Pq`Nd}lsMA&qdwRnh`;<+v)N3~zgImxn)5QRL#L82k&x=TO(&5)uREjq@K#w(4XIUJNQd)%RhgHmfOmWC# zw%KtJ5bN%(y>#Se6L`dNtSr~FNs+UG-=YD2qHuz$Ig@>Z5n6}rdSWuy>{g;$Lx`Kz zvW}cfLa6V1DG*@{=}@Eluo8 zxZemrE;`n@m4D+Ib$pzIMO<8v=G8o{$NG(Lxy`4|CrU&B+9~a(W$ui(?7q%sz_uO0rYpuewG=FUmM*bSQ^sC zvo?k!ZmSV2Bi9%TGU~!+@c3Y+O<91e2oO;a?q7t$hyW z81FoHQT@yKOwAoe!k8q+Z3N$xykxOfzu;}ViS7J=`CtNn?;*j5LzzQ!B~L8KPiysDAFi>0>uL+LlN%;*;zph9dNs`W zZ5n7eyT84N8KMAd(Px|-5N92{T74ChgkJGKikaOC1^-Ji6BJ`Ms5g<=C~m$&wW9um zwkRNGzj)nS(Y9Ij4{whvA!y3UNBS z^sxJQHuWGY4fLr_G04K}#=959!YCd)54MG0olBeL{S_2-=V?`}lVv%i^$ZZJ;8z1T zL_j>)(t_qeTS1i@TDku1n;9=vj}|r>OrKkr_}!k zg5q?67LTrb8=veU`)T+M305U8;gbBM-$^x^*4!shj%e7F6CY~aaMx8oq_x4VZ{56G z*v+#aI^90|$>sS6a7z0xlKM!$e(olUArC zoV|888w>b?6&T>Wdj|kPboW{T&YBFEnf6y0=KLiRNQ8jI1|W3iC$u1OR@E!>QXj9Gl||d06KeT|;4Ekg zuN9l$uy|KO|91H6XypQ`JcLv8Db6`m;RhjB%F{=+DD?S(L*u8S4}YnDCxvaL3MJWb zU0z*}qBT#=`9(~Tt&KkK6?j4RfJI(ASpqysUL)-97I~Vna9_+_)!x6ZX?=~ZdQrHn3%>O)OdS^|F1EaDj7jxlV!YVe z2egM6^vYe}q4D!#q>azr(PZH3ijJ1cb_h@4fdj}8F8frzfLY%CXP=jm(iVS3##E2x z5&!YMyLB5KJ(v(!xmi8nSLKf?f7KSD*sS&r?T%mnzbT1PZU3K3VnGc*$)Mh1?nxDq zt$fx+9=aom%wF0G-uQ2{cG%Z9AclaKLdz-R` zqZ;aJWX>|3oYtZ_I#nCd~NbLKWQEfNIrh*bumg3(OIJchcXmF*ME)&xP7)E?lhC;6>xM11e- z4#9A_b4GXD&ibI|fBSl9oK5R1C`R3RqZq03`>jI?@bRHpZb3AfC}KW;EiEn5yn*9N z%N=q6q?jk6MICx|^*;O5XsHlIM;)P1FDmA%g%jQjP+fSB9cFd4?sZa`(}V= zQJAcl;*nRWGeTHQjPL5IzU$Et>1eU-Vr#;tU3<{OJj)_6#=C8r09+TO8A+a|B4k3Kth zHnje@rty}ej{RjA@rgDZ@VrS~4Mdo5u7qp+5>8d#oWjopm_2 zO7H+{^M*#-<=HNj&6)4$;fJ)IIdmB|^T_LjjiDB@Q@)3MPmdZem`t&u)JFAY`Ne|{ z_-|^($u-P2y?nIc*0b1V&WFure0_){>AYr@#d5b?Lk+xrfER)H;S z+3}H2o`><{d|nymv6z9*Xc)pPvz)LV+h-Uj>GlgzM-MT)K~nebnPZ9yzL$UgR{?FW z2BOwa3YtAvrlO+zsh*I{O*0Ow#fQ!=b3;1!QwDr5cQp*eM2$EBbl1GJ@v&I6HZmu7 zx2b9RlZc$ByD8!KjdvNGt@q-ISc|Ww;-w?p9x9mhzK~e1lYd4b_O=D(Ez2+9xT|*s zatb^OqVkwFOkcF0$*#lQ7YiuB#f}LTfvjphZ&*6x9Te_k&zGF)umE4JjDQE<6fUul zJ-3{DT+n?WSKm1q6EnV6X=Z*Ty7D^FKtlvu7k!IlPnbnCr#t2i=9qG6qI6ubv5MCs zVmK1!!SX*0=p&H2%i`d}T*bwLOD6dk*l78*_ zP>VMTLe=n0HnFnWzdJsQs(7sa6JLVyh-IF_x`R)>3G7m#{ znum#4mr-+}k2?yAqD`yBt!2wX(ASja?Z}NJg{5KGyk~(YYc}4q$EjUEEu^AFg1R)k zaJJ;!r|N=@0%EpNkeGG9ywvD5NhQ}-Z?kEwJJKzw(5xs-%HV74W2l6>(|*moVGx^K zg(4a*4t-@M-08735RXy#xz3b?eiknOg-Q=iqV2zGZXF~4zt-G%#I3srX}kpVCu;?W z6F!})wkMegO&EsY{Z?P7`&Mvv{60f@4XhMHr)0io6VhAj&rf~@GWA_}qIQ?K=5joq z4AI1|L`CZiKl4v|`B#E5Jp1G46)A|(fIm5QUKLMqCeD{dA7^r^x3L0_|Ji)WZJ;Uk zW=vk`6sywI2_;*y9@2r=aNg(94jgqax9ppk zj7Bkj$1CIJ4LuZ7VRKUCxa9vMifUR2gqcH`SlTe`rs+)}G#ecQ*L8Zh5GGx$!8C=~SiJpn>GRWDHFp zE`#vf@gnfr9}{k&iFwzvxb7zK(X5LB8A;x!k_R9`t))f{MzT+W48}d++@^2Gajt*V zBg+vwjNuDF2Q<+%VTtfW3D>|+v<%d%GHwym2BG*tWNwt{ovD^{%P|LL@}UVN2r?vW zz3SeIYZXEx`nWyPE|b;!$d=mqD8m1ziAY@>m4tDRqK$` zM|=WX%)G#2)1x`4EmyC@F>O!QS*^kEdmyaz4fN@0*h>>qh?-ww`PH}x;$SPQK=WFO zW$})}4W99o$eup{>us@0abUD}T(YMPnR zSDL$-4ibR4?Y9T}&?bk=2KWNKkdl%nhaF!fkVRcTFX0p*EA4qdaRLO^75uSY=|aMt z-Fc@wn41;0W-1)(k#gS-0NElG)3J68FPeIyd|EF?&8J5nBBIt{Dp_c{NBIP4G;k6^ z!{u2I(c4q0*(0D!mM_14(VuM6hPt0-+e$g9NdMvbU4!btx)b_krah+O^q~K<9{v2C zrF;o%1gZ>M233dHzFq$V%Rp+h#a&ET(zMI-fiBJPWVX6D+80bRS<_@*l|~u8Nm zV|3_xvQk?#AwZw)a`o8bt~jhv;`f%v%vB=PY;%m6T&hL|m_RP1M|#!!3sfpslmCn( z!-8;6I@-0jfSmgLzmZb|;^9+`*Wwbp;tX|r>BvDtXE3?4hc>IrfvrtA=$uD@`&58f zlPNMUKs$zcN%dIuCL*(%Z-0V7fp}H57cW@q@Ts+yzezBMiHEwLm++pxAImt)hD;5$ zDv}uvkmRlkvXaPet`gETA_<-=cs$YOGr-rORafOKr zNW>sLN=C0Ybtxt5h?9L;y1ViJ%y1bdU2g&tFo!~%yZ&8MP-1^)xL1$aqj*3S^bB$DN;2*Zh!m5 zZ;6z?MnxM*#FVfea!AVS1G@}*D>-7MM$V+6Lg`i(w2C?t_3KtJ^$^?dJxz@*O6C$i zuK-u$4aeyYCj#p3QPB7bo%1>Q9|Dv>{lC~AhT1ljOaQ^}?|QfOd%cUQoCnBwnHmnM zJ($+g?tO(G-T%-%qnjbOVq*bG=Uh2b#p(Hv3aLpN`5k)D;JZ*-YHDdWD*RhPOmrJh z)&QhMlX_StG6Z>mS!6D&o+o6a+j9t6jkgkZviS=*FRXU(v1uDw0&1dl z92zzRp?%G#6~!Hk{Q3ANiXQtML(FQ%uK5Y3(N^_lTpU!4CjWH9aO!98)-F``Mkm*1 z6hWSo(o@l0{&bN;2^2xTD(+*v8`O;5GFx^~wyiV011Tf_xf} zCbbdo%L>987-RA;HS;vsD56dS*m#qqkS}q^#Fy<4}+KrZq zc^s;8fg>hsu~VPd4({}(Pf1(;NA937lEcT?A+4VCLfnIs>wzlAgN> zJG5?qSdKSt&0gpQ5`JrRhVWTmoSvCkZlzgVzu4@%mpR~f;98EVSR7x)u6mEwqd$rK z>2&c-eaW+4L2`miK~99mvTkDHwHG3GaVuC@9dOI6ILA6+dqn->_3>J%cK((?HZ80A zqJLU@vf^ntGl^u3H8;^*pzM?+^T}a}SAP~VrhNH_XzaB*Jvk3O(G$#n)hp%xXCKB% zy})x5`GW)johzvY#~ddCrSq{Y5hk46h28iJ(7$V^jwWr*lb>r~*BW#?R=IXn?k%~L z3!C0VPR!wKsg7HAVGe!*bzBN&-@D$uxku`LmS(0(M)oP_@XQDRZeV^Iyhm>4nc`Iz zX}lekEe20N`<>FXx*Yp$UsAsp<`;69cQI&VqO{e0cj{}`A$#LM*Qv2{GZ_DPX#{7NWKk4}(&At{zb z2*!8gc=jPhKEt;_jU1Z0UH0kZQsIjPZD6aDYNN!5=l?5>gsjT$atE5t+N6h=0~|f& zU9}o-GBT(5%adO7V7aRXQ3(ZZZ0pd0OD@|?&bY(YNb(+FQ@>@Rk%>?VK6;cnUU$l& z>Ps*YVcjiVYrt%dSDuk!C26`W;bLM%>P7z=L*%^Pf@`bn&I~CtP5H6xThqZx&oJIR z!8?6@O7I)CJl~e|N0qwjL08|rbQql;R8c7o(f~GJ$KyF*sH3i=8j8@Wf(90*t1XSq zW;~f(sDwX605y)hR+&_LT4kw_@xL&R)ncDwmju_15F{Un-35V|E#ID)y8(u4BNR(L zz_CKhX!Mhu*VGStJi^H;e`TfckHE!S6TL?Jjfy&JxaDv3cza!v`Rxoe!`(gT;ZIW^ zNZgPs(os;GkppV~;##gmD{1#9ayqWY84ONNQqG3oFMJ>WC}`M#{}O-NZcNR&^SQcY zEdF?eZqv@JOOJdXB&QFvTmMOZ>CoVy@4I((I1KM4Lfsi!LpKY3!)C_)G+*44U(vA( zbf=}zO#wykv0&+l_KEVETb9PP(nuiy(Wamv*x%2m$*H&efAZky&eYfqg--l)`)~dN zdbD8TyDe=I?~k7kpjMBs4hNRJan)PhPNOp^nvQWj_HoZ@8)?TrXuG+--i28*I;*#ku_8h@ zrO6F^&FXSTqxFaPQ!@#~Zlts>^P|JHfqZx+e0u_5BWF&*;^C1P-;}N8po);2(-yCxZ(e(T+PCu~mXZCzVg!=RVmHCe~ z{@(CX%hAU6l5+t*q#5#Lcnus?*aQ|A15f952uHl92O`wX4MqXJwSgPadep4!W9)|u z-jFQ_4{2Sp!%*?%7ZRC8Rs+Ib;X9{f0$|?#)1s{ZWypK8P%*7md+}XwF8QTBdYoD_4X8Hd zuM8=A&%$qhVK|12Xc>f3SYt1CJOEq(7DCHYR6~=q4Ek&u;uI=b3N1LiyW?IUCq69a zFCia7grCi!25gju@qR%2$G;3Lpqb;qRO;fB6&BMJJE;JQ=$iv%sfe+yK z)}!O&T%J}nlb(^`0Q;H5dDaG}y(4zbQ%_G_ZBS=V7tDhqJA+1}z(W`L^z@&x19V1| zDkfRg=;UO4L=zaxLKEOEXMcd{K||x?e%F_$OZy$99oMIGp*vF!^V<_;G-yl={@nfq z8D;`Jva#Oq6Sf#iBnf&Ba>`?@i!j`h?&>}NfsCc%*iT&E4XMeB&p>BuE%`U{@?Ru$ zn_=fbMXP_B%!LHtLCbppS~U>+6oB;1R6)t2#UGPu@1VyQ$mlp+|HbEXa~7)Mvl{+c zQ_SR!=zBNQAIWH9hTn&WI9O#tQCLz?;{n2auz0ADW~|6%NSS3exr%!}l5wb*Ufqo| ziP3uxMA7>F@c=B;tZxC{bQj$IzQ~&WU=iaOX#$h4S=}y0vvp3+;^fT5$H0*M?9G*n^5=-qxIYYhwxD@C*kAbftLnBJ3k zA~WyFNjQ{7^(1^)(JsdwTGPu1t)ca-0c<0Y&(Xuv($jx{|K5b5sU_c-XdkmXLEfz~ zGqi^4P2&~j=5cQv-!YeBCRwrGf%Dp8Klkp)2mAgeoTr@VGR_>KMZaP6KC*Zc7dBv9 z`nuo#Sf#=^n%?6!*7Rtzzc@0w{@c$(XK&puY_G@07q3bVfKvqIaBT2)a2V8sG>bJL zMW9k^)a>D^!fFck@2;t_-OnxnM#2>BwFHVOI(oQVspmhr8w9+|%)};%@A(eX!dU@G zm5XZHrP8JGvk`u=Mh|{t6O$6VEuGu%k$H@8hpvI`0uAAF29g)(Niky4tm9Jlc;k%|5$|N~S9QR? zY;gMxWL!AWxV(SoFbm(?CRVOLtk?fG7HgrxW`*i9SN;9trLLom;bRS*F+r0OgZ;&} z$B35-?*{(2UQM3~X$D)o>AUAl-`6idf>LTLVP&vZwdp?r(8|v*3M2op*k~sue76p5 zn5FQOnNIhr-t|mz*jK=0yhMrs)bsq)bVx6iN!0x<9}mU!G%eS;fC8U`R~+9`i~o@F-Zm=8)x27tTtWv~7tbNTbP3R!_~ zY92dGqf75t+#1Sb*~|zEs}_-uodM5keiN9}t>*CW6Wm@|X2t#H!2?RQ#bcXtayw(^xuCdJ}K@G$Kpb6svCTfV#Sg6k; zP#-Bn&I;kVp}-hh<5@v6x!xa75gY|^;6MM{NF0!Gg~oUGBKUqGig=PU1*Js%ci(3K zImg6cUQxIBvkViBek+ZX?=6+5!vK7bdwrN+8 zm+yiMFdGkk)8}!_NCP~FD5{{TR&J)ZEFa6hSY2!z@O#z%su4rO|Izb=837c5QZ?gZ z5tmnuUEw4!L8qm_Mi3B_nk5n%2?$~uI`^&#pB887y$~{a>37ZsAcKNW`>y_evYmer zP2f_DV;q;=@45y{&;IG@Pn~xO2^>Cu=KQzZY6?<&zI{RZn%df7S+9R>G+qF7N#gfV zztQvfNEm}ZfzDcRHI?XQqtH?`S9YM}Fqu82FP_Oahb4V@tZ0|sbC8V1=+Wcyp97&q z3(y70@x?EWi##B3_*n;kp?a{!DHZzXpS|}+Z%YdtZ|WjeC3@2577gT}v%d{<{6fz_ zMcBFCl!jpa?AF{R7@d8;O~3IonS!08%;-$e0nhcz<#gAS*f+I9Ou)G|>kc4A!fU5T zZF9s5k2+&^i_FnKEZX#`g`E5Hi~PsEuzaSJzpb*>=;!?uck<##geOEdzq_kRQC~mn z)BgZDCh>VZ;8L5c=5S+{N2QkEjpK*<_bS7dtTMS0Fw!+ zMkYZUlsT=^tHCKyykDSPC{THkXb0bS4f&5zahn!lX##j*jGmg#IB4MOYN-2j*sP*7 z++369G_LSy_Xq&82vO>$7cFVNLW;6`@9Bf<9ka?N@B+E&)aCtMS321~hGc=a)Cok> z2DyFMp>peTDP^z+cV^>1v&bDlGIkr8F(L6yuw9QEm+i?4Lcq8vlcUlyexdNT$JE@i zKanEGKS;NFbAvUWk0nvC&+Ji-@r^ErGLBNC%A$>!F0(KA1>xn?KVt?Uve4fGx^@Od z@o_t$#k?m{L)OJqgcDvJ3^th4P)(!?!@_&dG|>o#V$Af>qDy(xjZ!ILr2OZNeToJ| zJYzF62~kb40QTZzAmvXBK%9oArmcYBun(Ncu>>T{be!yP-yZ%EjT9G?X41TX`Xax$ zlVULHsfHHLpcNj{d}#r&^~zH4ZO0*Lkjdof{?}J52;i6Kye9DZL`1|Qko7V;I%T=o z`lRma;N|w*4NpyN=mI}{DJIS7J7uw+;lzxers6$`ka&5xnZ#p9F6oLIAv&pG^z(y2 zo*XFl4S?@lA3^k)eR+3ZP^=I1#i3qZYCw3_Qj~P*3f5sX0dxplV zQg|S>6Z?#9B%GED%=*dOv0F9$Zl7=Co{osN;Yb3KFKHD#>Hp2i%H$~qfMDU@y)1p< z1p+D%-b?9@&dd-y04hVU57}%fIA@4pnqaICqx-*z7PnjXzb$jF3e`N5tC+&T4uBtZ z{}mPdGaU@^e&;GB@moBE*zZgR?@U2;Zs6BBf0Be4Z|~-x#)5)3{pkyoW({X@U_D2KosdartTtvc6YB7t1B@Y z?Lb|dqvtY$o5)|AR8y(Gj zgXa_fPp4B`p_o7OWX?IBGlwI?jfc^!hUd&>imV;ef!aa;5G0F{lRK*&mA~Gw#H?*< z{ksUHmjpIL$OWGo_rdeA&-NWiTRa0?k7;~ozJB)a0S0mbg{BlxO9gMkQsvezPISKc z%iO1PvUQ=zzQBXS?69NEw-r7!*7f4W-{%e{@EvoRuReZV+(JMAQ5L3RLxf_yRhG`* zAvJH^g&*Iugd-eZ0EL(LYc-CS7-FLGH{dB-eA#>{&#^g-1hiZjS9`nAvhz?UGJ=u~ z$?+;Yfsl1PRztHVp>y)#NT`o?4}8eTuIJ;gS7ZeFlQeHy^kNiJCD5jhVA=~&J@YP$ zS zyt{2ci}@PdYuzsSq4Qk(lYQBmoKCzu?@fRu-WD$jmU*v`+gra>icyc(2(M=4vCw6R zfH^Zb0-}+Zpu;b?Xk3F6lfqYdv3nzWQ(@D`eaem=uijjfDiK{ z9P-Q?rFTXF0#x}Svd%r2Khz~yD{$Dtj;t~D-n#44{u)p{QpyJ^>SuVQV`=hOckdHoiSFC2#@^2?hSFPCr2MiHgaogr zd$nwVSTW)3u+T39jW));y7K(krPQuP(w4QTl@&d3B&F=+6tIC`*3cc696yTwvjr$G zzqzlS+!s3b%LND6gfPFv(uX(Hm78Ksghk{ zI@R1Z%N|kfq5W$$_OfmHGgx`o3kV++8Y1ycL)Onw*>L+%2ktf;vyVK(AkTsT}@-cf_(*%x<|qY2=YN zVKK{z80t;KqU{_lb$-Pa=KW5tT+?T@acYPm<}4A)gmq@bIelOEk>zPRiil>1uK)lX zvJ+Y(J(IKkSU*?gf_ywNSmK)Tc-@M3qZoZEt;7(lU(SfeQa3d|{q_G=+54L94$ zHsPJF`NUN>tCM1W{qi~_>rw@|-eXHn(?LTwQWHP?x>GOkz|k-jO^Ut?)kBi}WQ`U6 zwu%*H_IlrD{Q^dFH5SOw8q?@X#b)M=6&;P%yhbYk06b)CAvDVh&ErH@EcPQp&m==u zdsP_M#R`+Ln-j8IdpUjW5FXJNhh{QRZM73Xmpmja7E@@sC64YpaEH6lO*P|c=mI_QepmU9pS92rOcUBUi_{Ds2W-d# za;D5*n;VDyow)H!*l9S1E@{b$5hV=TU(=xE@0EfA=M707^pxwRO&1j={(>?Z%XAW% z_j;|=Jk1T$3_68(DYQCVsbP8^#mG{!+u9JdMeP1=<|`{jaL6%bddUle1zhPbl8HPJ zk;i6C&e0yMS+9_o3$cCcF)e&e>^I<*g-HuJm7~@7%h0C3&Rwk%^;dxLJ&P&{cuu{S zI2OLWR=S!K9}TO98%aw4&0MzVHJ**$3kXEmMmxzR)S_$UJ${@D(P_ z@`=lTHdXnXLHw(aYn>ix8Q?f9J@vHDo23m!Cok%2Tc$Q7A@Jz*FdphqBh;CB?dx>$?gSg+JM?~+X$|$v-P$bF>D;& zUMI&LKUX(uz#OJKjXh+CW>}vvnqC<&TkoMtb|Gui?h0027M_oncNH+ee6tlTT1!@r z9Zw{g878U@^LxVglR9L^IuiQ!MWez6#OUcvXs}k^OX(HIpl9HD_k83C+cbAlV^&l~ zHabZfNO$Ml>+R2OO*E?0w508GKQB|->p*i41zB7_cvHQM5f$zB=<>AjOMH9>V2wbX z29&DKKD#aE?j-c(pAy)72cy!Ys(Ji*zbHY64hqEWE?LPi`-ez%VbM^m8&}7+#7)zR z0NoeaJ+qDSa`#BClkutdms?ZT9M7O#dYlJyd^{Bo>J+S}gj6(CK5SM#2#=9U|1Kl7 zw$5Pkws328oP9s+Jg7#)&ya}8*dPslPu?;A^hNR(T@$V=XS{^_>0=eHQTA;zwxSUgdV zD=lf_Cv?nGV3*=}rqtE^6C|?+^GPgxUpc*INMtpM(%a6M=-dInG$&xt&nTVA5ycLl zq;0Fkv5E>`2WNbA?Nt%a?hzEBMM@+1W5D|A@|X*XfF_2q6j9 z2ZYX^o)0!Q%zPK?q3sWU_+wh+jo~wA<&T|JXexc+N937kN9d`GrasnB&7heZx=Q*m zAUWcIqWLSJC{I<=U1c;IU}Y1M5<`cIB6;!Y_c`Oa#UF*=U^CHrt?)~5m zz^sqj*}^3VooRJWJ@li0*hQWPH2@vRL!&BB1LyUGy7a}XjA=XP;EdXK;za^`CX1x~ zgGbf-Nq}&#`_2RmG5S$Y``NhX4-g(Y3+H`yu(nAxC+ap6y=?>|{b8`wZ=h%64OElk znb{q>=v+nAW47v_GG z4Jf#AF@P4(g7ORF(LXWkrTw~S8=44aS#wS@GV=bEsvcrH;qj ztr#jh-LEROeh%@Tx?w69HjZD4m2rFXRm{G5FLJ|k>5MrvE>r$EbG_9D$;C=lxJbT_ z?(kihmv@TN5Pf+C@jG7RJ#X(2c;z&JJWc9N&G!;Bj0AqqRx*OQ)fOlb2h5s!MWXDu z9`iio*Li}Mu!iVPbnIagcBi_w1%^3r%*9EFWCHG9M~tqYsxT3DjiI)-CGCCvMrHH& zg`s?B5c(X7IZ`~1qAd1vSH5YF8!k{lU#Qv4=%NOaN5y7YZle}EY9~)_)&spTN}kMH zp4|L?6nMrE#T~<&cC~rwr-y=s8qz#O@R)2%RB*XPRZbMik;ju#!K-TmJK6)p+>QC< zYCQ`z>72p8V2KE+1Qke5=tNHKnsn6!geehQ+sy1F&=VIiFWB*MM$M$fR=)74W8k>G zjGzkpm>XtviH0s;#x8FCuC9lvww}N__EvpSsWjU2p>er~Do*)_y3bHMzeJh78nou= zRylLRntzB&#Bm?X_5OZqca z66a#cKr%efk|lay^a%<@QdE4iPXFA`x^w=kH4HD89G{|gwdpf_845_75Wx*^3G40@ zJrcs&ecw$TEQ(@I(M;cIr#YJFm#Qf2<96u&kEPY?Fui+nAVOSW!jKx=H+{U06i%-! z_tTIiCOcbYNQo#@Bm1@(mNan3AOvjG7E!#mZGVP0@p~?(KbZ$81;tbIB`MP6 z8U?<+F|U_BGGovuPg{BC7LG}xk_!Yc%NzvSzGLlIQydl#P3{kwuCe%*%*q8A9EFi( zIc1(pikHoPB2h<^$~B{*E81$wvVzfRIXJMJ*KAPoV$Gb#?r?NOS+5&N3 z;cY7o6#XHC0J)eF#XR@{+*obP?1?c(o{F$wMjw#h6&xmQxiyW-p{@UCqG7gIp>hls zYg(F|eftXHddEE^UOtbLKtV)#S6zlFR!`#R`Qb@JwV~lb_o-G&*)Rk4_yO5?vFE?~ z5kMv~2C+hYx^Avg#Jf=eScbc61lk$!OzQrlx6I2KoR29Zo?tz&vH3o7{0TLzbGr ze+p=(CwPNNUlpCJ@mJ0Hz55IEBHr0^e*fra*sE!I2MqrFq8$YhDr-nS6?5m3`#nnH z9|NKJeP%z_w{O3AK7G?pF?Lho9NmK9^^XX2+uga%^RVX}UaB`ct3~rgY}e1(uXk_& zwuMSDtOxKoNZyh&3$15RyR=#-ii4xKe>oN48jk4&67|CwiN98&-CNq_^FhEq-&8=Y zeN>GR6L_%t#@1m<&c=zPUABr2aCobQ*sTLZjq<+|0q};Z?u4|Se@%^4T=*kQ2=YwI z$8&2l{*8O|BCFp6_cAQ_1Lm%`?jI+=ex-w9epZ{kx1XqTHV?h=56Nx}nY-CWx|rd( zr94-Jiqr7Kj3qb2zv4AA7~p~fth#)9Wu;)#&W|U8DNgDF_NQuS7le@V4#v{kFqRK3 zOoaOjfn4Ws4~Mkw;ah_AC8EDyx;0$6iOckg_l3G=u3YwF8?MIN**hlU(_jxnW5EsC zr&$U1neR6NA$q~GFO&Zr@%!jd-4!B9DgatxNlAfy9N^|Ta00Bj7*|qyOad^#HY@V8LG%0q>Z#4l!tu64jc*~!benbcu9oWQT|F6ov zI~=aH?Kg>FlgLCQ$_NrY(V|5sLWoZEI(qcpYod!5L~lWqXoEyA6VanZ9gGrnF#2d? zFlWi$``!C|r+sJr!F6#lta;Y+-1q%!A3xV=`~n74OJ*qsZ*jic^Lq=9bLt>fCX5d- z9x~fLwPbIG4UAcY@&}1_-t^xGQA4lxeCxl{xd8g+X*F)|7%*(O0j5Q#QOtE ziuyFqzx-W072oYXxoyIgs*|CQH~6Cq=7i{eW@fA;6Y|NEm<`!6lx9^;ATCLFh=Qu%Qs;->RyGlE2U~1*ZYvbPyrvbuGhCr zM@OH2M-rzSbZfv z(nIs72Jnd*ue&NB5s6|9nLEk+6{vJgoe)ukrQ4t_1vto5lRP9}|F>W^&LCyHljL@y zUW7gRB@rM<{mQOymm!R*1wg5LtbX^!ah7kU7{2JMm>)e{@;0` z>a_X^Z8xI&Y=pm%Yx4n4bQ($@o!%=v#ap`>F_nQTt=1G1j+}@Mxaoy7Q)881?iPE? zejTCjUd~|{g;I^`d7u7m*W%dtPy%)9#4{ zI_-}X4Hbjsr1pRI*^90K;-xmx`P_~g$|7Z#@mGh9$b7C!FjR76VquPr``3Cj>ncbw zD#3yMfgSY=;?`sg{Iq4*7mYa-=b7kIR0)pKbkug_RYQi-EbN`cYD*5NQOJ@ zY=`tq`k>HV$mhG_fpdBgI8#fq!G^z@N(36}|c`x#O2s(44(v z2kd?TdEn_%xKFeQrZschsy*1$>Wa6wx0QoK7v05fJoj4wulg;QT;Jdi;Fui$8Fy9X zDIn+5oRk0H!~0V$X~(K!C>LNPi4Z*T?9|po`v>Uam;T+(%_oa7%{6A{TX$&OMm0XS z^sRJPN{lTDir8A5dCFl+9KEGI^QN^SsR}FquYCvnqW{~(sES0nkt_qSH9o+0{_w*g z*AQQ5hXS|A;SW-%#u9e=)_KHF=55X{Eplue4Bf}0RnxhpYAnKg6In{(()~SyBY`gx zqwc3{XmNyFAT`jcUtbbLo(>zk)mp4szi6`48X95I{G`4{Hd;?mc831t?pJA{jTe-C zg_c9bev3oI%rM_l=y5)kWZY({LL8A=%`|&Q?fMfedzAK&@=R@&A-gfHU-Z8)lVMf? z@@x8c0E);HY^Tc&7ypAcM|(*Q%*D5F-ux}Sf17FA1=yh~$Z^6arJ8i==Oj(e&sOD* z-3x+VrT=b%?1^eOWz2RHSq->Bv?=1BZ8n6jI5L$`Sm*@3pr^~7mh}No?WEN7wlpVA zYU{Gr#?U?m`oV;fTio7>C2h-_?|#6_sa7Jd2e_$+JC~zAb~KK88b1J5NBR~`GLg@8 zLsDfOpVjQs01a)dJ>nae$l<%m!GA5`{5~BbyZ>kR(Vh5L_pyJt7Eq{HS@?-m($T^y z{hog(slNl2a$Ze7oo+U}+t`-0bbo;b$!Y{>uzd6DL6attPEpe6U4hAD_&MvYaT|b_ z+nX^I<)}v!T>lBASd6~ZoD)A_2By+&Hr>?&?(Vw#zw{>PVT>+@w5sRnLOs?3wWZ>{ z&qsypVvZlW=}uGuTW6<&K`rk;JLfR#0{P_W8BziYJc&G(gU#YLiogCLc`hpxG?;%= zAgIZ_%ApSMJtwPIxHE+hYiZ3+Rt0cWK$1Gey0=)E456;=pXGcqBi?K24^LO)ZYl!& zCP!~Yg8w)_{)KY_)fGLEhr;n^rop@5dL}lclW^6l7IBEJi?eGib;(e9z(ETKUHrdD zcQFzGfn(J-pERGleOV>&G;zaBVPdSBdHu}Wciw={@9&B=kiW6C`H!b|&qxqt|I}@H z&Q@gvwTw>{1QjokxaM2Dmi<4aOn55Yca7Nq4WyJav z&Pn!%f8Qvz{Y4_X-s9H8a>p2exk?4_hVr<>|5Z#ZcTi6zFdI0b#y-~_Tv8+%mKsqN z?Y7XC!oN8C!2iu$D-A6A-}Pxh#Fy1%^H{Tif9jE!kp#8Nz5qp)7En_to&zW#U+m5K z$Pa&(qd@ulRe`y3h0#pAof7sg@}JiR%9%Xp$5Q}uK4;bOyPg)RDiMrJhQ$3IC2MZjBJGM_T<^nq!af~@O;a^Kxx-gfZwroc?ChR9b+GpaHWLgu24`oB~08oi5pZ#4LXAQ8yWW0)vyYN9e7kBA7fT2`t4&wtVWDCsQ+ zTC9#PKF-a87T68{dvGMMW|}foCykZcG+1R9aB_gAji{H6hFYJT=$vjh*hGw>uyw$qcR|FHVN*==iVckyi&z zEPsYCjWU52M{v$Hv^U)RVYE9fmAar#sfkZE&mYv2x@y&}IWN7vX(tx)odE=DG^*D9 z{sTaL91xKL-7f5L5hLHrWy*wHfCZl9ldr;=scT@mcK?riPy;_Aifgv!DUTT}e@3z7{Ry6rSisyQzY zP;6{+)Rq0 zfUOc+U2N#m8#o^L;xrI9-?1Ybc*tg!Bh9n%>TDi|6*&a=byDCc4o?yD+3HYfKA*$m zV+LMcDO}}@SIxvs^@W(hCEl5YvlZLNm1vxwU!>LTE_Fed@^f%eB}}(DiY??t>sJ6& zTi#2gOWTPJ>PH90leFuHM3JWuj%oo|{IGH6RZd%{KEE2~;4^)k5OFisK|&nU%P#go zJg2<4dp7Xs&f}eVJa0?STmRgb*J^5k6c>w-)<_YL=92CLxg#Dwvn7s``sm*w93F|{qEsuDdfD#hbb@qbw_cTB-CW7psO^{h_)5B3=BDs20JQ?Snt26x7x>73r|b^`wB~#f!adaRW7!k8yu|b z?-krf`pP^uV5`vYbl-71kJ@z_fj91_PYRc>Oiwo7(t;1-PVl_|9%Rmi%}LA8nu_|u zii=oUu0)G$o)tf9>Q?k@4QoGphcL=*)xCBpedeiS9iKH1XXDy)gWFZgGQ@@`+2$hX zQaPf%!LA$m9Kp5NYNF=_Zt<^{V6Kas3tMd@Nq|T9-*}>H2k%WRTRLp5VjpTyT5QVU zPIL`GNPDN5vmXb^<*qXu{&E2=Rr{A&Jo*s#y(agVq0U$E^EwP0ck+E1vJidPB1b`q z_>6_@I#PH$wZHz35cohW5koD0#FOV=4B6Ow?}pFNeN#%_~uIPttq9X-3__BRiaQ9Jis8$qBHU& z0!UlFCaQDWNSqy2c(U`hF8?|v*8%~TkcKLF8Ey4EwvN_GvHa3yU={FZ1L+Fswa&Qq zEVyASYWN8dsbT}0gt}Z~@X`TT+s;--E;)D9ghN$YCt&4|{BrH-BzSuJzwjD5Ep+cz zWfn}b~cGn>8oV8Zwg1xT4k-%xTPz4ta7#AsTG$P*#2U%@i+=i18R8fwU?F|w*Ue8QTS7hI(JKm zMQI8V?)^$RsM*_hTr5hALCrFZo1V1M{SsINpFliO$E9&SHzP6EcfAf@8Tjo=o;MKg z1;<6AHc4&(@H3yT5p^IzY4-WeSPIloAI87%8ty;^7lN#{SbB3>#(e=7(E7C}0+3Cd z44raQ=ezG;&2z(bdF9?88^Q!Tna zq(2&T=(R1R)+LUHPHsJ&#iyV`JVI-3;@Whp{2jZmp|I7_7Xz=I_Fxy+p^MktB`jwx z9lQh2bgSlInB>7G#rY1{hOc0qUn!4CfOy+7c?8cWK83-1Czp&kMax~b`}Oa#zmGN| z(;_3SyML!vYwdQ zVR|VpdO%gvDjF(oHhqb!{EjGK^|rajG$a7ZHpcVlv^%V%BLV3IK{%Rz-VRCC7NlQ> zHC@lXAox&LHTZJ5rT(hv$RBM+wnl^u$vD8_Y2ePrif=V^gdrrEzg`L8P#LZ2;BK24 zen<1vMOheb6BXOPva=e)t&4^#Q zlblN>ApX4gdsuU@IF&5(Qiy%aA9SA?8)5_;0s><2aPxo~PpH+O&ggXfMJlR=D)FGc z*G1SF&-}a$;l&TT!6HjzY5MmCmVf7An2|OW>o`+;_n7X4UpyK8V$*N^iqW(?O9juo zXG2%gdWpAdJUWN45^8u$rTZ(e)?<)4Z6dj-6A!zo5!!TdURNGYjjQ;sI)zjx`|~)G zLfnDenW1_PEj+vsIsdJDbuN|*Qc#q^jwiY}jisy2o6W?D%y*$3{cZ{gQ7nEt*Z=7- zs2&}-P(<+%`33DX&L}nrB$Qf#TkF-!N(~^qn&ea{)(OnFzyQUvFMD=9iZ)q+{#OQQ z_TT2kA7=_=OM6E*L!9DU9g9g3U70DUv)S6Xm&5xug;|h}gD9FD+j-O8M8z$^RbV~O z4c)yLsJ4Z~!c1mU^7>~2ufXo0JQkLnI3CtiSzV}Qu}0<4x?jjo!B0l zV(e?FL{c291(d8HfgG{!x(~#WjY?yUI{PLQJ|fG1XtdDW9kqDq+vryoK}m^oHp056LC>g90boE$fj`iiJFg`_i}(>cn$5yPm3<^)g2b{QHk^(%_gg%7 zH}-HHp>xmgezrA8^J6v44u;V9-fOWDjDSR-jCUkNLz|$n`&X0+?so9+hE9NAYIA&> zS*L4PiKR_WyAij8!w+y*yX8_y-JVw6&=eIAdF-==`iZ*{ugxzmI(R>D!EMjGPf$PI zE%#(5a1o=4Qz&<36?1uslqy4Wb30ZIw*Ev$yI%~ML36b)RJlGJOP%^>-S^fWh>606 z!R@^3W&~1~W@_UUFV~~y&{E3c+`~mzDqbf~L$>MZ=mXGPTqH(tTY#V?XU`=2n7%Kd z_R}1%saWS%ijd<4JWVzL!^DaVGAlQ0&iDIozIX`h{A((#WxE;$SGTtce z!s?Q)`f5PH$eeF}63dr;9?2Tl=^0uJg}Mn9;+Azq{q?p9^W|Qko_?7Rq(Yr1zNU5( z{t?Gxwjz?YeDn;V=+IW$1}wy~M`x9yKEx`h?AOf9g~RW^BgxUC5_$kj(h_zy#dfkp z2hFBn_^(4b{?-vi!U$2JJ*cawd5)H$%elz4+(M!(R1W7{iPJN+g2F4+QuBL>#k%z9 zyo}@zJ6~WNd0(AG8uV0J_j_L7l4Ta!>S0O}N!d$VT$QWk;@Zxl?}FS0b2&(J-X2v- z5V$_EPTwtpq_DpLDxbfu-peC>(Ct*;6Nn?TUrW!7;SzHLx$v&ki>)>O`jUzqg(Gj= z;P!N(Xgw|;J#{VGE9c^pb}u6A^K6N34+kym=HJ+YSEnU9wJrDFPWeW$xnij-PHLJKNN;GC4Wt86F;+j4W6N4?bzUaqZ z<90>y$t3JVdh5-78Q|?Y>TUSh>Yq1zd{(*PD$|Or=w1l(b*ky(%Vn$^#b4MuXfL!p zGu3Q!{effLdaO=G@!heirUaU z+uw##Ue(-&j?$og^}Gy6T=iJCgxE=6 zNoB^+yWri-w&kiD44ocw z-W8BuJbKV|qUbEe64t2{=MJvh8^pZGkRbc}Qg@Maq25W>8?N7x(AZ)*w3E7tD@H%R zo1FYm#Q=&M5Av?BM9Z3%ox7HU=AtvmXF~8Tu)8~TzfAPGxBsmU+<;j|k*?H%lt!+~l z+%Ra5cY3Zm$AGpU@bx<;p>MFKOC#tjMY}c}=elLbLq$AVXwo_7uq4H$O~|pqkP>g<849FAWFkeJ$=85xvWia5Xo8uVdSeO}Xgy%^w%#2G_JoJW>t& zwq>q=x#neVA>1yqjgk<|r)ukIZM=>Xt>R3+w|O~vb^%mz@Q*$orjcc@R?`US^7W;y zNq2}QG9CCnSz&rL1W4iq@Q?NuDbKPgvy<~_uZ0vf5l@Y7=6DWrO`rYKFebtk zP>oi19u}9Sncl?x8i#W~@}+fXU14M85Dng$z&%*x%{`b%WWDI7d#J7m`S3b+NCTbm z!8&XEHjq?I7J9>hh@+d|;)m^$lTiY#V`1AIe4S9XLs%to$oe~qeeTH|XvIH%i+Awu zynnDFCqlMR61{PjiSTpbHk=8htSO6j-dic9kf1o`9UbFk!n#pKt>!x=l%3zDeT^-f z6qmS@qvU~|9DOtv&>Pj@y7S$WCF$eEA>G1Zc`Qc8j%6>+G3s1f=Uw=XFMD%ww-(BO z`o<%A#CC@JEc`H+^_{%oJK5$@5<+0~WqcIH=O=FoI%i-@?oSz7JqwiE?Apj1}kzRP`y(@hnkyQ5Elkb_< zm6-A5nbmUXFZ$iuVj|9nE)Vl0zQzgTeMg1Lz{j)D6oIk1ycy{j;Jsw6;&kUZx%(`! z2J@_n6Z`l^XDht@UG~Ipno)qp4jHPT@E-Q(@Xg#3?v!eK73pC{7gO5uWs>+!b9k@N zMZpogL&=|L`%buI`}oO5HeqMXN9QFATI#HvsHcIK5N_U>ZU_g|krXG)J{=U^oOXiY zo&SD3V5wr{Lp+c1CYPsuEO|5(_ba@%%bMJzl=$hIq{*--7tj0`MNggxPhQ^mEY}*^ zhb^IjHa;X&u5=yAFD7gu+%PeSWbx>Ae|vV^7or%?N+qre80?tgZ1O4W=3ap1GBVTX zlsp^LKp0wpHkg>|@hv=-jL@vBM2gK}2~bB>N@7+>Vdv-fWRx zUl^Rjf=hJs>}sQToT$Mg+DJtOX$F?zog*g`CpIF0a)wvBDt*0XS*~Y;dVca<)Ap&p zJ~)#?bK)Y?yySLD3kT`C3@Ue#|!vNEHHCeypF^TK?dbO>F1 zo6kQ$NQ58f(k-p2rGej{yFF?>uSnlGiCEma&5D!aFtj}}?W$oyKvpG!(();`-k^-oA z-kqDQemGg>O4H}IDz$vhh1(~+pJvMe2cmED zkjt4y!%NSX2&tLr-$B++7GCCSUQ)M2vM0j-QMo3~^wO~78Sxaa+_XyD*vJO#1L79O z9!-%uPXbfsp-EUE?Bx)Odnu;OUyDeiSG@S3nER5>68^rb%J_cS4bm89z=<%lw4(Z= z@i%B;wJF!>ZojR4{A)eoL2E#JV}$WlQYIkN`k3ty5?|5sbZ=)Xl58T~#)OKBq8JWP z+fz39$K0wfT6tHVHK0Djc%gbMRSW|MHMSB zMLtSGPL+yc|0KAW;MsJkQu|%zmvMq>66|*+!YY0|KM)XqQaBO%=c`$j_2^?*CLo=qd8p?iJg;E7CB~oxK>}^Ho~f!!Fq=6>vuPrm^f#%?L@82>xl3qYONY(af0pyg%UDMyMm_*2%)>a#TtMPBt z=kjxmCfP^b?+Z7a(j?ia)Ujhl7xz0mA0QbVKuhyeZm)5+1c%+pQa85F@I$a?CpI?#!ke05X z??ncVQY-SKF?K!*qc!#?YKlCFlofRE8hsGHqB6?zkFEZejK<-LXb68d}98fVw zrr&NWM>xX7%CBGJw}Sw@9fUcMWgXHG9C8KGFo+M4J5$w z>_{DSX32a^PD6iNt*fA8?-r=;Y_(mNkyST9!_z(Q(d{u%#&<-0iIfVDWv9MOcfNEi zQGV!21qA|zbX~QRhR6jkg%iuEf?fdVAm<><1V+0iiorVXn5@f_*%a~Pinb=*H6;g- zUQBqQ(pBfPSmwz$34ZZKOd@JurW3lq&Jkt-*|b71Q@(>xB+E{_Jec6Nmz$l$OBQfa z2mhv6RqVhL20Q^EE4fDPGMOw1LbU1Kdu>pYr3_AR-5D5O`B^XsK&vhjkqPPi-gjNm z2Eg=|JBiFnBkf^2Z#mK^>hz3&C)-5(Ap-BZugZprxceG|(tNKyXVzc$_3>&Wo&QS~MIO#c7<^9|NDOS<_C;Mmyx?446MnUOSXBKHa z7LGGzzBeY8DL$6?UvVu3=sT~rC|Ca@i;P9f_36zpcQGBjSF=a3Vytw@_CAhV#pDsa z<*t?T(L<0Pn45*sGUsVLftuvYy6pRfE}emow$xlmP@=K2O<_taUwAKTI3N_5gUV`S zds|uIjlxro)TDbDwvNjb`%~x>-b$u{)HZ!!Z<1I39Zf=7(90;XO=O-6JHOW*eDyM! z1aj4}ixfy3cu;{nc$ueGY%$QjMtQ<`%~KYqCgs)zVv>>Jbm^!Lt|q81qv=e%GaMo? z$-g_~nu(LZr9?HTBW!WWqhC!b~&2tJb4GZ!iobbj? z8O{A&w*gjISMlk#9ooo!lO|o6kz2w8^+KADxQ0dxBz(i4GwT?OY>&D%w&f?AedX|F zu<)Sl*8;k8zdPx3c1XyXeYv3VQRMB5GT(Vk($R)Y=yJ8`pn^atQ{edy5g*m)JIqO4 zq_+V67}Tgm^Ylvvvi+pkujjD2Yi*~yKkhK|bFG3^n$PTk&}d`K*68{nFZzBJ9b^7F zC(6yqgZ#NT=u2eiSDONti*vK&Jgu&|Q5|3-MENQiIwQ6uN~{gLYXokv;#% z1I&{Yli7+~9uAzx2a+l6_$*%x^@e+aJ7(fc9~)B*ZOZP_tG(|Xr*q7T<3BFRGQUEJnwi+wn{Uk3cY67>it5m$S}M9a_ebbt?Ud=!9*v_4pr;M$ z0h>vY?>zNr0R3=*Z|bV2but8QRSQ%;OS}7wkb^9!;U*mQf{?#F zHEt%w*};fDkLWL#3?5r`|Fb1JZ75GABOas|VB20s*Nl}WQvGrPOuAZI0WY8@@0UOK z1`0IOF!g|H44X-5=H#(4>5avo#|E}iN5|Vnci-1redST^(aa{etG^1*J}Z1kpZ8%c z+^&UjL^vYpyc~ zMlihmw3J#F0`#{}GdJ^lq8{tBoOQ)i_zQQR9V&emC7fCv7l(^;jn5?Q^#$tQc$S6J zs{3f+esw>~zxrm4)Z?nbn7At-@->X6d-0oe@A+wZY%mdrIy;1G)LYIZhf^Z+HRh90 zU%^l$kZdkyIt^GO$t~Y!N3$KNz3qoK#GM@rp2CSBER#^tW$}zSsZ}=~m`mfTrJI07 ze(SSiDW0P4C5$^MF%4ah9-Xt_ep44^cMcs%Lnz`-V!x8w}gnL}QjLLUJTflir5_@2V zbXdQ|N`Ae`6{eUY@mG3UeM=7H*Oa>2d0#t|P<^uNX)Rvf)ACSBs$R0^S!@PP!|!?1 zeVEl)uLsuk!g!D&iCTq;LQ9X}9UT9rIMtJPOW=tB_BfXqlk6cWIOQpLVlTSmX&^VW zz9PJn-!7n4F#BEb>q<#LuzqMBpnc7jQe9S2bbuhq$5p@>4Nf%x6_Mt1*%-3of1NWQFwLaWsj^iC{#^OL)hsG*ST$k z5lG;)*ZB7=%s_OamV8GSr4n`X{5C6apA%m81NZ?ew4)G5NJ2E5dCa>kN*` z@CeVE?5yI%(a7_{t_;&dhE}4o1=qSX+>>) z&AkmOd3Ib4RV_ZgOpH@J=`Y3e?6s>l3E2^C<)Td=B&bmR)nd2hldH~tR@~%(e27X# zlcO$v6NoPpZ4Nxyo41b$l{x1hbvD@bc?hJGLxG?S=x}06%>8af;gZdj#-i1yb|3uR z%1=rx^rM!x?^B&@XoE)551cz_S(M$Wl=8iHf=>+x9Zx0qKl;o?igw>olRx8Di)klG! zj9Zw;B*g!Sa?X|yWkvIzV(q_ebwrfH)x;k@F#gVG44#l2G#ts%x~ALhu|-3 ztaWb)t1$#c*iQaDe5PPd(oU7lem}B4j9;(c383?O4_64>tg0}H1j0h`>sE(y za%#U#09KABfFI>))(Kz`LNJfJ0FBMN(DstCSSawHJw^=OKG%5=7Q9s+56e^&u+;+6 zZn#uS3gb{V)sY zr^rD$I=W(JR27toP;!G zuX;72K3BUXEWM`xIZR`G?$$foXm)dzl%dl@JmhBSL554dl4+AL-j&EmXQg}PnZn0%V_V~|r;LbR)qX;#A-W)kqosN_{5Yb0~VxnGQ%r{DVB-5kKPzqr6a^tqxV0@T>5w zcosd1u9~&@HXEWBncdMtosHWc;FM8g!-LIXF-Osi@u&rjc*$G939cCX%kT{O*}S1V zV`?7is$ah>FlDO)w~b(hmkd|f&jKqi>M5|7Ndsp#x@9L1s+1{3uv%iP1MGQWKXi#J zC^#%5ux+`J_B*68j{j$OC38yqlc%|r47Y6(^5rxA5?$}`D=Z^9U}cdf_yCELgaUJ= zy8a=xrrI=4pk^}xMmwftxm+G%6aRBdt|+6~y2Kk0s{)Qpv-}b0zTb{apKO@>L}49$ z;?TWXmD!X@FH zr*9=={fskjYQSh6HS8d)T`Wpk^NRe@%JT;*82-Gowb^U}_#=d6U1JA=N-M8VDBI={& zb54d*J!r!+)LY=W48XM0ck|ojB0!@vOd8ClhQNJR>jCUBW0Jp300~KN329H#HyjmK zAFk`^uXaK-uR4@+m#TrXY|y$!qHDG9nvOc>saOHal4R#4%Bkx~N-AvA?qn1b%v&-E zr}+87HuI1ih>s%{{*<6+%fjiPE6aFzfwBhkVmvv-?ec7~JYnzM9Z>@Q93< z=#H!}8(?5$y#TCOs}YBj217HQocLYv#Bup=b+wAF`+2d= zo6f-Om&J-bg}nJjzwUCxf9LE|ss^rg1(TNV1VS-(CUxhaL+2 zvK7ga^{S(L76M}l7PHc*X;TE2Bs0^r|NA~~`SB+a+UBEL(jOiJhr{vedEU!YF9(D6 zuEy3=4+OUvPS6t9_YMp#=?TDQ;V75^05tV~(rgsGySs2Y0F z@W=cZwf{{oVvB+2eZnP#>N(O69@x50(OK#Cg&Zy!7GXQ zttrH_S=>ob9+#yqN4K&!iS0dS66)har<)bX(%r*nrxhkkg0bD{4uVhaFtIs4o&Hg`L!zGm4o}h^GFOTDmc4M8beQ3sQVotwKEFEu|b>f zpjQV*&FItKmV+eDe!6>qE-fFwv|y1LeQUV*X?!*mP-Dc~5A;ENQk8CQtT)o(1Il4m zPI{V-NU^br%9ol`i_0hsR=D92nUVb|fadWKS@nEU11NNeY&Pke@wrFu$7cWp zLLmQ>@-$P>(+jLB`Z`98&Ir|f$TuEK`;s!q;Pu|4Pv`@`wocI>)8wa$l zT+B_zHY801)J40i3><*TZ~yR4rEJiVD5g{tq(%E26476MipwNaG%D zTi01l>`o+wX0o*g04n9D9^bJgLpmpsRK-#+iOm&CogGVJnBtEmK@qM-C7@904`sXj zcHfnzKchWe;FT4Zwd9P?=3RK@DmK^pKD^Qy0+PMA;9orbC^;$e~9=ziHDeRQy7WGxB9T8JR_iik$!&6?=fhrLr@kG@%=YZe*?4d7g3)I zL1R+*U(9*6Hm>QeKL~7lq&%bfXan*FFr4=Q@<8f0N=gsk--{b3lLYM1F7t`y_rp>m ztjhY>=EJ+bADNt*WFZ@YpXxq@Uml;e|dn?13P+=h#Z|w-2UzEi0liGyPB1WF+r5tch5)8o0++L`y!gw^ zgx?0gS>pa+DD0w<*31h%ARudRtZ instance tracks changes made to entities. These tracked entities in turn drive the changes to the database when is called. This is covered in [Change Tracking in EF Core](xref:core/change-tracking/index), and this document assumes that entity states and the basics of Entity Framework Core (EF Core) change tracking are understood. + +Tracking property and relationship changes requires that the DbContext is able to detect these changes. This document covers how this detection happens, as well as how to use property notifications or change-tracking proxies to force immediate detection of changes. + +> [!TIP] +> You can run and debug into all the code in this document by [downloading the sample code from GitHub](https://github.com/dotnet/EntityFramework.Docs/tree/master/samples/core/ChangeTracking/ChangeDetectionAndNotifications). + +## Snapshot change tracking + +By default, EF Core creates a snapshot of every entity's property values when it is first tracked by a DbContext instance. The values stored in this snapshot are then compared against the current values of the entity in order to determine which property values have changed. + +This detection of changes happens when SaveChanges is called to ensure all changed values are detected before sending updates to the database. However, the detection of changes also happens at other times to ensure the application is working with up-to-date tracking information. Detection of changes can be forced at any time by calling . + +### When change detection is needed + +Detection of changes is needed when a property or navigation has been changed _without using EF Core to make this change_. For example, consider loading blogs and posts and then making changes to these entities: + + +[!code-csharp[Snapshot_change_tracking_1](../../../samples/core/ChangeTracking/ChangeDetectionAndNotifications/SnapshotSamples.cs?name=Snapshot_change_tracking_1)] + +Looking at the [change tracker debug view](xref:core/change-tracking/debug-views) before calling shows that the changes made have not been detected and hence are not reflected in the entity states and modified property data: + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog (Updated!)' Originally '.NET Blog' + Posts: [{Id: 1}, {Id: 2}, ] +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} +Post {Id: 2} Unchanged + Id: 2 PK + BlogId: 1 FK + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: {Id: 1} +``` + +Specifically, the state of the blog entry is still `Unchanged`, and the new post does not appear as a tracked entity. (The astute will notice properties report their new values, even though these changes have not yet been detected by EF Core. This is because the debug view is reading current values directly from the entity instance.) + +Contrast this with the debug view after calling DetectChanges: + +```output +Blog {Id: 1} Modified + Id: 1 PK + Name: '.NET Blog (Updated!)' Modified Originally '.NET Blog' + Posts: [{Id: 1}, {Id: 2}, {Id: -2147482643}] +Post {Id: -2147482643} Added + Id: -2147482643 PK Temporary + BlogId: 1 FK + Content: '.NET 5.0 was released recently and has come with many...' + Title: 'What's next for System.Text.Json?' + Blog: {Id: 1} +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} +Post {Id: 2} Unchanged + Id: 2 PK + BlogId: 1 FK + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: {Id: 1} +``` + +Now the blog is correctly marked as `Modified` and the new post has been detected and is tracked as `Added`. + +At the start of this section we stated that detecting changes is needed when not using _using EF Core to make the change_. This is what is happening in the code above. That is, the changes to the property and navigation are made _directly on the entity instances_, and not by using any EF Core methods. + +Contrast this to the following code which modifies the entities in the same way, but this time using EF Core methods: + + +[!code-csharp[Snapshot_change_tracking_2](../../../samples/core/ChangeTracking/ChangeDetectionAndNotifications/SnapshotSamples.cs?name=Snapshot_change_tracking_2)] + +In this case the change tracker debug view shows that all entity states and property modifications are known, even though detection of changes has not happened. This is because is an EF Core method, which means that EF Core immediately knows about the change made by this method. Likewise, calling allows EF Core to immediately know about the new entity and track it appropriately. + +> [!TIP] +> Don't attempt to avoid detecting changes by always using EF Core methods to make entity changes. Doing so is often more cumbersome and performs less well than making changes to entities in the normal way. The intention of this document is to inform as to when detecting changes is needed and when it is not. The intention is not to encourage avoidance of change detection. + +### Methods that automatically detect changes + + is called automatically by methods where doing so is likely to impact the results. These methods are: + +- and , to ensure that all changes are detected before updating the database. +- and , to ensure entity states and modified properties are up-to-date. +- , to ensure that the result is accurate. +- , to ensure correct entity states for principal/parent entities before cascading. +- , to ensure that the tracked graph is up-to-date. + +There are also some places where detection of changes happens on only a single entity instance, rather than on the entire graph of tracked entities. These places are: + +- When using , to ensure that the entity's state and modified properties are up-to-date. +- When using methods such as `Property`, `Collection`, `Reference` or `Member` to ensure property modifications, current values, etc. are up-to-date. +- When a dependent/child entity is going to be deleted because a required relationship has been severed. This detects when an entity should not be deleted because it has been re-parented. + +Local detection of changes for a single entity can be triggered explicitly by calling . + +> [!NOTE] +> Local detect changes can miss some changes that a full detection would find. This happens when cascading actions resulting from undetected changes to other entities have an impact on the entity in question. In such situations the application may need to force a full scan of all entities by explicitly calling . + +### Disabling automatic change detection + +The performance of detecting changes is not a bottleneck for most applications. However, detecting changes can become a performance problem for some applications that track thousands of entities. (The exact number will dependent on many things, such as the number of properties in the entity.) For this reason the automatic detection of changes can be disabled using . For example, consider processing join entities in a many-to-many relationship with payloads: + + +[!code-csharp[SaveChanges](../../../samples/core/ChangeTracking/ChangeDetectionAndNotifications/SnapshotSamples.cs?name=SaveChanges)] + +As we know from the previous section, both and automatically detect changes. However, after calling Entries, the code does not then make any entity or property state changes. (Setting normal property values on Added entities does not cause any state changes.) The code therefore disables unnecessary automatic change detection when calling down into the base SaveChanges method. The code also makes use of a try/finally block to ensure that the default setting is restored even if SaveChanges fails. + +> [!TIP] +> Do not assume that your code must disable automatic change detection to to perform well. This is only needed when profiling an application tracking many entities indicates that performance of change detection is an issue. + +### Detecting changes and value conversions + +To use snapshot change tracking with an entity type, EF Core must be able to: + +- Make a snapshot of each property value when the entity is tracked +- Compare this value to the current value of the property +- Generate a hash code for the value + +This is handled automatically by EF Core for types that can be directly mapped to the database. However, when a [value converter is used to map a property](xref:core/modeling/value-conversions), then that converter must specify how to perform these actions. This is achieved with a value comparer, and is described in detail in the [Value Comparers](xref:core/modeling/value-comparers) documentation. + +## Notification entities + +Snapshot change tracking is recommended for most applications. However, applications that track many entities and/or make many changes to those entities may benefit from implementing entities that automatically notify EF Core when their property and navigation values change. These are known as "notification entities". + +### Implementing notification entities + +Notification entities make use of the and interfaces, which are part of the .NET base class library (BCL). These interfaces define events that must be fired before and after changing a property value. For example: + + +[!code-csharp[Model](../../../samples/core/ChangeTracking/ChangeDetectionAndNotifications/NotificationEntitiesSamples.cs?name=Model)] + +In addition, any collection navigations must implement `INotifyCollectionChanged`; in the example above this satisfied by using an of posts. EF Core also ships with an implementation that has more efficient lookups at the expense of stable ordering. + +Most of this notification code is typically moved into an unmapped base class. For example: + + +[!code-csharp[Model](../../../samples/core/ChangeTracking/ChangeDetectionAndNotifications/NotificationWithBaseSamples.cs?name=Model)] + +### Configuring notification entities + +There is no way for EF Core to validate that `INotifyPropertyChanging` or `INotifyPropertyChanged` are fully implemented for use with EF Core. In particular, some uses of these interfaces do so with notifications only on certain properties, rather than on all properties (including navigations) as required by EF Core. For this reason, EF Core does not automatically hook into these events. + +Instead, EF Core must be configured to use these notification entities. This is usually done for all entity types by calling . For example: + + +[!code-csharp[OnModelCreating](../../../samples/core/ChangeTracking/ChangeDetectionAndNotifications/NotificationWithBaseSamples.cs?name=OnModelCreating)] + +(The strategy can also be set differently for different entity types using , but this is usually counterproductive since DetectChanges is still required for those types that are not notification entities.) + +Full notification change tracking requires that both `INotifyPropertyChanging` and `INotifyPropertyChanged` are implemented. This allows original values to be saved just before the property value is changed, avoiding the need for EF Core to create a snapshot when tracking the entity. Entity types that implement only `INotifyPropertyChanged` can also be used with EF Core. In this case, EF still creates a snapshot when tracking an entity to keep track of original values, but then uses the notifications to detect changes immediately, rather than needing DetectChanges to be called. + +The different values are summarized in the the following table. + +| ChangeTrackingStrategy | Interfaces needed | Needs DetectChanges | Snapshots original values +|:----------------------------------------------------|--------------------------------------------------------|---------------------|-------------------------- +| Snapshot | None | Yes | Yes +| ChangedNotifications | INotifyPropertyChanged | No | Yes +| ChangingAndChangedNotifications | INotifyPropertyChanged and INotifyPropertyChanging | No | No +| ChangingAndChangedNotificationsWithOriginalValues | INotifyPropertyChanged and INotifyPropertyChanging | No | Yes + +### Using notification entities + +Notification entities behave like any other entities, except that making changes to the entity instances do not require a call to to detect these changes. For example: + + +[!code-csharp[Notification_entities_2](../../../samples/core/ChangeTracking/ChangeDetectionAndNotifications/NotificationWithBaseSamples.cs?name=Notification_entities_2)] + +With normal entities, the [change tracker debug view](xref:core/change-tracking/debug-views) showed that these changes were not detected until DetectChanges was called. Looking at the debug view when notification entities are used shows that these changes have been detected immediately: + +```output +Blog {Id: 1} Modified + Id: 1 PK + Name: '.NET Blog (Updated!)' Modified + Posts: [{Id: 1}, {Id: 2}, {Id: -2147482643}] +Post {Id: -2147482643} Added + Id: -2147482643 PK Temporary + BlogId: 1 FK + Content: '.NET 5.0 was released recently and has come with many...' + Title: 'What's next for System.Text.Json?' + Blog: {Id: 1} +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} +Post {Id: 2} Unchanged + Id: 2 PK + BlogId: 1 FK + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: {Id: 1} +``` + +## Change-tracking proxies + +> [!NOTE] +> Change-tracking proxies were introduced in EF Core 5.0. + +EF Core can dynamically generate proxy types that implement and . This requires installing the [Microsoft.EntityFrameworkCore.Proxies](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Proxies/) NuGet package, and enabling change-tracking proxies with For example: + + +[!code-csharp[OnConfiguring](../../../samples/core/ChangeTracking/ChangeDetectionAndNotifications/ChangeTrackingProxiesSamples.cs?name=OnConfiguring)] + +Creating a dynamic proxy involves creating a new, dynamic .NET type (using the [Castle.Core](https://www.nuget.org/packages/Castle.Core/) proxies implementation), which inherits from the entity type and then overrides all property setters. Entity types for proxies must therefore be types that can be inherited from and must have properties that can be overridden. Also, collection navigations created explicitly must implement For example: + + +[!code-csharp[Model](../../../samples/core/ChangeTracking/ChangeDetectionAndNotifications/ChangeTrackingProxiesSamples.cs?name=Model)] + +One significant downside to change-tracking proxies is that EF Core must always track instances of the proxy, never instances of the underlying entity type. This is because instances of the underlying entity type will not generate notifications, which means changes made to these entities will be missed. + +EF Core creates proxy instances automatically when querying the database, so this downside is generally limited to tracking new entity instances. These instances must be created using the extension methods, and **not** in the normal way using `new`. This means the code from the previous examples must now make use of `CreateProxy`: + + +[!code-csharp[Change_tracking_proxies_1](../../../samples/core/ChangeTracking/ChangeDetectionAndNotifications/ChangeTrackingProxiesSamples.cs?name=Change_tracking_proxies_1)] + +## Change tracking events + +EF Core fires the event when an entity is tracked for the first time. Future entity state changes result in events. See [.NET Events in EF Core](xref:core/logging-events-diagnostics/events) for more information. + +> [!NOTE] +> The `StateChanged` event is not fired when an entity is first tracked, even though the state has changed from `Detached` to one of the other states. Make sure to listen for both `StateChanged` and `Tracked` events to get all relevant notifications. diff --git a/entity-framework/core/change-tracking/debug-views.md b/entity-framework/core/change-tracking/debug-views.md new file mode 100644 index 0000000000..1caeef20df --- /dev/null +++ b/entity-framework/core/change-tracking/debug-views.md @@ -0,0 +1,327 @@ +--- +title: Change Tracker Debugging - EF Core +description: Using the ChangeTracker DebugView and log messages to debug EF Core change tracking +author: ajcvickers +ms.date: 12/30/2020 +uid: core/change-tracking/debug-views +--- + +# Change Tracker Debugging + +The Entity Framework Core (EF Core) change tracker generates two kinds of output to help with debugging: + +- The provides a human-readable view of all entities being tracked +- Debug-level log messages are generated when the change tracker detects state and fixes up relationships + +> [!TIP] +> This document assumes that entity states and the basics of EF Core change tracking are understood. See [Change Tracking in EF Core](xref:core/change-tracking/index) for more information on these topics. + +> [!TIP] +> You can run and debug into all the code in this document by [downloading the sample code from GitHub](https://github.com/dotnet/EntityFramework.Docs/tree/master/samples/core/ChangeTracking/ChangeTrackerDebugging). + +## Change tracker debug view + +The change tracker debug view can be accessed in the debugger of your IDE. For example, with Visual Studio: + +![Accessing the change tracker debug view from the Visual Studio debugger](_static/debug-view.png) + +It can also be accessed directly from code, for example to send the debug view to the console: + + +[!code-csharp[Change_tracker_debug_view_1b](../../../samples/core/ChangeTracking/ChangeTrackerDebugging/Samples.cs?name=Change_tracker_debug_view_1b)] + +The debug view has a short form and a long form. The short form shows tracked entities, their state, and key values. The long form also includes all property and navigation values and state. + +### The short view + +Let's look at a debug view example using the model shown at the end of this document. First, we will track some entities and put them in some different states, just so we have good change tracking data to view: + + +[!code-csharp[Change_tracker_debug_view_1a](../../../samples/core/ChangeTracking/ChangeTrackerDebugging/Samples.cs?name=Change_tracker_debug_view_1a)] + +Printing the short view at this point, as shown above, results in the following output: + +```output +Blog {Id: 1} Modified AK {AssetsId: ed727978-1ffe-4709-baee-73913e8e44a0} +Blog {Id: 2} Unchanged AK {AssetsId: 3a54b880-2b9d-486b-9403-dc2e52d36d65} +BlogAssets {Id: 3a54b880-2b9d-486b-9403-dc2e52d36d65} Unchanged FK {Id: 3a54b880-2b9d-486b-9403-dc2e52d36d65} +BlogAssets {Id: ed727978-1ffe-4709-baee-73913e8e44a0} Unchanged FK {Id: ed727978-1ffe-4709-baee-73913e8e44a0} +Post {Id: -2147482643} Added FK {BlogId: 1} +Post {Id: 1} Unchanged FK {BlogId: 1} +Post {Id: 2} Unchanged FK {BlogId: 1} +Post {Id: 3} Unchanged FK {BlogId: 2} +Post {Id: 4} Deleted FK {BlogId: 2} +PostTag (Dictionary) {PostsId: 1, TagsId: 1} Unchanged FK {PostsId: 1} FK {TagsId: 1} +PostTag (Dictionary) {PostsId: 1, TagsId: 3} Unchanged FK {PostsId: 1} FK {TagsId: 3} +PostTag (Dictionary) {PostsId: 2, TagsId: 1} Unchanged FK {PostsId: 2} FK {TagsId: 1} +PostTag (Dictionary) {PostsId: 3, TagsId: 2} Unchanged FK {PostsId: 3} FK {TagsId: 2} +PostTag (Dictionary) {PostsId: 4, TagsId: 2} Deleted FK {PostsId: 4} FK {TagsId: 2} +Tag {Id: 1} Unchanged +Tag {Id: 2} Unchanged +Tag {Id: 3} Unchanged +``` + +Notice: + +- Each tracked entity is listed with its primary key (PK) value. For example, `Blog {Id: 1}`. +- If the entity is a shared-type entity type, then it's CLR type is also shown. For example, `PostTag (Dictionary)`. +- The is shown next. This will be one of `Unchanged`, `Added`, `Modified`, or `Deleted`. +- Values for any alternate keys (AKs) are shown next. For example, `AK {AssetsId: ed727978-1ffe-4709-baee-73913e8e44a0}`. +- Finally, values for any foreign keys (FKs) are shown. For example, `FK {PostsId: 4} FK {TagsId: 2}`. + +### The long view + +The long view can be sent to the console in the same way as the short view: + + +[!code-csharp[Change_tracker_debug_view_1c](../../../samples/core/ChangeTracking/ChangeTrackerDebugging/Samples.cs?name=Change_tracker_debug_view_1c)] + +The output for the same state as the short view above is: + +```output +Blog {Id: 1} Modified + Id: 1 PK + AssetsId: 'ed727978-1ffe-4709-baee-73913e8e44a0' AK + Name: '.NET Blog (All new!)' Modified Originally '.NET Blog' + Assets: {Id: ed727978-1ffe-4709-baee-73913e8e44a0} + Posts: [{Id: 1}, {Id: 2}, {Id: -2147482643}] +Blog {Id: 2} Unchanged + Id: 2 PK + AssetsId: '3a54b880-2b9d-486b-9403-dc2e52d36d65' AK + Name: 'Visual Studio Blog' + Assets: {Id: 3a54b880-2b9d-486b-9403-dc2e52d36d65} + Posts: [{Id: 3}] +BlogAssets {Id: 3a54b880-2b9d-486b-9403-dc2e52d36d65} Unchanged + Id: '3a54b880-2b9d-486b-9403-dc2e52d36d65' PK FK + Banner: + Blog: {Id: 2} +BlogAssets {Id: ed727978-1ffe-4709-baee-73913e8e44a0} Unchanged + Id: 'ed727978-1ffe-4709-baee-73913e8e44a0' PK FK + Banner: + Blog: {Id: 1} +Post {Id: -2147482643} Added + Id: -2147482643 PK Temporary + BlogId: 1 FK + Content: '.NET 5.0 was released recently and has come with many new fe...' + Title: 'What's next for System.Text.Json?' + Blog: {Id: 1} + Tags: [] +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} + Tags: [{Id: 1}, {Id: 3}] +Post {Id: 2} Unchanged + Id: 2 PK + BlogId: 1 FK + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: {Id: 1} + Tags: [{Id: 1}] +Post {Id: 3} Unchanged + Id: 3 PK + BlogId: 2 FK + Content: 'If you are focused on squeezing out the last bits of perform...' + Title: 'Disassembly improvements for optimized managed debugging' + Blog: {Id: 2} + Tags: [{Id: 2}] +Post {Id: 4} Deleted + Id: 4 PK + BlogId: 2 FK + Content: 'Examine when database queries were executed and measure how ...' + Title: 'Database Profiling with Visual Studio' + Blog: + Tags: [{Id: 2}] +PostTag (Dictionary) {PostsId: 1, TagsId: 1} Unchanged + PostsId: 1 PK FK + TagsId: 1 PK FK +PostTag (Dictionary) {PostsId: 1, TagsId: 3} Unchanged + PostsId: 1 PK FK + TagsId: 3 PK FK +PostTag (Dictionary) {PostsId: 2, TagsId: 1} Unchanged + PostsId: 2 PK FK + TagsId: 1 PK FK +PostTag (Dictionary) {PostsId: 3, TagsId: 2} Unchanged + PostsId: 3 PK FK + TagsId: 2 PK FK +PostTag (Dictionary) {PostsId: 4, TagsId: 2} Deleted + PostsId: 4 PK FK + TagsId: 2 PK FK +Tag {Id: 1} Unchanged + Id: 1 PK + Text: '.NET' + Posts: [{Id: 1}, {Id: 2}] +Tag {Id: 2} Unchanged + Id: 2 PK + Text: 'Visual Studio' + Posts: [{Id: 3}, {Id: 4}] +Tag {Id: 3} Unchanged + Id: 3 PK + Text: 'EF Core' + Posts: [{Id: 1}] +``` + +Each tracked entity and its state is shown as before. However, the long view also shows property and navigation values. + +#### Property values + +For each property, the long view shows whether or not the property is part of a primary key (PK), alternate key (AK), or foreign key (FK). For example: + +- `Blog.Id` is a primary key property: `Id: 1 PK` +- `Blog.AssetsId` is an alternate key property: `AssetsId: 'ed727978-1ffe-4709-baee-73913e8e44a0' AK` +- `Post.BlogId` is a foreign key property: `BlogId: 2 FK` +- `BlogAssets.Id` is both a primary key and a foreign key property: `Id: '3a54b880-2b9d-486b-9403-dc2e52d36d65' PK FK` + +Property values that have been modified are marked as such, and the original value of the property is also shown. For example, `Name: '.NET Blog (All new!)' Modified Originally '.NET Blog'`. + +Finally, `Added` entities with temporary key values indicate that the value is temporary. For example, `Id: -2147482643 PK Temporary`. + +#### Navigation values + +Navigation values are displayed using the primary key values of the entities that the navigations reference. For example, in the output above, post 3 is related to blog 2. This means that the `Post.Blog` navigation points to the `Blog` instance with ID 2. This is shown as `Blog: {Id: 2}`. + +The same thing happens for collection navigations, except that in this case there can be multiple related entities. For example, the collection navigation `Blog.Posts` contains three entities, with key values 1, 2, and -2147482643 respectively. This is shown as `[{Id: 1}, {Id: 2}, {Id: -2147482643}]`. + +## Change tracker logging + +The change tracker logs messages at the `Debug` whenever it [detects property or navigation changes](xref:core/change-tracking/debug-views). For example, when is called in the code at the top of this document and [debug logging is enabled](xref:core/logging-events-diagnostics/index), then the following logs are generated: + +```output +dbug: 12/30/2020 13:52:44.815 CoreEventId.DetectChangesStarting[10800] (Microsoft.EntityFrameworkCore.ChangeTracking) + DetectChanges starting for 'BlogsContext'. +dbug: 12/30/2020 13:52:44.818 CoreEventId.PropertyChangeDetected[10802] (Microsoft.EntityFrameworkCore.ChangeTracking) + The unchanged property 'Blog.Name' was detected as changed from '.NET Blog' to '.NET Blog (All new!)' and will be marked as modified for entity with key '{Id: 1}'. +dbug: 12/30/2020 13:52:44.820 CoreEventId.StateChanged[10807] (Microsoft.EntityFrameworkCore.ChangeTracking) + The 'Blog' entity with key '{Id: 1}' tracked by 'BlogsContext' changed state from 'Unchanged' to 'Modified'. +dbug: 12/30/2020 13:52:44.821 CoreEventId.CollectionChangeDetected[10804] (Microsoft.EntityFrameworkCore.ChangeTracking) + 1 entities were added and 0 entities were removed from navigation 'Blog.Posts' on entity with key '{Id: 1}'. +dbug: 12/30/2020 13:52:44.822 CoreEventId.ValueGenerated[10808] (Microsoft.EntityFrameworkCore.ChangeTracking) + 'BlogsContext' generated temporary value '-2147482638' for the property 'Id.Post'. +dbug: 12/30/2020 13:52:44.822 CoreEventId.StartedTracking[10806] (Microsoft.EntityFrameworkCore.ChangeTracking) + Context 'BlogsContext' started tracking 'Post' entity with key '{Id: -2147482638}'. +dbug: 12/30/2020 13:52:44.827 CoreEventId.CollectionChangeDetected[10804] (Microsoft.EntityFrameworkCore.ChangeTracking) + 0 entities were added and 1 entities were removed from navigation 'Blog.Posts' on entity with key '{Id: 2}'. +dbug: 12/30/2020 13:52:44.827 CoreEventId.StateChanged[10807] (Microsoft.EntityFrameworkCore.ChangeTracking) + The 'Post' entity with key '{Id: 4}' tracked by 'BlogsContext' changed state from 'Unchanged' to 'Modified'. +dbug: 12/30/2020 13:52:44.829 CoreEventId.CascadeDeleteOrphan[10003] (Microsoft.EntityFrameworkCore.Update) + An entity of type 'Post' with key '{Id: 4}' changed to 'Deleted' state due to severed required relationship to its parent entity of type 'Blog'. +dbug: 12/30/2020 13:52:44.829 CoreEventId.StateChanged[10807] (Microsoft.EntityFrameworkCore.ChangeTracking) + The 'Post' entity with key '{Id: 4}' tracked by 'BlogsContext' changed state from 'Modified' to 'Deleted'. +dbug: 12/30/2020 13:52:44.829 CoreEventId.CollectionChangeDetected[10804] (Microsoft.EntityFrameworkCore.ChangeTracking) + 0 entities were added and 1 entities were removed from navigation 'Blog.Posts' on entity with key '{Id: 2}'. +dbug: 12/30/2020 13:52:44.831 CoreEventId.CascadeDelete[10002] (Microsoft.EntityFrameworkCore.Update) + A cascade state change of an entity of type 'PostTag' with key '{PostsId: 4, TagsId: 2}' to 'Deleted' occurred due to the deletion of its parent entity of type 'Post' with key '{Id: 4}'. +dbug: 12/30/2020 13:52:44.831 CoreEventId.StateChanged[10807] (Microsoft.EntityFrameworkCore.ChangeTracking) + The 'PostTag' entity with key '{PostsId: 4, TagsId: 2}' tracked by 'BlogsContext' changed state from 'Unchanged' to 'Deleted'. +dbug: 12/30/2020 13:52:44.831 CoreEventId.DetectChangesCompleted[10801] (Microsoft.EntityFrameworkCore.ChangeTracking) + DetectChanges completed for 'BlogsContext'. +``` + +The following table summaries the change tracker logging messages: + +| Event ID | Description +|:-----------------------------------------------------------------------------------------------------------------------|---- +| | is starting +| | has completed +| | A normal property value has changed +| | A foreign key property value has changed +| | A non-skip collection navigation has had related entities added or removed. +| | A reference navigation has been changed to point to another entity, or set to null +| | EF Core started tracking an entity +| | The of an entity has changed +| | A value was generated for a property +| | A skip collection navigation has had related entities added or removed + +## The model + +The model used for the examples above contains the following entity types: + + +[!code-csharp[Model](../../../samples/core/ChangeTracking/ChangeTrackerDebugging/Samples.cs?name=Model)] + +The model is mostly configured by convention, with just a few lines in OnModelCreating: + + +[!code-csharp[OnModelCreating](../../../samples/core/ChangeTracking/ChangeTrackerDebugging/Samples.cs?name=OnModelCreating)] diff --git a/entity-framework/core/change-tracking/entity-entries.md b/entity-framework/core/change-tracking/entity-entries.md new file mode 100644 index 0000000000..7eea85e6e3 --- /dev/null +++ b/entity-framework/core/change-tracking/entity-entries.md @@ -0,0 +1,598 @@ +--- +title: Accessing Tracked Entities - EF Core +description: Using EntityEntry, DbContext.Entries, and DbSet.Local to access tracked entities +author: ajcvickers +ms.date: 12/30/2020 +uid: core/change-tracking/entity-entries +--- + +# Accessing Tracked Entities + +There are four main APIs for accessing entities tracked by a : + +- returns an instance for a given entity instance. +- returns instances for all tracked entities, or for all tracked entities of a given type. +- , , , and find a single entity by primary key, first looking in tracked entities, and then querying the database if needed. +- returns actual entities (not EntityEntry instances) for entities of the entity type represented by the DbSet. + +Each of these is described in more detail in the sections below. + +> [!TIP] +> This document assumes that entity states and the basics of EF Core change tracking are understood. See [Change Tracking in EF Core](xref:core/change-tracking/index) for more information on these topics. + +> [!TIP] +> You can run and debug into all the code in this document by [downloading the sample code from GitHub](https://github.com/dotnet/EntityFramework.Docs/tree/master/samples/core/ChangeTracking/AccessingTrackedEntities). + +## Using DbContext.Entry and EntityEntry instances + +For each tracked entity, Entity Framework Core (EF Core) keeps track of: + +- The overall state of the entity. This is one of `Unchanged`, `Modified`, `Added`, or `Deleted`; see [Change Tracking in EF Core](xref:core/change-tracking/index) for more information. +- The relationships between tracked entities. For example, the blog to which a post belongs. +- The "current values" of properties. +- The "original values" of properties, when this information is available. Original values are the property values that existed when entity was queried from the database. +- Which property values have been modified since they were queried. +- Other information about property values, such as whether or not the value is [temporary](xref:core/change-tracking/miscellaneous#temporary-values). + +Passing an entity instance to results in an providing access to this information for the given entity. For example: + + +[!code-csharp[Using_DbContext_Entry_and_EntityEntry_instances_1](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Using_DbContext_Entry_and_EntityEntry_instances_1)] + +The following sections show how to use an EntityEntry to access and manipulate entity state, as well as the state of the entity's properties and navigations. + +### Working with the entity + +The most common use of is to access the current of an entity. For example: + + +[!code-csharp[Work_with_the_entity_1](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_the_entity_1)] + +The Entry method can also be used on entities that are not yet tracked. This _does not start tracking the entity_; the state of the entity is still `Detatched`. However, the returned EntityEntry can then be used to change the entity state, at which point the entity will become tracked in the given state. For example, the following code will start tracking a Blog instance as `Added`: + + +[!code-csharp[Work_with_the_entity_2](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_the_entity_2)] + +> [!TIP] +> Unlike in EF6, setting the state of an individual entity will not cause all connected entities to be tracked. This makes setting the state this way a lower-level operation than calling `Add`, `Attach`, or `Update`, which operate on an entire graph of entities. + +The following table summarizes ways to use an EntityEntry to work with an entire entity: + +| EntityEntry member | Description +|:-----------------------------------------------------------------------------------------------------------|---------------------- +| | Gets and sets the of the entity. +| | Gets the entity instance. +| | The that is tracking this entity. +| | metadata for the type of entity. +| | Whether or not the entity has had its key value set. +| | Overwrites property values with values read from the database. +| | Forces detection of changes for this entity only; see [Change Detection and Notifications](xref:core/change-tracking/change-detection). + +### Working with a single property + +Several overloads of allow access to information about an individual property of an entity. For example, using a strongly-typed, fluent-like API: + + +[!code-csharp[Work_with_a_single_property_1a](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_a_single_property_1a)] + +The property name can instead be passed as a string. For example: + + +[!code-csharp[Work_with_a_single_property_1b](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_a_single_property_1b)] + +The returned can then be used to access information about the property. For example, it can be used to get and set the current value of the property on this entity: + + +[!code-csharp[Work_with_a_single_property_1d](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_a_single_property_1d)] + +Both of the Property methods used above return a strongly-typed generic instance. Using this generic type is preferred because it allows access to property values without [boxing value types](/dotnet/csharp/programming-guide/types/boxing-and-unboxing). However, if the type of entity or property is not known at compile-time, then a non-generic can be obtained instead: + + +[!code-csharp[Work_with_a_single_property_1c](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_a_single_property_1c)] + +This allows access to property information for any property regardless of its type, at the expense of boxing value types. For example: + + +[!code-csharp[Work_with_a_single_property_1e](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_a_single_property_1e)] + +The following table summarizes property information exposed by PropertyEntry: + +| PropertyEntry member | Description +|:-------------------------------------------------|---------------------- +| | Gets and sets the current value of the property. +| | Gets and sets the original value of the property, if available. +| | A back reference to the for the entity. +| | metadata for the property. +| | Indicates whether this property is marked as modified, and allows this state to be changed. +| | Indicates whether this property is marked as [temporary](xref:core/change-tracking/miscellaneous#temporary-values#temporary-values), and allows this state to be changed. + +Notes: + +- The original value of a property is the value that the property had when the entity was queried from the database. However, original values are not available if the entity was disconnected and then explicitly attached to another DbContext, for example with `Attach` or `Update`. In this case, the original value returned will be the same as the current value. +- will only update properties marked as modified. Set to true to force EF Core to update a given property value, or set it to false to prevent EF Core from updating the property value. +- [Temporary values](xref:core/change-tracking/miscellaneous) are typically generated by EF Core [value generators](xref:core/modeling/generated-properties). Setting the current value of a property will replace the temporary value with the given value and mark the property as not temporary. Set to true to force a value to be temporary even after it has been explicitly set. + +### Working with a single navigation + +Several overloads of , , and allow access to information about an individual navigation. + +Reference navigations to a single related entity are accessed through the methods. Reference navigations point to the "one" sides of one-to-many relationships, and both sides of one-to-one relationships. For example: + + +[!code-csharp[Work_with_a_single_navigation_1](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_a_single_navigation_1)] + +Navigations can also be collections of related entities when used for the "many" sides of one-to-many and many-to-many relationships. The methods are used to access collection navigations. For example: + + +[!code-csharp[Work_with_a_single_navigation_2a](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_a_single_navigation_2a)] + +Some operations are common for all navigations. These can be accessed for both reference and collection navigations using the method. Note that only non-generic access is available when accessing all navigations together. For example: + + +[!code-csharp[Work_with_a_single_navigation_2b](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_a_single_navigation_2b)] + +The following table summarizes ways to use , , and : + +| NavigationEntry member | Description +|:----------------------------------------------------------------------------------------------------------|---------------------- +| | Gets and sets the current value of the navigation. This is the entire collection for collection navigations. +| | metadata for the navigation. +| | Gets or sets a value indicating whether the related entity or collection has been fully loaded from the database. +| | Loads the related entity or collection from the database; see [Explicit Loading of Related Data](xref:core/querying/related-data/explicit). +| | The query EF Core would use to load this navigation as an `IQueryable` that can be further composed; see [Explicit Loading of Related Data](xref:core/querying/related-data/explicit). + +### Working with all properties of an entity + + returns an of for every property of the entity. This can be used to perform an action for every property of the entity. For example, to set any DateTime property to `DateTime.Now`: + + +[!code-csharp[Work_with_all_properties_of_an_entity_1](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_all_properties_of_an_entity_1)] + +In addition, EntityEntry contains several methods to get and set all property values at the same time. These methods use the class, which represents a collection of properties and their values. PropertyValues can be obtained for current or original values, or for the values as currently stored in the database. For example: + + +[!code-csharp[Work_with_all_properties_of_an_entity_2a](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_all_properties_of_an_entity_2a)] + +These PropertyValues objects are not very useful on their own. However, they can be combined to perform common operations needed when manipulating entities. This is useful when working with data transfer objects and when resolving [optimistic concurrency conflicts](xref:core/saving/concurrency). The following sections show some examples. + +#### Setting current or original values from an entity or DTO + +The current or original values of an entity can be updated by copying values from another object. For example, consider a `BlogDto` data transfer object (DTO) with the same properties as the entity type: + + +[!code-csharp[BlogDto](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=BlogDto)] + +This can be used to set the current values of a tracked entity using : + + +[!code-csharp[Work_with_all_properties_of_an_entity_2b](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_all_properties_of_an_entity_2b)] + +This technique is sometimes used when updating an entity with values obtained from a service call or a client in an n-tier application. Note that the object used does not have to be of the same type as the entity so long as it has properties whose names match those of the entity. In the example above, an instance of the DTO `BlogDto` is used to set the current values of a tracked `Blog` entity. + +Note that properties will only be marked as modified if the value set differs from the current value. + +#### Setting current or original values from a dictionary + +The previous example set values from an entity or DTO instance. The same behavior is available when property values are stored as name/value pairs in a dictionary. For example: + + +[!code-csharp[Work_with_all_properties_of_an_entity_2d](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_all_properties_of_an_entity_2d)] + +#### Setting current or original values from the database + +The current or original values of an entity can be updated with the latest values from the database by calling or and using the returned object to set current or original values, or both. For example: + + +[!code-csharp[Work_with_all_properties_of_an_entity_2c](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_all_properties_of_an_entity_2c)] + +#### Creating a cloned object containing current, original, or database values + +The PropertyValues object returned from CurrentValues, OriginalValues, or GetDatabaseValues can be used to create a clone of the entity using . For example: + + +[!code-csharp[Work_with_all_properties_of_an_entity_2e](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_all_properties_of_an_entity_2e)] + +Note that `ToObject` returns a new instance that is not tracked by the DbContext. The returned object also does not have any relationships set to other entities. + +The cloned object can be useful for resolving issues related to concurrent updates to the database, especially when data binding to objects of a certain type. See [optimistic concurrency](xref:core/saving/concurrency) for more information. + +### Working with all navigations of an entity + + returns an of for every navigation of the entity. and do the same thing, but restricted to reference or collection navigations respectively. This can be used to perform an action for every navigation of the entity. For example, to force loading of all related entities: + + +[!code-csharp[Work_with_all_navigations_of_an_entity_1](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_all_navigations_of_an_entity_1)] + +### Working with all members of an entity + +Regular properties and navigation properties have different state and behavior. It is therefore common to process navigations and non-navigations separately, as shown in the sections above. However, sometimes it can be useful to do something with any member of the entity, regardless of whether it is a regular property or navigation. and are provided for this purpose. For example: + + +[!code-csharp[Work_with_all_members_of_an_entity_1](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Work_with_all_members_of_an_entity_1)] + +Running this code on a blog from the sample generates the following output: + +```output +Member Id is of type int and has value 1 +Member Name is of type string and has value .NET Blog +Member Posts is of type IList and has value System.Collections.Generic.List`1[Post] +``` + +> [!TIP] +> The [change tracker debug view](xref:core/change-tracking/debug-views) shows information like this. The debug view for the entire change tracker is generated from the individual of each tracked entity. + +## Find and FindAsync + +, , , and are designed for efficient lookup of a single entity when its primary key is known. Find first checks if the entity is already tracked, and if so returns the entity immediately. A database query is only made if the entity is not tracked locally. For example, consider this code that calls Find twice for the same entity: + + +[!code-csharp[Find_and_FindAsync_1](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Find_and_FindAsync_1)] + +The output from this code (including EF Core logging) when using SQLite is: + +```output +First call to Find... +info: 12/29/2020 07:45:53.682 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) + Executed DbCommand (1ms) [Parameters=[@__p_0='1' (DbType = String)], CommandType='Text', CommandTimeout='30'] + SELECT "b"."Id", "b"."Name" + FROM "Blogs" AS "b" + WHERE "b"."Id" = @__p_0 + LIMIT 1 +...found blog .NET Blog + +Second call to Find... +...returned the same instance without executing a query. +``` + +Notice that the first call does not find the entity locally and so executes a database query. Conversely, the second call returns the same instance without querying the database because it is already being tracked. + +Find returns null if an entity with the given key is not tracked locally and does not exist in the database. + +### Composite keys + +Find can also be used with composite keys. For example, consider an `OrderLine` entity with a composite key consisting of the order ID and the product ID: + + +[!code-csharp[OrderLine](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=OrderLine)] + +The composite key must be configured in to define the key parts _and their order_. For example: + + +[!code-csharp[OnModelCreating](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=OnModelCreating)] + +Notice that `OrderId` is the first part of the key and `ProductId` is the second part of the key. This order must be used when passing key values to Find. For example: + + +[!code-csharp[Find_and_FindAsync_2](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Find_and_FindAsync_2)] + +## Using ChangeTracker.Entries to access all tracked entities + +So far we have accessed only a single at a time. returns an EntityEntry for every entity currently tracked by the DbContext. For example: + + +[!code-csharp[Using_ChangeTracker_Entries_to_access_all_tracked_entities_1a](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Using_ChangeTracker_Entries_to_access_all_tracked_entities_1a)] + +This code generates the following output: + +```output +Found Blog entity with ID 1 +Found Post entity with ID 1 +Found Post entity with ID 2 +``` + +Notice that entries for both blogs and posts are returned. The results can instead be filtered to a specific entity type using the generic overload: + + +[!code-csharp[Using_ChangeTracker_Entries_to_access_all_tracked_entities_1b](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Using_ChangeTracker_Entries_to_access_all_tracked_entities_1b)] + +The output from this code shows that only posts are returned: + +```output +Found Post entity with ID 1 +Found Post entity with ID 2 +``` + +Also, using the generic overload returns generic instances. This is what allows that fluent-like access to the `Id` property in this example. + +The generic type used for filtering does not have to be a mapped entity type; an unmapped base type or interface can be used instead. For example, if all the entity types in the model implement an interface defining their key property: + + +[!code-csharp[IEntityWithKey](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=IEntityWithKey)] + +Then this interface can be used to work with the key of any tracked entity in a strongly-typed manner. For example: + + +[!code-csharp[Using_ChangeTracker_Entries_to_access_all_tracked_entities_1c](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Using_ChangeTracker_Entries_to_access_all_tracked_entities_1c)] + +## Using DbSet.Local to query tracked entities + +EF Core queries are always executed on the database, and only return entities that have been saved to the database. provides a mechanism to query the DbContext for local, tracked entities. + +Since `DbSet.Local` is used to query tracked entities, it is typical to load entities into the DbContext and then work with those loaded entities. This is especially true for data binding, but can also be useful in other situations. For example, in the following code the database is first queried for all blogs and posts. The extension method is used to execute this query with the results tracked by the context without being returned directly to the application. (Using `ToList` or similar has the same effect but with the overhead of creating the returned list, which is not needed here.) The example then uses `DbSet.Local` to access the locally tracked entities: + + +[!code-csharp[Using_DbSet_Local_to_query_tracked_entities_1](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Using_DbSet_Local_to_query_tracked_entities_1)] + +Notice that, unlike , `DbSet.Local` returns entity instances directly. An EntityEntry can, of course, always be obtained for the returned entity by calling . + +### The local view + + returns a view of locally tracked entities that reflects the current of those entities. Specifically, this means that: + +- `Added` entities are included. Note that this is not the case for normal EF Core queries, since `Added` entities do not yet exist in the database and so are therefore never returned by a database query. +- `Deleted` entities are excluded. Note that this is again not the case for normal EF Core queries, since `Deleted` entities still exist in the database and so _are_ returned by database queries. + +All of this means that `DbSet.Local` is view over the data that reflects the current conceptual state of the entity graph, with `Added` entities included and `Deleted` entities excluded. This matches what database state is expected to be after SaveChanges is called. + +This is typically the ideal view for data binding, since it presents to the user the data as they understand it based on the changes made by the application. + +The following code demonstrates this my marking one post as `Deleted` and then adding a new post, marking it as `Added`: + + +[!code-csharp[Using_DbSet_Local_to_query_tracked_entities_2](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Using_DbSet_Local_to_query_tracked_entities_2)] + +The output from this code is: + +```output +Local view after loading posts: + Post: Announcing the Release of EF Core 5.0 + Post: Announcing F# 5 + Post: Announcing .NET 5.0 +Local view after adding and deleting posts: + Post: What’s next for System.Text.Json? + Post: Announcing the Release of EF Core 5.0 + Post: Announcing .NET 5.0 +``` + +Notice that the deleted post is removed from the local view, and the added post is included. + +### Using Local to add and remove entities + + returns an instance of . This is an implementation of that generates and responds to notifications when entities are added and removed from the collection. (This is the same concept as , but implemented as a projection over existing EF Core change tracking entries, rather than as an independent collection.) + +The local view's notifications are hooked into DbContext change tracking such that the local view stays in sync with the DbContext. Specifically: + +- Adding a new entity to `DbSet.Local` causes it to be tracked by the DbContext, typically in the `Added` state. (If the entity already has a generated key value, then it is tracked as `Unchanged` instead.) +- Removing an entity from `DbSet.Local` causes it to be marked as `Deleted`. +- An entity that becomes tracked by the DbContext will automatically appear in the `DbSet.Local` collection. For example, executing a query to bring in more entities automatically causes the local view to be updated. +- An entity that is marked as `Deleted` will be removed from the local collection automatically. + +This means the local view can be used to manipulate tracked entities simply by adding and removing from the collection. For example, lets modify the previous example code to add and remove posts from the local collection: + + +[!code-csharp[Using_DbSet_Local_to_query_tracked_entities_3](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Using_DbSet_Local_to_query_tracked_entities_3)] + +The output remains unchanged from the previous example because changes made to the local view are synced with the DbContext. + +### Using the local view for Windows Forms or WPF data binding + + forms the basis for data binding to EF Core entities. However, both Windows Forms and WPF work best when used with the specific type of notifying collection that they expect. The local view supports creating these specific collection types: + +- returns an for WPF data binding. +- returns a for Windows Forms data binding. + +For example: + + +[!code-csharp[Using_DbSet_Local_to_query_tracked_entities_4](../../../samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs?name=Using_DbSet_Local_to_query_tracked_entities_4)] + +See [Get Started with WPF](xref:core/get-started/wpf) for more information on WPF data binding with EF Core. + +> [!TIP] +> The local view for a given DbSet instance is created lazily when first accessed and then cached. LocalView creation itself is fast and it does not use significant memory. However, it does call [DetectChanges](xref:core/change-tracking/change-detection), which can be slow for large numbers of entities. The collections created by `ToObservableCollection` and `ToBindingList` are also created lazily and and then cached. Both of these methods create new collections, which can be slow and use a lot of memory when thousands of entities are involved. diff --git a/entity-framework/core/change-tracking/entity-state.md.stub b/entity-framework/core/change-tracking/entity-state.md.stub deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/entity-framework/core/change-tracking/explicit-tracking.md b/entity-framework/core/change-tracking/explicit-tracking.md new file mode 100644 index 0000000000..59802b9280 --- /dev/null +++ b/entity-framework/core/change-tracking/explicit-tracking.md @@ -0,0 +1,990 @@ +--- +title: Explicitly Tracking Entities - EF Core +description: Explicitly tracking entities with DbContext using Add, Attach, Update, and Remove +author: ajcvickers +ms.date: 12/30/2020 +uid: core/change-tracking/explicit-tracking +--- + +# Explicitly Tracking Entities + +Each instance tracks changes made to entities. These tracked entities in turn drive the changes to the database when is called. + +Entity Framework Core (EF Core) change tracking works best when the same instance is used to both query for entities and update them by calling . This is because EF Core automatically tracks the state of queried entities and then detects any changes made to these entities when SaveChanges is called. This approach is covered in [Change Tracking in EF Core](xref:core/change-tracking/index). + +> [!TIP] +> This document assumes that entity states and the basics of EF Core change tracking are understood. See [Change Tracking in EF Core](xref:core/change-tracking/index) for more information on these topics. + +> [!TIP] +> You can run and debug into all the code in this document by [downloading the sample code from GitHub](https://github.com/dotnet/EntityFramework.Docs/tree/master/samples/core/ChangeTracking/ChangeTrackingInEFCore). + +> [!TIP] +> For simplicity, this document uses and references synchronous methods such as rather their async equivalents such as . Calling and awaiting the async method can be substituted unless otherwise noted. + +## Introduction + +Entities can be explicitly "attached" to a such that the context then tracks those entities. This is primarily useful when: + +1. Creating new entities that will be inserted into the database. +2. Re-attaching disconnected entities that were previously queried by a _different_ DbContext instance. + +The first of these will be needed by most applications, and is primary handled by the methods. + +The second is only needed by applications that change entities or their relationships **_while the entities are not being tracked_**. For example, a web application may send entities to the web client where the user makes changes and sends the entities back. These entities are referred to as "disconnected" since they were originally queried from a DbContext, but were then disconnected from that context when sent to the client. + +The web application must now re-attach these entities so that they are again tracked and indicate the changes that have been made such that can make appropriate updates to the database. This is primarily handled by the and methods. + +> [!TIP] +> Attaching entities to the _same DbContext instance_ that they were queried from should not normally be needed. Do not routinely perform a no-tracking query and then attach the returned entities to the same context. This will be slower than using a tracking query, and may also result in issues such as missing shadow property values, making it harder to get right. + +### Generated verses explicit key values + +By default, integer and GUID [key properties](xref:core/modeling/keys) are configured to use [automatically generated key values](xref:core/modeling/generated-properties). This has a **major advantage for change tracking: an unset key value indicates that the entity is "new"**. By "new", we mean that it has not yet been inserted into the database. + +Two models are used in the following sections. The first is configured to **not** use generated key values: + + +[!code-csharp[Model](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysSamples.cs?name=Model)] + +Non-generated (i.e. explicitly set) key values are shown first in each example because everything is very explicit and easy to follow. This is then followed by an example where generated key values are used: + + +[!code-csharp[Model](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/GeneratedKeysSamples.cs?name=Model)] + +Notice that the key properties in this model need no additional configuration here since using generated key values is the [default for simple integer keys](xref:core/modeling/generated-properties). + +## Inserting new entities + +### Explicit key values + +An entity must be tracked in the `Added` state to be inserted by . Entities are typically put in the Added state by calling one of , , , , or the equivalent methods on . + +> [!TIP] +> These methods all work in the same way in the context of change tracking. See [Additional Change Tracking Features](xref:core/change-tracking/miscellaneous) for more information. + +For example, to start tracking a new blog: + + +[!code-csharp[Inserting_new_entities_1](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysSamples.cs?name=Inserting_new_entities_1)] + +Inspecting the [change tracker debug view](xref:core/change-tracking/debug-views) following this call shows that the context is tracking the new entity in the `Added` state: + +```output +Blog {Id: 1} Added + Id: 1 PK + Name: '.NET Blog' + Posts: [] +``` + +However, the Add methods don't just work on an individual entity. They actually start tracking an _entire graph of related entities_, putting them all to the `Added` state. For example, to insert a new blog and associated new posts: + + +[!code-csharp[Inserting_new_entities_2](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysSamples.cs?name=Inserting_new_entities_2)] + +The context is now tracking all these entities as `Added`: + +```output +Blog {Id: 1} Added + Id: 1 PK + Name: '.NET Blog' + Posts: [{Id: 1}, {Id: 2}] +Post {Id: 1} Added + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} +Post {Id: 2} Added + Id: 2 PK + BlogId: 1 FK + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: {Id: 1} +``` + +Notice that explicit values have been set for the `Id` key properties in the examples above. This is because the model here has been configured to use explicitly set key values, rather than automatically generated key values. When not using generated keys, the key properties must be explicitly set _before_ calling `Add`. These key values are then inserted when SaveChanges is called. For example, when using SQLite: + +```sql +-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='.NET Blog' (Size = 9)], CommandType='Text', CommandTimeout='30'] +INSERT INTO "Blogs" ("Id", "Name") +VALUES (@p0, @p1); + +-- Executed DbCommand (0ms) [Parameters=[@p2='1' (DbType = String), @p3='1' (DbType = String), @p4='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p5='Announcing the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30'] +INSERT INTO "Posts" ("Id", "BlogId", "Content", "Title") +VALUES (@p2, @p3, @p4, @p5); + +-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String), @p1='1' (DbType = String), @p2='F# 5 is the latest version of F#, the functional programming language...' (Size = 72), @p3='Announcing F# 5' (Size = 15)], CommandType='Text', CommandTimeout='30'] +INSERT INTO "Posts" ("Id", "BlogId", "Content", "Title") +VALUES (@p0, @p1, @p2, @p3); +``` + +All of these entities are tracked in the `Unchanged` state after SaveChanges completes, since these entities now exist in the database: + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog' + Posts: [{Id: 1}, {Id: 2}] +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} +Post {Id: 2} Unchanged + Id: 2 PK + BlogId: 1 FK + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: {Id: 1} +``` + +### Generated key values + +As mentioned above, integer and GUID [key properties](xref:core/modeling/keys) are configured to use [automatically generated key values](xref:core/modeling/generated-properties) by default. This means that the application _must not set any key value explicitly_. For example, to insert a new blog and posts all with generated key values: + + +[!code-csharp[Inserting_new_entities_3](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/GeneratedKeysSamples.cs?name=Inserting_new_entities_3)] + +As with explicit key values, the context is now tracking all these entities as `Added`: + +```output +Blog {Id: -2147482644} Added + Id: -2147482644 PK Temporary + Name: '.NET Blog' + Posts: [{Id: -2147482637}, {Id: -2147482636}] +Post {Id: -2147482637} Added + Id: -2147482637 PK Temporary + BlogId: -2147482644 FK Temporary + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: -2147482644} +Post {Id: -2147482636} Added + Id: -2147482636 PK Temporary + BlogId: -2147482644 FK Temporary + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: {Id: -2147482644} +``` + +Notice in this case that [temporary key values](xref:core/change-tracking/miscellaneous#temporary-values) have been generated for each entity. These values are used by EF Core until SaveChanges is called, at which point real key values are read back from the database. For example, when using SQLite: + +```sql +-- Executed DbCommand (0ms) [Parameters=[@p0='.NET Blog' (Size = 9)], CommandType='Text', CommandTimeout='30'] +INSERT INTO "Blogs" ("Name") +VALUES (@p0); +SELECT "Id" +FROM "Blogs" +WHERE changes() = 1 AND "rowid" = last_insert_rowid(); + +-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p2='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p3='Announcing the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30'] +INSERT INTO "Posts" ("BlogId", "Content", "Title") +VALUES (@p1, @p2, @p3); +SELECT "Id" +FROM "Posts" +WHERE changes() = 1 AND "rowid" = last_insert_rowid(); + +-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='F# 5 is the latest version of F#, the functional programming language...' (Size = 72), @p2='Announcing F# 5' (Size = 15)], CommandType='Text', CommandTimeout='30'] +INSERT INTO "Posts" ("BlogId", "Content", "Title") +VALUES (@p0, @p1, @p2); +SELECT "Id" +FROM "Posts" +WHERE changes() = 1 AND "rowid" = last_insert_rowid(); +``` + +After SaveChanges completes, all of the entities have been updated with their real key values and are tracked in the `Unchanged` state since they now match the state in the database: + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog' + Posts: [{Id: 1}, {Id: 2}] +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} +Post {Id: 2} Unchanged + Id: 2 PK + BlogId: 1 FK + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: {Id: 1} +``` + +This is exactly the same end-state as the previous example that used explicit key values. + +> [!TIP] +> An explicit key value can still be set even when using generated key values. EF Core will then attempt to insert using this key value. Some database configurations, including SQL Server with Identity columns, do not support such inserts and will throw. + +## Attaching existing entities + +### Explicit key values + +Entities returned from queries are tracked in the `Unchanged` state. The `Unchanged` state means that the entity has not been modified since it was queried. A disconnected entity, perhaps returned from a web client in an HTTP request, can be put into this state using either , , or the equivalent methods on . For example, to start tracking an existing blog: + + +[!code-csharp[Attaching_existing_entities_1](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysSamples.cs?name=Attaching_existing_entities_1)] + +> [!NOTE] +> The examples here are creating entities explicitly with `new` for simplicity. Normally the entity instances will have come from another source, such as being deserialized from a client, or being created from data in an HTTP Post. + +Inspecting the [change tracker debug view](xref:core/change-tracking/debug-views) following this call shows that the entity is tracked in the `Unchanged` state: + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog' + Posts: [] +``` + +Just like `Add`, `Attach` actually sets an entire graph of connected entities to the `Unchanged` state. For example, to attach an existing blog and associated existing posts: + + +[!code-csharp[Attaching_existing_entities_2](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysSamples.cs?name=Attaching_existing_entities_2)] + +The context is now tracking all these entities as `Unchanged`: + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog' + Posts: [{Id: 1}, {Id: 2}] +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} +Post {Id: 2} Unchanged + Id: 2 PK + BlogId: 1 FK + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: {Id: 1} +``` + +Calling SaveChanges at this point will have no effect. All the entities are marked as `Unchanged`, so there is nothing to update in the database. + +### Generated key values + +As mentioned above, integer and GUID [key properties](xref:core/modeling/keys) are configured to use [automatically generated key values](xref:core/modeling/generated-properties) by default. This has a major advantage when working with disconnected entities: an unset key value indicates that the entity has not yet been inserted into the database. This allows the change tracker to automatically detect new entities and put them in the `Added` state. For example, consider attaching this graph of a blog and posts: + +```c# + context.Attach( + new Blog + { + Id = 1, + Name = ".NET Blog", + Posts = + { + new Post + { + Id = 1, + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Id = 2, + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + new Post + { + Title = "Announcing .NET 5.0", + Content = ".NET 5.0 includes many enhancements, including single file applications, more..." + }, + } + }); +``` + + +[!code-csharp[Attaching_existing_entities_3](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/GeneratedKeysSamples.cs?name=Attaching_existing_entities_3)] + +The blog has a key value of 1, indicating that it already exists in the database. Two of the posts also have key values set, but the third does not. EF Core will see this key value as 0, the CLR default for an integer. This results in EF Core marking the new entity as `Added` instead of `Unchanged`: + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog' + Posts: [{Id: 1}, {Id: 2}, {Id: -2147482636}] +Post {Id: -2147482636} Added + Id: -2147482636 PK Temporary + BlogId: 1 FK + Content: '.NET 5.0 includes many enhancements, including single file a...' + Title: 'Announcing .NET 5.0' + Blog: {Id: 1} +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} +Post {Id: 2} Unchanged + Id: 2 PK + BlogId: 1 FK + Content: 'F# 5 is the latest version of F#, the functional programming...' +``` + +Calling SaveChanges at this point does nothing with the `Unchanged` entities, but inserts the new entity into the database. For example, when using SQLite: + +```sql +-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='.NET 5.0 includes many enhancements, including single file applications, more...' (Size = 80), @p2='Announcing .NET 5.0' (Size = 19)], CommandType='Text', CommandTimeout='30'] +INSERT INTO "Posts" ("BlogId", "Content", "Title") +VALUES (@p0, @p1, @p2); +SELECT "Id" +FROM "Posts" +WHERE changes() = 1 AND "rowid" = last_insert_rowid(); +``` + +The important point to notice here is that, with generated key values, EF Core is able to **automatically distinguish new from existing entities in a disconnected graph**. In a nutshell, when using generated keys, EF Core will always insert an entity when that entity has no key value set. + +## Updating existing entities + +### Explicit key values + +, , and the equivalent methods on behave exactly as the `Attach` methods described above, except that entities are put into the `Modfied` instead of the `Unchanged` state. For example, to start tracking an existing blog as `Modified`: + + +[!code-csharp[Updating_existing_entities_1](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysSamples.cs?name=Updating_existing_entities_1)] + +Inspecting the [change tracker debug view](xref:core/change-tracking/debug-views) following this call shows that the context is tracking this entity in the `Modified` state: + +```output +Blog {Id: 1} Modified + Id: 1 PK + Name: '.NET Blog' Modified + Posts: [] +``` + +Just like with `Add` and `Attach`, `Update` actually marks an _entire graph_ of related entities as `Modified`. For example, to attach an existing blog and associated existing posts as `Modified`: + + +[!code-csharp[Updating_existing_entities_2](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysSamples.cs?name=Updating_existing_entities_2)] + +The context is now tracking all these entities as `Modified`: + +```output +Blog {Id: 1} Modified + Id: 1 PK + Name: '.NET Blog' Modified + Posts: [{Id: 1}, {Id: 2}] +Post {Id: 1} Modified + Id: 1 PK + BlogId: 1 FK Modified Originally + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' Modified + Title: 'Announcing the Release of EF Core 5.0' Modified + Blog: {Id: 1} +Post {Id: 2} Modified + Id: 2 PK + BlogId: 1 FK Modified Originally + Content: 'F# 5 is the latest version of F#, the functional programming...' Modified + Title: 'Announcing F# 5' Modified + Blog: {Id: 1} +``` + +Calling SaveChanges at this point will cause updates to be sent to the database for all these entities. For example, when using SQLite: + +```sql +-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0='.NET Blog' (Size = 9)], CommandType='Text', CommandTimeout='30'] +UPDATE "Blogs" SET "Name" = @p0 +WHERE "Id" = @p1; +SELECT changes(); + +-- Executed DbCommand (0ms) [Parameters=[@p3='1' (DbType = String), @p0='1' (DbType = String), @p1='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p2='Announcing the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30'] +UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2 +WHERE "Id" = @p3; +SELECT changes(); + +-- Executed DbCommand (0ms) [Parameters=[@p3='2' (DbType = String), @p0='1' (DbType = String), @p1='F# 5 is the latest version of F#, the functional programming language...' (Size = 72), @p2='Announcing F# 5' (Size = 15)], CommandType='Text', CommandTimeout='30'] +UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2 +WHERE "Id" = @p3; +SELECT changes(); +``` + +### Generated key values + +As with `Attach`, generated key values have the same major benefit for `Update`: an unset key value indicates that the entity is new and has not yet been inserted into the database. As with `Attach`, this allows the DbContext to automatically detect new entities and put them in the `Added` state. For example, consider calling `Update` with this graph of a blog and posts: + + +[!code-csharp[Updating_existing_entities_3](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/GeneratedKeysSamples.cs?name=Updating_existing_entities_3)] + +As with the `Attach` example, the post with no key value is detected as new and set to the `Added` state. The other entities are marked as `Modified`: + +```output +Blog {Id: 1} Modified + Id: 1 PK + Name: '.NET Blog' Modified + Posts: [{Id: 1}, {Id: 2}, {Id: -2147482633}] +Post {Id: -2147482633} Added + Id: -2147482633 PK Temporary + BlogId: 1 FK + Content: '.NET 5.0 includes many enhancements, including single file a...' + Title: 'Announcing .NET 5.0' + Blog: {Id: 1} +Post {Id: 1} Modified + Id: 1 PK + BlogId: 1 FK Modified Originally + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' Modified + Title: 'Announcing the Release of EF Core 5.0' Modified + Blog: {Id: 1} +Post {Id: 2} Modified + Id: 2 PK + BlogId: 1 FK Modified Originally + Content: 'F# 5 is the latest version of F#, the functional programming...' Modified + Title: 'Announcing F# 5' Modified + Blog: {Id: 1} +``` + +Calling `SaveChanges` at this point will cause updates to be sent to the database for all the existing entities, while the new entity is inserted. For example, when using SQLite: + +```sql +-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0='.NET Blog' (Size = 9)], CommandType='Text', CommandTimeout='30'] +UPDATE "Blogs" SET "Name" = @p0 +WHERE "Id" = @p1; +SELECT changes(); + +-- Executed DbCommand (0ms) [Parameters=[@p3='1' (DbType = String), @p0='1' (DbType = String), @p1='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p2='Announcing the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30'] +UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2 +WHERE "Id" = @p3; +SELECT changes(); + +-- Executed DbCommand (0ms) [Parameters=[@p3='2' (DbType = String), @p0='1' (DbType = String), @p1='F# 5 is the latest version of F#, the functional programming language...' (Size = 72), @p2='Announcing F# 5' (Size = 15)], CommandType='Text', CommandTimeout='30'] +UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2 +WHERE "Id" = @p3; +SELECT changes(); + +-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='.NET 5.0 includes many enhancements, including single file applications, more...' (Size = 80), @p2='Announcing .NET 5.0' (Size = 19)], CommandType='Text', CommandTimeout='30'] +INSERT INTO "Posts" ("BlogId", "Content", "Title") +VALUES (@p0, @p1, @p2); +SELECT "Id" +FROM "Posts" +WHERE changes() = 1 AND "rowid" = last_insert_rowid(); +``` + +This is a very easy way to generate updates and inserts from a disconnected graph. However, it results in updates or inserts being sent to the database for every property of every tracked entity, even when some property values may not have been changed. Don't be too scared by this; for many applications with small graphs, this can be an easy and pragmatic way of generating updates. That being said, other more complex patterns can sometimes result in more efficient updates, as described in [Identity Resolution in EF Core](xref:core/change-tracking/identity-resolution). + +## Deleting existing entities + +For an entity to be deleted by SaveChanges it must be tracked in the `Deleted` state. Entities are typically put in the `Deleted` state by calling one of , , or the equivalent methods on . For example, to mark an existing post as `Deleted`: + + +[!code-csharp[Deleting_existing_entities_1](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysSamples.cs?name=Deleting_existing_entities_1)] + +Inspecting the [change tracker debug view](xref:core/change-tracking/debug-views) following this call shows that the context is tracking the entity in the `Deleted` state: + +```output +Post {Id: 2} Deleted + Id: 2 PK + BlogId: FK + Content: + Title: + Blog: +``` + +This entity will be deleted when SaveChanges is called. For example, when using SQLite: + +```sql +-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text', CommandTimeout='30'] +DELETE FROM "Posts" +WHERE "Id" = @p0; +SELECT changes(); +``` + +After SaveChanges completes, the deleted entity is detached from the DbContext since it no longer exists in the database. The debug view is therefore empty because no entities are being tracked. + +## Deleting dependent/child entities + +Deleting dependent/child entities from a graph is more straightforward than deleting principal/parent entities. See the next section and [Changing Foreign Keys and Navigations](xref:core/change-tracking/relationship-changes) for more information. + +It is unusual to call `Remove` on an entity created with `new`. Further, unlike `Add`, `Attach` and `Update`, it is uncommon to call `Remove` on an entity that isn't already tracked in the `Unchanged` or `Modified` state. Instead it is typical to track a single entity or graph of related entities, and then call `Remove` on the entities that should be deleted. This graph of tracked entities is typically created by either: + +1. Running a query for the entities +2. Using the `Attach` or `Update` methods on a graph of disconnected entities, as described in the preceding sections. + +For example, the code in the previous section is more likely obtain a post from a client and then do something like this: + + +[!code-csharp[Deleting_dependent_child_entities_1](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysSamples.cs?name=Deleting_dependent_child_entities_1)] + +This behaves exactly the same way as the previous example, since calling `Remove` on an un-tracked entity causes it to first be attached and then marked as `Deleted`. + +In more realistic examples, a graph of entities is first attached, and then some of those entities are marked as deleted. For example: + + +[!code-csharp[Deleting_dependent_child_entities_2](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysSamples.cs?name=Deleting_dependent_child_entities_2)] + +All entities are marked as `Unchanged`, except the one on which `Remove` was called: + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog' + Posts: [{Id: 1}, {Id: 2}] +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} +Post {Id: 2} Deleted + Id: 2 PK + BlogId: 1 FK + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: {Id: 1} +``` + +This entity will be deleted when SaveChanges is called. For example, when using SQLite: + +```sql +-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text', CommandTimeout='30'] +DELETE FROM "Posts" +WHERE "Id" = @p0; +SELECT changes(); +``` + +After SaveChanges completes, the deleted entity is detached from the DbContext since it no longer exists in the database. Other entities remain in the `Unchanged` state: + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog' + Posts: [{Id: 1}] +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} +``` + +## Deleting principal/parent entities + +Each relationship that connects two entity types has a principal or parent end, and a dependent or child end. The dependent/child entity is the one with the foreign key property. In a one-to-many relationship, the principal/parent is on the "one" side, and the dependent/child is on the "many" side. See [Relationships](xref:core/modeling/relationships) for more information. + +In the preceding examples we were deleting a post, which is a dependent/child entity in the blog-posts one-to-many relationship. This is relatively straightforward since removal of a dependent/child entity does not have any impact on other entities. On the other hand, deleting a principal/parent entity must also impact any dependent/child entities. Not doing so would leave a foreign key value referencing a primary key value that no longer exists. This is an invalid model state and results in a referential constraint error in most databases. + +This invalid model state can be handled in two ways: + +1. Setting FK values to null. This indicates that the dependents/children are no longer related to any principal/parent. This is the default for optional relationships where the foreign key must be nullable. Setting the FK to null is not valid for required relationships, where the foreign key is typically non-nullable. +2. Deleting the the dependents/children. This is the default for required relationships, and is also valid for optional relationships. + +See [Changing Foreign Keys and Navigations](xref:core/change-tracking/relationship-changes) for detailed information on change tracking and relationships. + +### Optional relationships + +The `Post.BlogId` foreign key property is nullable in the model we have been using. This means the relationship is optional, and hence the default behavior of EF Core is to set `BlogId` foreign key properties to null when the blog is deleted. For example: + + +[!code-csharp[Deleting_principal_parent_entities_1](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysSamples.cs?name=Deleting_principal_parent_entities_1)] + +Inspecting the [change tracker debug view](xref:core/change-tracking/debug-views) following the call to `Remove` shows that, as expected, the blog is now marked as `Deleted`: + +```output +Blog {Id: 1} Deleted + Id: 1 PK + Name: '.NET Blog' + Posts: [{Id: 1}, {Id: 2}] +Post {Id: 1} Modified + Id: 1 PK + BlogId: FK Modified Originally 1 + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: +Post {Id: 2} Modified + Id: 2 PK + BlogId: FK Modified Originally 1 + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: +``` + +More interestingly, all the related posts are now marked as `Modified`. This is because the foreign key property in each entity has been set to null. Calling SaveChanges updates the foreign key value for each post to null in the database, before then deleting the blog: + +```sql +-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0=NULL], CommandType='Text', CommandTimeout='30'] +UPDATE "Posts" SET "BlogId" = @p0 +WHERE "Id" = @p1; +SELECT changes(); + +-- Executed DbCommand (0ms) [Parameters=[@p1='2' (DbType = String), @p0=NULL], CommandType='Text', CommandTimeout='30'] +UPDATE "Posts" SET "BlogId" = @p0 +WHERE "Id" = @p1; +SELECT changes(); + +-- Executed DbCommand (0ms) [Parameters=[@p2='1' (DbType = String)], CommandType='Text', CommandTimeout='30'] +DELETE FROM "Blogs" +WHERE "Id" = @p2; +SELECT changes(); +``` + +After SaveChanges completes, the deleted entity is detached from the DbContext since it no longer exists in the database. Other entities are now marked as `Unchanged` with null foreign key values, which matches the state of the database: + +```output +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: +Post {Id: 2} Unchanged + Id: 2 PK + BlogId: FK + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: +``` + +### Required relationships + +If the `Post.BlogId` foreign key property is non-nullable, then the relationship between blogs and posts becomes "required". In this situation, EF Core will, by default, delete dependent/child entities when the principal/parent is deleted. For example, deleting a blog with related posts as in the previous example: + + +[!code-csharp[Deleting_principal_parent_entities_1](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysRequiredSamples.cs?name=Deleting_principal_parent_entities_1)] + +Inspecting the [change tracker debug view](xref:core/change-tracking/debug-views) following the call to `Remove` shows that, as expected, the blog is again marked as `Deleted`: + +```output +Blog {Id: 1} Deleted + Id: 1 PK + Name: '.NET Blog' + Posts: [{Id: 1}, {Id: 2}] +Post {Id: 1} Deleted + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} +Post {Id: 2} Deleted + Id: 2 PK + BlogId: 1 FK + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: {Id: 1} +``` + +More interestingly in this case is that all related posts have also been marked as `Deleted`. Calling SaveChanges causes the blog and all related posts to be deleted from the database: + +```sql +-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String)], CommandType='Text', CommandTimeout='30'] +DELETE FROM "Posts" +WHERE "Id" = @p0; +SELECT changes(); + +-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text', CommandTimeout='30'] +DELETE FROM "Posts" +WHERE "Id" = @p0; +SELECT changes(); + +-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String)], CommandType='Text', CommandTimeout='30'] +DELETE FROM "Blogs" +WHERE "Id" = @p1; +``` + +After SaveChanges completes, all the deleted entities are detached from the DbContext since they no longer exist in the database. Output from the debug view is therefore empty. + +> [!NOTE] +> This document only scratches the surface on working with relationships in EF Core. See [Relationships](xref:core/modeling/relationships) for more information on modeling relationships, and [Changing Foreign Keys and Navigations](xref:core/change-tracking/relationship-changes) for more information on updating/deleting dependent/child entities when calling SaveChanges. + +## Custom tracking with TrackGraph + + works like `Add`, `Attach` and `Update` except that it generates a callback for every entity instance before tracking it. This allows custom logic to be used when determining how to track individual entities in a graph. + +For example, consider the rule EF Core uses when tracking entities with generated key values: if the kye value is zero, then the entity is new and should be inserted. Let's extend this rule to say if the key value is negative, then the entity should be deleted. This allows us to change the primary key values in entities of a disconnected graph to mark deleted entities: + + +[!code-csharp[Custom_tracking_with_TrackGraph_1a](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/GeneratedKeysSamples.cs?name=Custom_tracking_with_TrackGraph_1a)] + +This disconnected graph can then be tracked using TrackGraph: + + +[!code-csharp[Custom_tracking_with_TrackGraph_1b](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/GeneratedKeysSamples.cs?name=Custom_tracking_with_TrackGraph_1b)] + +For each entity in the graph, the code above checks the primary key value _before tracking the entity_. For unset (zero) key values, the code does what EF Core would normally do. That is, if the key is not set, then the entity is marked as `Added`. If the key is set and the value is non-negative, then the entity is marked as `Modified`. However, if a negative key value is found, then its real, non-negative value is restored and the entity is tracked as `Deleted`. + +The output from running this code is: + +```output +Tracking Blog with key value 1 as Modified +Tracking Post with key value 1 as Modified +Tracking Post with key value -2 as Deleted +Tracking Post with key value 0 as Added +``` + +> [!NOTE] +> For simplicity, this code assumes each entity has an integer primary key property called `Id`. This could be codified into an abstract base class or interface. Alternately, the primary key property or properties could be obtained from the metadata such that this code would work with any type of entity. + +TrackGraph has two overloads. In the simple overload used above, EF Core determines when to stop traversing the graph. Specifically, it stops visiting new related entities from a given entity when that entity is either already tracked, or when the callback does not start tracking the entity. + +The advanced overload, , has a callback that returns a bool. If the callback returns false, then graph traversal stops, otherwise it continues. Care must be taken to avoid infinite loops when using this overload. + +The advanced overload also allows state to be supplied to TrackGraph and this state is then passed to each callback. diff --git a/entity-framework/core/change-tracking/find-local-queries.md.stub b/entity-framework/core/change-tracking/find-local-queries.md.stub deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/entity-framework/core/change-tracking/identity-resolution.md b/entity-framework/core/change-tracking/identity-resolution.md new file mode 100644 index 0000000000..072a772ba7 --- /dev/null +++ b/entity-framework/core/change-tracking/identity-resolution.md @@ -0,0 +1,665 @@ +--- +title: Identity Resolution - EF Core +description: Resolving multiple entity instances into a single instance using primary key values +author: ajcvickers +ms.date: 12/30/2020 +uid: core/change-tracking/identity-resolution +--- + +# Identity Resolution in EF Core + +A can only track one entity instance with any given primary key value. This means multiple instances of an entity with the same key value must be resolved to a single instance. This is called "identity resolution". Identity resolution ensures Entity Framework Core (EF Core) is tracking a consistent graph with no ambiguities about the relationships or property values of the entities. + +> [!TIP] +> This document assumes that entity states and the basics of EF Core change tracking are understood. See [Change Tracking in EF Core](xref:core/change-tracking/index) for more information on these topics. + +> [!TIP] +> You can run and debug into all the code in this document by [downloading the sample code from GitHub](https://github.com/dotnet/EntityFramework.Docs/tree/master/samples/core/ChangeTracking/IdentityResolutionInEFCore). + +## Introduction + +The following code queries for an entity and then attempts to attach a different instance with the same primary key value: + + +[!code-csharp[Identity_Resolution_in_EF_Core_1](../../../samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionSamples.cs?name=Identity_Resolution_in_EF_Core_1)] + +Running this code results in the following exception: + +> System.InvalidOperationException: The instance of entity type 'Blog' cannot be tracked because another instance with the key value '{Id: 1}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. + +EF Core requires a single instance because: + +- Property values may be different between multiple instances. When updating the database, EF Core needs to know which property values to use. +- Relationships with other entities may be different between multiple instances. For example, "blogA" may be related to a different collection of posts than "blogB". + +The exception above is commonly encountered in these situations: + +- When attempting to update an entity +- When attempting to track a serialized graph of entities +- When failing to set a key value that is not automatically generated +- When reusing a DbContext instance for multiple units-of-work + +Each of these situations is discussed in the following sections. + +## Updating an entity + +There are several different approaches to update an entity with new values, as covered in [Change Tracking in EF Core](xref:core/change-tracking/index) and [Explicitly Tracking Entities](xref:core/change-tracking/explicit-tracking). These approaches are outlined below in the context of identity resolution. An important point to notice is that each of the approaches use either a query or a call to one of `Update` or `Attach`, but **_never both_**. + +### Call Update + +Often the entity to update does not come from a query on the DbContext that we are going to use for SaveChanges. For example, in a web application, an entity instance may be created from the information in a POST request. The simplest way to handle this is to use or . For example: + + +[!code-csharp[Updating_an_entity_1](../../../samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionSamples.cs?name=Updating_an_entity_1)] + +In this case: + +- Only a single instance of the entity is created. +- The entity instance is **not** queried from the database as part of making the update. +- All property values will be updated in the database, regardless of whether they have actually changed or not. +- One database round-trip is made. + +### Query then apply changes + +Usually it is not known which property values have actually been changed when an entity is created from information in a POST request or similar. Often it is fine to just update all values in the database, as we did in the previous example. However, if the application is handling many entities and only a small number of those have actual changes, then it may be useful to limit the updates sent. This can be achieved by executing a query to track the entities as they currently exist in the database, and then applying changes to these tracked entities. For example: + + +[!code-csharp[Updating_an_entity_2](../../../samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionSamples.cs?name=Updating_an_entity_2)] + +In this case: + +- Only a single instance of the entity is tracked; the one that is returned from the database by the `Find` query. +- `Update`, `Attach`, etc. are **not** used. +- Only property values that have actually changed are updated in the database. +- Two database round-trips are made. + +EF Core has some helpers for transferring property values like this. For example, will copy all the values from the given object and set them on the tracked object: + + +[!code-csharp[Updating_an_entity_3](../../../samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionSamples.cs?name=Updating_an_entity_3)] + +`SetValues` accepts various object types, including data transfer objects (DTOs) with property names that match the properties of the entity type. For example: + + +[!code-csharp[Updating_an_entity_4](../../../samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionSamples.cs?name=Updating_an_entity_4)] + +Or a dictionary with name/value entries for the property values: + + +[!code-csharp[Updating_an_entity_5](../../../samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionSamples.cs?name=Updating_an_entity_5)] + +See [Accessing tracked entities](xref:core/change-tracking/entity-entries) for more information on working with property values like this. + +### Use original values + +So far each approach has either executed a query before making the update, or updated all property values regardless of whether or not they have changed. To update only values that have changed without querying as part of the update requires specific information about which property values have changed. A common way to get this information is to send back both the current and original values in the HTTP Post or similar. For example: + + +[!code-csharp[Updating_an_entity_6](../../../samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionSamples.cs?name=Updating_an_entity_6)] + +In this code the entity with modified values is first attached. This causes EF Core to track the entity in the `Unchanged` state; that is, with no property values marked as modified. The dictionary of original values is then applied to this tracked entity. This will mark as modified properties with different current and original values. Properties that have the same current and original values will not be marked as modified. + +In this case: + +- Only a single instance of the entity is tracked, using Attach. +- The entity instance is **not** queried from the database as part of making the update. +- Applying the original values ensures that only property values that have actually changed are updated in the database. +- One database round-trip is made. + +As with the examples in the previous section, the original values do not have to passed as a dictionary; an entity instance or DTO will also work. + +> [!TIP] +> While this approach has appealing characteristics, it does require sending the entity's original values to and from the web client. Carefully consider whether this extra complexity is worth the benefits; for many applications one of the simpler approaches is more pragmatic. + +## Attaching a serialized graph + +EF Core works with graphs of entities connected via foreign keys and navigation properties, as described in [Changing Foreign Keys and Navigations](xref:core/change-tracking/relationship-changes). If these graphs are created outside of EF Core using, for example, from a JSON file, then they can have multiple instances of the same entity. These duplicates need to be resolved into single instances before the graph can be tracked. + +### Graphs with no duplicates + +Before going any further it is important to recognize that: + +- Serializers often have options for handling loops and duplicate instances in the graph. +- The choice of object used as the graph root can often help reduce or remove duplicates. + +If possible, use serialization options and choose roots that do not result in duplicates. For example, the following code uses [Json.NET](https://www.nuget.org/packages/Newtonsoft.Json/) to serialize a list of blogs each with its associated posts: + + +[!code-csharp[Attaching_a_serialized_graph_1a](../../../samples/core/ChangeTracking/IdentityResolutionInEFCore/SerializedGraphExamples.cs?name=Attaching_a_serialized_graph_1a)] + +The JSON generated from this code is: + +```json +[ + { + "Id": 1, + "Name": ".NET Blog", + "Summary": "Posts about .NET", + "Posts": [ + { + "Id": 1, + "Title": "Announcing the Release of EF Core 5.0", + "Content": "Announcing the release of EF Core 5.0, a full featured cross-platform...", + "BlogId": 1 + }, + { + "Id": 2, + "Title": "Announcing F# 5", + "Content": "F# 5 is the latest version of F#, the functional programming language...", + "BlogId": 1 + } + ] + }, + { + "Id": 2, + "Name": "Visual Studio Blog", + "Summary": "Posts about Visual Studio", + "Posts": [ + { + "Id": 3, + "Title": "Disassembly improvements for optimized managed debugging", + "Content": "If you are focused on squeezing out the last bits of performance for your .NET service or...", + "BlogId": 2 + }, + { + "Id": 4, + "Title": "Database Profiling with Visual Studio", + "Content": "Examine when database queries were executed and measure how long the take using...", + "BlogId": 2 + } + ] + } +] +``` + +Notice that there are no duplicate blogs or posts in the JSON. This means that simple calls to `Update` will work to update these entities in the database: + + +[!code-csharp[Attaching_a_serialized_graph_1b](../../../samples/core/ChangeTracking/IdentityResolutionInEFCore/SerializedGraphExamples.cs?name=Attaching_a_serialized_graph_1b)] + +### Handling duplicates + +The code in the previous example serialized each blog with its associated posts. If this is changed to serialize each post with its associated blog, then duplicates are introduced into the serialized JSON. For example: + + +[!code-csharp[Attaching_a_serialized_graph_2](../../../samples/core/ChangeTracking/IdentityResolutionInEFCore/SerializedGraphExamples.cs?name=Attaching_a_serialized_graph_2)] + +The serialized JSON now looks like this: + +```json +[ + { + "Id": 1, + "Title": "Announcing the Release of EF Core 5.0", + "Content": "Announcing the release of EF Core 5.0, a full featured cross-platform...", + "BlogId": 1, + "Blog": { + "Id": 1, + "Name": ".NET Blog", + "Summary": "Posts about .NET", + "Posts": [ + { + "Id": 2, + "Title": "Announcing F# 5", + "Content": "F# 5 is the latest version of F#, the functional programming language...", + "BlogId": 1 + } + ] + } + }, + { + "Id": 2, + "Title": "Announcing F# 5", + "Content": "F# 5 is the latest version of F#, the functional programming language...", + "BlogId": 1, + "Blog": { + "Id": 1, + "Name": ".NET Blog", + "Summary": "Posts about .NET", + "Posts": [ + { + "Id": 1, + "Title": "Announcing the Release of EF Core 5.0", + "Content": "Announcing the release of EF Core 5.0, a full featured cross-platform...", + "BlogId": 1 + } + ] + } + }, + { + "Id": 3, + "Title": "Disassembly improvements for optimized managed debugging", + "Content": "If you are focused on squeezing out the last bits of performance for your .NET service or...", + "BlogId": 2, + "Blog": { + "Id": 2, + "Name": "Visual Studio Blog", + "Summary": "Posts about Visual Studio", + "Posts": [ + { + "Id": 4, + "Title": "Database Profiling with Visual Studio", + "Content": "Examine when database queries were executed and measure how long the take using...", + "BlogId": 2 + } + ] + } + }, + { + "Id": 4, + "Title": "Database Profiling with Visual Studio", + "Content": "Examine when database queries were executed and measure how long the take using...", + "BlogId": 2, + "Blog": { + "Id": 2, + "Name": "Visual Studio Blog", + "Summary": "Posts about Visual Studio", + "Posts": [ + { + "Id": 3, + "Title": "Disassembly improvements for optimized managed debugging", + "Content": "If you are focused on squeezing out the last bits of performance for your .NET service or...", + "BlogId": 2 + } + ] + } + } +] +``` + +Notice that the graph now includes multiple Blog instances with the same key value, as well as multiple Post instance with the same key value. Attempting to track this graph like we did in the previous example will throw: + +> System.InvalidOperationException: The instance of entity type 'Post' cannot be tracked because another instance with the key value '{Id: 2}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. + +We can fix this in two ways: + +- Use JSON serialization options that preserve references +- Perform identity resolution while the graph is being tracked + +#### Preserve references + +Json.NET provides the `PreserveReferencesHandling` option to handle this. For example: + + +[!code-csharp[Attaching_a_serialized_graph_3](../../../samples/core/ChangeTracking/IdentityResolutionInEFCore/SerializedGraphExamples.cs?name=Attaching_a_serialized_graph_3)] + +The resulting JSON now looks like this: + +```json +{ + "$id": "1", + "$values": [ + { + "$id": "2", + "Id": 1, + "Title": "Announcing the Release of EF Core 5.0", + "Content": "Announcing the release of EF Core 5.0, a full featured cross-platform...", + "BlogId": 1, + "Blog": { + "$id": "3", + "Id": 1, + "Name": ".NET Blog", + "Summary": "Posts about .NET", + "Posts": [ + { + "$ref": "2" + }, + { + "$id": "4", + "Id": 2, + "Title": "Announcing F# 5", + "Content": "F# 5 is the latest version of F#, the functional programming language...", + "BlogId": 1, + "Blog": { + "$ref": "3" + } + } + ] + } + }, + { + "$ref": "4" + }, + { + "$id": "5", + "Id": 3, + "Title": "Disassembly improvements for optimized managed debugging", + "Content": "If you are focused on squeezing out the last bits of performance for your .NET service or...", + "BlogId": 2, + "Blog": { + "$id": "6", + "Id": 2, + "Name": "Visual Studio Blog", + "Summary": "Posts about Visual Studio", + "Posts": [ + { + "$ref": "5" + }, + { + "$id": "7", + "Id": 4, + "Title": "Database Profiling with Visual Studio", + "Content": "Examine when database queries were executed and measure how long the take using...", + "BlogId": 2, + "Blog": { + "$ref": "6" + } + } + ] + } + }, + { + "$ref": "7" + } + ] +} +``` + +Notice that this JSON has replaced duplicates with references like `"$ref": "5"` that refer to the already existing instance in the graph. This graph can again be tracked using the simple calls to `Update`, as shown above. + +The support in the .NET base class libraries (BCL) has a similar option which produces the same result. For example: + + +[!code-csharp[Attaching_a_serialized_graph_4](../../../samples/core/ChangeTracking/IdentityResolutionInEFCore/SerializedGraphExamples.cs?name=Attaching_a_serialized_graph_4)] + +#### Resolve duplicates + +If it is not possible to eliminate duplicates in the serialization process, then provides a way to handle this. TrackGraph works like `Add`, `Attach` and `Update` except that it generates a callback for every entity instance before tracking it. This callback can be used to either track the entity or ignore it. For example: + + +[!code-csharp[Attaching_a_serialized_graph_5](../../../samples/core/ChangeTracking/IdentityResolutionInEFCore/SerializedGraphExamples.cs?name=Attaching_a_serialized_graph_5)] + +For each entity in the graph, this code will: + +- Find the entity type and key value of the entity +- Lookup the entity with this key in the change tracker + - If the entity is found, then no further action is taken as the entity is a duplicate + - If the entity is not found, then it is tracked by setting the state to `Modified` + +The output from running this code is: + +```output +Tracking EntityType: Post entity with key value 1 +Tracking EntityType: Blog entity with key value 1 +Tracking EntityType: Post entity with key value 2 +Discarding duplicate EntityType: Post entity with key value 2 +Tracking EntityType: Post entity with key value 3 +Tracking EntityType: Blog entity with key value 2 +Tracking EntityType: Post entity with key value 4 +Discarding duplicate EntityType: Post entity with key value 4 +``` + +> [!IMPORTANT] +> This code **assumes that all duplicates are identical**. This makes it safe to arbitrarily choose one of the duplicates to track while discarding the others. If the duplicates can differ, then the code will need to decide how to determine which one to use, and how to combine property and navigation values together. + +> [!NOTE] +> For simplicity, this code assumes each entity has a primary key property called `Id`. This could be codified into an abstract base class or interface. Alternately, the primary key property or properties could be obtained from the metadata such that this code would work with any type of entity. + +## Failing to set key values + +Entity types are often configured to use [automatically generated key values](xref:core/modeling/generated-properties). This is the default for integer and GUID properties of non-composite keys. However, if the entity type is not configured to use automatically generated key values, then an explicit key value must be set before tracking the entity. For example, using the following entity type: + + +[!code-csharp[Pet](../../../samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionSamples.cs?name=Pet)] + +Consider code that attempts to tracker two new entity instances without setting key values: + + +[!code-csharp[Failing_to_set_key_values_1](../../../samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionSamples.cs?name=Failing_to_set_key_values_1)] + +This code will throw: + +> System.InvalidOperationException: The instance of entity type 'Pet' cannot be tracked because another instance with the key value '{Id: 0}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. + +The fix for this is to either to set key values explicitly or configure the key property to use generated key values. See [Generated Values](xref:core/modeling/generated-properties) for more information. + +## Overusing a single DbContext instance + + is designed to represent a short-lived unit-of-work, as described in [DbContext Initialization and Configuration](xref:core/dbcontext-configuration/index), and elaborated on in [Change Tracking in EF Core](xref:core/change-tracking/index). Not following this guidance makes it is easy to run into situations where an attempt is made to track multiple instances of the same entity. Common examples are: + +- Using the same DbContext instance to both setup test state and then execute the test. This often results in the DbContext still tracking one entity instance from test setup, while then attempting to attach a new instance in the test proper. Instead, use a different DbContext instance for setting up test state and the test code proper. +- Using a shared DbContext instance in a repository or similar code. Instead, make sure your repository uses a single DbContext instance for each unit-of-work. + +## Identity resolution and queries + +Identity resolution happens automatically when entities are tracked from a query. This means that if an entity instance with a given key value is already tracked, then this existing tracked instance is used instead of creating a new instance. This has an important consequence: if the data has changed in the database, then this will not be reflected in the results of the query. This is angood reason to use a new DbContext instance for each unit-of-work, as described in [DbContext Initialization and Configuration](xref:core/dbcontext-configuration/index), and elaborated on in [Change Tracking in EF Core](xref:core/change-tracking/index). + +> [!IMPORTANT] +> It is important to understand that EF Core always executes a LINQ query on a DbSet against the database and only returns results based on what is in the database. However, for a tracking query, if the entities returned are already tracked, then the tracked instances are used instead of creating a instances from the data in the database. + + or can be used when tracked entities need to be refreshed with the latest data from the database. See [Accessing Tracked Entities](xref:core/change-tracking/entity-entries) for more information. + +In contrast to tracking queries, no-tracking queries do not perform identity resolution. This means that no-tracking queries can return duplicates just like in the JSON serialization case described earlier. This is usually not an issue if the query results are going to be serialized and sent to the client. + +> [!TIP] +> Do not routinely perform a no-tracking query and then attach the returned entities to the same context. This will be both slower and harder to get right than using a tracking query. + +No-tracking queries do not perform identity resolution because doing so impacts the performance of streaming a large number of entities from a query. This is because identity resolution requires keeping track of each instance returned so that it can be used instead of later creating a duplicate. + +Starting with EF Core 5.0, no-tracking queries can be forced to perform identity resolution by using . The query will then keep track of returned instances (without tracking them in the normal way) and ensure no duplicates are created in the query results. + +## Overriding object equality + +EF Core uses [reference equality](/dotnet/csharp/programming-guide/statements-expressions-operators/equality-comparisons) when comparing entity instances. This is the case even if the entity types override or otherwise change object equality. However, there is one place where overriding equality can impact EF Core behavior: when collection navigations use the overridden equality instead of reference equality, and hence report multiple instances as the same. + +Because of this it is recommended that overriding entity equality should be avoided. If it is used, then make sure to create collection navigations that force reference equality. For example, create an equality comparer that uses reference equality: + + +[!code-csharp[ReferenceEqualityComparer](../../../samples/core/ChangeTracking/IdentityResolutionInEFCore/ReferenceEqualityComparer.cs?name=ReferenceEqualityComparer)] + +(Starting with .NET 5, this is included in the BCL as .) + +This comparer can then be used when creating collection navigations. For example: + + +[!code-csharp[OrdersCollection](../../../samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionSamples.cs?name=OrdersCollection)] + +### Comparing key properties + +In addition to equality comparisons, key values also need to be ordered. This is important for avoiding deadlocks when updating multiple entities in a single call to SaveChanges. All types used for primary, alternate, or foreign key properties, as well as those used for unique indexes, must implement and . Types normally used as keys (int, Guid, string, etc.) already support these interfaces. Custom key types may to add these interfaces. diff --git a/entity-framework/core/change-tracking/index.md b/entity-framework/core/change-tracking/index.md new file mode 100644 index 0000000000..37a691d492 --- /dev/null +++ b/entity-framework/core/change-tracking/index.md @@ -0,0 +1,271 @@ +--- +title: Change Tracking - EF Core +description: Overview of change tracking for EF Core +author: ajcvickers +ms.date: 12/30/2020 +uid: core/change-tracking/index +--- + +# Change Tracking in EF Core + +Each instance tracks changes made to entities. These tracked entities in turn drive the changes to the database when is called. + +This document presents an overview of Entity Framework Core (EF Core) change tracking and how it relates to queries and updates. + +> [!TIP] +> You can run and debug into all the code in this document by [downloading the sample code from GitHub](https://github.com/dotnet/EntityFramework.Docs/tree/master/samples/core/ChangeTracking/ChangeTrackingInEFCore). + +> [!TIP] +> For simplicity, this document uses and references synchronous methods such as rather their async equivalents such as . Calling and awaiting the async method can be substituted unless otherwise noted. + +## How to track entities + +Entity instances become tracked when they are: + +- Returned from a query executed against the database +- Explicitly attached to the DbContext by `Add`, `Attach`, `Update`, or similar methods +- Detected as new entities connected to existing tracked entities + +Entity instances are no longer tracked when: + +- The DbContext is disposed +- The change tracker is cleared (EF Core 5.0 and later) +- The entities are explicitly detached + +DbContext is designed to represent a short-lived unit-of-work, as described in [DbContext Initialization and Configuration](xref:core/dbcontext-configuration/index). This means that disposing the DbContext is _the normal way_ to stop tracking entities. In other words, the lifetime of a DbContext should be: + +1. Create the DbContext instance +2. Track some entities +3. Make some changes to the entities +4. Call SaveChanges to update the database +5. Dispose the DbContext instance + +> [!TIP] +> It is not necessary to clear the change tracker or explicitly detach entity instances when taking this approach. However, if you do need to detach entities, then calling is more efficient than detaching entities one-by-one. + +## Entity states + +Every entity is is associated with a given : + +- `Detached` entities are not being tracked by the . +- `Added` entities are new and have not yet been inserted into the database. This means they will be inserted when is called. +- `Unchanged` entities have _not_ been changed since they were queried from the database. All entities returned from queries are initially in this state. +- `Modified` entities have been changed since they were queried from the database. This means they will be updated when SaveChanges is called. +- `Deleted` entities exist in the database, but are marked to be deleted when SaveChanges is called. + +EF Core tracks changes at the property level. For example, if only a single property value is modified, then a database update will change only that value. However, properties can only be marked as modified when the entity itself is in the Modified state. (Or, from an alternate perspective, the Modified state means that at least one property value has been marked as modified.) + +The following table summarizes the different states: + +| Entity state | Tracked by DbContext | Exists in database | Properties modified | Action on SaveChanges +|:-----------------|----------------------|--------------------|---------------------|----------------------- +| `Detached` | No | - | - | - +| `Added` | Yes | No | - | Insert +| `Unchanged` | Yes | Yes | No | - +| `Modified` | Yes | Yes | Yes | Update +| `Deleted` | Yes | Yes | - | Delete + +> [!NOTE] +> This text uses relational database terms for clarity. NoSQL databases typically support similar operations but possibly with different names. Consult your database provider documentation for more information. + +## Tracking from queries + +EF Core change tracking works best when the same instance is used to both query for entities and update them by calling . This is because EF Core automatically tracks the state of queried entities and then detects any changes made to these entities when SaveChanges is called. + +This approach has several advantages over [explicitly tracking entity instances](xref:core/change-tracking/explicit-tracking): + +- It is simple. Entity states rarely need to be manipulated explicitly--EF Core takes care of state changes. +- Updates are limited to only those values that have actually changed. +- The values of [shadow properties](xref:core/modeling/shadow-properties) are preserved and used as needed. This is especially relevant when foreign keys are stored in shadow state. +- The original values of properties are preserved automatically and used for efficient updates. + +## Simple query and update + +For example, consider a simple blog/posts model: + + +[!code-csharp[Model](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/GeneratedKeysSamples.cs?name=Model)] + +We can use this model to query for blogs and posts and then make some updates to the database: + + +[!code-csharp[Simple_query_and_update_1](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/GeneratedKeysSamples.cs?name=Simple_query_and_update_1)] + +Calling SaveChanges results in the following database updates, using SQLite as an example database: + +```sql +-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0='.NET Blog (Updated!)' (Size = 20)], CommandType='Text', CommandTimeout='30'] +UPDATE "Blogs" SET "Name" = @p0 +WHERE "Id" = @p1; +SELECT changes(); + +-- Executed DbCommand (0ms) [Parameters=[@p1='2' (DbType = String), @p0='Announcing F# 5.0' (Size = 17)], CommandType='Text', CommandTimeout='30'] +UPDATE "Posts" SET "Title" = @p0 +WHERE "Id" = @p1; +SELECT changes(); +``` + +The [change tracker debug view](xref:core/change-tracking/debug-views) is a great way visualize which entities are being tracked and what their states are. For example, inserting the following code into the sample above before calling SaveChanges: + + +[!code-csharp[Simple_query_and_update_2](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/GeneratedKeysSamples.cs?name=Simple_query_and_update_2)] + +Generates the following output: + +```output +Blog {Id: 1} Modified + Id: 1 PK + Name: '.NET Blog (Updated!)' Modified Originally '.NET Blog' + Posts: [{Id: 1}, {Id: 2}, {Id: 3}] +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} +Post {Id: 2} Modified + Id: 2 PK + BlogId: 1 FK + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5.0' Modified Originally 'Announcing F# 5' + Blog: {Id: 1} +``` + +Notice specifically: + +- The `Blog.Name` property is marked as modified (`Name: '.NET Blog (Updated!)' Modified Originally '.NET Blog'`), and this results in the blog being in the `Modified` state. +- The `Post.Title` property of post 2 is marked as modified (`Title: 'Announcing F# 5.0' Modified Originally 'Announcing F# 5'`), and this results in this post being in the `Modified` state. +- The other property values of post 2 have not changed and are therefore not marked as modified. This is why these values are not included in the database update. +- The other post was not modified in any way. This is why it is still in the `Unchanged` state and is not included in the database update. + +## Query then insert, update, and delete + +Updates like those in the previous example can be combined with inserts and deletes in the same unit-of-work. For example: + + +[!code-csharp[Query_then_insert_update_and_delete_1](../../../samples/core/ChangeTracking/ChangeTrackingInEFCore/GeneratedKeysSamples.cs?name=Query_then_insert_update_and_delete_1)] + +In this example: + +- A blog and related posts are queried from the database and tracked +- The `Blog.Name` property is changed +- A new post is added to the collection of existing posts for the blog +- An existing post is marked for deletion by calling + +Looking again at the [change tracker debug view](xref:core/change-tracking/debug-views) before calling SaveChanges shows how EF Core is tracking these changes: + +```output +Blog {Id: 1} Modified + Id: 1 PK + Name: '.NET Blog (Updated!)' Modified Originally '.NET Blog' + Posts: [{Id: 1}, {Id: 2}, {Id: 3}, {Id: -2147482638}] +Post {Id: -2147482638} Added + Id: -2147482638 PK Temporary + BlogId: 1 FK + Content: '.NET 5.0 was released recently and has come with many...' + Title: 'What's next for System.Text.Json?' + Blog: {Id: 1} +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} +Post {Id: 2} Deleted + Id: 2 PK + BlogId: 1 FK + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: {Id: 1} +``` + +Notice that: + +- The blog is marked as `Modified`. This will generate a database update. +- Post 2 is marked as `Deleted`. This will generate a database delete. +- A new post with a temporary ID is associated with blog 1 and is marked as `Added`. This will generate a database insert. + +This results in the following database commands (using SQLite) when SaveChanges is called: + +```sql +-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0='.NET Blog (Updated!)' (Size = 20)], CommandType='Text', CommandTimeout='30'] +UPDATE "Blogs" SET "Name" = @p0 +WHERE "Id" = @p1; +SELECT changes(); + +-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text', CommandTimeout='30'] +DELETE FROM "Posts" +WHERE "Id" = @p0; +SELECT changes(); + +-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='.NET 5.0 was released recently and has come with many...' (Size = 56), @p2='What's next for System.Text.Json?' (Size = 33)], CommandType='Text', CommandTimeout='30'] +INSERT INTO "Posts" ("BlogId", "Content", "Title") +VALUES (@p0, @p1, @p2); +SELECT "Id" +FROM "Posts" +WHERE changes() = 1 AND "rowid" = last_insert_rowid(); +``` + +See [Explicitly Tracking Entities](xref:core/change-tracking/explicit-tracking) for more information on inserting and deleting entities. See [Change Detection and Notifications](xref:core/change-tracking/change-detection) for more information on how EF Core automatically detects changes like this. + +> [!TIP] +> Call to determine whether any changes have been made that will cause SaveChanges to make updates to the database. If HasChanges return false, then SaveChanges will be a no-op. diff --git a/entity-framework/core/change-tracking/index.md.stub b/entity-framework/core/change-tracking/index.md.stub deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/entity-framework/core/change-tracking/miscellaneous.md b/entity-framework/core/change-tracking/miscellaneous.md new file mode 100644 index 0000000000..512ab72db8 --- /dev/null +++ b/entity-framework/core/change-tracking/miscellaneous.md @@ -0,0 +1,509 @@ +--- +title: Additional Change Tracking Features - EF Core +description: Miscellaneous features and scenarios involving EF Core change tracking +author: ajcvickers +ms.date: 12/30/2020 +uid: core/change-tracking/miscellaneous +--- + +# Additional Change Tracking Features + +This document covers miscellaneous features and scenarios involving change tracking. + +> [!TIP] +> This document assumes that entity states and the basics of EF Core change tracking are understood. See [Change Tracking in EF Core](xref:core/change-tracking/index) for more information on these topics. + +> [!TIP] +> You can run and debug into all the code in this document by [downloading the sample code from GitHub](https://github.com/dotnet/EntityFramework.Docs/tree/master/samples/core/ChangeTracking/AdditionalChangeTrackingFeatures). + +## Add verses AddAsync + +Entity Framework Core (EF Core) provides async methods whenever using that method may result in a database interaction. Synchronous methods are also provided to avoid overhead when using databases that do not support high performance asynchronous access. + + and do not normally access the database, since these methods inherently just start tracking entities. However, some forms of value generation _may_ access the database in order to generate a key value. The only value generator that does this and ships with EF Core is . Using this generator is uncommon; it is never configured by default. This means that the vast majority of applications should use `Add`, and not `AddAsync`. + +Other similar methods like `Update`, `Attach`, and `Remove` do not have async overloads because they never generate new key values, and hence never need to access the database. + +## AddRange, UpdateRange, AttachRange, and RemoveRange + + and provide alternate versions of `Add`, `Update`, `Attach`, and `Remove` that accept multiple instances in a single call. These methods are called `AddRange`, `UpdateRange`, `AttachRange`, and `RemoveRange` respectively. + +These methods are provided as a convenience. Using a "range" method has the same functionality as multiple calls to the equivalent non-range method. There is no significant performance difference between the two approaches. + +> [!NOTE] +> This is different from EF6, where AddRange and Add both automatically called DetectChanges, but calling Add multiple times caused DetectChanges to be called multiple times instead of once. This made AddRange more efficient in EF6. In EF Core, neither of these methods automatically call DetectChanges. + +## DbContext verses DbSet methods + +Many methods, including `Add`, `Update`, `Attach`, and `Remove`, have implementations on both and . These methods have _exactly the same behavior_ for normal entity types. This is because the CLR type of the entity is mapped onto one and only one entity type in the EF Core model. Therefore, the CLR type fully defines where the entity fits in the model, and so the DbSet to use can be determined implicitly. + +The exception to this rule is when using shared-type entity types, which were introduced in EF Core 5.0, primarily for many-to-many join entities. When using a shared-type entity type, a DbSet must first be created for the EF Core model type that is being used. Methods like `Add`, `Update`, `Attach`, and `Remove` can then be used on the DbSet without any ambiguity as to which EF Core model type is being used. + +Shared-type entity types are used by default for the join entities in many-to-many relationships. A shared-type entity type can also be explicitly configured for use in a many-to-many relationship. For example, the code below configures `Dictionary` as a join entity type: + + +[!code-csharp[OnModelCreating](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/Samples.cs?name=OnModelCreating)] + +[Changing Foreign Keys and Navigations](xref:core/change-tracking/relationship-changes) shows how to associate two entities by tracking a new join entity instance. The code below does this for the `Dictionary` shared-type entity type used for the join entity: + + +[!code-csharp[DbContext_verses_DbSet_methods_1](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/Samples.cs?name=DbContext_verses_DbSet_methods_1)] + +Notice that is used to create a DbSet for the `PostTag` entity type. This DbSet can then be used to call `Add` with the new join entity instance. + +> [!IMPORTANT] +> The CLR type used for join entity types by convention may change in future releases to improve performance. Do not depend on any specific join entity type unless it has been explicitly configured as is done for `Dictionary` in the code above. + +## Property verses field access + +Starting with EF Core 3.0, access to entity properties uses the backing field of the property by default. This is efficient and avoids triggering side effects from calling property getters and setters. For example, this is how lazy-loading is able to avoid triggering infinite loops. See [Backing Fields](xref:core/modeling/backing-field) for more information on configuring backing fields in the model. + +Sometimes it may be desirable for EF Core to generate side-effects when it modifies property values. For example, when data binding to entities, setting a property may generate notifications to the U.I. which do not happen when setting the field directly. This can be achieved by changing the for: + +- All entity types in the model using +- All properties and navigations of a specific entity type using +- A specific property using +- A specific navigation using + +Property access modes `Field` and `PreferField` will cause EF Core to access the property value through its backing field. Likewise, `Property` and `PreferProperty` will cause EF Core to access the property value through its getter and setter. + +If `Field` or `Property` are used and EF Core cannot access the value through the field or property getter/setter respectively, then EF Core will throw an exception. This ensures EF Core is always using field/property access when you think it is. + +On the other hand, the `PreferField` and `PreferProperty` modes will fall back to using the property or backing field respectively if it is not possible to use the preferred access. `PreferField` is the default from EF Core 3.0 onwards. This means EF Core will use fields whenever it can, but will not fail if a property must be accessed through its getter or setter instead. + +`FieldDuringConstruction` and `PreferFieldDuringConstruction` configure EF Core to use of backing fields _only when creating entity instances_. This allows queries to be executed without getter and setter side effects, while later property changes by EF Core will cause these side effects. `PreferFieldDuringConstruction` was the default prior to EF Core 3.0. + +The different property access modes are summarized in the following table: + +| PropertyAccessMode | Preference | Preference creating entities | Fallback | Fallback creating entities +|:--------------------------------|------------|------------------------------|----------|--------------------------- +| `Field` | Field | Field | Throws | Throws +| `Property` | Property | Property | Throws | Throws +| `PreferField` | Field | Field | Property | Property +| `PreferProperty` | Property | Property | Field | Field +| `FieldDuringConstruction` | Property | Field | Field | Throws +| `PreferFieldDuringConstruction` | Property | Field | Field | Property + +## Temporary values + +EF Core creates temporary key values when tracking new entities that will have real key values generated by the database when SaveChanges is called. See [Change Tracking in EF Core](xref:core/change-tracking/index) for an overview of how these temporary values are used. + +### Accessing temporary values + +Starting with EF Core 3.0, temporary values are stored in the change tracker and not set onto entity instances directly. However, these temporary values _are_ exposed when using the various mechanisms for [Accessing Tracked Entities](xref:core/change-tracking/entity-entries). For example, the following code accesses a temporary value using : + + +[!code-csharp[Temporary_values_1](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/Samples.cs?name=Temporary_values_1)] + +The output from this code is: + +```output +Blog.Id set on entity is 0 +Blog.Id tracked by EF is -2147482643 +``` + + can be used to check for temporary values. + +### Manipulating temporary values + +It is sometimes useful to explicitly work with temporary values. For example, a collection of new entities might be created on a web client and then serialized back to the server. Foreign key values are one way to set up relationships between these entities. The following code uses this approach to associate a graph of new entities by foreign key, while still allowing real key values to be generated when SaveChanges is called. + + +[!code-csharp[Temporary_values_2](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/Samples.cs?name=Temporary_values_2)] + +Notice that: + +- Negative numbers are used as temporary key values; this is not required, but is a common convention to prevent key clashes. +- The `Post.BlogId` FK property is assigned the same negative value as the PK of the associated blog. +- The PK values are marked as temporary by setting after each entity is tracked. This is necessary because any key value supplied by the application is assumed to be a real key value. + +Looking at the [change tracker debug view](xref:core/change-tracking/debug-views) before calling SaveChanges shows that the PK values are marked as temporary and posts are associated with the correct blogs, including fixup of navigations: + +```output +Blog {Id: -2} Added + Id: -2 PK Temporary + Name: 'Visual Studio Blog' + Posts: [{Id: -2}] +Blog {Id: -1} Added + Id: -1 PK Temporary + Name: '.NET Blog' + Posts: [{Id: -1}] +Post {Id: -2} Added + Id: -2 PK Temporary + BlogId: -2 FK + Content: 'If you are focused on squeezing out the last bits of perform...' + Title: 'Disassembly improvements for optimized managed debugging' + Blog: {Id: -2} + Tags: [] +Post {Id: -1} Added + Id: -1 PK Temporary + BlogId: -1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: -1} +``` + +After calling , these temporary values have been replaced by real values generated by the database: + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog' + Posts: [{Id: 1}] +Blog {Id: 2} Unchanged + Id: 2 PK + Name: 'Visual Studio Blog' + Posts: [{Id: 2}] +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} + Tags: [] +Post {Id: 2} Unchanged + Id: 2 PK + BlogId: 2 FK + Content: 'If you are focused on squeezing out the last bits of perform...' + Title: 'Disassembly improvements for optimized managed debugging' + Blog: {Id: 2} + Tags: [] +``` + +## Working with default values + +EF Core allows a property to get its default value from the database when is called. Like with generated key values, EF Core will only use a default from the database if no value has been explicitly set. For example, consider the following entity type: + + +[!code-csharp[Token](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs?name=Token)] + +The `ValidFrom` property is configured to get a default value from the database: + + +[!code-csharp[OnModelCreating_Token](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs?name=OnModelCreating_Token)] + +When inserting an entity of this type, EF Core will let the database generate the value unless an explicit value has been set instead. For example: + + +[!code-csharp[Working_with_default_values_1](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs?name=Working_with_default_values_1)] + +Looking at the [change tracker debug view](xref:core/change-tracking/debug-views) shows that the first token had `ValidFrom` generated by the database, while the second token used the value explicitly set: + +```output +Token {Id: 1} Unchanged + Id: 1 PK + Name: 'A' + ValidFrom: '12/30/2020 6:36:06 PM' +Token {Id: 2} Unchanged + Id: 2 PK + Name: 'B' + ValidFrom: '11/11/1111 11:11:11 AM' +``` + +> [!NOTE] +> Using database default values requires that the database column has a default value constraint configured. This is done automatically by EF Core migrations when using or . Make sure to create the default constraint on the column in some other way when not using EF Core migrations. + +### Using nullable properties + +EF Core is able to determine whether or not a property has been set by comparing the property value to the CLR default for the that type. This works well in most cases, but means that the CLR default cannot be explicitly inserted into the database. For example, consider an entity with an integer property: + + +[!code-csharp[Foo1](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs?name=Foo1)] + +Where that property is configured to have a database default of -1: + + +[!code-csharp[OnModelCreating_Foo1](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs?name=OnModelCreating_Foo1)] + +The intention is that the default of -1 will be used whenever an explicit value is not set. However, setting the value to 0 (the CLR default for integers) is indistinguishable to EF Core from not setting any value, this means that it is not possible to insert 0 for this property. For example: + + +[!code-csharp[Working_with_default_values_2](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs?name=Working_with_default_values_2)] + +Notice that the instance where `Count` was explicitly set to 0 is still gets the default value from the database, which is not what we intended. An easy way to deal with this is to make the `Count` property nullable: + + +[!code-csharp[Foo2](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs?name=Foo2)] + +This makes the CLR default null, instead of 0, which means 0 will now be inserted when explicitly set: + + +[!code-csharp[Working_with_default_values_3](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs?name=Working_with_default_values_3)] + +### Using nullable backing fields + +> [!NOTE] +> This nullable backing field pattern is supported by EF Core 5.0 and later. + +The problem with making the property nullable that it may not be conceptually nullable in the domain model. Forcing the property to be nullable therefore compromises the model. + +Starting with EF Core 5.0, the property can be left non-nullable, with only the backing field being nullable. For example: + + +[!code-csharp[Foo3](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs?name=Foo3)] + +This allows the CLR default (0) to be inserted if the property is explicitly set to 0, while not needing to expose the property as nullable in the domain model. For example: + + +[!code-csharp[Working_with_default_values_4](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs?name=Working_with_default_values_4)] + +#### Nullable backing fields for bool properties + +This pattern is especially useful when using bool properties with store-generated defaults. Since the CLR default for `bool` is "false", it means that "false" cannot be inserted explicitly using the normal pattern. For example, consider a `User` entity type: + + +[!code-csharp[User](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs?name=User)] + +The `IsAuthorized` property is configured with a database default value of "true": + + +[!code-csharp[OnModelCreating_User](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs?name=OnModelCreating_User)] + +The `IsAuthorized` property can be set to "true" or "false" explicitly before inserting, or can be left unset in which case the database default will be used: + + +[!code-csharp[Working_with_default_values_5](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs?name=Working_with_default_values_5)] + +The output from SaveChanges when using SQLite shows that the database default is used for Mac, while explicit values are set for Alice and Baxter: + +```sql +-- Executed DbCommand (0ms) [Parameters=[@p0='Mac' (Size = 3)], CommandType='Text', CommandTimeout='30'] +INSERT INTO "User" ("Name") +VALUES (@p0); +SELECT "Id", "IsAuthorized" +FROM "User" +WHERE changes() = 1 AND "rowid" = last_insert_rowid(); + +-- Executed DbCommand (0ms) [Parameters=[@p0='True' (DbType = String), @p1='Alice' (Size = 5)], CommandType='Text', CommandTimeout='30'] +INSERT INTO "User" ("IsAuthorized", "Name") +VALUES (@p0, @p1); +SELECT "Id" +FROM "User" +WHERE changes() = 1 AND "rowid" = last_insert_rowid(); + +-- Executed DbCommand (0ms) [Parameters=[@p0='False' (DbType = String), @p1='Baxter' (Size = 6)], CommandType='Text', CommandTimeout='30'] +INSERT INTO "User" ("IsAuthorized", "Name") +VALUES (@p0, @p1); +SELECT "Id" +FROM "User" +WHERE changes() = 1 AND "rowid" = last_insert_rowid(); +``` + +### Schema defaults only + +Sometimes it is useful to have defaults in the database schema created by EF Core migrations without EF Core ever using these values for inserts. This can be achieved by configuring the property as For example: + + +[!code-csharp[OnModelCreating_Bar](../../../samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs?name=OnModelCreating_Bar)] diff --git a/entity-framework/core/change-tracking/notification-entities.md.stub b/entity-framework/core/change-tracking/notification-entities.md.stub deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/entity-framework/core/change-tracking/property-changes.md.stub b/entity-framework/core/change-tracking/property-changes.md.stub deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/entity-framework/core/change-tracking/proxies.md.stub b/entity-framework/core/change-tracking/proxies.md.stub deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/entity-framework/core/change-tracking/relationship-changes.md b/entity-framework/core/change-tracking/relationship-changes.md new file mode 100644 index 0000000000..52412a6bbe --- /dev/null +++ b/entity-framework/core/change-tracking/relationship-changes.md @@ -0,0 +1,1181 @@ +--- +title: Changing Foreign Keys and Navigations - EF Core +description: How to change relationships between entities by manipulating foreign keys and navigations +author: ajcvickers +ms.date: 12/30/2020 +uid: core/change-tracking/relationship-changes +--- + +# Changing Foreign Keys and Navigations + +## Overview of foreign keys and navigations + +Relationships in an Entity Framework Core (EF Core) model are represented using foreign keys (FKs). An FK consists of one or more properties on the dependent or child entity in the relationship. This dependent/child entity is associated with a given principal/parent entity when the values of the foreign key properties on the dependent/child match the values of the alternate or primary key (PK) properties on the principal/parent. + +Foreign keys are a good way to store and manipulate relationships in the database, but are not very friendly when working with multiple related entities in application code. Therefore, most EF Core models also layer "navigations" over the FK representation. Navigations form C#/.NET references between entity instances that reflect the associations found by matching foreign key values to primary or alternate key values. + +Navigations can be used on both sides of the relationship, on one side only, or not at all, leaving only the FK property. The FK property can be hidden by making it a [shadow property](xref:core/modeling/shadow-properties). See [Relationships](xref:core/modeling/relationships) for more information on modelling relationships. + +> [!TIP] +> This document assumes that entity states and the basics of EF Core change tracking are understood. See [Change Tracking in EF Core](xref:core/change-tracking/index) for more information on these topics. + +> [!TIP] +> You can run and debug into all the code in this document by [downloading the sample code from GitHub](https://github.com/dotnet/EntityFramework.Docs/tree/master/samples/core/ChangeTracking/ChangingFKsAndNavigations). + +### Example model + +The following model contains four entity types with relationships between them. The comments in the code indicate which properties are foreign keys, primary keys, and navigations. + + +[!code-csharp[Model](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/OptionalRelationshipsSamples.cs?name=Model)] + +The three relationships in this model are: + +- Each blog can have many posts (one-to-many): + - `Blog` is the principal/parent. + - `Post` is the dependent/child. It contains the FK property `Post.BlogId`, the value of which must match the `Blog.Id` PK value of the related blog. + - `Post.Blog` is a reference navigation from a post to the associated blog. `Post.Blog` is the inverse navigation for `Blog.Posts`. + - `Blog.Posts` is a collection navigation from a blog to all the associated posts. `Blog.Posts` is the inverse navigation for `Post.Blog`. +- Each blog can have one assets (one-to-one): + - `Blog` is the principal/parent. + - `BlogAssets` is the dependent/child. It contains the FK property `BlogAssets.BlogId`, the value of which must match the `Blog.Id` PK value of the related blog. + - `BlogAssets.Blog` is a reference navigation from the assets to the associated blog. `BlogAssets.Blog` is the inverse navigation for `Blog.Assets`. + - `Blog.Assets` is a reference navigation from the blog to the associated assets. `Blog.Assets` is the inverse navigation for `BlogAssets.Blog`. +- Each post can have many tags and each tag can have many posts (many-to-many): + - Many-to-many relationships are a further layer over two one-to-many relationships. Many-to-many relationships are covered later in this document. + - `Post.Tags` is a collection navigation from a post to all the associated tags. `Post.Tags` is the inverse navigation for `Tag.Posts`. + - `Tag.Posts` is a collection navigation from a tag to all the associated posts. `Tag.Posts` is the inverse navigation for `Post.Tags`. + +See [Relationships](xref:core/modeling/relationships) for more information on how to model and configure relationships. + +## Relationship fixup + +EF Core keeps navigations in alignment with foreign key values and vice versa. That is, if a foreign key value changes such that it now refers to a different principal/parent entity, then the navigations are updated to reflect this change. Likewise, if a navigation is changed, then the foreign key values of the entities involved are updated to reflect this change. This is called "relationship fixup". + +### Fixup by query + +Fixup first occurs when entities are queried from the database. The database has only foreign key values, so when EF Core creates an entity instance from the database it uses the foreign key values to set reference navigations and add entities to collection navigations as appropriate. For example, consider a query for blogs and its associated posts and assets: + + +[!code-csharp[Relationship_fixup_1](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/OptionalRelationshipsSamples.cs?name=Relationship_fixup_1)] + +For each blog, EF Core will first create a `Blog` instance. Then, as each post is loaded from the database its `Post.Blog` reference navigation is set to point to the associated blog. Likewise, the post is added to the `Blog.Posts` collection navigation. The same thing happens with `BlogAssets`, except in this case both navigations are references. The `Blog.Assets` navigation is set to point to the assets instance, and the `BlogAsserts.Blog` navigation is set to point to the blog instance. + +Looking at the [change tracker debug view](xref:core/change-tracking/debug-views) after this query shows two blogs, each with one assets and two posts being tracked: + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog' + Assets: {Id: 1} + Posts: [{Id: 1}, {Id: 2}] +Blog {Id: 2} Unchanged + Id: 2 PK + Name: 'Visual Studio Blog' + Assets: {Id: 2} + Posts: [{Id: 3}, {Id: 4}] +BlogAssets {Id: 1} Unchanged + Id: 1 PK + Banner: + BlogId: 1 FK + Blog: {Id: 1} +BlogAssets {Id: 2} Unchanged + Id: 2 PK + Banner: + BlogId: 2 FK + Blog: {Id: 2} +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} + Tags: [] +Post {Id: 2} Unchanged + Id: 2 PK + BlogId: 1 FK + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: {Id: 1} + Tags: [] +Post {Id: 3} Unchanged + Id: 3 PK + BlogId: 2 FK + Content: 'If you are focused on squeezing out the last bits of perform...' + Title: 'Disassembly improvements for optimized managed debugging' + Blog: {Id: 2} + Tags: [] +Post {Id: 4} Unchanged + Id: 4 PK + BlogId: 2 FK + Content: 'Examine when database queries were executed and measure how ...' + Title: 'Database Profiling with Visual Studio' + Blog: {Id: 2} + Tags: [] +``` + +The debug view shows both key values and navigations. Navigations are shown using the primary key values of the related entities. For example, `Posts: [{Id: 1}, {Id: 2}]` in the output above indicates that the `Blog.Posts` collection navigation contains two related posts with primary keys 1 and 2 respectively. Similarly, for each post associated with the first blog, the `Blog: {Id: 1}` line indicates that the `Post.Blog` navigation references the Blog with primary key 1. + +### Fixup to locally tracked entities + +Relationship fixup also happens between entities returned from a tracking query and entities already tracked by the DbContext. For example, consider executing three separate queries for blogs, posts, and assets: + + +[!code-csharp[Relationship_fixup_2](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/OptionalRelationshipsSamples.cs?name=Relationship_fixup_2)] + +Looking again at the debug views, after the first query only the two blogs are tracked: + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog' + Assets: + Posts: [] +Blog {Id: 2} Unchanged + Id: 2 PK + Name: 'Visual Studio Blog' + Assets: + Posts: [] +``` + +The `Blog.Assets` reference navigations are null, and the `Blog.Posts` collection navigations are empty because no associated entities are currently being tracked by the context. + +After the second query, the `Blogs.Assets` reference navigations have been fixed up to point to the newly tracked `BlogAsset` instances. Likewise, the `BlogAssets.Blog` reference navigations are set to point to the appropriate already tracked `Blog` instance. + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog' + Assets: {Id: 1} + Posts: [] +Blog {Id: 2} Unchanged + Id: 2 PK + Name: 'Visual Studio Blog' + Assets: {Id: 2} + Posts: [] +BlogAssets {Id: 1} Unchanged + Id: 1 PK + Banner: + BlogId: 1 FK + Blog: {Id: 1} +BlogAssets {Id: 2} Unchanged + Id: 2 PK + Banner: + BlogId: 2 FK + Blog: {Id: 2} +``` + +Finally, after the third query, the `Blog.Posts` collection navigations now contain all related posts, and the `Post.Blog` references point to the appropriate `Blog` instance: + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog' + Assets: {Id: 1} + Posts: [{Id: 1}, {Id: 2}] +Blog {Id: 2} Unchanged + Id: 2 PK + Name: 'Visual Studio Blog' + Assets: {Id: 2} + Posts: [{Id: 3}, {Id: 4}] +BlogAssets {Id: 1} Unchanged + Id: 1 PK + Banner: + BlogId: 1 FK + Blog: {Id: 1} +BlogAssets {Id: 2} Unchanged + Id: 2 PK + Banner: + BlogId: 2 FK + Blog: {Id: 2} +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} + Tags: [] +Post {Id: 2} Unchanged + Id: 2 PK + BlogId: 1 FK + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: {Id: 1} + Tags: [] +Post {Id: 3} Unchanged + Id: 3 PK + BlogId: 2 FK + Content: 'If you are focused on squeezing out the last bits of perform...' + Title: 'Disassembly improvements for optimized managed debugging' + Blog: {Id: 2} + Tags: [] +Post {Id: 4} Unchanged + Id: 4 PK + BlogId: 2 FK + Content: 'Examine when database queries were executed and measure how ...' + Title: 'Database Profiling with Visual Studio' + Blog: {Id: 2} + Tags: [] +``` + +This is the same end-state as was achieved with the original single query, since EF Core fixed up navigations as entities were tracked, even when coming from multiple different queries. + +> [!NOTE] +> Fixup never causes more data to be returned from the database. It only connects entities that are already returned by the query or already tracked by the DbContext. See [Identity Resolution in EF Core](xref:core/change-tracking/identity-resolution) for information about handling duplicates when serializing entities. + +## Changing relationships using navigations + +The easiest way to change the relationship between two entities is by manipulating a navigation, while leaving EF Core to fixup the inverse navigation and FK values appropriately. This can be done by: + +- Adding or removing an entity from a collection navigation. +- Changing a reference navigation to point to a different entity, or setting it to null. + +### Adding or removing from collection navigations + +For example, let's move one of the posts from the Visual Studio blog to the .NET blog. This requires first loading the blogs and posts, and then moving the post from the navigation collection on one blog to the navigation collection on the other blog: + + +[!code-csharp[Changing_relationships_using_navigations_1](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/OptionalRelationshipsSamples.cs?name=Changing_relationships_using_navigations_1)] + +> [!TIP] +> A call to is needed here because accessing the debug view does not cause [automatic detection of changes](xref:core/change-tracking/change-detection). + +This is the debug view printed after running the code above: + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog' + Assets: + Posts: [{Id: 1}, {Id: 2}, {Id: 3}] +Blog {Id: 2} Unchanged + Id: 2 PK + Name: 'Visual Studio Blog' + Assets: + Posts: [{Id: 4}] +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} + Tags: [] +Post {Id: 2} Unchanged + Id: 2 PK + BlogId: 1 FK + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: {Id: 1} + Tags: [] +Post {Id: 3} Modified + Id: 3 PK + BlogId: 1 FK Modified Originally 2 + Content: 'If you are focused on squeezing out the last bits of perform...' + Title: 'Disassembly improvements for optimized managed debugging' + Blog: {Id: 1} + Tags: [] +Post {Id: 4} Unchanged + Id: 4 PK + BlogId: 2 FK + Content: 'Examine when database queries were executed and measure how ...' + Title: 'Database Profiling with Visual Studio' + Blog: {Id: 2} + Tags: [] +``` + +The `Blog.Posts` navigation on the .NET Blog now has three posts (`Posts: [{Id: 1}, {Id: 2}, {Id: 3}]`). Likewise, the `Blog.Posts` navigation on the Visual Studio blog only has one post (`Posts: [{Id: 4}]`). This is to be expected since the code explicitly changed these collections. + +More interestingly, even though the code did not explicitly change the `Post.Blog` navigation, it has been fixed-up to point to the Visual Studio blog (`Blog: {Id: 1}`). Also, the `Post.BlogId` foreign key value has been updated to match the primary key value of the .NET blog. This change to the FK value in then persisted to the database when SaveChanges is called: + +```sql +-- Executed DbCommand (0ms) [Parameters=[@p1='3' (DbType = String), @p0='1' (Nullable = true) (DbType = String)], CommandType='Text', CommandTimeout='30'] +UPDATE "Posts" SET "BlogId" = @p0 +WHERE "Id" = @p1; +SELECT changes(); +``` + +### Changing reference navigations + +In the previous example, a post was moved from one blog to another by manipulating the collection navigation of posts on each blog. The same thing can be achieved by instead changing the `Post.Blog` reference navigation to point to the new blog. For example: + + +[!code-csharp[Changing_relationships_using_navigations_2](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/OptionalRelationshipsSamples.cs?name=Changing_relationships_using_navigations_2)] + +The debug view after this change is _exactly the same_ as it was in the previous example. This because EF Core detected the reference navigation change and then fixed up the collection navigations and FK value to match. + +## Changing relationships using foreign key values + +In the previous section, relationships were manipulated by navigations leaving foreign key values to be updated automatically. This is the recommended way to manipulate relationships in EF Core. However, it is also possible to manipulate FK values directly. For example, we can move a post from one blog to another by changing the `Post.BlogId` foreign key value: + + +[!code-csharp[Changing_relationships_using_foreign_key_values_1](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/OptionalRelationshipsSamples.cs?name=Changing_relationships_using_foreign_key_values_1)] + +Notice how this is very similar to changing the reference navigation, as shown in the previous example. + +The debug view after this change is again _exactly the same_ as was the case for the previous two examples. This because EF Core detected the FK value change and then fixed up both the reference and collection navigations to match. + +> [!TIP] +> Do not write code to manipulate all navigations and FK values each time a relationship changes. Such code is more complicated and must ensure consistent changes to foreign keys and navigations in every case. If possible, just manipulate a single navigation, or maybe both navigations. If needed, just manipulate FK values. Avoid manipulating both navigations and FK values. + +## Fixup for added or deleted entities + +### Adding to a collection navigation + +EF Core performs the following actions when it [detects](xref:core/change-tracking/change-detection) that a new dependent/child entity has been added to a collection navigation: + +- If the entity is not tracked, then it is tracked. (The entity will usually be in the `Added` state. However, if the entity type is configured to use generated keys and the primary key value is set, then the entity is tracked in the `Unchanged` state.) +- If the entity is associated with a different principal/parent, then that relationship is severed. +- The entity becomes associated with the principal/parent that owns the collection navigation. +- Navigations and foreign key values are fixed up for all entities involved. + +Based on this we can see that to move a post from one blog to another we don't actually need to remove it from the old collection navigation before adding it to the new. So the code from the example above can be changed from: + + +[!code-csharp[Fixup_for_added_or_deleted_entities_1](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/OptionalRelationshipsSamples.cs?name=Fixup_for_added_or_deleted_entities_1)] + +To: + + +[!code-csharp[Fixup_for_added_or_deleted_entities_2](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/OptionalRelationshipsSamples.cs?name=Fixup_for_added_or_deleted_entities_2)] + +EF Core sees that the post has been added to a new blog and automatically removes it from the collection on the first blog. + +### Removing from a collection navigation + +Removing a dependent/child entity from the collection navigation of the principal/parent causes severing of the relationship to that principal/parent. What happens next depends on whether the relationship is optional or required. + +#### Optional relationships + +By default for optional relationships, the foreign key value is set to null. This means that the dependent/child is no longer associated with _any_ principal/parent. For example, let's load a blog and posts and then remove one of the posts from the `Blog.Posts` collection navigation: + + +[!code-csharp[Fixup_for_added_or_deleted_entities_3](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/OptionalRelationshipsSamples.cs?name=Fixup_for_added_or_deleted_entities_3)] + +Looking at the [change tracking debug view](xref:core/change-tracking/debug-views) after this change shows that: + +- The `Post.BlogId` FK has been set to null (`BlogId: FK Modified Originally 1`) +- The `Post.Blog` reference navigation has been set to null (`Blog: `) +- The post has been removed from `Blog.Posts` collection navigation (`Posts: [{Id: 1}]`) + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog' + Assets: + Posts: [{Id: 1}] +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} + Tags: [] +Post {Id: 2} Modified + Id: 2 PK + BlogId: FK Modified Originally 1 + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: + Tags: [] +``` + +Notice that the post is _not_ marked as `Deleted`. It is marked as `Modified` so that the FK value in the database will be set to null when SaveChanges is called. + +#### Required relationships + +Setting the FK value to null is not allowed (and is usually not possible) for required relationships. Therefore, severing a required relationship means that the dependent/child entity must be either re-parented to a new principal/parent, or removed from the database when SaveChanges is called to avoid a referential constraint violation. This is known as "deleting orphans", and is the default behavior in EF Core for required relationships. + +For example, let's change the relationship between blog and posts to be required and then run the same code as in the previous example: + + +[!code-csharp[Fixup_for_added_or_deleted_entities_4](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/RequiredRelationshipsSamples.cs?name=Fixup_for_added_or_deleted_entities_4)] + +Looking at the debug view after this change shows that: + +- The post has been marked as `Deleted` such that it will be deleted from the database when SaveChanges is called. +- The `Post.Blog` reference navigation has been set to null (`Blog: `). +- The post has been removed from `Blog.Posts` collection navigation (`Posts: [{Id: 1}]`). + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog' + Assets: + Posts: [{Id: 1}] +Post {Id: 1} Unchanged + Id: 1 PK + BlogId: 1 FK + Content: 'Announcing the release of EF Core 5.0, a full featured cross...' + Title: 'Announcing the Release of EF Core 5.0' + Blog: {Id: 1} + Tags: [] +Post {Id: 2} Deleted + Id: 2 PK + BlogId: 1 FK + Content: 'F# 5 is the latest version of F#, the functional programming...' + Title: 'Announcing F# 5' + Blog: + Tags: [] +``` + +Notice that the `Post.BlogId` remains unchanged since for a required relationship it cannot be set to null. + +Calling SaveChanges results in the orphaned post being deleted: + +```sql +-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text', CommandTimeout='30'] +DELETE FROM "Posts" +WHERE "Id" = @p0; +SELECT changes(); +``` + +#### Delete orphans timing and re-parenting + +By default, marking orphans as `Deleted` happens as soon as the relationship change is [detected](xref:core/change-tracking/change-detection). However, this process can be delayed until SaveChanges is actually called. This can be useful to avoid making orphans of entities that have been removed from one principal/parent, but will be re-parented with a new principal/parent before SaveChanges is called. is used to set this timing. For example: + + +[!code-csharp[Fixup_for_added_or_deleted_entities_5](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/RequiredRelationshipsSamples.cs?name=Fixup_for_added_or_deleted_entities_5)] + +After removing the post from the first collection the object is not marked as `Deleted` as it was in the previous example. Instead, EF Core is tracking that the relationship is severed _even though this is a required relationship_. (The FK value is considered null by EF Core even though it cannot really be null because the type is not nullable. This is known as a "conceptual null".) + +```output +Post {Id: 3} Modified + Id: 3 PK + BlogId: FK Modified Originally 2 + Content: 'If you are focused on squeezing out the last bits of perform...' + Title: 'Disassembly improvements for optimized managed debugging' + Blog: + Tags: [] +``` + +Calling SaveChanges at this time would result in the orphaned post being deleted. However, if as in the example above, post is associated with a new blog before SaveChanges is called, then it will be fixed up appropriately to that new blog and is no longer considered an orphan: + +```output +Post {Id: 3} Modified + Id: 3 PK + BlogId: 1 FK Modified Originally 2 + Content: 'If you are focused on squeezing out the last bits of perform...' + Title: 'Disassembly improvements for optimized managed debugging' + Blog: {Id: 1} + Tags: [] +``` + +SaveChanges called at this point will update the post in the database rather than deleting it. + +It is also possible to turn off automatic deletion of orphans. This will result in an exception if SaveChanges is called while an orphan is being tracked. For example, this code: + + +[!code-csharp[Fixup_for_added_or_deleted_entities_6](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/RequiredRelationshipsSamples.cs?name=Fixup_for_added_or_deleted_entities_6)] + +Will throw this exception: + +> System.InvalidOperationException: The association between entities 'Blog' and 'Post' with the key value '{BlogId: 1}' has been severed, but the relationship is either marked as required or is implicitly required because the foreign key is not nullable. If the dependent/child entity should be deleted when a required relationship is severed, configure the relationship to use cascade deletes. + +Deletion of orphans, as well as cascade deletes, can be forced at any time by calling . Combining this with setting the delete orphan timing to `Never` will ensure orphans are never deleted unless EF Core is explicitly instructed to do so. + +### Changing a reference navigation + +Changing the reference navigation of a one-to-many relationship has the same effect as changing the collection navigation on the other end of the relationship. Setting the reference navigation of dependent/child to null is equivalent to removing the entity from the collection navigation of the principal/parent. All fixup and database changes happen as described in the previous section, including making the entity an orphan if the relationship is required. + +#### Optional one-to-one relationships + +For one-to-one relationships, changing a reference navigation causes any previous relationship to be severed. For optional relationships, this means that the FK value on the previously related dependent/child is set to null. For example: + + +[!code-csharp[Fixup_for_added_or_deleted_entities_7](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/OptionalRelationshipsSamples.cs?name=Fixup_for_added_or_deleted_entities_7)] + +The debug view before calling SaveChanges shows that the new assets has replaced the existing assets, which is now marked as `Modified` with a null `BlogAssets.BlogId` FK value: + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog' + Assets: {Id: -2147482629} + Posts: [] +BlogAssets {Id: -2147482629} Added + Id: -2147482629 PK Temporary + Banner: + BlogId: 1 FK + Blog: {Id: 1} +BlogAssets {Id: 1} Modified + Id: 1 PK + Banner: + BlogId: FK Modified Originally 1 + Blog: +``` + +This results in an update and an insert when SaveChanges is called: + +```sql +-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0=NULL], CommandType='Text', CommandTimeout='30'] +UPDATE "Assets" SET "BlogId" = @p0 +WHERE "Id" = @p1; +SELECT changes(); + +-- Executed DbCommand (0ms) [Parameters=[@p2=NULL, @p3='1' (Nullable = true) (DbType = String)], CommandType='Text', CommandTimeout='30'] +INSERT INTO "Assets" ("Banner", "BlogId") +VALUES (@p2, @p3); +SELECT "Id" +FROM "Assets" +WHERE changes() = 1 AND "rowid" = last_insert_rowid(); +``` + +#### Required one-to-one relationships + +Running the same code as in the previous example, but this time with a required one-to-one relationship, shows that the previously associated `BlogAssets` is now marked as `Deleted`, since it becomes an orphan when the new `BlogAssets` takes its place: + +```output +Blog {Id: 1} Unchanged + Id: 1 PK + Name: '.NET Blog' + Assets: {Id: -2147482639} + Posts: [] +BlogAssets {Id: -2147482639} Added + Id: -2147482639 PK Temporary + Banner: + BlogId: 1 FK + Blog: {Id: 1} +BlogAssets {Id: 1} Deleted + Id: 1 PK + Banner: + BlogId: 1 FK + Blog: +``` + +This then results in an delete an and insert when SaveChanges is called: + +```sql +-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String)], CommandType='Text', CommandTimeout='30'] +DELETE FROM "Assets" +WHERE "Id" = @p0; +SELECT changes(); + +-- Executed DbCommand (0ms) [Parameters=[@p1=NULL, @p2='1' (DbType = String)], CommandType='Text', CommandTimeout='30'] +INSERT INTO "Assets" ("Banner", "BlogId") +VALUES (@p1, @p2); +SELECT "Id" +FROM "Assets" +WHERE changes() = 1 AND "rowid" = last_insert_rowid(); +``` + +The timing of marking orphans as deleted can be changed in the same way as shown for collection navigations and has the same effects. + +### Deleting an entity + +#### Optional relationships + +When an entity is marked as `Deleted`, for example by calling , then references to the deleted entity are removed from the navigations of other entities. For optional relationships, the FK values in dependent entities are set to null. + +For example, let's mark the Visual Studio blog as `Deleted`: + + +[!code-csharp[Deleting_an_entity_1](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/OptionalRelationshipsSamples.cs?name=Deleting_an_entity_1)] + +Looking at the [change tracker debug view](xref:core/change-tracking/debug-views) before calling SaveChanges shows: + +```output +Blog {Id: 2} Deleted + Id: 2 PK + Name: 'Visual Studio Blog' + Assets: {Id: 2} + Posts: [{Id: 3}, {Id: 4}] +BlogAssets {Id: 2} Modified + Id: 2 PK + Banner: + BlogId: FK Modified Originally 2 + Blog: +Post {Id: 3} Modified + Id: 3 PK + BlogId: FK Modified Originally 2 + Content: 'If you are focused on squeezing out the last bits of perform...' + Title: 'Disassembly improvements for optimized managed debugging' + Blog: + Tags: [] +Post {Id: 4} Modified + Id: 4 PK + BlogId: FK Modified Originally 2 + Content: 'Examine when database queries were executed and measure how ...' + Title: 'Database Profiling with Visual Studio' + Blog: + Tags: [] +``` + +Notice that: + +- The blog is marked as `Deleted`. +- The assets related to the deleted blog has a null FK value (`BlogId: FK Modified Originally 2`) and a null reference navigation (`Blog: `) +- Each post related to the deleted blog has a null FK value (`BlogId: FK Modified Originally 2`) and a null reference navigation (`Blog: `) + +#### Required relationships + +The fixup behavior for required relationships is the same as for optional relationships except that the dependent/child entities are marked as `Deleted` since they cannot exist without a principal/parent and must be removed from the database when SaveChanges is called to avoid a referential constraint exception. This is known as "cascade delete", and is the default behavior in EF Core for required relationships. For example, running the same code as in the previous example but with a required relationship results in the following debug view before SaveChanges is called: + +```output +Blog {Id: 2} Deleted + Id: 2 PK + Name: 'Visual Studio Blog' + Assets: {Id: 2} + Posts: [{Id: 3}, {Id: 4}] +BlogAssets {Id: 2} Deleted + Id: 2 PK + Banner: + BlogId: 2 FK + Blog: {Id: 2} +Post {Id: 3} Deleted + Id: 3 PK + BlogId: 2 FK + Content: 'If you are focused on squeezing out the last bits of perform...' + Title: 'Disassembly improvements for optimized managed debugging' + Blog: {Id: 2} + Tags: [] +Post {Id: 4} Deleted + Id: 4 PK + BlogId: 2 FK + Content: 'Examine when database queries were executed and measure how ...' + Title: 'Database Profiling with Visual Studio' + Blog: {Id: 2} + Tags: [] +``` + +As expected, the dependents/children are now marked as `Deleted`. However, notice that the navigations on the deleted entities have _not_ changed. This may seem strange, but it avoids completely shredding a deleted graph of entities by clearing all navigations. That is, the blog, asset, and posts still form a graph of entities even after having been deleted. This makes it much easier to un-delete a graph of entities than was the case in EF6 where the graph was shredded. + +#### Cascade delete timing and re-parenting + +By default, cascade delete happens as soon as the parent/principal is marked as `Deleted`. This is the same as for deleting orphans, as described previously. As with deleting orphans, this process can be delayed until SaveChanges is called, or even disabled entirely, by setting appropriately. This is useful in the same way as it is for deleting orphans, including for re-parenting children/dependents after deletion of a principal/parent. + +Cascade deletes, as well as deleting orphans, can be forced at any time by calling . Combining this with setting the cascade delete timing to `Never` will ensure cascade deletes never happen unless EF Core is explicitly instructed to do so. + +> [!TIP] +> Cascade delete and deleting orphans are closely related. Both result in deleting dependent/child entities when the relationship to their required principal/parent is severed. For cascade delete, this severing happens because the principal/parent is itself deleted. For orphans, the principal/parent entity still exists, but is no longer related to the dependent/child entities. + +## Many-to-many relationships + +Many-to-many relationships in EF Core are implemented using a join entity. Each side the many-to-many relationship is related to this join entity with a one-to-many relationship. Before EF Core 5.0, this join entity had to explicitly defined and mapped. Starting with EF Core 5.0, it can be created implicitly and hidden. However, in both cases the underlying behavior is the same. We will look at this underlying behavior first to understand how tracking of many-to-many relationships works. + +### How many-to-many relationships work + +Consider this EF Core model that creates a many-to-many relationship between posts and tags using an explicitly defined join entity type: + + +[!code-csharp[Model](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntitySamples.cs?name=Model)] + +Notice that the `PostTag` join entity type contains two foreign key properties. In this model, for a post to be related to a tag, there must be a PostTag join entity where the `PostTag.PostId` foreign key value matches the `Post.Id` primary key value, and where the `PostTag.TagId` foreign key value matches the `Tag.Id` primary key value. For example: + + +[!code-csharp[Many_to_many_relationships_1](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntitySamples.cs?name=Many_to_many_relationships_1)] + +Looking at the [change tracker debug view](xref:core/change-tracking/debug-views) after running this code shows that the post and tag are related by the new `PostTag` join entity: + +```output +Post {Id: 3} Unchanged + Id: 3 PK + BlogId: 2 FK + Content: 'If you are focused on squeezing out the last bits of perform...' + Title: 'Disassembly improvements for optimized managed debugging' + Blog: + PostTags: [{PostId: 3, TagId: 1}] +PostTag {PostId: 3, TagId: 1} Added + PostId: 3 PK FK + TagId: 1 PK FK + Post: {Id: 3} + Tag: {Id: 1} +Tag {Id: 1} Unchanged + Id: 1 PK + Text: '.NET' + PostTags: [{PostId: 3, TagId: 1}] +``` + +Notice that the collection navigations on `Post` and `Tag` have been fixed up, as have the reference navigations on `PostTag`. These relationships can be manipulated by navigations instead of FK values, just as in all the preceding examples. For example, the code above can be modified to add the relationship by setting the reference navigations on the join entity: + + +[!code-csharp[Many_to_many_relationships_2](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntitySamples.cs?name=Many_to_many_relationships_2)] + +This results in exactly the same change to FKs and navigations as in the previous example. + +### Skip navigations + +> [!NOTE] +> Skip navigations were introduced in EF Core 5.0. + +Manipulating the join table manually can be cumbersome. Starting with EF Core 5.0, many-to-many relationships can be manipulated directly using special collection navigations that "skip over" the join entity. For example, two skip navigations can be added to the model above; one from Post to Tags, and the other from Tag to Posts: + + +[!code-csharp[Model](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityAndSkipsSamples.cs?name=Model)] + +This many-to-many relationship requires the following configuration to ensure the skip navigations and normal navigations are all used for the same many-to-many relationship: + + +[!code-csharp[OnModelCreating](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityAndSkipsSamples.cs?name=OnModelCreating)] + +See [Relationships](xref:core/modeling/relationships) for more information on mapping many-to-many relationships. + +Skip navigations look and behave like normal collection navigations. However, the way they work with foreign key values is different. Let's associate a post with a tag, but this time using a skip navigation: + + +[!code-csharp[Many_to_many_relationships_3](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityAndSkipsSamples.cs?name=Many_to_many_relationships_3)] + +Notice that this code doesn't use the join entity. It instead just adds an entity to a navigation collection in the same way as would be done if this were a one-to-many relationship. The resulting debug view is essentially the same as before: + +```output +Post {Id: 3} Unchanged + Id: 3 PK + BlogId: 2 FK + Content: 'If you are focused on squeezing out the last bits of perform...' + Title: 'Disassembly improvements for optimized managed debugging' + Blog: + PostTags: [{PostId: 3, TagId: 1}] + Tags: [{Id: 1}] +PostTag {PostId: 3, TagId: 1} Added + PostId: 3 PK FK + TagId: 1 PK FK + Post: {Id: 3} + Tag: {Id: 1} +Tag {Id: 1} Unchanged + Id: 1 PK + Text: '.NET' + PostTags: [{PostId: 3, TagId: 1}] + Posts: [{Id: 3}] +``` + +Notice that an instance of the `PostTag` join entity was created automatically with FK values set to the PK values of the tag and post that are now associated. All the normal reference and collection navigations have been fixed up to match these FK values. Also, since this model contains skip navigations, these have also been fixed up. Specifically, even though we added the tag to the `Post.Tags` skip navigation, the `Tag.Posts` inverse skip navigation on the other side of this relationship has also been fixed up to contain the associated post. + +It is worth noting that the underlying many-to-many relationships can still be manipulated directly even when skip navigations have been layered on top. For example, the tag and Post could be associated as we did before introducing skip navigations: + + +[!code-csharp[Many_to_many_relationships_4](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityAndSkipsSamples.cs?name=Many_to_many_relationships_4)] + +Or using FK values: + + +[!code-csharp[Many_to_many_relationships_5](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityAndSkipsSamples.cs?name=Many_to_many_relationships_5)] + +This will still result in the skip navigations being fixed up correctly, resulting in the same debug view output as in the previous example. + +### Skip navigations only + +In the previous section we added skip navigations _in addition to_ fully defining the two underlying one-to-many relationships. This is useful to illustrate what happens to FK values, but is often unnecessary. Instead, the many-to-many relationship can be defined using _only skip navigations_. This is how the many-to-many relationship is defined in the model at the very top of this document. Using this model, we can again associate a Post and a Tag by adding a post to the `Tag.Posts` skip navigation (or, alternately, adding a tag to the `Post.Tags` skip navigation): + + +[!code-csharp[Many_to_many_relationships_6](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/OptionalRelationshipsSamples.cs?name=Many_to_many_relationships_6)] + +Looking at the debug view after making this change reveals that EF Core has created an instance of `Dictionary` to represent the join entity. This join entity contains both `PostsId` and `TagsId` foreign key properties which have been set to match the PK values of the post and tag that are associated. + +```output +Post {Id: 3} Unchanged + Id: 3 PK + BlogId: 2 FK + Content: 'If you are focused on squeezing out the last bits of perform...' + Title: 'Disassembly improvements for optimized managed debugging' + Blog: + Tags: [{Id: 1}] +Tag {Id: 1} Unchanged + Id: 1 PK + Text: '.NET' + Posts: [{Id: 3}] +PostTag (Dictionary) {PostsId: 3, TagsId: 1} Added + PostsId: 3 PK FK + TagsId: 1 PK FK +``` + +See [Relationships](xref:core/modeling/relationships) for more information about implicit join entities and the use of `Dictionary` entity types. + +> [!IMPORTANT] +> The CLR type used for join entity types by convention may change in future releases to improve performance. Do not depend on the join type being `Dictionary` unless this has been explicitly configured. + +### Join entities with payloads + +So far all the examples have used a join entity type (whether explicit or implicit) that contains only the two foreign key properties needed for the many-to-many relationship. Neither of these FK values need to be explicitly set by the application when manipulating relationships because their values come from the primary key properties of the related entities. This allows EF Core to create instances of the join entity without missing data. + +#### Payloads with generated values + +EF Core supports adding additional properties to the join entity type. This is known as giving the join entity a "payload". For example, let's add `TaggedOn` property to the `PostTag` join entity: + + +[!code-csharp[Model](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityWithPayloadSamples.cs?name=Model)] + +This payload property will not be set when EF Core creates a join entity instance. The most common way to deal with this is to use payload properties with automatically generated values. For example, the `TaggedOn` property can be configured to use a store-generated timestamp when each new entity is inserted: + + +[!code-csharp[OnModelCreating](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityWithPayloadSamples.cs?name=OnModelCreating)] + +A post can now be tagged in the same way as before: + + +[!code-csharp[Many_to_many_relationships_7](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityWithPayloadSamples.cs?name=Many_to_many_relationships_7)] + +Looking at the [change tracker debug view](xref:core/change-tracking/debug-views) after calling SaveChanges shows that the payload property has been set appropriately: + +```output +Post {Id: 3} Unchanged + Id: 3 PK + BlogId: 2 FK + Content: 'If you are focused on squeezing out the last bits of perform...' + Title: 'Disassembly improvements for optimized managed debugging' + Blog: + Tags: [{Id: 1}] +PostTag {PostId: 3, TagId: 1} Unchanged + PostId: 3 PK FK + TagId: 1 PK FK + TaggedOn: '12/29/2020 8:13:21 PM' +Tag {Id: 1} Unchanged + Id: 1 PK + Text: '.NET' + Posts: [{Id: 3}] +``` + +#### Explicitly setting payload values + +Following on from the previous example, let's add a payload property that does not use an automatically generated value: + + +[!code-csharp[Model](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityWithStringPayloadSamples.cs?name=Model)] + +A post can now be tagged in the same way as before, and the join entity will still be created automatically. This entity can then be accessed using one of the mechanisms described in [Accessing Tracked Entities](xref:core/change-tracking/entity-entries). For example, the code below uses to access the join entity instance: + + +[!code-csharp[Many_to_many_relationships_8](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityWithStringPayloadSamples.cs?name=Many_to_many_relationships_8)] + +Once the join entity has been located it can be manipulated in the normal way--in this example, to set the `TaggedBy` payload property before calling SaveChanges. + +> [!NOTE] +> Note that a call to is required here to give EF Core a chance to detect the navigation property change and create the join entity instance before `Find` is used. See [Change Detection and Notifications](xref:core/change-tracking/change-detection) for more information. + +Alternately, the join entity can be created explicitly to associate a post with a tag. For example: + + +[!code-csharp[Many_to_many_relationships_9](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityWithStringPayloadSamples.cs?name=Many_to_many_relationships_9)] + +Finally, another way to set payload data is by either overriding or using the event to process entities before updating the database. For example: + + +[!code-csharp[SaveChanges](../../../samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityWithStringPayloadSamples.cs?name=SaveChanges)] diff --git a/entity-framework/core/change-tracking/relationship-changes.md.stub b/entity-framework/core/change-tracking/relationship-changes.md.stub deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/entity-framework/toc.yml b/entity-framework/toc.yml index 13c5b40e01..0c247f0158 100644 --- a/entity-framework/toc.yml +++ b/entity-framework/toc.yml @@ -215,15 +215,24 @@ - name: Explicit values for generated properties href: core/saving/explicit-values-generated-properties.md - #- name: Change tracking - # items: - # - name: Overview - # - name: Property changes - # - name: Relationship changes - # - name: Find and local queries - # - name: Accessing entity state - # - name: Notification entities - # - name: Change tracking proxies + - name: Change tracking + items: + - name: Overview + href: core/change-tracking/index.md + - name: Explicitly tracking entities + href: core/change-tracking/explicit-tracking.md + - name: Accessing tracked entities + href: core/change-tracking/entity-entries.md + - name: Changing foreign keys and navigations + href: core/change-tracking/relationship-changes.md + - name: Change detection and notifications + href: core/change-tracking/change-detection.md + - name: Identity resolution + href: core/change-tracking/identity-resolution.md + - name: Additional change tracking features + href: core/change-tracking/miscellaneous.md + - name: Change tracker debugging + href: core/change-tracking/debug-views.md - name: Logging, events, and diagnostics items: diff --git a/samples/core/ChangeTracking/AccessingTrackedEntities/AccessingTrackedEntities.csproj b/samples/core/ChangeTracking/AccessingTrackedEntities/AccessingTrackedEntities.csproj new file mode 100644 index 0000000000..c23f725513 --- /dev/null +++ b/samples/core/ChangeTracking/AccessingTrackedEntities/AccessingTrackedEntities.csproj @@ -0,0 +1,14 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + diff --git a/samples/core/ChangeTracking/AccessingTrackedEntities/Program.cs b/samples/core/ChangeTracking/AccessingTrackedEntities/Program.cs new file mode 100644 index 0000000000..45bbb28d45 --- /dev/null +++ b/samples/core/ChangeTracking/AccessingTrackedEntities/Program.cs @@ -0,0 +1,30 @@ +using System; + +public class Program +{ + public static void Main() + { + Console.WriteLine("Samples for _Accessing Tracked Entities_"); + Console.WriteLine(); + + Samples.Using_DbContext_Entry_and_EntityEntry_instances_1(); + Samples.Work_with_the_entity_1(); + Samples.Work_with_the_entity_2(); + Samples.Work_with_a_single_property_1(); + Samples.Work_with_a_single_navigation_1(); + Samples.Work_with_a_single_navigation_2(); + Samples.Work_with_all_properties_of_an_entity_1(); + Samples.Work_with_all_navigations_of_an_entity_1(); + Samples.Work_with_all_members_of_an_entity_1(); + + Samples.Find_and_FindAsync_1(); + Samples.Find_and_FindAsync_2(); + + Samples.Using_ChangeTracker_Entries_to_access_all_tracked_entities_1(); + + Samples.Using_DbSet_Local_to_query_tracked_entities_1(); + Samples.Using_DbSet_Local_to_query_tracked_entities_2(); + Samples.Using_DbSet_Local_to_query_tracked_entities_3(); + Samples.Using_DbSet_Local_to_query_tracked_entities_4(); + } +} diff --git a/samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs b/samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs new file mode 100644 index 0000000000..0a2bc7ff4b --- /dev/null +++ b/samples/core/ChangeTracking/AccessingTrackedEntities/Samples.cs @@ -0,0 +1,634 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; + +public static class Samples +{ + public static void Using_DbContext_Entry_and_EntityEntry_instances_1() + { + Console.WriteLine($">>>> Sample: {nameof(Using_DbContext_Entry_and_EntityEntry_instances_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Using_DbContext_Entry_and_EntityEntry_instances_1 + using var context = new BlogsContext(); + + var blog = context.Blogs.Single(e => e.Id == 1); + var entityEntry = context.Entry(blog); + #endregion + + Console.WriteLine(); + } + + public static void Work_with_the_entity_1() + { + Console.WriteLine($">>>> Sample: {nameof(Work_with_the_entity_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var blog = context.Blogs.Single(e => e.Id == 1); + + #region Work_with_the_entity_1 + var currentState = context.Entry(blog).State; + if (currentState == EntityState.Unchanged) + { + context.Entry(blog).State = EntityState.Modified; + } + #endregion + + Console.WriteLine(); + } + + public static void Work_with_the_entity_2() + { + Console.WriteLine($">>>> Sample: {nameof(Work_with_the_entity_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + #region Work_with_the_entity_2 + var newBlog = new Blog(); + Debug.Assert(context.Entry(newBlog).State == EntityState.Detached); + + context.Entry(newBlog).State = EntityState.Added; + Debug.Assert(context.Entry(newBlog).State == EntityState.Added); + #endregion + + Console.WriteLine(); + } + + public static void Work_with_a_single_property_1() + { + Console.WriteLine($">>>> Sample: {nameof(Work_with_a_single_property_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + { + var blog = context.Blogs.Single(e => e.Id == 1); + + #region Work_with_a_single_property_1a + PropertyEntry propertyEntry = context.Entry(blog).Property(e => e.Name); + #endregion + } + + { + var blog = context.Blogs.Single(e => e.Id == 1); + + #region Work_with_a_single_property_1b + PropertyEntry propertyEntry = context.Entry(blog).Property("Name"); + #endregion + } + + { + var blog = context.Blogs.Single(e => e.Id == 1); + + #region Work_with_a_single_property_1c + PropertyEntry propertyEntry = context.Entry(blog).Property("Name"); + #endregion + } + + { + var blog = context.Blogs.Single(e => e.Id == 1); + + #region Work_with_a_single_property_1d + string currentValue = context.Entry(blog).Property(e => e.Name).CurrentValue; + context.Entry(blog).Property(e => e.Name).CurrentValue = "1unicorn2"; + #endregion + } + + { + #region Work_with_a_single_property_1e + object blog = context.Blogs.Single(e => e.Id == 1); + + object currentValue = context.Entry(blog).Property("Name").CurrentValue; + context.Entry(blog).Property("Name").CurrentValue = "1unicorn2"; + #endregion + } + + Console.WriteLine(); + } + + public static void Work_with_a_single_navigation_1() + { + Console.WriteLine($">>>> Sample: {nameof(Work_with_a_single_navigation_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var post = context.Posts.Include(e => e.Blog).Single(e => e.Id == 1); + + #region Work_with_a_single_navigation_1 + ReferenceEntry referenceEntry1 = context.Entry(post).Reference(e => e.Blog); + ReferenceEntry referenceEntry2 = context.Entry(post).Reference("Blog"); + ReferenceEntry referenceEntry3 = context.Entry(post).Reference("Blog"); + #endregion + + Console.WriteLine(); + } + + public static void Work_with_a_single_navigation_2() + { + Console.WriteLine($">>>> Sample: {nameof(Work_with_a_single_navigation_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var blog = context.Blogs.Include(e => e.Posts).Single(e => e.Id == 1); + + #region Work_with_a_single_navigation_2a + CollectionEntry collectionEntry1 = context.Entry(blog).Collection(e => e.Posts); + CollectionEntry collectionEntry2 = context.Entry(blog).Collection("Posts"); + CollectionEntry collectionEntry3 = context.Entry(blog).Collection("Posts"); + #endregion + + #region Work_with_a_single_navigation_2b + NavigationEntry navigationEntry = context.Entry(blog).Navigation("Posts"); + #endregion + + Console.WriteLine(); + } + + public static void Work_with_all_properties_of_an_entity_1() + { + Console.WriteLine($">>>> Sample: {nameof(Work_with_all_properties_of_an_entity_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var blog = context.Blogs.Include(e => e.Posts).Single(e => e.Id == 1); + + #region Work_with_all_properties_of_an_entity_1 + foreach (var propertyEntry in context.Entry(blog).Properties) + { + if (propertyEntry.Metadata.ClrType == typeof(DateTime)) + { + propertyEntry.CurrentValue = DateTime.Now; + } + } + #endregion + + Console.WriteLine(); + } + + public static void Work_with_all_properties_of_an_entity_2() + { + Console.WriteLine($">>>> Sample: {nameof(Work_with_all_properties_of_an_entity_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var blog = context.Blogs.Include(e => e.Posts).Single(e => e.Id == 1); + + { + #region Work_with_all_properties_of_an_entity_2a + var currentValues = context.Entry(blog).CurrentValues; + var originalValues = context.Entry(blog).OriginalValues; + var databaseValues = context.Entry(blog).GetDatabaseValues(); + #endregion + } + + { + #region Work_with_all_properties_of_an_entity_2b + var blogDto = new BlogDto { Id = 1, Name = "1unicorn2" }; + + context.Entry(blog).CurrentValues.SetValues(blogDto); + #endregion + } + + { + #region Work_with_all_properties_of_an_entity_2c + var databaseValues = context.Entry(blog).GetDatabaseValues(); + context.Entry(blog).CurrentValues.SetValues(databaseValues); + context.Entry(blog).OriginalValues.SetValues(databaseValues); + #endregion + } + + { + #region Work_with_all_properties_of_an_entity_2d + var blogDictionary = new Dictionary + { + ["Id"] = 1, + ["Name"] = "1unicorn2" + }; + + context.Entry(blog).CurrentValues.SetValues(blogDictionary); + #endregion + } + + { + #region Work_with_all_properties_of_an_entity_2e + var clonedBlog = context.Entry(blog).GetDatabaseValues().ToObject(); + #endregion + } + + Console.WriteLine(); + } + + public static void Work_with_all_navigations_of_an_entity_1() + { + Console.WriteLine($">>>> Sample: {nameof(Work_with_all_navigations_of_an_entity_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var blog = context.Blogs.Single(e => e.Id == 1); + + #region Work_with_all_navigations_of_an_entity_1 + foreach (var navigationEntry in context.Entry(blog).Navigations) + { + navigationEntry.Load(); + } + #endregion + + Console.WriteLine(); + } + + public static void Work_with_all_members_of_an_entity_1() + { + Console.WriteLine($">>>> Sample: {nameof(Work_with_all_members_of_an_entity_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var blog = context.Blogs.Single(e => e.Id == 1); + + #region Work_with_all_members_of_an_entity_1 + foreach (var memberEntry in context.Entry(blog).Members) + { + Console.WriteLine( + $"Member {memberEntry.Metadata.Name} is of type {memberEntry.Metadata.ClrType.ShortDisplayName()} and has value {memberEntry.CurrentValue}"); + } + #endregion + + Console.WriteLine(); + } + + public static void Find_and_FindAsync_1() + { + Console.WriteLine($">>>> Sample: {nameof(Find_and_FindAsync_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Find_and_FindAsync_1 + using var context = new BlogsContext(); + + Console.WriteLine("First call to Find..."); + var blog1 = context.Blogs.Find(1); + + Console.WriteLine($"...found blog {blog1.Name}"); + + Console.WriteLine(); + Console.WriteLine("Second call to Find..."); + var blog2 = context.Blogs.Find(1); + Debug.Assert(blog1 == blog2); + + Console.WriteLine("...returned the same instance without executing a query."); + #endregion + + Console.WriteLine(); + } + + public static void Find_and_FindAsync_2() + { + Console.WriteLine($">>>> Sample: {nameof(Find_and_FindAsync_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + var orderId = 1; + var productId = 2; + + #region Find_and_FindAsync_2 + var orderline = context.OrderLines.Find(orderId, productId); + #endregion + + Console.WriteLine(); + } + + public static void Using_ChangeTracker_Entries_to_access_all_tracked_entities_1() + { + Console.WriteLine($">>>> Sample: {nameof(Using_ChangeTracker_Entries_to_access_all_tracked_entities_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Using_ChangeTracker_Entries_to_access_all_tracked_entities_1a + using var context = new BlogsContext(); + var blogs = context.Blogs.Include(e => e.Posts).ToList(); + + foreach (var entityEntry in context.ChangeTracker.Entries()) + { + Console.WriteLine($"Found {entityEntry.Metadata.Name} entity with ID {entityEntry.Property("Id").CurrentValue}"); + } + #endregion + + Console.WriteLine(); + + #region Using_ChangeTracker_Entries_to_access_all_tracked_entities_1b + foreach (var entityEntry in context.ChangeTracker.Entries()) + { + Console.WriteLine( + $"Found {entityEntry.Metadata.Name} entity with ID {entityEntry.Property(e => e.Id).CurrentValue}"); + } + #endregion + + Console.WriteLine(); + + #region Using_ChangeTracker_Entries_to_access_all_tracked_entities_1c + foreach (var entityEntry in context.ChangeTracker.Entries()) + { + Console.WriteLine( + $"Found {entityEntry.Metadata.Name} entity with ID {entityEntry.Property(e => e.Id).CurrentValue}"); + } + #endregion + + Console.WriteLine(); + } + + public static void Using_DbSet_Local_to_query_tracked_entities_1() + { + Console.WriteLine($">>>> Sample: {nameof(Using_DbSet_Local_to_query_tracked_entities_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Using_DbSet_Local_to_query_tracked_entities_1 + using var context = new BlogsContext(); + + context.Blogs.Include(e => e.Posts).Load(); + + foreach (var blog in context.Blogs.Local) + { + Console.WriteLine($"Blog: {blog.Name}"); + } + + foreach (var post in context.Posts.Local) + { + Console.WriteLine($"Post: {post.Title}"); + } + #endregion + + Console.WriteLine(); + } + + public static void Using_DbSet_Local_to_query_tracked_entities_2() + { + Console.WriteLine($">>>> Sample: {nameof(Using_DbSet_Local_to_query_tracked_entities_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Using_DbSet_Local_to_query_tracked_entities_2 + using var context = new BlogsContext(); + + var posts = context.Posts.Include(e => e.Blog).ToList(); + + Console.WriteLine("Local view after loading posts:"); + + foreach (var post in context.Posts.Local) + { + Console.WriteLine($" Post: {post.Title}"); + } + + context.Remove(posts[1]); + + context.Add( + new Post + { + Title = "What’s next for System.Text.Json?", + Content = ".NET 5.0 was released recently and has come with many...", + Blog = posts[0].Blog + }); + + Console.WriteLine("Local view after adding and deleting posts:"); + + foreach (var post in context.Posts.Local) + { + Console.WriteLine($" Post: {post.Title}"); + } + #endregion + + Console.WriteLine(); + } + + public static void Using_DbSet_Local_to_query_tracked_entities_3() + { + Console.WriteLine($">>>> Sample: {nameof(Using_DbSet_Local_to_query_tracked_entities_3)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Using_DbSet_Local_to_query_tracked_entities_3 + using var context = new BlogsContext(); + + var posts = context.Posts.Include(e => e.Blog).ToList(); + + Console.WriteLine("Local view after loading posts:"); + + foreach (var post in context.Posts.Local) + { + Console.WriteLine($" Post: {post.Title}"); + } + + context.Posts.Local.Remove(posts[1]); + + context.Posts.Local.Add( + new Post + { + Title = "What’s next for System.Text.Json?", + Content = ".NET 5.0 was released recently and has come with many...", + Blog = posts[0].Blog + }); + + Console.WriteLine("Local view after adding and deleting posts:"); + + foreach (var post in context.Posts.Local) + { + Console.WriteLine($" Post: {post.Title}"); + } + #endregion + + Console.WriteLine(); + } + + public static void Using_DbSet_Local_to_query_tracked_entities_4() + { + Console.WriteLine($">>>> Sample: {nameof(Using_DbSet_Local_to_query_tracked_entities_4)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + context.Posts.Include(e => e.Blog).Load(); + + #region Using_DbSet_Local_to_query_tracked_entities_4 + ObservableCollection observableCollection = context.Posts.Local.ToObservableCollection(); + BindingList bindingList = context.Posts.Local.ToBindingList(); + #endregion + + Console.WriteLine(); + } +} + +public static class Helpers +{ + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Add( + new Blog + { + Name = ".NET Blog", + Posts = + { + new Post + { + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + } + }); + + context.SaveChanges(); + } +} + +#region OrderLine +public class OrderLine +{ + public int OrderId { get; set; } + public int ProductId { get; set; } + + //... +} +#endregion + +#region BlogDto +public class BlogDto +{ + public int Id { get; set; } + public string Name { get; set; } +} +#endregion + +public class Blog : IEntityWithKey +{ + public int Id { get; set; } + public string Name { get; set; } + + public IList Posts { get; } = new List(); +} + +public class Post : IEntityWithKey +{ + public int Id { get; set; } + public string Title { get; set; } + public string Content { get; set; } + + public int? BlogId { get; set; } + public Blog Blog { get; set; } +} + +#region IEntityWithKey +public interface IEntityWithKey +{ + int Id { get; set; } +} +#endregion + +public class BlogsContext : DbContext +{ + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + public DbSet OrderLines { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + } + + #region OnModelCreating + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder + .Entity() + .HasKey(e => new { e.OrderId, e.ProductId }); + } + #endregion +} diff --git a/samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/AdditionalChangeTrackingFeatures.csproj b/samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/AdditionalChangeTrackingFeatures.csproj new file mode 100644 index 0000000000..c23f725513 --- /dev/null +++ b/samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/AdditionalChangeTrackingFeatures.csproj @@ -0,0 +1,14 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + diff --git a/samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs b/samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs new file mode 100644 index 0000000000..a5e84dd0d9 --- /dev/null +++ b/samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/DefaultValueSamples.cs @@ -0,0 +1,272 @@ +using System; +using System.Diagnostics; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace DefaultValues +{ + public class DefaultValueSamples + { + public static void Working_with_default_values_1() + { + Console.WriteLine($">>>> Sample: {nameof(Working_with_default_values_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + + #region Working_with_default_values_1 + using var context = new BlogsContext(); + + context.AddRange( + new Token { Name = "A" }, + new Token { Name = "B", ValidFrom = new DateTime(1111, 11, 11, 11, 11, 11) }); + + context.SaveChanges(); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + #endregion + + Console.WriteLine(); + } + + public static void Working_with_default_values_2() + { + Console.WriteLine($">>>> Sample: {nameof(Working_with_default_values_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + + #region Working_with_default_values_2 + using var context = new BlogsContext(); + + var fooA = new Foo1 { Count = 10 }; + var fooB = new Foo1 { Count = 0 }; + var fooC = new Foo1(); + + context.AddRange(fooA, fooB, fooC); + context.SaveChanges(); + + Debug.Assert(fooA.Count == 10); + Debug.Assert(fooB.Count == -1); // Not what we want! + Debug.Assert(fooC.Count == -1); + #endregion + + Console.WriteLine(); + } + + public static void Working_with_default_values_3() + { + Console.WriteLine($">>>> Sample: {nameof(Working_with_default_values_3)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + + #region Working_with_default_values_3 + using var context = new BlogsContext(); + + var fooA = new Foo2 { Count = 10 }; + var fooB = new Foo2 { Count = 0 }; + var fooC = new Foo2(); + + context.AddRange(fooA, fooB, fooC); + context.SaveChanges(); + + Debug.Assert(fooA.Count == 10); + Debug.Assert(fooB.Count == 0); + Debug.Assert(fooC.Count == -1); + #endregion + + Console.WriteLine(); + } + + public static void Working_with_default_values_4() + { + Console.WriteLine($">>>> Sample: {nameof(Working_with_default_values_4)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + + #region Working_with_default_values_4 + using var context = new BlogsContext(); + + var fooA = new Foo3 { Count = 10 }; + var fooB = new Foo3 { Count = 0 }; + var fooC = new Foo3(); + + context.AddRange(fooA, fooB, fooC); + context.SaveChanges(); + + Debug.Assert(fooA.Count == 10); + Debug.Assert(fooB.Count == 0); + Debug.Assert(fooC.Count == -1); + #endregion + + Console.WriteLine(); + } + + public static void Working_with_default_values_5() + { + Console.WriteLine($">>>> Sample: {nameof(Working_with_default_values_5)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + + #region Working_with_default_values_5 + using var context = new BlogsContext(); + + var userA = new User { Name = "Mac" }; + var userB = new User { Name = "Alice", IsAuthorized = true }; + var userC = new User { Name = "Baxter", IsAuthorized = false }; // Always deny Baxter access! + + context.AddRange(userA, userB, userC); + + context.SaveChanges(); + #endregion + + Console.WriteLine(); + } + } + + public static class Helpers + { + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + } + + #region Token + public class Token + { + public int Id { get; set; } + public string Name { get; set; } + public DateTime ValidFrom { get; set; } + } + #endregion + + #region Foo1 + public class Foo1 + { + public int Id { get; set; } + public int Count { get; set; } + } + #endregion + + #region Foo2 + public class Foo2 + { + public int Id { get; set; } + public int? Count { get; set; } + } + #endregion + + #region Foo3 + public class Foo3 + { + public int Id { get; set; } + + private int? _count; + + public int Count + { + get => _count ?? -1; + set => _count = value; + } + } + #endregion + + #region Bar + public class Bar + { + public int Id { get; set; } + public int Count { get; set; } + } + #endregion + + #region User + public class User + { + public int Id { get; set; } + public string Name { get; set; } + + private bool? _isAuthorized; + + public bool IsAuthorized + { + get => _isAuthorized ?? true; + set => _isAuthorized = value; + } + } + #endregion + + public class BlogsContext : DbContext + { + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + #region OnModelCreating_Token + modelBuilder + .Entity() + .Property(e => e.ValidFrom) + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + #endregion + + #region OnModelCreating_Foo1 + modelBuilder + .Entity() + .Property(e => e.Count) + .HasDefaultValue(-1); + #endregion + + #region OnModelCreating_Foo2 + modelBuilder + .Entity() + .Property(e => e.Count) + .HasDefaultValue(-1); + #endregion + + #region OnModelCreating_Foo3 + modelBuilder + .Entity() + .Property(e => e.Count) + .HasDefaultValue(-1); + #endregion + + #region OnModelCreating_User + modelBuilder + .Entity() + .Property(e => e.IsAuthorized) + .HasDefaultValue(true); + #endregion + + #region OnModelCreating_Bar + modelBuilder + .Entity() + .Property(e => e.Count) + .HasDefaultValue(-1) + .ValueGeneratedNever(); + #endregion + } + } +} diff --git a/samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/Program.cs b/samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/Program.cs new file mode 100644 index 0000000000..ee89c3854f --- /dev/null +++ b/samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/Program.cs @@ -0,0 +1,22 @@ +using System; +using DefaultValues; +using Optional; + +public class Program +{ + public static void Main() + { + Console.WriteLine("Samples for _Identity Resolution in EF Core_"); + Console.WriteLine(); + + Samples.DbContext_verses_DbSet_methods_1(); + Samples.Temporary_values_1(); + Samples.Temporary_values_2(); + + DefaultValueSamples.Working_with_default_values_1(); + DefaultValueSamples.Working_with_default_values_2(); + DefaultValueSamples.Working_with_default_values_3(); + DefaultValueSamples.Working_with_default_values_4(); + DefaultValueSamples.Working_with_default_values_5(); + } +} diff --git a/samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/Samples.cs b/samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/Samples.cs new file mode 100644 index 0000000000..e5295edbf4 --- /dev/null +++ b/samples/core/ChangeTracking/AdditionalChangeTrackingFeatures/Samples.cs @@ -0,0 +1,261 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace Optional +{ + public class Samples + { + public static void DbContext_verses_DbSet_methods_1() + { + Console.WriteLine($">>>> Sample: {nameof(DbContext_verses_DbSet_methods_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region DbContext_verses_DbSet_methods_1 + using var context = new BlogsContext(); + + var post = context.Posts.Single(e => e.Id == 3); + var tag = context.Tags.Single(e => e.Id == 1); + + var joinEntitySet = context.Set>("PostTag"); + var joinEntity = new Dictionary + { + ["PostId"] = post.Id, + ["TagId"] = tag.Id + }; + joinEntitySet.Add(joinEntity); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + #endregion + + Console.WriteLine(); + } + + public static void Temporary_values_1() + { + Console.WriteLine($">>>> Sample: {nameof(Temporary_values_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Temporary_values_1 + using var context = new BlogsContext(); + + var blog = new Blog { Name = ".NET Blog" }; + + context.Add(blog); + + Console.WriteLine($"Blog.Id set on entity is {blog.Id}"); + Console.WriteLine($"Blog.Id tracked by EF is {context.Entry(blog).Property(e => e.Id).CurrentValue}"); + #endregion + + Console.WriteLine(); + } + + public static void Temporary_values_2() + { + Console.WriteLine($">>>> Sample: {nameof(Temporary_values_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + + #region Temporary_values_2 + var blogs = new List + { + new Blog { Id = -1, Name = ".NET Blog" }, + new Blog { Id = -2, Name = "Visual Studio Blog" } + }; + + var posts = new List + { + new Post + { + Id = -1, + BlogId = -1, + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Id = -2, + BlogId = -2, + Title = "Disassembly improvements for optimized managed debugging", + Content = "If you are focused on squeezing out the last bits of performance for your .NET service or..." + } + }; + + using var context = new BlogsContext(); + + foreach (var blog in blogs) + { + context.Add(blog).Property(e => e.Id).IsTemporary = true; + } + + foreach (var post in posts) + { + context.Add(post).Property(e => e.Id).IsTemporary = true; + } + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + #endregion + + Console.WriteLine(); + } + } + + public static class Helpers + { + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.AddRange( + new Blog + { + Name = ".NET Blog", + Posts = + { + new Post + { + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + }, + }, + new Blog + { + Name = "Visual Studio Blog", + Posts = + { + new Post + { + Title = "Disassembly improvements for optimized managed debugging", + Content = "If you are focused on squeezing out the last bits of performance for your .NET service or..." + }, + new Post + { + Title = "Database Profiling with Visual Studio", + Content = "Examine when database queries were executed and measure how long the take using..." + }, + } + }, + new Tag + { + Text = ".NET" + }, + new Tag + { + Text = "Visual Studio" + }, + new Tag + { + Text = "EF Core" + }); + + context.SaveChanges(); + } + } + + #region Model + public class Blog + { + public int Id { get; set; } + public string Name { get; set; } + + public IList Posts { get; } = new List(); + } + + public class Post + { + public int Id { get; set; } + public string Title { get; set; } + public string Content { get; set; } + + public int? BlogId { get; set; } + public Blog Blog { get; set; } + + public IList Tags { get; } = new List(); + } + + public class Tag + { + public int Id { get; set; } // Primary key + public string Text { get; set; } + + public IList Posts { get; } = new List(); // Collection navigation + } + #endregion + + public class BlogsContext : DbContext + { + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + public DbSet Tags { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + } + + #region OnModelCreating + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder + .SharedTypeEntity>( + "PostTag", + b => + { + b.IndexerProperty("TagId"); + b.IndexerProperty("PostId"); + }); + + modelBuilder.Entity() + .HasMany(p => p.Tags) + .WithMany(p => p.Posts) + .UsingEntity>( + "PostTag", + j => j.HasOne().WithMany(), + j => j.HasOne().WithMany()); + } + #endregion + } +} diff --git a/samples/core/ChangeTracking/ChangeDetectionAndNotifications/ChangeDetectionAndNotifications.csproj b/samples/core/ChangeTracking/ChangeDetectionAndNotifications/ChangeDetectionAndNotifications.csproj new file mode 100644 index 0000000000..3abb06b34a --- /dev/null +++ b/samples/core/ChangeTracking/ChangeDetectionAndNotifications/ChangeDetectionAndNotifications.csproj @@ -0,0 +1,15 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + diff --git a/samples/core/ChangeTracking/ChangeDetectionAndNotifications/ChangeTrackingProxiesSamples.cs b/samples/core/ChangeTracking/ChangeDetectionAndNotifications/ChangeTrackingProxiesSamples.cs new file mode 100644 index 0000000000..64cfb2ee43 --- /dev/null +++ b/samples/core/ChangeTracking/ChangeDetectionAndNotifications/ChangeTrackingProxiesSamples.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace Proxies +{ + public class ChangeTrackingProxiesSamples + { + public static void Change_tracking_proxies_1() + { + Console.WriteLine($">>>> Sample: {nameof(Change_tracking_proxies_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Change_tracking_proxies_1 + using var context = new BlogsContext(); + var blog = context.Blogs.Include(e => e.Posts).First(e => e.Name == ".NET Blog"); + + // Change a property value + blog.Name = ".NET Blog (Updated!)"; + + // Add a new entity to a navigation + blog.Posts.Add( + context.CreateProxy( + p => + { + p.Title = "What’s next for System.Text.Json?"; + p.Content = ".NET 5.0 was released recently and has come with many..."; + })); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + #endregion + + Console.WriteLine(); + } + } + + public static class Helpers + { + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.AddRange( + context.CreateProxy( + b => + { + b.Name = ".NET Blog"; + b.Posts.Add( + context.CreateProxy( + p => + { + p.Title = "Announcing the Release of EF Core 5.0"; + p.Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."; + })); + b.Posts.Add( + context.CreateProxy( + p => + { + p.Title = "Announcing F# 5"; + p.Content = "F# 5 is the latest version of F#, the functional programming language..."; + })); + }), + context.CreateProxy( + b => + { + b.Name = "Visual Studio Blog"; + b.Posts.Add( + context.CreateProxy( + p => + { + p.Title = "Disassembly improvements for optimized managed debugging"; + p.Content = "If you are focused on squeezing out the last bits of performance for your .NET service or..."; + })); + b.Posts.Add( + context.CreateProxy( + p => + { + p.Title = "Database Profiling with Visual Studio"; + p.Content = "Examine when database queries were executed and measure how long the take using..."; + })); + })); + + context.SaveChanges(); + } + } + + #region Model + public class Blog + { + public virtual int Id { get; set; } + public virtual string Name { get; set; } + + public virtual IList Posts { get; } = new ObservableCollection(); + } + + public class Post + { + public virtual int Id { get; set; } + public virtual string Title { get; set; } + public virtual string Content { get; set; } + + public virtual int BlogId { get; set; } + public virtual Blog Blog { get; set; } + } + #endregion + + public class BlogsContextBase : DbContext + { + #region OnConfiguring + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.UseChangeTrackingProxies(); + #endregion + } + + public class BlogsContext : BlogsContextBase + { + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + + base.OnConfiguring(optionsBuilder); + } + } +} diff --git a/samples/core/ChangeTracking/ChangeDetectionAndNotifications/NotificationEntitiesSamples.cs b/samples/core/ChangeTracking/ChangeDetectionAndNotifications/NotificationEntitiesSamples.cs new file mode 100644 index 0000000000..903c901941 --- /dev/null +++ b/samples/core/ChangeTracking/ChangeDetectionAndNotifications/NotificationEntitiesSamples.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace Notification +{ + public class NotificationEntitiesSamples + { + public static void Notification_entities_1() + { + Console.WriteLine($">>>> Sample: {nameof(Notification_entities_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Notification_entities_1 + using var context = new BlogsContext(); + var blog = context.Blogs.Include(e => e.Posts).First(e => e.Name == ".NET Blog"); + + // Change a property value + blog.Name = ".NET Blog (Updated!)"; + + // Add a new entity to a navigation + blog.Posts.Add( + new Post + { + Title = "What’s next for System.Text.Json?", + Content = ".NET 5.0 was released recently and has come with many..." + }); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + #endregion + + Console.WriteLine(); + } + } + + public static class Helpers + { + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.AddRange( + new Blog + { + Name = ".NET Blog", + Posts = + { + new Post + { + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + }, + }, + new Blog + { + Name = "Visual Studio Blog", + Posts = + { + new Post + { + Title = "Disassembly improvements for optimized managed debugging", + Content = "If you are focused on squeezing out the last bits of performance for your .NET service or..." + }, + new Post + { + Title = "Database Profiling with Visual Studio", + Content = "Examine when database queries were executed and measure how long the take using..." + }, + } + }); + + context.SaveChanges(); + } + } + + #region Model + public class Blog : INotifyPropertyChanging, INotifyPropertyChanged + { + public event PropertyChangingEventHandler PropertyChanging; + public event PropertyChangedEventHandler PropertyChanged; + + private int _id; + + public int Id + { + get => _id; + set + { + PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Id))); + _id = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Id))); + } + } + + private string _name; + + public string Name + { + get => _name; + set + { + PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Name))); + _name = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name))); + } + } + + public IList Posts { get; } = new ObservableCollection(); + } + #endregion + + public class Post : INotifyPropertyChanging, INotifyPropertyChanged + { + public event PropertyChangingEventHandler PropertyChanging; + public event PropertyChangedEventHandler PropertyChanged; + + private int _id; + + public int Id + { + get => _id; + set + { + PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Id))); + _id = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Id))); + } + } + + private string _title; + + public string Title + { + get => _title; + set + { + PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Title))); + _title = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Title))); + } + } + + private string _content; + + public string Content + { + get => _content; + set + { + PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Content))); + _content = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Content))); + } + } + + private int? _blogId; + + public int? BlogId + { + get => _blogId; + set + { + PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(BlogId))); + _blogId = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BlogId))); + } + } + + private Blog _blog; + + public Blog Blog + { + get => _blog; + set + { + PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Blog))); + _blog = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Blog))); + } + } + } + + public class BlogsContext : DbContext + { + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.HasChangeTrackingStrategy(ChangeTrackingStrategy.ChangingAndChangedNotifications); + } + } +} diff --git a/samples/core/ChangeTracking/ChangeDetectionAndNotifications/NotificationWithBaseSamples.cs b/samples/core/ChangeTracking/ChangeDetectionAndNotifications/NotificationWithBaseSamples.cs new file mode 100644 index 0000000000..8a16b69105 --- /dev/null +++ b/samples/core/ChangeTracking/ChangeDetectionAndNotifications/NotificationWithBaseSamples.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace NotificationWithBase +{ + public class NotificationWithBaseSamples + { + public static void Notification_entities_2() + { + Console.WriteLine($">>>> Sample: {nameof(Notification_entities_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Notification_entities_2 + using var context = new BlogsContext(); + var blog = context.Blogs.Include(e => e.Posts).First(e => e.Name == ".NET Blog"); + + // Change a property value + blog.Name = ".NET Blog (Updated!)"; + + // Add a new entity to a navigation + blog.Posts.Add( + new Post + { + Title = "What’s next for System.Text.Json?", + Content = ".NET 5.0 was released recently and has come with many..." + }); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + #endregion + + Console.WriteLine(); + } + } + + public static class Helpers + { + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.AddRange( + new Blog + { + Name = ".NET Blog", + Posts = + { + new Post + { + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + }, + }, + new Blog + { + Name = "Visual Studio Blog", + Posts = + { + new Post + { + Title = "Disassembly improvements for optimized managed debugging", + Content = "If you are focused on squeezing out the last bits of performance for your .NET service or..." + }, + new Post + { + Title = "Database Profiling with Visual Studio", + Content = "Examine when database queries were executed and measure how long the take using..." + }, + } + }); + + context.SaveChanges(); + } + } + + public class Post : NotifyingEntity + { + private int _id; + private string _title; + private string _content; + private int? _blogId; + private Blog _blog; + + public int Id + { + get => _id; + set => SetWithNotify(value, out _id); + } + + public string Title + { + get => _title; + set => SetWithNotify(value, out _title); + } + + public string Content + { + get => _content; + set => SetWithNotify(value, out _content); + } + + public int? BlogId + { + get => _blogId; + set => SetWithNotify(value, out _blogId); + } + + public Blog Blog + { + get => _blog; + set => SetWithNotify(value, out _blog); + } + } + + #region Model + public class Blog : NotifyingEntity + { + private int _id; + + public int Id + { + get => _id; + set => SetWithNotify(value, out _id); + } + + private string _name; + + public string Name + { + get => _name; + set => SetWithNotify(value, out _name); + } + + public IList Posts { get; } = new ObservableCollection(); + } + + public abstract class NotifyingEntity : INotifyPropertyChanging, INotifyPropertyChanged + { + protected void SetWithNotify(T value, out T field, [CallerMemberName] string propertyName = "") + { + NotifyChanging(propertyName); + field = value; + NotifyChanged(propertyName); + } + + public event PropertyChangingEventHandler PropertyChanging; + public event PropertyChangedEventHandler PropertyChanged; + + private void NotifyChanged(string propertyName) + => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + + private void NotifyChanging(string propertyName) + => PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName)); + } + #endregion + + public class BlogsContext : DbContext + { + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + } + + #region OnModelCreating + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.HasChangeTrackingStrategy(ChangeTrackingStrategy.ChangingAndChangedNotifications); + } + #endregion + } +} diff --git a/samples/core/ChangeTracking/ChangeDetectionAndNotifications/Program.cs b/samples/core/ChangeTracking/ChangeDetectionAndNotifications/Program.cs new file mode 100644 index 0000000000..09fd03249e --- /dev/null +++ b/samples/core/ChangeTracking/ChangeDetectionAndNotifications/Program.cs @@ -0,0 +1,22 @@ +using System; +using Notification; +using NotificationWithBase; +using Proxies; +using Snapshot; + +public class Program +{ + public static void Main() + { + Console.WriteLine("Samples for _Change Detection and Notifications_"); + Console.WriteLine(); + + SnapshotSamples.Snapshot_change_tracking_1(); + SnapshotSamples.Snapshot_change_tracking_2(); + + NotificationEntitiesSamples.Notification_entities_1(); + NotificationWithBaseSamples.Notification_entities_2(); + + ChangeTrackingProxiesSamples.Change_tracking_proxies_1(); + } +} diff --git a/samples/core/ChangeTracking/ChangeDetectionAndNotifications/SnapshotSamples.cs b/samples/core/ChangeTracking/ChangeDetectionAndNotifications/SnapshotSamples.cs new file mode 100644 index 0000000000..b2d1690fdd --- /dev/null +++ b/samples/core/ChangeTracking/ChangeDetectionAndNotifications/SnapshotSamples.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace Snapshot +{ + public class SnapshotSamples + { + public static void Snapshot_change_tracking_1() + { + Console.WriteLine($">>>> Sample: {nameof(Snapshot_change_tracking_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Snapshot_change_tracking_1 + using var context = new BlogsContext(); + var blog = context.Blogs.Include(e => e.Posts).First(e => e.Name == ".NET Blog"); + + // Change a property value + blog.Name = ".NET Blog (Updated!)"; + + // Add a new entity to a navigation + blog.Posts.Add( + new Post + { + Title = "What’s next for System.Text.Json?", + Content = ".NET 5.0 was released recently and has come with many..." + }); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + #endregion + + Console.WriteLine(); + } + + public static void Snapshot_change_tracking_2() + { + Console.WriteLine($">>>> Sample: {nameof(Snapshot_change_tracking_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Snapshot_change_tracking_2 + using var context = new BlogsContext(); + var blog = context.Blogs.Include(e => e.Posts).First(e => e.Name == ".NET Blog"); + + // Change a property value + context.Entry(blog).Property(e => e.Name).CurrentValue = ".NET Blog (Updated!)"; + + // Add a new entity to the DbContext + context.Add( + new Post + { + Blog = blog, + Title = "What’s next for System.Text.Json?", + Content = ".NET 5.0 was released recently and has come with many..." + }); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + #endregion + + Console.WriteLine(); + } + } + + public static class Helpers + { + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.AddRange( + new Blog + { + Name = ".NET Blog", + Posts = + { + new Post + { + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + }, + }, + new Blog + { + Name = "Visual Studio Blog", + Posts = + { + new Post + { + Title = "Disassembly improvements for optimized managed debugging", + Content = "If you are focused on squeezing out the last bits of performance for your .NET service or..." + }, + new Post + { + Title = "Database Profiling with Visual Studio", + Content = "Examine when database queries were executed and measure how long the take using..." + }, + } + }); + + context.SaveChanges(); + } + } + + public class Blog + { + public int Id { get; set; } + public string Name { get; set; } + + public IList Posts { get; } = new List(); + } + + public class Post + { + public int Id { get; set; } + public string Title { get; set; } + public string Content { get; set; } + + public int? BlogId { get; set; } + public Blog Blog { get; set; } + } + + public class PostTag + { + public int PostId { get; set; } + public int TagId { get; set; } + + public DateTime TaggedOn { get; set; } + public string TaggedBy { get; set; } + } + + public class BlogsContext : DbContext + { + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasKey(e => new { e.PostId, e.TagId }); + } + + #region SaveChanges + public override int SaveChanges() + { + foreach (var entityEntry in ChangeTracker.Entries()) // Detects changes automatically + { + if (entityEntry.State == EntityState.Added) + { + entityEntry.Entity.TaggedBy = "ajcvickers"; + entityEntry.Entity.TaggedOn = DateTime.Now; + } + } + + try + { + ChangeTracker.AutoDetectChangesEnabled = false; + return base.SaveChanges(); // Avoid automatically detecting changes again here + } + finally + { + ChangeTracker.AutoDetectChangesEnabled = true; + } + } + #endregion + } +} diff --git a/samples/core/ChangeTracking/ChangeTrackerDebugging/ChangeTrackerDebugging.csproj b/samples/core/ChangeTracking/ChangeTrackerDebugging/ChangeTrackerDebugging.csproj new file mode 100644 index 0000000000..c23f725513 --- /dev/null +++ b/samples/core/ChangeTracking/ChangeTrackerDebugging/ChangeTrackerDebugging.csproj @@ -0,0 +1,14 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + diff --git a/samples/core/ChangeTracking/ChangeTrackerDebugging/Program.cs b/samples/core/ChangeTracking/ChangeTrackerDebugging/Program.cs new file mode 100644 index 0000000000..b878822988 --- /dev/null +++ b/samples/core/ChangeTracking/ChangeTrackerDebugging/Program.cs @@ -0,0 +1,13 @@ +using System; + +public class Program +{ + public static void Main() + { + Console.WriteLine("Samples for _Change Tracker Debugging_"); + Console.WriteLine(); + + Samples.Change_tracker_debug_view_1(); + Samples.Change_tracker_logging_1(); + } +} diff --git a/samples/core/ChangeTracking/ChangeTrackerDebugging/Samples.cs b/samples/core/ChangeTracking/ChangeTrackerDebugging/Samples.cs new file mode 100644 index 0000000000..af1a1eba8d --- /dev/null +++ b/samples/core/ChangeTracking/ChangeTrackerDebugging/Samples.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.Extensions.Logging; + +public class Samples +{ + public static void Change_tracker_debug_view_1() + { + Console.WriteLine($">>>> Sample: {nameof(Change_tracker_debug_view_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Change_tracker_debug_view_1a + using var context = new BlogsContext(); + + var blogs = context.Blogs + .Include(e => e.Posts).ThenInclude(e => e.Tags) + .Include(e => e.Assets) + .ToList(); + + // Mark something Added + blogs[0].Posts.Add( + new Post + { + Title = "What’s next for System.Text.Json?", + Content = ".NET 5.0 was released recently and has come with many new features and..." + }); + + // Mark something Deleted + blogs[1].Posts.Remove(blogs[1].Posts[1]); + + // Make something Modified + blogs[0].Name = ".NET Blog (All new!)"; + + context.ChangeTracker.DetectChanges(); + #endregion + + Console.WriteLine(); + + #region Change_tracker_debug_view_1b + Console.WriteLine(context.ChangeTracker.DebugView.ShortView); + #endregion + + Console.WriteLine(); + + #region Change_tracker_debug_view_1c + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + #endregion + + Console.WriteLine(); + } + + public static void Change_tracker_logging_1() + { + Console.WriteLine($">>>> Sample: {nameof(Change_tracker_logging_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(LogLevel.Debug); + + var blogs = context.Blogs + .Include(e => e.Posts).ThenInclude(e => e.Tags) + .Include(e => e.Assets) + .ToList(); + + // Mark something Added + blogs[0].Posts.Add( + new Post + { + Title = "What’s next for System.Text.Json?", + Content = ".NET 5.0 was released recently and has come with many new features and..." + }); + + // Mark something Deleted + blogs[1].Posts.Remove(blogs[1].Posts[1]); + + // Make something Modified + blogs[0].Name = ".NET Blog (All new!)"; + + context.ChangeTracker.DetectChanges(); + + Console.WriteLine(); + } +} + +public static class Helpers +{ + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(LogLevel.Error); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(LogLevel.Error); + + var blogs = new[] + { + new Blog + { + Name = ".NET Blog", + Assets = new BlogAssets(), + Posts = + { + new Post + { + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + }, + }, + new Blog + { + Name = "Visual Studio Blog", + Assets = new BlogAssets(), + Posts = + { + new Post + { + Title = "Disassembly improvements for optimized managed debugging", + Content = "If you are focused on squeezing out the last bits of performance for your .NET service or..." + }, + new Post + { + Title = "Database Profiling with Visual Studio", + Content = "Examine when database queries were executed and measure how long the take using..." + }, + } + } + }; + + var tags = new[] + { + new Tag + { + Text = ".NET", + Posts = { blogs[0].Posts[0], blogs[0].Posts[1] } + }, + new Tag + { + Text = "Visual Studio", + Posts = { blogs[1].Posts[0], blogs[1].Posts[1] } + }, + new Tag + { + Text = "EF Core", + Posts = { blogs[0].Posts[0] } + } + }; + + context.AddRange(blogs); + context.AddRange(tags); + + context.SaveChanges(); + } +} + +#region Model +public class Blog +{ + public int Id { get; set; } // Primary key + public Guid AssetsId { get; set; } // Alternate key + public string Name { get; set; } + + public IList Posts { get; } = new List(); // Collection navigation + public BlogAssets Assets { get; set; } // Reference navigation +} + +public class BlogAssets +{ + public Guid Id { get; set; } // Primary key and foreign key + public byte[] Banner { get; set; } + + public Blog Blog { get; set; } // Reference navigation +} + +public class Post +{ + public int Id { get; set; } // Primary key + public string Title { get; set; } + public string Content { get; set; } + + public int BlogId { get; set; } // Foreign key + public Blog Blog { get; set; } // Reference navigation + + public IList Tags { get; } = new List(); // Skip collection navigation +} + +public class Tag +{ + public int Id { get; set; } // Primary key + public string Text { get; set; } + + public IList Posts { get; } = new List(); // Skip collection navigation +} +#endregion + +public class BlogsContext : DbContext +{ + private readonly LogLevel _logLevel; + + public BlogsContext(LogLevel logLevel = LogLevel.Information) + { + _logLevel = logLevel; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + public DbSet Assets { get; set; } + public DbSet Tags { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .LogTo(Console.WriteLine, _logLevel) + .ConfigureWarnings(w => w.Ignore(RelationalEventId.MultipleCollectionIncludeWarning)) + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + } + + #region OnModelCreating + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder + .Entity() + .Property(e => e.AssetsId) + .ValueGeneratedOnAdd(); + + modelBuilder + .Entity() + .HasOne(e => e.Blog) + .WithOne(e => e.Assets) + .HasForeignKey(e => e.Id) + .HasPrincipalKey(e => e.AssetsId); + } + #endregion +} diff --git a/samples/core/ChangeTracking/ChangeTrackingInEFCore/ChangeTrackingInEFCore.csproj b/samples/core/ChangeTracking/ChangeTrackingInEFCore/ChangeTrackingInEFCore.csproj new file mode 100644 index 0000000000..c23f725513 --- /dev/null +++ b/samples/core/ChangeTracking/ChangeTrackingInEFCore/ChangeTrackingInEFCore.csproj @@ -0,0 +1,14 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + diff --git a/samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysRequiredSamples.cs b/samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysRequiredSamples.cs new file mode 100644 index 0000000000..8f8492d2b5 --- /dev/null +++ b/samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysRequiredSamples.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace ExplicitKeysRequired +{ + public static class ExplicitKeysRequiredSamples + { + public static void Deleting_principal_parent_entities_1() + { + Console.WriteLine($">>>> Sample: {nameof(Deleting_principal_parent_entities_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var blog = GetDisconnectedBlogAndPosts(); + + #region Deleting_principal_parent_entities_1 + // Attach a blog and associated posts + context.Attach(blog); + + // Mark the blog as deleted + context.Remove(blog); + #endregion + + Console.WriteLine("Before SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine("After SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + Console.WriteLine(); + + Blog GetDisconnectedBlogAndPosts() + { + using var tempContext = new BlogsContext(); + return tempContext.Blogs.Include(e => e.Posts).Single(); + } + } + } + + public static class Helpers + { + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Add( + new Blog + { + Id = 1, + Name = ".NET Blog", + Posts = + { + new Post + { + Id = 1, + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Id = 2, + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + } + }); + + context.SaveChanges(); + } + } + + #region Model + public class Blog + { + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public int Id { get; set; } + + public string Name { get; set; } + + public IList Posts { get; } = new List(); + } + + public class Post + { + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public int Id { get; set; } + + public string Title { get; set; } + public string Content { get; set; } + + public int BlogId { get; set; } + public Blog Blog { get; set; } + } + #endregion + + public class BlogsContext : DbContext + { + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + } + } +} diff --git a/samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysSamples.cs b/samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysSamples.cs new file mode 100644 index 0000000000..4a13e67245 --- /dev/null +++ b/samples/core/ChangeTracking/ChangeTrackingInEFCore/ExplicitKeysSamples.cs @@ -0,0 +1,461 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace ExplicitKeys +{ + public static class ExplicitKeysSamples + { + public static void Inserting_new_entities_1() + { + Console.WriteLine($">>>> Sample: {nameof(Inserting_new_entities_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + + using var context = new BlogsContext(); + + #region Inserting_new_entities_1 + context.Add( + new Blog + { + Id = 1, + Name = ".NET Blog", + }); + #endregion + + Console.WriteLine("Before SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine("After SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + Console.WriteLine(); + } + + public static void Inserting_new_entities_2() + { + Console.WriteLine($">>>> Sample: {nameof(Inserting_new_entities_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + + using var context = new BlogsContext(); + + #region Inserting_new_entities_2 + context.Add( + new Blog + { + Id = 1, + Name = ".NET Blog", + Posts = + { + new Post + { + Id = 1, + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Id = 2, + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + } + } + }); + #endregion + + Console.WriteLine("Before SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine("After SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + Console.WriteLine(); + } + + public static void Attaching_existing_entities_1() + { + Console.WriteLine($">>>> Sample: {nameof(Attaching_existing_entities_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + #region Attaching_existing_entities_1 + context.Attach( + new Blog + { + Id = 1, + Name = ".NET Blog", + }); + #endregion + + Console.WriteLine("Before SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine("After SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + Console.WriteLine(); + } + + public static void Attaching_existing_entities_2() + { + Console.WriteLine($">>>> Sample: {nameof(Attaching_existing_entities_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + #region Attaching_existing_entities_2 + context.Attach( + new Blog + { + Id = 1, + Name = ".NET Blog", + Posts = + { + new Post + { + Id = 1, + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Id = 2, + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + } + } + }); + #endregion + + Console.WriteLine("Before SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine("After SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + Console.WriteLine(); + } + + public static void Updating_existing_entities_1() + { + Console.WriteLine($">>>> Sample: {nameof(Updating_existing_entities_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + #region Updating_existing_entities_1 + context.Update( + new Blog + { + Id = 1, + Name = ".NET Blog", + }); + #endregion + + Console.WriteLine("Before SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine("After SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + Console.WriteLine(); + } + + public static void Updating_existing_entities_2() + { + Console.WriteLine($">>>> Sample: {nameof(Updating_existing_entities_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + #region Updating_existing_entities_2 + context.Update( + new Blog + { + Id = 1, + Name = ".NET Blog", + Posts = + { + new Post + { + Id = 1, + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Id = 2, + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + } + } + }); + #endregion + + Console.WriteLine("Before SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine("After SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + Console.WriteLine(); + } + + public static void Deleting_existing_entities_1() + { + Console.WriteLine($">>>> Sample: {nameof(Deleting_existing_entities_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + #region Deleting_existing_entities_1 + context.Remove( + new Post + { + Id = 2 + }); + #endregion + + Console.WriteLine("Before SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine("After SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + Console.WriteLine(); + } + + public static void Deleting_dependent_child_entities_1() + { + Console.WriteLine($">>>> Sample: {nameof(Deleting_dependent_child_entities_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var post = GetDisconnectedPost(); + + #region Deleting_dependent_child_entities_1 + context.Attach(post); + context.Remove(post); + #endregion + + Console.WriteLine("Before SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine("After SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + Console.WriteLine(); + + Post GetDisconnectedPost() + { + using var tempContext = new BlogsContext(); + return tempContext.Posts.Find(2); + } + } + + public static void Deleting_dependent_child_entities_2() + { + Console.WriteLine($">>>> Sample: {nameof(Deleting_dependent_child_entities_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var blog = GetDisconnectedBlogAndPosts(); + + #region Deleting_dependent_child_entities_2 + // Attach a blog and associated posts + context.Attach(blog); + + // Mark one post as Deleted + context.Remove((object)blog.Posts[1]); + #endregion + + Console.WriteLine("Before SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine("After SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + Console.WriteLine(); + + Blog GetDisconnectedBlogAndPosts() + { + using var tempContext = new BlogsContext(); + return tempContext.Blogs.Include(e => e.Posts).Single(); + } + } + + public static void Deleting_principal_parent_entities_1() + { + Console.WriteLine($">>>> Sample: {nameof(Deleting_principal_parent_entities_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var blog = GetDisconnectedBlogAndPosts(); + + #region Deleting_principal_parent_entities_1 + // Attach a blog and associated posts + context.Attach(blog); + + // Mark the blog as deleted + context.Remove(blog); + #endregion + + Console.WriteLine("Before SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine("After SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + Console.WriteLine(); + + Blog GetDisconnectedBlogAndPosts() + { + using var tempContext = new BlogsContext(); + return tempContext.Blogs.Include(e => e.Posts).Single(); + } + } + } + + public static class Helpers + { + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Add( + new Blog + { + Id = 1, + Name = ".NET Blog", + Posts = + { + new Post + { + Id = 1, + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Id = 2, + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + } + }); + + context.SaveChanges(); + } + } + + #region Model + public class Blog + { + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public int Id { get; set; } + + public string Name { get; set; } + + public IList Posts { get; } = new List(); + } + + public class Post + { + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public int Id { get; set; } + + public string Title { get; set; } + public string Content { get; set; } + + public int? BlogId { get; set; } + public Blog Blog { get; set; } + } + #endregion + + public class BlogsContext : DbContext + { + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + } + } +} diff --git a/samples/core/ChangeTracking/ChangeTrackingInEFCore/GeneratedKeysSamples.cs b/samples/core/ChangeTracking/ChangeTrackingInEFCore/GeneratedKeysSamples.cs new file mode 100644 index 0000000000..f24236e59c --- /dev/null +++ b/samples/core/ChangeTracking/ChangeTrackingInEFCore/GeneratedKeysSamples.cs @@ -0,0 +1,386 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace GeneratedKeys +{ + public static class GeneratedKeysSamples + { + public static void Simple_query_and_update_1() + { + Console.WriteLine($">>>> Sample: {nameof(Simple_query_and_update_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Simple_query_and_update_1 + using var context = new BlogsContext(); + + var blog = context.Blogs.Include(e => e.Posts).First(e => e.Name == ".NET Blog"); + + blog.Name = ".NET Blog (Updated!)"; + + foreach (var post in blog.Posts.Where(e => !e.Title.Contains("5.0"))) + { + post.Title = post.Title.Replace("5", "5.0"); + } + + context.SaveChanges(); + #endregion + + Console.WriteLine(); + } + + public static void Simple_query_and_update_2() + { + Console.WriteLine($">>>> Sample: {nameof(Simple_query_and_update_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var blog = context.Blogs.Include(e => e.Posts).First(e => e.Name == ".NET Blog"); + + blog.Name = ".NET Blog (Updated!)"; + + foreach (var post in blog.Posts.Where(e => !e.Title.Contains("5.0"))) + { + post.Title = post.Title.Replace("5", "5.0"); + } + + #region Simple_query_and_update_2 + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + #endregion + + context.SaveChanges(); + + Console.WriteLine(); + } + + public static void Query_then_insert_update_and_delete_1() + { + Console.WriteLine($">>>> Sample: {nameof(Query_then_insert_update_and_delete_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Query_then_insert_update_and_delete_1 + using var context = new BlogsContext(); + + var blog = context.Blogs.Include(e => e.Posts).First(e => e.Name == ".NET Blog"); + + // Modify property values + blog.Name = ".NET Blog (Updated!)"; + + // Insert a new Post + blog.Posts.Add( + new Post + { + Title = "What’s next for System.Text.Json?", + Content = ".NET 5.0 was released recently and has come with many..." + }); + + // Mark an existing Post as Deleted + var postToDelete = blog.Posts.Single(e => e.Title == "Announcing F# 5"); + context.Remove((object)postToDelete); + + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + #endregion + } + + public static void Inserting_new_entities_3() + { + Console.WriteLine($">>>> Sample: {nameof(Inserting_new_entities_3)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + + using var context = new BlogsContext(); + + #region Inserting_new_entities_3 + context.Add( + new Blog + { + Name = ".NET Blog", + Posts = + { + new Post + { + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + } + } + }); + #endregion + + Console.WriteLine("Before SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine("After SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + Console.WriteLine(); + } + + public static void Attaching_existing_entities_3() + { + Console.WriteLine($">>>> Sample: {nameof(Attaching_existing_entities_3)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + #region Attaching_existing_entities_3 + context.Attach( + new Blog + { + Id = 1, + Name = ".NET Blog", + Posts = + { + new Post + { + Id = 1, + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Id = 2, + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + new Post + { + Title = "Announcing .NET 5.0", + Content = ".NET 5.0 includes many enhancements, including single file applications, more..." + }, + } + }); + #endregion + + Console.WriteLine("Before SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine("After SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + Console.WriteLine(); + } + + public static void Updating_existing_entities_3() + { + Console.WriteLine($">>>> Sample: {nameof(Updating_existing_entities_3)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + #region Updating_existing_entities_3 + context.Update( + new Blog + { + Id = 1, + Name = ".NET Blog", + Posts = + { + new Post + { + Id = 1, + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Id = 2, + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + new Post + { + Title = "Announcing .NET 5.0", + Content = ".NET 5.0 includes many enhancements, including single file applications, more..." + }, + } + }); + #endregion + + Console.WriteLine("Before SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine("After SaveChanges:"); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + Console.WriteLine(); + } + + public static void Custom_tracking_with_TrackGraph_1() + { + Console.WriteLine($">>>> Sample: {nameof(Custom_tracking_with_TrackGraph_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var blog = context.Blogs.AsNoTracking().Include(e => e.Posts).Single(e => e.Name == ".NET Blog"); + + #region Custom_tracking_with_TrackGraph_1a + blog.Posts.Add( + new Post + { + Title = "Announcing .NET 5.0", + Content = ".NET 5.0 includes many enhancements, including single file applications, more..." + } + ); + + var toDelete = blog.Posts.Single(e => e.Title == "Announcing F# 5"); + toDelete.Id = -toDelete.Id; + #endregion + + UpdateBlog(blog); + + Console.WriteLine(); + } + + #region Custom_tracking_with_TrackGraph_1b + public static void UpdateBlog(Blog blog) + { + using var context = new BlogsContext(); + + context.ChangeTracker.TrackGraph( + blog, node => + { + var propertyEntry = node.Entry.Property("Id"); + var keyValue = (int)propertyEntry.CurrentValue; + + if (keyValue == 0) + { + node.Entry.State = EntityState.Added; + } + else if (keyValue < 0) + { + propertyEntry.CurrentValue = -keyValue; + node.Entry.State = EntityState.Deleted; + } + else + { + node.Entry.State = EntityState.Modified; + } + + Console.WriteLine($"Tracking {node.Entry.Metadata.DisplayName()} with key value {keyValue} as {node.Entry.State}"); + }); + + context.SaveChanges(); + } + #endregion + } + + public static class Helpers + { + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Add( + new Blog + { + Name = ".NET Blog", + Posts = + { + new Post + { + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + } + }); + + context.SaveChanges(); + } + } + + #region Model + public class Blog + { + public int Id { get; set; } + public string Name { get; set; } + + public IList Posts { get; } = new List(); + } + + public class Post + { + public int Id { get; set; } + public string Title { get; set; } + public string Content { get; set; } + + public int? BlogId { get; set; } + public Blog Blog { get; set; } + } + #endregion + + public class BlogsContext : DbContext + { + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + } + } +} diff --git a/samples/core/ChangeTracking/ChangeTrackingInEFCore/Program.cs b/samples/core/ChangeTracking/ChangeTrackingInEFCore/Program.cs new file mode 100644 index 0000000000..4460eb108b --- /dev/null +++ b/samples/core/ChangeTracking/ChangeTrackingInEFCore/Program.cs @@ -0,0 +1,37 @@ +using System; +using ExplicitKeys; +using ExplicitKeysRequired; +using GeneratedKeys; + +public class Program +{ + public static void Main() + { + Console.WriteLine("Samples for _Change Tracking in EF Core_"); + Console.WriteLine(); + + GeneratedKeysSamples.Simple_query_and_update_1(); + GeneratedKeysSamples.Simple_query_and_update_2(); + GeneratedKeysSamples.Query_then_insert_update_and_delete_1(); + + ExplicitKeysSamples.Inserting_new_entities_1(); + ExplicitKeysSamples.Inserting_new_entities_2(); + GeneratedKeysSamples.Inserting_new_entities_3(); + + ExplicitKeysSamples.Attaching_existing_entities_1(); + ExplicitKeysSamples.Attaching_existing_entities_2(); + GeneratedKeysSamples.Attaching_existing_entities_3(); + + ExplicitKeysSamples.Updating_existing_entities_1(); + ExplicitKeysSamples.Updating_existing_entities_2(); + GeneratedKeysSamples.Updating_existing_entities_3(); + + ExplicitKeysSamples.Deleting_existing_entities_1(); + ExplicitKeysSamples.Deleting_dependent_child_entities_1(); + ExplicitKeysSamples.Deleting_dependent_child_entities_2(); + ExplicitKeysSamples.Deleting_principal_parent_entities_1(); + ExplicitKeysRequiredSamples.Deleting_principal_parent_entities_1(); + + GeneratedKeysSamples.Custom_tracking_with_TrackGraph_1(); + } +} diff --git a/samples/core/ChangeTracking/ChangingFKsAndNavigations/ChangingFKsAndNavigations.csproj b/samples/core/ChangeTracking/ChangingFKsAndNavigations/ChangingFKsAndNavigations.csproj new file mode 100644 index 0000000000..c23f725513 --- /dev/null +++ b/samples/core/ChangeTracking/ChangingFKsAndNavigations/ChangingFKsAndNavigations.csproj @@ -0,0 +1,14 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + diff --git a/samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityAndSkipsSamples.cs b/samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityAndSkipsSamples.cs new file mode 100644 index 0000000000..966dcf13d9 --- /dev/null +++ b/samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityAndSkipsSamples.cs @@ -0,0 +1,225 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace JoinEntityWithSkips +{ + public class ExplicitJoinEntityWithSkipsSamples + { + public static void Many_to_many_relationships_3() + { + Console.WriteLine($">>>> Sample: {nameof(Many_to_many_relationships_3)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Many_to_many_relationships_3 + using var context = new BlogsContext(); + + var post = context.Posts.Single(e => e.Id == 3); + var tag = context.Tags.Single(e => e.Id == 1); + + post.Tags.Add(tag); + + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + #endregion + + Console.WriteLine(); + } + + public static void Many_to_many_relationships_4() + { + Console.WriteLine($">>>> Sample: {nameof(Many_to_many_relationships_4)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var post = context.Posts.Single(e => e.Id == 3); + var tag = context.Tags.Single(e => e.Id == 1); + + #region Many_to_many_relationships_4 + context.Add(new PostTag { Post = post, Tag = tag }); + #endregion + + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + Console.WriteLine(); + } + + public static void Many_to_many_relationships_5() + { + Console.WriteLine($">>>> Sample: {nameof(Many_to_many_relationships_5)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var post = context.Posts.Single(e => e.Id == 3); + var tag = context.Tags.Single(e => e.Id == 1); + + #region Many_to_many_relationships_5 + context.Add(new PostTag { PostId = post.Id, TagId = tag.Id }); + #endregion + + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + Console.WriteLine(); + } + } + + public static class Helpers + { + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.AddRange( + new Blog + { + Name = ".NET Blog", + Posts = + { + new Post + { + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + }, + }, + new Blog + { + Name = "Visual Studio Blog", + Posts = + { + new Post + { + Title = "Disassembly improvements for optimized managed debugging", + Content = "If you are focused on squeezing out the last bits of performance for your .NET service or..." + }, + new Post + { + Title = "Database Profiling with Visual Studio", + Content = "Examine when database queries were executed and measure how long the take using..." + }, + } + }, + new Tag + { + Text = ".NET" + }, + new Tag + { + Text = "Visual Studio" + }, + new Tag + { + Text = "EF Core" + }); + + context.SaveChanges(); + } + } + + public class Blog + { + public int Id { get; set; } + public string Name { get; set; } + + public IList Posts { get; } = new List(); + } + + #region Model + public class Post + { + public int Id { get; set; } + public string Title { get; set; } + public string Content { get; set; } + + public int? BlogId { get; set; } + public Blog Blog { get; set; } + + public IList Tags { get; } = new List(); // Skip collection navigation + public IList PostTags { get; } = new List(); // Collection navigation + } + + public class Tag + { + public int Id { get; set; } + public string Text { get; set; } + + public IList Posts { get; } = new List(); // Skip collection navigation + public IList PostTags { get; } = new List(); // Collection navigation + } + + public class PostTag + { + public int PostId { get; set; } // First part of composite PK; FK to Post + public int TagId { get; set; } // Second part of composite PK; FK to Tag + + public Post Post { get; set; } // Reference navigation + public Tag Tag { get; set; } // Reference navigation + } + #endregion + + public class BlogsContext : DbContext + { + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + public DbSet Tags { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + } + + #region OnModelCreating + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasMany(p => p.Tags) + .WithMany(p => p.Posts) + .UsingEntity( + j => j.HasOne(t => t.Tag).WithMany(p => p.PostTags), + j => j.HasOne(t => t.Post).WithMany(p => p.PostTags)); + } + #endregion + } +} diff --git a/samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntitySamples.cs b/samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntitySamples.cs new file mode 100644 index 0000000000..69a17f6a4a --- /dev/null +++ b/samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntitySamples.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace JoinEntity +{ + public class ExplicitJoinEntitySamples + { + public static void Many_to_many_relationships_1() + { + Console.WriteLine($">>>> Sample: {nameof(Many_to_many_relationships_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Many_to_many_relationships_1 + using var context = new BlogsContext(); + + var post = context.Posts.Single(e => e.Id == 3); + var tag = context.Tags.Single(e => e.Id == 1); + + context.Add(new PostTag { PostId = post.Id, TagId = tag.Id }); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + #endregion + + context.SaveChanges(); + + Console.WriteLine(); + } + + public static void Many_to_many_relationships_2() + { + Console.WriteLine($">>>> Sample: {nameof(Many_to_many_relationships_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var post = context.Posts.Single(e => e.Id == 3); + var tag = context.Tags.Single(e => e.Id == 1); + + #region Many_to_many_relationships_2 + context.Add(new PostTag { Post = post, Tag = tag }); + #endregion + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine(); + } + } + + public static class Helpers + { + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.AddRange( + new Blog + { + Name = ".NET Blog", + Posts = + { + new Post + { + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + }, + }, + new Blog + { + Name = "Visual Studio Blog", + Posts = + { + new Post + { + Title = "Disassembly improvements for optimized managed debugging", + Content = "If you are focused on squeezing out the last bits of performance for your .NET service or..." + }, + new Post + { + Title = "Database Profiling with Visual Studio", + Content = "Examine when database queries were executed and measure how long the take using..." + }, + } + }, + new Tag + { + Text = ".NET" + }, + new Tag + { + Text = "Visual Studio" + }, + new Tag + { + Text = "EF Core" + }); + + context.SaveChanges(); + } + } + + public class Blog + { + public int Id { get; set; } + public string Name { get; set; } + + public IList Posts { get; } = new List(); + } + + #region Model + public class Post + { + public int Id { get; set; } + public string Title { get; set; } + public string Content { get; set; } + + public int? BlogId { get; set; } + public Blog Blog { get; set; } + + public IList PostTags { get; } = new List(); // Collection navigation + } + + public class Tag + { + public int Id { get; set; } + public string Text { get; set; } + + public IList PostTags { get; } = new List(); // Collection navigation + } + + public class PostTag + { + public int PostId { get; set; } // First part of composite PK; FK to Post + public int TagId { get; set; } // Second part of composite PK; FK to Tag + + public Post Post { get; set; } // Reference navigation + public Tag Tag { get; set; } // Reference navigation + } + #endregion + + public class BlogsContext : DbContext + { + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + public DbSet Tags { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasKey(e => new { e.PostId, e.TagId }); + } + } +} diff --git a/samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityWithPayloadSamples.cs b/samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityWithPayloadSamples.cs new file mode 100644 index 0000000000..b1cbf5702b --- /dev/null +++ b/samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityWithPayloadSamples.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace JoinEntityWithPayload +{ + public class ExplicitJoinEntityWithPayloadSamples + { + public static void Many_to_many_relationships_7() + { + Console.WriteLine($">>>> Sample: {nameof(Many_to_many_relationships_7)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Many_to_many_relationships_7 + using var context = new BlogsContext(); + + var post = context.Posts.Single(e => e.Id == 3); + var tag = context.Tags.Single(e => e.Id == 1); + + post.Tags.Add(tag); + + context.SaveChanges(); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + #endregion + + Console.WriteLine(); + } + } + + public static class Helpers + { + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.AddRange( + new Blog + { + Name = ".NET Blog", + Posts = + { + new Post + { + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + }, + }, + new Blog + { + Name = "Visual Studio Blog", + Posts = + { + new Post + { + Title = "Disassembly improvements for optimized managed debugging", + Content = "If you are focused on squeezing out the last bits of performance for your .NET service or..." + }, + new Post + { + Title = "Database Profiling with Visual Studio", + Content = "Examine when database queries were executed and measure how long the take using..." + }, + } + }, + new Tag + { + Text = ".NET" + }, + new Tag + { + Text = "Visual Studio" + }, + new Tag + { + Text = "EF Core" + }); + + context.SaveChanges(); + } + } + + public class Blog + { + public int Id { get; set; } + public string Name { get; set; } + + public IList Posts { get; } = new List(); + } + + public class Post + { + public int Id { get; set; } + public string Title { get; set; } + public string Content { get; set; } + + public int? BlogId { get; set; } + public Blog Blog { get; set; } + + public IList Tags { get; } = new List(); // Skip collection navigation + } + + public class Tag + { + public int Id { get; set; } + public string Text { get; set; } + + public IList Posts { get; } = new List(); // Skip collection navigation + } + + #region Model + public class PostTag + { + public int PostId { get; set; } // First part of composite PK; FK to Post + public int TagId { get; set; } // Second part of composite PK; FK to Tag + + public DateTime TaggedOn { get; set; } // Payload + } + #endregion + + public class BlogsContext : DbContext + { + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + public DbSet Tags { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + } + + #region OnModelCreating + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasMany(p => p.Tags) + .WithMany(p => p.Posts) + .UsingEntity( + j => j.HasOne().WithMany(), + j => j.HasOne().WithMany(), + j => j.Property(e => e.TaggedOn).HasDefaultValueSql("CURRENT_TIMESTAMP")); + } + #endregion + } +} diff --git a/samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityWithStringPayloadSamples.cs b/samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityWithStringPayloadSamples.cs new file mode 100644 index 0000000000..bf936cb539 --- /dev/null +++ b/samples/core/ChangeTracking/ChangingFKsAndNavigations/ExplicitJoinEntityWithStringPayloadSamples.cs @@ -0,0 +1,230 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace JoinEntityWithStringPayload +{ + public class ExplicitJoinEntityWithStringPayloadSamples + { + public static void Many_to_many_relationships_8() + { + Console.WriteLine($">>>> Sample: {nameof(Many_to_many_relationships_8)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Many_to_many_relationships_8 + using var context = new BlogsContext(); + + var post = context.Posts.Single(e => e.Id == 3); + var tag = context.Tags.Single(e => e.Id == 1); + + post.Tags.Add(tag); + + context.ChangeTracker.DetectChanges(); + + var joinEntity = context.Set().Find(post.Id, tag.Id); + + joinEntity.TaggedBy = "ajcvickers"; + + context.SaveChanges(); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + #endregion + + Console.WriteLine(); + } + + public static void Many_to_many_relationships_9() + { + Console.WriteLine($">>>> Sample: {nameof(Many_to_many_relationships_9)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Many_to_many_relationships_9 + using var context = new BlogsContext(); + + var post = context.Posts.Single(e => e.Id == 3); + var tag = context.Tags.Single(e => e.Id == 1); + + context.Add( + new PostTag + { + PostId = post.Id, + TagId = tag.Id, + TaggedBy = "ajcvickers" + }); + + context.SaveChanges(); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + #endregion + + Console.WriteLine(); + } + } + + public static class Helpers + { + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.AddRange( + new Blog + { + Name = ".NET Blog", + Posts = + { + new Post + { + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + }, + }, + new Blog + { + Name = "Visual Studio Blog", + Posts = + { + new Post + { + Title = "Disassembly improvements for optimized managed debugging", + Content = "If you are focused on squeezing out the last bits of performance for your .NET service or..." + }, + new Post + { + Title = "Database Profiling with Visual Studio", + Content = "Examine when database queries were executed and measure how long the take using..." + }, + } + }, + new Tag + { + Text = ".NET" + }, + new Tag + { + Text = "Visual Studio" + }, + new Tag + { + Text = "EF Core" + }); + + context.SaveChanges(); + } + } + + public class Blog + { + public int Id { get; set; } + public string Name { get; set; } + + public IList Posts { get; } = new List(); + } + + public class Post + { + public int Id { get; set; } + public string Title { get; set; } + public string Content { get; set; } + + public int? BlogId { get; set; } + public Blog Blog { get; set; } + + public IList Tags { get; } = new List(); // Skip collection navigation + } + + public class Tag + { + public int Id { get; set; } + public string Text { get; set; } + + public IList Posts { get; } = new List(); // Skip collection navigation + } + + #region Model + public class PostTag + { + public int PostId { get; set; } // First part of composite PK; FK to Post + public int TagId { get; set; } // Second part of composite PK; FK to Tag + + public DateTime TaggedOn { get; set; } // Auto-generated payload property + public string TaggedBy { get; set; } // Not-generated payload property + } + #endregion + + public class BlogsContext : DbContext + { + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + public DbSet Tags { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + } + + #region OnModelCreating + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasMany(p => p.Tags) + .WithMany(p => p.Posts) + .UsingEntity( + j => j.HasOne().WithMany(), + j => j.HasOne().WithMany(), + j => j.Property(e => e.TaggedOn).HasDefaultValueSql("CURRENT_TIMESTAMP")); + } + #endregion + + #region SaveChanges + public override int SaveChanges() + { + foreach (var entityEntry in ChangeTracker.Entries()) + { + if (entityEntry.State == EntityState.Added) + { + entityEntry.Entity.TaggedBy = "ajcvickers"; + } + } + + return base.SaveChanges(); + } + #endregion + } +} diff --git a/samples/core/ChangeTracking/ChangingFKsAndNavigations/OptionalRelationshipsSamples.cs b/samples/core/ChangeTracking/ChangingFKsAndNavigations/OptionalRelationshipsSamples.cs new file mode 100644 index 0000000000..9a21497c4b --- /dev/null +++ b/samples/core/ChangeTracking/ChangingFKsAndNavigations/OptionalRelationshipsSamples.cs @@ -0,0 +1,437 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace Optional +{ + public class OptionalRelationshipsSamples + { + public static void Relationship_fixup_1() + { + Console.WriteLine($">>>> Sample: {nameof(Relationship_fixup_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Relationship_fixup_1 + using var context = new BlogsContext(); + + var blogs = context.Blogs + .Include(e => e.Posts) + .Include(e => e.Assets) + .ToList(); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + #endregion + + Console.WriteLine(); + } + + public static void Relationship_fixup_2() + { + Console.WriteLine($">>>> Sample: {nameof(Relationship_fixup_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Relationship_fixup_2 + using var context = new BlogsContext(); + + var blogs = context.Blogs.ToList(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + var assets = context.Assets.ToList(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + var posts = context.Posts.ToList(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + #endregion + + Console.WriteLine(); + } + + public static void Changing_relationships_using_navigations_1() + { + Console.WriteLine($">>>> Sample: {nameof(Changing_relationships_using_navigations_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Changing_relationships_using_navigations_1 + using var context = new BlogsContext(); + + var dotNetBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == ".NET Blog"); + var vsBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == "Visual Studio Blog"); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + var post = vsBlog.Posts.Single(e => e.Title.StartsWith("Disassembly improvements")); + vsBlog.Posts.Remove(post); + dotNetBlog.Posts.Add(post); + + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + #endregion + + Console.WriteLine(); + } + + public static void Changing_relationships_using_navigations_2() + { + Console.WriteLine($">>>> Sample: {nameof(Changing_relationships_using_navigations_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var dotNetBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == ".NET Blog"); + var vsBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == "Visual Studio Blog"); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + #region Changing_relationships_using_navigations_2 + var post = vsBlog.Posts.Single(e => e.Title.StartsWith("Disassembly improvements")); + post.Blog = dotNetBlog; + #endregion + + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine(); + } + + public static void Changing_relationships_using_foreign_key_values_1() + { + Console.WriteLine($">>>> Sample: {nameof(Changing_relationships_using_foreign_key_values_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var dotNetBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == ".NET Blog"); + var vsBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == "Visual Studio Blog"); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + #region Changing_relationships_using_foreign_key_values_1 + var post = vsBlog.Posts.Single(e => e.Title.StartsWith("Disassembly improvements")); + post.BlogId = dotNetBlog.Id; + #endregion + + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine(); + } + + public static void Fixup_for_added_or_deleted_entities_1() + { + Console.WriteLine($">>>> Sample: {nameof(Fixup_for_added_or_deleted_entities_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var dotNetBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == ".NET Blog"); + var vsBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == "Visual Studio Blog"); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + #region Fixup_for_added_or_deleted_entities_1 + var post = vsBlog.Posts.Single(e => e.Title.StartsWith("Disassembly improvements")); + vsBlog.Posts.Remove(post); + dotNetBlog.Posts.Add(post); + #endregion + + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine(); + } + + public static void Fixup_for_added_or_deleted_entities_2() + { + Console.WriteLine($">>>> Sample: {nameof(Fixup_for_added_or_deleted_entities_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var dotNetBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == ".NET Blog"); + var vsBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == "Visual Studio Blog"); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + #region Fixup_for_added_or_deleted_entities_2 + var post = vsBlog.Posts.Single(e => e.Title.StartsWith("Disassembly improvements")); + dotNetBlog.Posts.Add(post); + #endregion + + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine(); + } + + public static void Fixup_for_added_or_deleted_entities_3() + { + Console.WriteLine($">>>> Sample: {nameof(Fixup_for_added_or_deleted_entities_3)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var dotNetBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == ".NET Blog"); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + #region Fixup_for_added_or_deleted_entities_3 + var post = dotNetBlog.Posts.Single(e => e.Title == "Announcing F# 5"); + dotNetBlog.Posts.Remove(post); + #endregion + + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine(); + } + + public static void Fixup_for_added_or_deleted_entities_7() + { + Console.WriteLine($">>>> Sample: {nameof(Fixup_for_added_or_deleted_entities_7)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Fixup_for_added_or_deleted_entities_7 + using var context = new BlogsContext(); + + var dotNetBlog = context.Blogs.Include(e => e.Assets).Single(e => e.Name == ".NET Blog"); + dotNetBlog.Assets = new BlogAssets(); + + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + #endregion + + Console.WriteLine(); + } + + public static void Deleting_an_entity_1() + { + Console.WriteLine($">>>> Sample: {nameof(Deleting_an_entity_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Deleting_an_entity_1 + using var context = new BlogsContext(); + + var vsBlog = context.Blogs + .Include(e => e.Posts) + .Include(e => e.Assets) + .Single(e => e.Name == "Visual Studio Blog"); + + context.Remove(vsBlog); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + #endregion + + Console.WriteLine(); + } + + public static void Many_to_many_relationships_6() + { + Console.WriteLine($">>>> Sample: {nameof(Many_to_many_relationships_6)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Many_to_many_relationships_6 + using var context = new BlogsContext(); + + var post = context.Posts.Single(e => e.Id == 3); + var tag = context.Tags.Single(e => e.Id == 1); + + post.Tags.Add(tag); + + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + #endregion + + Console.WriteLine(); + } + } + + public static class Helpers + { + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.AddRange( + new Blog + { + Name = ".NET Blog", + Assets = new BlogAssets(), + Posts = + { + new Post + { + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + }, + }, + new Blog + { + Name = "Visual Studio Blog", + Assets = new BlogAssets(), + Posts = + { + new Post + { + Title = "Disassembly improvements for optimized managed debugging", + Content = "If you are focused on squeezing out the last bits of performance for your .NET service or..." + }, + new Post + { + Title = "Database Profiling with Visual Studio", + Content = "Examine when database queries were executed and measure how long the take using..." + }, + } + }, + new Tag + { + Text = ".NET" + }, + new Tag + { + Text = "Visual Studio" + }, + new Tag + { + Text = "EF Core" + }); + + context.SaveChanges(); + } + } + + #region Model + public class Blog + { + public int Id { get; set; } // Primary key + public string Name { get; set; } + + public IList Posts { get; } = new List(); // Collection navigation + public BlogAssets Assets { get; set; } // Reference navigation + } + + public class BlogAssets + { + public int Id { get; set; } // Primary key + public byte[] Banner { get; set; } + + public int? BlogId { get; set; } // Foreign key + public Blog Blog { get; set; } // Reference navigation + } + + public class Post + { + public int Id { get; set; } // Primary key + public string Title { get; set; } + public string Content { get; set; } + + public int? BlogId { get; set; } // Foreign key + public Blog Blog { get; set; } // Reference navigation + + public IList Tags { get; } = new List(); // Skip collection navigation + } + + public class Tag + { + public int Id { get; set; } // Primary key + public string Text { get; set; } + + public IList Posts { get; } = new List(); // Skip collection navigation + } + #endregion + + public class BlogsContext : DbContext + { + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + public DbSet Assets { get; set; } + public DbSet Tags { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + } + } +} diff --git a/samples/core/ChangeTracking/ChangingFKsAndNavigations/Program.cs b/samples/core/ChangeTracking/ChangingFKsAndNavigations/Program.cs new file mode 100644 index 0000000000..2abbf78996 --- /dev/null +++ b/samples/core/ChangeTracking/ChangingFKsAndNavigations/Program.cs @@ -0,0 +1,46 @@ +using System; +using JoinEntity; +using JoinEntityWithPayload; +using JoinEntityWithSkips; +using JoinEntityWithStringPayload; +using Optional; +using Required; + +public class Program +{ + public static void Main() + { + Console.WriteLine("Samples for _Changing Foreign Keys and Navigations_"); + Console.WriteLine(); + + OptionalRelationshipsSamples.Relationship_fixup_1(); + OptionalRelationshipsSamples.Relationship_fixup_2(); + + OptionalRelationshipsSamples.Changing_relationships_using_navigations_1(); + OptionalRelationshipsSamples.Changing_relationships_using_navigations_2(); + + OptionalRelationshipsSamples.Changing_relationships_using_foreign_key_values_1(); + + OptionalRelationshipsSamples.Fixup_for_added_or_deleted_entities_1(); + OptionalRelationshipsSamples.Fixup_for_added_or_deleted_entities_2(); + OptionalRelationshipsSamples.Fixup_for_added_or_deleted_entities_3(); + RequiredRelationshipsSamples.Fixup_for_added_or_deleted_entities_4(); + RequiredRelationshipsSamples.Fixup_for_added_or_deleted_entities_5(); + RequiredRelationshipsSamples.Fixup_for_added_or_deleted_entities_6(); + OptionalRelationshipsSamples.Fixup_for_added_or_deleted_entities_7(); + RequiredRelationshipsSamples.Fixup_for_added_or_deleted_entities_8(); + + OptionalRelationshipsSamples.Deleting_an_entity_1(); + RequiredRelationshipsSamples.Deleting_an_entity_2(); + + ExplicitJoinEntitySamples.Many_to_many_relationships_1(); + ExplicitJoinEntitySamples.Many_to_many_relationships_2(); + ExplicitJoinEntityWithSkipsSamples.Many_to_many_relationships_3(); + ExplicitJoinEntityWithSkipsSamples.Many_to_many_relationships_4(); + ExplicitJoinEntityWithSkipsSamples.Many_to_many_relationships_5(); + OptionalRelationshipsSamples.Many_to_many_relationships_6(); + ExplicitJoinEntityWithPayloadSamples.Many_to_many_relationships_7(); + ExplicitJoinEntityWithStringPayloadSamples.Many_to_many_relationships_8(); + ExplicitJoinEntityWithStringPayloadSamples.Many_to_many_relationships_9(); + } +} diff --git a/samples/core/ChangeTracking/ChangingFKsAndNavigations/RequiredRelationshipsSamples.cs b/samples/core/ChangeTracking/ChangingFKsAndNavigations/RequiredRelationshipsSamples.cs new file mode 100644 index 0000000000..9f0597adc7 --- /dev/null +++ b/samples/core/ChangeTracking/ChangingFKsAndNavigations/RequiredRelationshipsSamples.cs @@ -0,0 +1,291 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace Required +{ + public class RequiredRelationshipsSamples + { + public static void Fixup_for_added_or_deleted_entities_4() + { + Console.WriteLine($">>>> Sample: {nameof(Fixup_for_added_or_deleted_entities_4)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var dotNetBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == ".NET Blog"); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + #region Fixup_for_added_or_deleted_entities_4 + var post = dotNetBlog.Posts.Single(e => e.Title == "Announcing F# 5"); + dotNetBlog.Posts.Remove(post); + #endregion + + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + + Console.WriteLine(); + } + + public static void Fixup_for_added_or_deleted_entities_5() + { + Console.WriteLine($">>>> Sample: {nameof(Fixup_for_added_or_deleted_entities_5)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var dotNetBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == ".NET Blog"); + var vsBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == "Visual Studio Blog"); + + #region Fixup_for_added_or_deleted_entities_5 + context.ChangeTracker.DeleteOrphansTiming = CascadeTiming.OnSaveChanges; + + var post = vsBlog.Posts.Single(e => e.Title.StartsWith("Disassembly improvements")); + vsBlog.Posts.Remove(post); + + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + dotNetBlog.Posts.Add(post); + + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + #endregion + + Console.WriteLine(); + } + + public static void Fixup_for_added_or_deleted_entities_6() + { + Console.WriteLine($">>>> Sample: {nameof(Fixup_for_added_or_deleted_entities_6)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + try + { + #region Fixup_for_added_or_deleted_entities_6 + var dotNetBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == ".NET Blog"); + + context.ChangeTracker.DeleteOrphansTiming = CascadeTiming.Never; + + var post = dotNetBlog.Posts.Single(e => e.Title == "Announcing F# 5"); + dotNetBlog.Posts.Remove(post); + + context.SaveChanges(); // Throws + #endregion + } + catch (Exception e) + { + Console.WriteLine($"{e.GetType().FullName}: {e.Message}"); + } + + Console.WriteLine(); + } + + public static void Fixup_for_added_or_deleted_entities_8() + { + Console.WriteLine($">>>> Sample: {nameof(Fixup_for_added_or_deleted_entities_8)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Fixup_for_added_or_deleted_entities_8 + using var context = new BlogsContext(); + + var dotNetBlog = context.Blogs.Include(e => e.Assets).Single(e => e.Name == ".NET Blog"); + dotNetBlog.Assets = new BlogAssets(); + + context.ChangeTracker.DetectChanges(); + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + #endregion + + Console.WriteLine(); + } + + public static void Deleting_an_entity_2() + { + Console.WriteLine($">>>> Sample: {nameof(Deleting_an_entity_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Deleting_an_entity_2 + using var context = new BlogsContext(); + + var vsBlog = context.Blogs + .Include(e => e.Posts) + .Include(e => e.Assets) + .Single(e => e.Name == "Visual Studio Blog"); + + context.Remove(vsBlog); + + Console.WriteLine(context.ChangeTracker.DebugView.LongView); + + context.SaveChanges(); + #endregion + + Console.WriteLine(); + } + } + + public static class Helpers + { + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.AddRange( + new Blog + { + Name = ".NET Blog", + Assets = new BlogAssets(), + Posts = + { + new Post + { + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + }, + }, + new Blog + { + Name = "Visual Studio Blog", + Assets = new BlogAssets(), + Posts = + { + new Post + { + Title = "Disassembly improvements for optimized managed debugging", + Content = "If you are focused on squeezing out the last bits of performance for your .NET service or..." + }, + new Post + { + Title = "Database Profiling with Visual Studio", + Content = "Examine when database queries were executed and measure how long the take using..." + }, + } + }, + new Tag + { + Text = ".NET" + }, + new Tag + { + Text = "Visual Studio" + }, + new Tag + { + Text = "EF Core" + }); + + context.SaveChanges(); + } + } + + #region Model + public class Blog + { + public int Id { get; set; } // Primary key + public string Name { get; set; } + + public IList Posts { get; } = new List(); // Collection navigation + public BlogAssets Assets { get; set; } // Reference navigation + } + + public class BlogAssets + { + public int Id { get; set; } // Primary key + public byte[] Banner { get; set; } + + public int BlogId { get; set; } // Foreign key + public Blog Blog { get; set; } // Reference navigation + } + + public class Post + { + public int Id { get; set; } // Primary key + public string Title { get; set; } + public string Content { get; set; } + + public int BlogId { get; set; } // Foreign key + public Blog Blog { get; set; } // Reference navigation + + public IList Tags { get; } = new List(); // Collection navigation + } + + public class Tag + { + public int Id { get; set; } // Primary key + public string Text { get; set; } + + public IList Posts { get; } = new List(); // Collection navigation + } + #endregion + + public class BlogsContext : DbContext + { + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + public DbSet Assets { get; set; } + public DbSet Tags { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + } + } +} diff --git a/samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionInEFCore.csproj b/samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionInEFCore.csproj new file mode 100644 index 0000000000..e001035c5f --- /dev/null +++ b/samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionInEFCore.csproj @@ -0,0 +1,15 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + diff --git a/samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionSamples.cs b/samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionSamples.cs new file mode 100644 index 0000000000..52c35a3f38 --- /dev/null +++ b/samples/core/ChangeTracking/IdentityResolutionInEFCore/IdentityResolutionSamples.cs @@ -0,0 +1,367 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace Updates +{ + public static class IdentityResolutionSamples + { + public static void Identity_Resolution_in_EF_Core_1() + { + Console.WriteLine($">>>> Sample: {nameof(Identity_Resolution_in_EF_Core_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Identity_Resolution_in_EF_Core_1 + using var context = new BlogsContext(); + + var blogA = context.Blogs.Single(e => e.Id == 1); + var blogB = new Blog { Id = 1, Name = ".NET Blog (All new!)" }; + + try + { + context.Update(blogB); // This will throw + } + catch (Exception e) + { + Console.WriteLine($"{e.GetType().FullName}: {e.Message}"); + } + #endregion + + Console.WriteLine(); + } + + public static void Updating_an_entity_1() + { + Console.WriteLine($">>>> Sample: {nameof(Updating_an_entity_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + UpdateFromHttpPost1( + new Blog + { + Id = 1, + Name = ".NET Blog (All new!)", + Summary = "Posts about .NET" + }); + + Console.WriteLine(); + } + + #region Updating_an_entity_1 + public static void UpdateFromHttpPost1(Blog blog) + { + using var context = new BlogsContext(); + + context.Update(blog); + + context.SaveChanges(); + } + #endregion + + public static void Updating_an_entity_2() + { + Console.WriteLine($">>>> Sample: {nameof(Updating_an_entity_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + UpdateFromHttpPost2( + new Blog + { + Id = 1, + Name = ".NET Blog (All new!)", + Summary = "Posts about .NET" + }); + + Console.WriteLine(); + } + + #region Updating_an_entity_2 + public static void UpdateFromHttpPost2(Blog blog) + { + using var context = new BlogsContext(); + + var trackedBlog = context.Blogs.Find(blog.Id); + + trackedBlog.Name = blog.Name; + trackedBlog.Summary = blog.Summary; + + context.SaveChanges(); + } + #endregion + + public static void Updating_an_entity_3() + { + Console.WriteLine($">>>> Sample: {nameof(Updating_an_entity_3)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + UpdateFromHttpPost3( + new Blog + { + Id = 1, + Name = ".NET Blog (All new!)", + Summary = "Posts about .NET" + }); + + Console.WriteLine(); + } + + #region Updating_an_entity_3 + public static void UpdateFromHttpPost3(Blog blog) + { + using var context = new BlogsContext(); + + var trackedBlog = context.Blogs.Find(blog.Id); + + context.Entry(trackedBlog).CurrentValues.SetValues(blog); + + context.SaveChanges(); + } + #endregion + + public static void Updating_an_entity_4() + { + Console.WriteLine($">>>> Sample: {nameof(Updating_an_entity_4)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + UpdateFromHttpPost4( + new BlogDto + { + Id = 1, + Name = ".NET Blog (All new!)", + Summary = "Posts about .NET" + }); + + Console.WriteLine(); + } + + #region Updating_an_entity_4 + public static void UpdateFromHttpPost4(BlogDto dto) + { + using var context = new BlogsContext(); + + var trackedBlog = context.Blogs.Find(dto.Id); + + context.Entry(trackedBlog).CurrentValues.SetValues(dto); + + context.SaveChanges(); + } + #endregion + + public static void Updating_an_entity_5() + { + Console.WriteLine($">>>> Sample: {nameof(Updating_an_entity_5)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + UpdateFromHttpPost5( + new Dictionary + { + ["Id"] = 1, + ["Name"] = ".NET Blog (All new!)", + ["Summary"] = "Posts about .NET" + }); + + Console.WriteLine(); + } + + #region Updating_an_entity_5 + public static void UpdateFromHttpPost5(Dictionary propertyValues) + { + using var context = new BlogsContext(); + + var trackedBlog = context.Blogs.Find(propertyValues["Id"]); + + context.Entry(trackedBlog).CurrentValues.SetValues(propertyValues); + + context.SaveChanges(); + } + #endregion + + public static void Updating_an_entity_6() + { + Console.WriteLine($">>>> Sample: {nameof(Updating_an_entity_6)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + UpdateFromHttpPost6( + new Blog + { + Id = 1, + Name = ".NET Blog (All new!)", + Summary = "Posts about .NET" + }, + new Dictionary + { + ["Id"] = 1, + ["Name"] = ".NET Blog", + ["Summary"] = "Posts about .NET" + }); + + Console.WriteLine(); + } + + #region Updating_an_entity_6 + public static void UpdateFromHttpPost6(Blog blog, Dictionary originalValues) + { + using var context = new BlogsContext(); + + context.Attach(blog); + context.Entry(blog).OriginalValues.SetValues(originalValues); + + context.SaveChanges(); + } + #endregion + + public static void Failing_to_set_key_values_1() + { + Console.WriteLine($">>>> Sample: {nameof(Failing_to_set_key_values_1)}"); + Console.WriteLine(); + + #region Failing_to_set_key_values_1 + using var context = new BlogsContext(); + + context.Add(new Pet { Name = "Smokey" }); + + try + { + context.Add(new Pet { Name = "Clippy" }); // This will throw + } + catch (Exception e) + { + Console.WriteLine($"{e.GetType().FullName}: {e.Message}"); + } + #endregion + } + } + + public static class Helpers + { + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Add( + new Blog + { + Name = ".NET Blog", + Posts = + { + new Post + { + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + } + }); + + context.SaveChanges(); + } + } + + #region Pet + public class Pet + { + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public int Id { get; set; } + + public string Name { get; set; } + } + #endregion + + public class Customer + { + #region OrdersCollection + public ICollection Orders { get; set; } + = new HashSet(ReferenceEqualityComparer.Instance); + #endregion + } + + public class Order + { + } + + public class BlogDto + { + public int Id { get; set; } + public string Name { get; set; } + public string Summary { get; set; } + } + + public class Blog + { + public int Id { get; set; } + public string Name { get; set; } + public string Summary { get; set; } + + public IList Posts { get; } = new List(); + } + + public class Post + { + public int Id { get; set; } + public string Title { get; set; } + public string Content { get; set; } + + public int? BlogId { get; set; } + public Blog Blog { get; set; } + } + + public class BlogsContext : DbContext + { + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + public DbSet Pets { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + } + } +} diff --git a/samples/core/ChangeTracking/IdentityResolutionInEFCore/Program.cs b/samples/core/ChangeTracking/IdentityResolutionInEFCore/Program.cs new file mode 100644 index 0000000000..26ca274cba --- /dev/null +++ b/samples/core/ChangeTracking/IdentityResolutionInEFCore/Program.cs @@ -0,0 +1,28 @@ +using System; +using Graphs; +using Updates; + +public class Program +{ + public static void Main() + { + Console.WriteLine("Samples for _Identity Resolution in EF Core_"); + Console.WriteLine(); + + IdentityResolutionSamples.Identity_Resolution_in_EF_Core_1(); + IdentityResolutionSamples.Updating_an_entity_1(); + IdentityResolutionSamples.Updating_an_entity_2(); + IdentityResolutionSamples.Updating_an_entity_3(); + IdentityResolutionSamples.Updating_an_entity_4(); + IdentityResolutionSamples.Updating_an_entity_5(); + IdentityResolutionSamples.Updating_an_entity_6(); + + SerializedGraphExamples.Attaching_a_serialized_graph_1(); + SerializedGraphExamples.Attaching_a_serialized_graph_2(); + SerializedGraphExamples.Attaching_a_serialized_graph_3(); + SerializedGraphExamples.Attaching_a_serialized_graph_4(); + SerializedGraphExamples.Attaching_a_serialized_graph_5(); + + IdentityResolutionSamples.Failing_to_set_key_values_1(); + } +} diff --git a/samples/core/ChangeTracking/IdentityResolutionInEFCore/ReferenceEqualityComparer.cs b/samples/core/ChangeTracking/IdentityResolutionInEFCore/ReferenceEqualityComparer.cs new file mode 100644 index 0000000000..9a8ba9f62c --- /dev/null +++ b/samples/core/ChangeTracking/IdentityResolutionInEFCore/ReferenceEqualityComparer.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +#region ReferenceEqualityComparer +public sealed class ReferenceEqualityComparer : IEqualityComparer +{ + private ReferenceEqualityComparer() + { + } + + public static ReferenceEqualityComparer Instance { get; } = new ReferenceEqualityComparer(); + + bool IEqualityComparer.Equals(object x, object y) => x == y; + + int IEqualityComparer.GetHashCode(object obj) => RuntimeHelpers.GetHashCode(obj); +} +#endregion diff --git a/samples/core/ChangeTracking/IdentityResolutionInEFCore/SerializedGraphExamples.cs b/samples/core/ChangeTracking/IdentityResolutionInEFCore/SerializedGraphExamples.cs new file mode 100644 index 0000000000..8420cba23f --- /dev/null +++ b/samples/core/ChangeTracking/IdentityResolutionInEFCore/SerializedGraphExamples.cs @@ -0,0 +1,337 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Newtonsoft.Json; +using JsonSerializer = System.Text.Json.JsonSerializer; + +namespace Graphs +{ + public static class SerializedGraphExamples + { + public static void Attaching_a_serialized_graph_1() + { + Console.WriteLine($">>>> Sample: {nameof(Attaching_a_serialized_graph_1)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Attaching_a_serialized_graph_1a + using var context = new BlogsContext(); + + var blogs = context.Blogs.Include(e => e.Posts).ToList(); + + var serialized = JsonConvert.SerializeObject( + blogs, + new JsonSerializerSettings + { + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + Formatting = Formatting.Indented + }); + + Console.WriteLine(serialized); + #endregion + + UpdateBlogsFromJson(serialized); + } + + #region Attaching_a_serialized_graph_1b + public static void UpdateBlogsFromJson(string json) + { + using var context = new BlogsContext(); + + var blogs = JsonConvert.DeserializeObject>(json); + + foreach (var blog in blogs) + { + context.Update(blog); + } + + context.SaveChanges(); + } + #endregion + + public static void Attaching_a_serialized_graph_2() + { + Console.WriteLine($">>>> Sample: {nameof(Attaching_a_serialized_graph_2)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + #region Attaching_a_serialized_graph_2 + using var context = new BlogsContext(); + + var posts = context.Posts.Include(e => e.Blog).ToList(); + + var serialized = JsonConvert.SerializeObject( + posts, + new JsonSerializerSettings + { + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + Formatting = Formatting.Indented + }); + + Console.WriteLine(serialized); + #endregion + + UpdatePostsFromJsonBad(serialized); + } + + public static void UpdatePostsFromJsonBad(string json) + { + using var context = new BlogsContext(); + + var posts = JsonConvert.DeserializeObject>(json); + + try + { + foreach (var post in posts) + { + context.Update(post); // Will throw + } + + context.SaveChanges(); + } + catch (Exception e) + { + Console.WriteLine($"{e.GetType().FullName}: {e.Message}"); + } + } + + public static void Attaching_a_serialized_graph_3() + { + Console.WriteLine($">>>> Sample: {nameof(Attaching_a_serialized_graph_3)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var posts = context.Posts.Include(e => e.Blog).ToList(); + + #region Attaching_a_serialized_graph_3 + var serialized = JsonConvert.SerializeObject( + posts, + new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.All, + Formatting = Formatting.Indented + }); + #endregion + + Console.WriteLine(serialized); + + UpdatePostsFromJson(serialized); + } + + public static void UpdatePostsFromJson(string json) + { + using var context = new BlogsContext(); + + var posts = JsonConvert.DeserializeObject>(json); + + foreach (var post in posts) + { + context.Update(post); + } + + context.SaveChanges(); + } + + public static void Attaching_a_serialized_graph_4() + { + Console.WriteLine($">>>> Sample: {nameof(Attaching_a_serialized_graph_4)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var posts = context.Posts.Include(e => e.Blog).ToList(); + + #region Attaching_a_serialized_graph_4 + var serialized = JsonSerializer.Serialize( + posts, new JsonSerializerOptions + { + ReferenceHandler = ReferenceHandler.Preserve, + WriteIndented = true + }); + #endregion + + Console.WriteLine(serialized); + + UpdatePostsFromJson(serialized); + } + + public static void Attaching_a_serialized_graph_5() + { + Console.WriteLine($">>>> Sample: {nameof(Attaching_a_serialized_graph_4)}"); + Console.WriteLine(); + + Helpers.RecreateCleanDatabase(); + Helpers.PopulateDatabase(); + + using var context = new BlogsContext(); + + var posts = context.Posts.Include(e => e.Blog).ToList(); + + var serialized = JsonConvert.SerializeObject( + posts, + new JsonSerializerSettings + { + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + Formatting = Formatting.Indented + }); + + Console.WriteLine(serialized); + + Console.WriteLine() + ; + UpdatePostsFromJsonWithIdentityResolution(serialized); + } + + #region Attaching_a_serialized_graph_5 + public static void UpdatePostsFromJsonWithIdentityResolution(string json) + { + using var context = new BlogsContext(); + + var posts = JsonConvert.DeserializeObject>(json); + + foreach (var post in posts) + { + context.ChangeTracker.TrackGraph( + post, node => + { + var keyValue = node.Entry.Property("Id").CurrentValue; + var entityType = node.Entry.Metadata; + + var existingEntity = node.Entry.Context.ChangeTracker.Entries() + .FirstOrDefault( + e => Equals(e.Metadata, entityType) + && Equals(e.Property("Id").CurrentValue, keyValue)); + + if (existingEntity == null) + { + Console.WriteLine($"Tracking {entityType.DisplayName()} entity with key value {keyValue}"); + + node.Entry.State = EntityState.Modified; + } + else + { + Console.WriteLine($"Discarding duplicate {entityType.DisplayName()} entity with key value {keyValue}"); + } + }); + } + + context.SaveChanges(); + } + #endregion + } + + public static class Helpers + { + public static void RecreateCleanDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } + + public static void PopulateDatabase() + { + using var context = new BlogsContext(quiet: true); + + context.AddRange( + new Blog + { + Name = ".NET Blog", + Summary = "Posts about .NET", + Posts = + { + new Post + { + Title = "Announcing the Release of EF Core 5.0", + Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..." + }, + new Post + { + Title = "Announcing F# 5", + Content = "F# 5 is the latest version of F#, the functional programming language..." + }, + }, + }, + new Blog + { + Name = "Visual Studio Blog", + Summary = "Posts about Visual Studio", + Posts = + { + new Post + { + Title = "Disassembly improvements for optimized managed debugging", + Content = "If you are focused on squeezing out the last bits of performance for your .NET service or..." + }, + new Post + { + Title = "Database Profiling with Visual Studio", + Content = "Examine when database queries were executed and measure how long the take using..." + }, + } + }); + + context.SaveChanges(); + } + } + + public class Blog + { + public int Id { get; set; } + public string Name { get; set; } + public string Summary { get; set; } + + public IList Posts { get; } = new List(); + } + + public class Post + { + public int Id { get; set; } + public string Title { get; set; } + public string Content { get; set; } + + public int? BlogId { get; set; } + public Blog Blog { get; set; } + } + + public class BlogsContext : DbContext + { + private readonly bool _quiet; + + public BlogsContext(bool quiet = false) + { + _quiet = quiet; + } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .EnableSensitiveDataLogging() + .UseSqlite("DataSource=test.db"); + + if (!_quiet) + { + optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted }); + } + } + } +} diff --git a/samples/core/Samples.sln b/samples/core/Samples.sln index e178afc449..8d8f1df197 100644 --- a/samples/core/Samples.sln +++ b/samples/core/Samples.sln @@ -133,6 +133,22 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Benchmarks\Be EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Performance", "Performance\Performance.csproj", "{A870AAA4-EAF1-4F7D-A71E-83B20219EF6C}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ChangeTracking", "ChangeTracking", "{F55D545B-2B45-47A5-8C55-39F972528400}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChangeTrackingInEFCore", "ChangeTracking\ChangeTrackingInEFCore\ChangeTrackingInEFCore.csproj", "{81D6316C-B612-418E-9DA1-013A9D3881BF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AccessingTrackedEntities", "ChangeTracking\AccessingTrackedEntities\AccessingTrackedEntities.csproj", "{0EBD2D46-533A-4DEA-A37A-453908975B9D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChangingFKsAndNavigations", "ChangeTracking\ChangingFKsAndNavigations\ChangingFKsAndNavigations.csproj", "{45C2F9E0-76D3-4C4A-8B3F-9465393D4155}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChangeDetectionAndNotifications", "ChangeTracking\ChangeDetectionAndNotifications\ChangeDetectionAndNotifications.csproj", "{0D3C4BCC-5F19-4864-A794-2823DAD22917}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IdentityResolutionInEFCore", "ChangeTracking\IdentityResolutionInEFCore\IdentityResolutionInEFCore.csproj", "{6BC461D1-C015-4350-9DA8-F2468D71F718}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdditionalChangeTrackingFeatures", "ChangeTracking\AdditionalChangeTrackingFeatures\AdditionalChangeTrackingFeatures.csproj", "{B696F0AC-5282-4E95-9043-A177E3A9C5C0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChangeTrackerDebugging", "ChangeTracking\ChangeTrackerDebugging\ChangeTrackerDebugging.csproj", "{D1AB173F-582D-43C3-9EC0-20CE9FECFEB6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -363,6 +379,34 @@ Global {A870AAA4-EAF1-4F7D-A71E-83B20219EF6C}.Debug|Any CPU.Build.0 = Debug|Any CPU {A870AAA4-EAF1-4F7D-A71E-83B20219EF6C}.Release|Any CPU.ActiveCfg = Release|Any CPU {A870AAA4-EAF1-4F7D-A71E-83B20219EF6C}.Release|Any CPU.Build.0 = Release|Any CPU + {81D6316C-B612-418E-9DA1-013A9D3881BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {81D6316C-B612-418E-9DA1-013A9D3881BF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {81D6316C-B612-418E-9DA1-013A9D3881BF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {81D6316C-B612-418E-9DA1-013A9D3881BF}.Release|Any CPU.Build.0 = Release|Any CPU + {0EBD2D46-533A-4DEA-A37A-453908975B9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0EBD2D46-533A-4DEA-A37A-453908975B9D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0EBD2D46-533A-4DEA-A37A-453908975B9D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0EBD2D46-533A-4DEA-A37A-453908975B9D}.Release|Any CPU.Build.0 = Release|Any CPU + {45C2F9E0-76D3-4C4A-8B3F-9465393D4155}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {45C2F9E0-76D3-4C4A-8B3F-9465393D4155}.Debug|Any CPU.Build.0 = Debug|Any CPU + {45C2F9E0-76D3-4C4A-8B3F-9465393D4155}.Release|Any CPU.ActiveCfg = Release|Any CPU + {45C2F9E0-76D3-4C4A-8B3F-9465393D4155}.Release|Any CPU.Build.0 = Release|Any CPU + {0D3C4BCC-5F19-4864-A794-2823DAD22917}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0D3C4BCC-5F19-4864-A794-2823DAD22917}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D3C4BCC-5F19-4864-A794-2823DAD22917}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0D3C4BCC-5F19-4864-A794-2823DAD22917}.Release|Any CPU.Build.0 = Release|Any CPU + {6BC461D1-C015-4350-9DA8-F2468D71F718}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BC461D1-C015-4350-9DA8-F2468D71F718}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BC461D1-C015-4350-9DA8-F2468D71F718}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BC461D1-C015-4350-9DA8-F2468D71F718}.Release|Any CPU.Build.0 = Release|Any CPU + {B696F0AC-5282-4E95-9043-A177E3A9C5C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B696F0AC-5282-4E95-9043-A177E3A9C5C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B696F0AC-5282-4E95-9043-A177E3A9C5C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B696F0AC-5282-4E95-9043-A177E3A9C5C0}.Release|Any CPU.Build.0 = Release|Any CPU + {D1AB173F-582D-43C3-9EC0-20CE9FECFEB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1AB173F-582D-43C3-9EC0-20CE9FECFEB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1AB173F-582D-43C3-9EC0-20CE9FECFEB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1AB173F-582D-43C3-9EC0-20CE9FECFEB6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -418,6 +462,13 @@ Global {AF719729-AED8-4DEB-B895-61D8EBB50A01} = {85AFD7F1-6943-40FE-B8EC-AA9DBB42CCA6} {73503DF2-CD85-4710-BE94-B83B87054709} = {85AFD7F1-6943-40FE-B8EC-AA9DBB42CCA6} {11AB574E-5DA9-4C37-A342-41B3DB5D7C0D} = {1AD64707-0BE0-48B0-A803-916FF96DCB4F} + {81D6316C-B612-418E-9DA1-013A9D3881BF} = {F55D545B-2B45-47A5-8C55-39F972528400} + {0EBD2D46-533A-4DEA-A37A-453908975B9D} = {F55D545B-2B45-47A5-8C55-39F972528400} + {45C2F9E0-76D3-4C4A-8B3F-9465393D4155} = {F55D545B-2B45-47A5-8C55-39F972528400} + {0D3C4BCC-5F19-4864-A794-2823DAD22917} = {F55D545B-2B45-47A5-8C55-39F972528400} + {6BC461D1-C015-4350-9DA8-F2468D71F718} = {F55D545B-2B45-47A5-8C55-39F972528400} + {B696F0AC-5282-4E95-9043-A177E3A9C5C0} = {F55D545B-2B45-47A5-8C55-39F972528400} + {D1AB173F-582D-43C3-9EC0-20CE9FECFEB6} = {F55D545B-2B45-47A5-8C55-39F972528400} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {20C98D35-54EF-46A6-8F3B-1855C1AE4F70}