From cb0b7233eafcb87c1d6c4f13b5c2868074e8521c Mon Sep 17 00:00:00 2001 From: Luke Ni Date: Thu, 30 Dec 2021 15:53:09 +0800 Subject: [PATCH] translate to zh-Hans --- docs/zh-Hans/Tutorials/Part-7.md | 239 +++++++++++++++++- .../bookstore-efcore-migration-authors.png | Bin 0 -> 7937 bytes 2 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 docs/zh-Hans/Tutorials/images/bookstore-efcore-migration-authors.png diff --git a/docs/zh-Hans/Tutorials/Part-7.md b/docs/zh-Hans/Tutorials/Part-7.md index 602306c484..95b016e451 100644 --- a/docs/zh-Hans/Tutorials/Part-7.md +++ b/docs/zh-Hans/Tutorials/Part-7.md @@ -1 +1,238 @@ -TODO.. \ No newline at end of file +# Web应用程序开发教程 - 第七章: 数据库集成 +````json +//[doc-params] +{ + "UI": ["MVC","Blazor","BlazorServer","NG"], + "DB": ["EF","Mongo"] +} +```` +## 关于本教程 + +在本系列教程中, 你将构建一个名为 `Acme.BookStore` 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发的: + +* **{{DB_Text}}** 做为ORM提供程序. +* **{{UI_Value}}** 做为UI框架. + +本教程分为以下部分: + +- [Part 1: 创建服务端](Part-1.md) +- [Part 2: 图书列表页面](Part-2.md) +- [Part 3: 创建,更新和删除图书](Part-2.md) +- [Part 4: 集成测试](Part-4.md) +- [Part 5: 授权](Part-5.md) +- [Part 6: 作者: 领域层](Part-6.md) +- **Part 7: 数据库集成**(本章) +- [Part 8: 作者: 应用服务层](Part-8.md) +- [Part 9: 作者: 用户页面](Part-9.md) +- [Part 10: 图书到作者的关系](Part-10.md) + +## 下载源码 + +本教程根据你的**UI** 和 **数据库**偏好有多个版本,我们准备了几种可供下载的源码组合: + +* [MVC (Razor Pages) UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Blazor UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore) +* [Angular UI 与 MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) + +> 如果你在Windows中遇到 "文件名太长" or "解压错误", 很可能与Windows最大文件路径限制有关. Windows文件路径的最大长度为250字符. 为了解决这个问题,参阅 [在Windows 10中启用长路径](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later). + +> 如果你遇到与Git相关的长路径错误, 尝试使用下面的命令在Windows中启用长路径. 参阅 https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path +> `git config --system core.longpaths true` + +## 简介 + +这章阐述如何为前一章介绍的 `作者` 实体配置数据库集成. + +{{if DB=="EF"}} + +## DB Context + +打开 `Acme.BookStore.EntityFrameworkCore` 项目中的 `BookStoreDbContext` 加入 `DbSet` 属性: + +````csharp +public DbSet Authors { get; set; } +```` + +定位到相同项目中的 `BookStoreDbContext` 类中的 `OnModelCreating` 方法, 加入以下代码到方法的结尾: + +````csharp +builder.Entity(b => +{ + b.ToTable(BookStoreConsts.DbTablePrefix + "Authors", + BookStoreConsts.DbSchema); + + b.ConfigureByConvention(); + + b.Property(x => x.Name) + .IsRequired() + .HasMaxLength(AuthorConsts.MaxNameLength); + + b.HasIndex(x => x.Name); +}); +```` + +这和前面的 `Book` 实体做的一样, 所以不再赘述. + +## 创建数据库迁移 + +配置启动解决方案为使用 [Entity Framework Core Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/). 因为我们还没有修改数据库映射配置,所以需要创建一个新的迁移并对数据库应用变更. + +打开命令行终端, 切换当前目录为 `Acme.BookStore.EntityFrameworkCore` 项目目录, 输入以下命令: + +````bash +dotnet ef migrations add Added_Authors +```` + +这会在项目中添加一个迁移类: + +![bookstore-efcore-migration-authors](./images/bookstore-efcore-migration-authors.png) + +你可以在同一个命令行终端中使用以下命令对数据库应用更改: + +````bash +dotnet ef database update +```` + +> 如果你使用 Visual Studio, 可能希望在 *Package Manager Console (PMC)* 使用 `Add-Migration Added_Authors -c BookStoreMigrationsDbContext` 和 `Update-Database -c BookStoreMigrationsDbContext` 命令. 如果这样, 保证 {{if UI=="MVC"}}`Acme.BookStore.Web`{{else if UI=="BlazorServer"}}`Acme.BookStore.Blazor`{{else if UI=="Blazor" || UI=="NG"}}`Acme.BookStore.HttpApi.Host`{{end}} 是启动项目并且在PMC中 `Acme.BookStore.EntityFrameworkCore` 是 *默认项目* . + +{{else if DB=="Mongo"}} + +## DB Context + +打开 `Acme.BookStore.MongoDB` 项目 `MongoDb 文件夹`中的 `BookStoreMongoDbContext`, 在类中加入以下属性: + +````csharp +public IMongoCollection Authors => Collection(); +```` + +{{end}} + +## 实现 IAuthorRepository + +{{if DB=="EF"}} + +在 `Acme.BookStore.EntityFrameworkCore` 项目 (`Authors` 文件夹)中创建一个新类 `EfCoreAuthorRepository`, 粘贴以下代码: + +````csharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading.Tasks; +using Acme.BookStore.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace Acme.BookStore.Authors +{ + public class EfCoreAuthorRepository + : EfCoreRepository, + IAuthorRepository + { + public EfCoreAuthorRepository( + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public async Task FindByNameAsync(string name) + { + var dbSet = await GetDbSetAsync(); + return await dbSet.FirstOrDefaultAsync(author => author.Name == name); + } + + public async Task> GetListAsync( + int skipCount, + int maxResultCount, + string sorting, + string filter = null) + { + var dbSet = await GetDbSetAsync(); + return await dbSet + .WhereIf( + !filter.IsNullOrWhiteSpace(), + author => author.Name.Contains(filter) + ) + .OrderBy(sorting) + .Skip(skipCount) + .Take(maxResultCount) + .ToListAsync(); + } + } +} +```` + +* 继承自 `EfCoreRepository`, 所以继承了标准repository的方法实现. +* `WhereIf` 是ABP 框架的快捷扩展方法. 它仅当第一个条件满足时, 执行 `Where` 查询. (根据名字查询, 仅当 filter 不为空). 你可以不使用这个方法, 但这些快捷方法可以提高效率. +* `sorting` 可以是一个字符串, 如 `Name`, `Name ASC` 或 `Name DESC`. 通过使用 [System.Linq.Dynamic.Core](https://www.nuget.org/packages/System.Linq.Dynamic.Core) NuGet 包是可能的. + +> 参阅 [EF Core 集成文档](../Entity-Framework-Core.md) 获得基于EF Core的repositories的更多信息. + +{{else if DB=="Mongo"}} + +在 `Acme.BookStore.MongoDB` 项目 (`Authors` 文件夹)中创建一个新类 `MongoDbAuthorRepository`, 粘贴以下代码: + +```csharp +using System; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Collections.Generic; +using System.Threading.Tasks; +using Acme.BookStore.MongoDB; +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using Volo.Abp.Domain.Repositories.MongoDB; +using Volo.Abp.MongoDB; + +namespace Acme.BookStore.Authors +{ + public class MongoDbAuthorRepository + : MongoDbRepository, + IAuthorRepository + { + public MongoDbAuthorRepository( + IMongoDbContextProvider dbContextProvider + ) : base(dbContextProvider) + { + } + + public async Task FindByNameAsync(string name) + { + var queryable = await GetMongoQueryableAsync(); + return await queryable.FirstOrDefaultAsync(author => author.Name == name); + } + + public async Task> GetListAsync( + int skipCount, + int maxResultCount, + string sorting, + string filter = null) + { + var queryable = await GetMongoQueryableAsync(); + return await queryable + .WhereIf>( + !filter.IsNullOrWhiteSpace(), + author => author.Name.Contains(filter) + ) + .OrderBy(sorting) + .As>() + .Skip(skipCount) + .Take(maxResultCount) + .ToListAsync(); + } + } +} +``` + +* 继承自 `MongoDbRepository`, 所以继承了标准repository的方法实现. +* `WhereIf` 是ABP 框架的快捷扩展方法. 它仅当第一个条件满足时, 执行 `Where` 查询. (根据名字查询, 仅当 filter 不为空). 你可以不使用这个方法, 但这些快捷方法可以提高效率. +* `sorting` 可以是一个字符串, 如 `Name`, `Name ASC` 或 `Name DESC`. 通过使用 [System.Linq.Dynamic.Core](https://www.nuget.org/packages/System.Linq.Dynamic.Core) NuGet 包是可能的. + +> 参阅 [MongoDB 集成文档](../MongoDB.md) 获得基于MongoDB的repositories的更多信息. + +{{end}} + +## 下一章 + +查看本教程的[下一章](Part-8.md). diff --git a/docs/zh-Hans/Tutorials/images/bookstore-efcore-migration-authors.png b/docs/zh-Hans/Tutorials/images/bookstore-efcore-migration-authors.png new file mode 100644 index 0000000000000000000000000000000000000000..2cfb29f86f8d2d54e6cfbfe85f973525179ec51d GIT binary patch literal 7937 zcmaKxcQ~8<`}d=|P&BAfMWt%gDtenGRin0|HEM*|wQ383yJ%yys2Z)ks%nNxY*ne* zQZ!bK)?T4PkVN81@9*(^pX0cX-|zY3n#XZ{u5rH4*ZKaOSHgXhd#uctnE?O*tG=GL zIRL;QN8d-EKTBVSQ#|?T8-_shdv^gfLs!=62dCXNjWq!PWD3i%(;50P(-S?LKmdTP z`_IDA>-XLT01(X7*VcRx=77(m)(Ub$X92vJQJt-l6b%Ey(PD~~Y{wC_krWE9*$!Vgb56kGa&@0n4ghd<$_%I$zVAVt zi`Y@wRXrLz@&7JYHp2X^o(TYWRfG{X1Xh?M!04p&O6``3D{UBzgtcA~fUDs9KX57!La3nxy;?oD6*3?he}H0(T3 z;(!}CHAXDrSHIPlrM99qPDmTo{PqQYb7AG-;b#uRWYf5iLdSSFVh^~TkBKkOLUnmC zh=p5@LnH}KIe0N=($eq07~TkXo;hv96@H)GuWD*%&S;N&w}*Z#x1Pfr_y!8La)00M zdgvwA=fD#h?RQq_Vq}V|N~)LyRLau4uj=C!I4ERL8r{_CEVu=x9_-ANE1WbANO7u* z^T_R*D|%dVgUicC)SDs;Diwar)l(WdOUW3?y5`ET?|Vi@iQ`$c;{>x)x*ou5BgI%A zO<3IR%o+GjLSKI8Zq%J7Q@Ovoh@-h@_0$j@n_!}Ho_6Y>zTA@OJp(k#FZ{l05zyAw zrU17wn65}g&dcoi|AeIXC^q3Wa5!A27exV;Vn)Wza%V+0L@w0(X>hJk=Vj**qOwUu z3-%>k2E6bdbG^Ea?@+sF>p0wVFr{s79tbDEzO@Gv7rkWprh8H&TC&#Rb;EZLYIhv? zuRESj04DJ=@$Rn0->c*~p?=xG&UZYrKT2W2_V=2!1Ppf&y@Q9f4nIbNLkS&y`K@Lz zp6F`^!d}FzV{O!)G&$@gCk!?-VHh>pr&VQLQ_f{xxtTk`rl?f+nIU(mWbtUshPgoy zV?R&9`A{l1U#?v%M;E8HhAhlHPWQPeb*06ATcO+woY0g9JA6c58QqZUA;O5m_~h2P zU+o#rZ-)LFn?6tZj5<(l2kE9hfuGbyD;s<|F9D?-?$ipZoFF)B8?1&c#b`^~!Q|#K zEF97?oI&Yn^n1{2BItEu{uWh^P7Ru4Qw~j6({w84&s8~8{H!k`mSY29lj(jQ@^po8 zIY;RN&}~x5Mq^*tPfbd#CZ!?>rlDbq(5G~9Wja>{y?tMOG8MA5_apep`U`L^R#E`{ zA#io}`O5pv+1$+tuc_4J$Ja)9YS0sWB2bGokS?x=XD+%+^QkWE>jbJ}1@i(%nviXk zAIN|1AQ}t_rYh*^0GGTaEm_TCw?ks&Wvu@Gz@{Acb&Q*YG=M z*Q5Jjp0%(}gWX7_iM-dwdig!Rw8o0TN3A$Ijh)eFu-=*Mk+EJm<~~e%-{FcHuvrg~ z!c2gDO!Fy=N2(W1ti3vwQ-;v!R_V(V0#WVM<~FoUt&MTL0169dRNu+3VKL;9W2*iv zbG&f(BgZxWbV<2KZv<3>A{-6b0ne<&g1#!HTY26BZ1Lx-gvORN6j^Dsu+9zKx3`b> zaPXKP;NcezlI9dI)~BZ1d{zT(_$;?}3G zSl>pSqR_SH)d61m-dDN?=OZ#g49iej03+8jI-dbaXa7+8@^Q&UI=k;+0013EAR~aw z7zsilk-r?oSN(3SXOC9Z17g|@>B@oX)XJ|bySzRt%;?x|oq)(w_AhQBv`k>_i7 ztdpW*evM*nv>la@)%kd;Wk1y>BqOYIu39F~X3HR8@$2$qFQyQNN$DzYwProx99igW zshe4A2Px=5NQRUl1~9o=0WR!!Y^HKAoJY3hRC%qiKLlVwWqQf>`vjkH& zLLC(Lovk{&Yj@*_u9T6CUmR;9wN-8|l-fcD_G)i=tXKmEb9`o?f)92U4m($F&tbd9Sn zwXbjJ#(4tXnSWJ4#u_grnMM)P(>y%QO5`78Zg{ALgDAH+V%&<8GMKeEXFi1bHUK;R z4nCz>r269OTbRYxUr&2ztSDTm7sKho)0QA;;F;uwRQ2QE-PlI2iQN0}-(BZMJ|x8_ z6W(9QC{ul4H9dOw=V%<@0+2CU#?mi#Q(Ut@aq@ITthSU)_xH!CajZD~`y7%$WOFBH z$wO@8qu1gN;9VMJrxlwibI95yKl0$2 zuh^nI2jN;4`zK%LW%h&eLY=Q1#I%iiaTC_T06;|Sf==?8 zEj37UD|tRtFd+uPP=}`LR2tON;b5yL6F%djZu<<+TA}pL5Gmql$>z zOD4bOy)f*L8F;$~Q0am3-bm0mQ+$%3@ZE(fjG>C1Vs#Mt19yH;M>Pvi_iSk z`Pk0K1eYQ*c$r4}PpTeO?Nxw8(dindr!~BXY=3bKS1J1$e5HQx5EmOtSG>qB6cYo= zPmp%NV3_qzGbG1#GgyET zu2In@2U1j8>CiBpa-@#~7&g%M2Fe{!tP>H8CErp$P5q)r;A7U0vy zqyi|jyIzHd{xw=-q1sjKm2>ic(?7kM_+2`CuAcc@SLN!pKd?(fY4<)jA##vuSC7?REJm$8ZnPy2;^5#TEW?%1 z8yA!(add#FSoLvBLM=}B)zHn+>yS{jsm;;0wi-xcXWVy9$F4shxgzWgY$$$M8iSC| zdQjlv{ns0|Px99P30;C~l66j01xv6NPQ9S!^RHX3viWR#sOTW!w{0@HLG3S8;mY$h z$(zH!U`^^aCO!M*A~cSj>dz_(RMIj!yn5bE&>O{sM!(Jup)xxnUYN}?25eZt>i+p-p?$;OC>vHJN=V^|uAxv+4)rFF9Ksjno z6Vy(2(=TuJw9V1VD5|9}@rq*_&Qs?Llir#Z&JAbMLo;@7?y6n;sDGtus0m19^I2tH zke+XJ$3vEEF7Cl+E)Vzrv;Zf>`6*G<3U%It85F6u~avjD!*6Y5-mxjIy=Fy_1Gq z6%SEs4vhBpGs~+RkMbAH^Yaa{y;U=KN<>FwLNp=OJH;y9yswWNuBOt#+A0gUC>dP` z&iImO-K1W`1U1dy&J~sLa}9vau$9GErq`L=u&1FmTK-dY#V*)`r+6fi$D3=ss1SzD zTxw?LnWRX)rRC|Hc*G|29epIi|xh2hs4!Bs!2KYn}*TonV z&YD-?F9IB4|6j^nkUL@IRV;Uk<4qn$|k6yts4|%14K!m6!*z4&!=FZK27hJi*%_4my#Mw3U=0>_l>z&|0#n z>!^BwS*ha!E=s#l*cI+}Au!NFgudnmszO)AC;BPE-{uh?RE=pot&v+UbgnA9#og$x zb@Ad%i2O$tMUxJc5VKQ@V4lJfr=^SIYz(3~!yJ7oF<$5OC0p&yt|*T&uy>X}K_W%y z=umw!3%rNGvk1ldcs0FZbQBOb!SodCc$-<{z;sec7+~H(r*c@T-!Db)FaWkLd_hc_ zltib*SMvi}8R`5g^tcKI#1zx%1xWmliTc;ccj7=b{{`*f<%n9-#HU3nL2IcIe|gS# zz_-sK_lI@GUwj8=US|{1@Sdg*FQt>f+bfd8kzy3^^V8oOM-_|MsY>_Cq|O#=`{N@h z@z^xK_9e9o`S^rJ#q!VyELe+^qX)rjeMSFFhi_~ZJU z?}kwDXsZI+ae1ZR$ppI6ibl!UND#VhrXK!lWTQDWzw%|??G;OVNcgfLHXR&UuS%XO zF~Rt@px6@26m;RtdOyM8bfoo`@8@@0Pbe4e)%0bE?IvKZV|@pP zjiQJBTR1Nq2>XC905O`r(q`z094_&rE7xO{~PMXfrFfit3KP zmiMB9-flm~%8IVqGDE~>AxRR!pSFu8&r!H43+~!mlv+!I&!{s-tdk8*Fx%uU`!s|) z-N*Sxz-aSBR`ZcNIoQF@sAlp8KO~qa6%m)~9d7>Ee`MLhGj{8Tr=94-d@Jn=e9p2V z#6cc@2U4Dx4X%mrIR-Oo>@0YaaYXE4XT`+%*E1XqwJpio5b&dsN1iQ$)&gE=j%!6N z$2o&07&bu--UQxVVl*k7_|r-#=uaD?L4HtBlQUNA$Hm>WWj+%66K|_p z>=TyDGfaF8%~CxmL$#LKZM@b#P4&e!V3Xqlh7noV%oO6x4XbiVXNtSMZCzm1tb$xK zhZ?!LBrS&S11%vRnM<&|)dL>Ey7LZ?ZO5+;C+Pc*k`sOyA2SD{anea)4pADe#%Io{0A}o5tGk_$m@bqG8Kg60=?Nd{i(Fo0RAzn zd|@EV#(Ibabz`?+y`gqw`Fec~_hneV>dOHg$nDACX}a_1spEB4Yi^+5Bd{qn%8c5IPIR&;wQeIW57|Tw%v5Cu0*HAm|Zk6`%VHOSr%uU{aOH` z$o<-mRG0jr9A`|Fd_xPen((l~Whv*p`Shy31|>ag(QBqMlHbPdX&aQcCt363`59}0 zFB+2tv9!ENw8pU|ae1QCPM~eEkYY@a8C5qqN>aCzZwy>}^~l2dbL<;Ymh#>5pc=Z1 z6*X0!l2)szuOM`IlZ8K`G6)J8+g}`;;Oi+gYPRk;v6so;9QLs#@W5+?br5D209%L_ zTJk3C)kPCj4~Yy?1&v&CcWKaq!o?r8M2XpY2EZh2+!FI?TeCKV-oJ3Bo7Vq@J^uz0 zoo00F1Sq6$xq)Av-btp`vF2qG6js5S_&%N%3tH(wdFsw>4y_F-PHI$H-aN^0E!3cQ zbmE;05O3RcDn#;&kJu_RA9PEJRz7V^dZV|qW%kG?r9|817&#Ew(qk%mY3|;U{JFte z;eG5Fse^s?ul&!vkn(*=EBD0qhvrKPNR_*~wtARJv=0V`ET{;2?6+sGZexQ9Kb}}t zvHyHmE%Z`4bYp3&vV5-`b&b#2*&w1>HR*Ts8!n2YklX?Ma+ZFEyYNq+c&Wt!zK;Fn z^&s?UA!^OS(PFpE-@)rDot{>VmQZ5_HOsY_kU*YcGgaQm zVe6LuMYwIoDE9!>=Aj=Q01MjExvE66zz3?Pu!NdEy*(1@FSuNR)LGP|bVD!MM?VY& z{wGVdlBrGh1)01t>eP(lbb{E!{6YO=5jHprfw&Z~+}gh>dJ{rvq57JOiMkhe99T?@ zO@`zszR*CPciFp~W1FyhO`pU=x<1sq3Iop~{n@KKg4U^UUM(|_qSdGKBqO>tc|p%I z`BC`nlhM`$2>Zs^Doum1TFwJ8$VA5_>sUk&gpz)AWZO+8bf?#B_P!?vY8&l$KXz_H zVADGf>>aF+ z2>!LDln$>0Gch>7w^yI65QMAoL3mV^EK`>verDAwG)1KkX?#DuHE+V)YL=5%eWbcf zXk+D2G*srDjk;lEnU*)7ai*O9d$4SyHE1NuiTxn{MRJPPo(rTIyne}h zQbY6503r*QsnwB5!4=(t`Qmnt3+1lXmPA@AKmT{`eNxo|xxtmI-!@*7tBd$vFqw4x z+Cfwee`tc*u}o&P9H|7*TPeIotr?eaJ$e65!2i)E{Lfjrmwq+kcy5j>+I(GQKO&r) zJlkFblj!-uw*Yx5L1Qrer%KZ&Krc=zLO7#Bh~m{XSz*hi!vzmoP}g>Ao8n}BZI`xK zeGKb8PDG{;xK3iq``n>{amlDu1kw+O*n@)meQzkzE=E7wH|%yL53Iyp&B}s$7PLdvg0##neyAZ5JbqRv0`qh>b zx)00V%)ntp%<98)nu>wJYcrtDf)7GRsr^XNYaO3$>Q;tE)to#~u4m~a_ z%)G({3Bo-cBQ?LuWjRg4m3F_x32hAD2Pe3+zUWjLS^s8eyaWV||97Ozw0GLcMVkg4 zeGzv{$wQ?Zc(;G@!H*zS)c=}&@O^m8tRR@lT`YU6o65J~?iCve#5diV$jtqeCq8!i zN=II@l|Zv<{gtsDNa#!^d*qt;>QEy@t5%q_qoI7tcCP%#xpQ5_s#?ilm7EEaPoe|XFSiq2`|fc+@{|k@D;NyC``tcE_~4c^br0;mzGVjY#X+=w6=?9# z44hsJ^dE5CT^;V0bHbL?U$4sq>GnakID>M@jT&XPiU5Gx@N^>Yr21M$m#;1BR>;Y? zz`v~R|E}&o*+EKzlOJk8w;1n>dPzW&=%S-0svQdcytuFCt2viA=hHyG)Vb(M>HBTi zQ{B07eLe3PB|Cg%ww*FLb}z|kLR6~3Vn>Q?w~=ey1jEhk3}M#=+T6+H*Lv*ei3>9E z|D7dwu_UT_&-e3TW54W;bFz_2w1EKUY@BJU>5iqXrTF#jow$Q%M-qdj+m$s97oHtG zmTR1ow^0L@NH2k)Tbnt}h@6~y&4Tgq$8LO#1N9POwl&5A;cjMM@#Qtct6ZZhiQhFR z9_i%p4qODgzWD*SKVwn5{v&+Q1+Q(S8-i;;$q%eIiq ze=Eb4Yvze*w8V_mO|kOOiL1*EL;M`Q5C_P%tw?EtR~PBZ=iaI1&92}oN-^pXemH`a zBY&j@Tt-VHafxF=?J8Ayh_w6k)>TxHR&4*zy(2dg1CVVdNKhcOc%*THH4f!#8x__% z`^hqRQ{NP(I-7+*c2aA5dVaca@#_E-5sfbu0m_R;xr;5UVSjZd5EgQ;mA34UpUBK- zeWQGZ5o{j)a9be1+o^<0ujQtprd7w`MP!2~#e@;!5V3g={BfNE`trSuA20i=(IMem z0L^?TOpKdJfD1QH!o6h%(z!8a?Y8xjhJHL>NH>M!WrZF8lmpLquT^&X7PMneq0klt`avsIFj(L;Y79 z3fFLGLV0}%2vx^R3BTCHKY8I))&y(elb^(882>Ggqjay5d`BO(Z$C@h`kw!da0#=nF zfCL`OX@T;nYSZ;hRzL{LBct&IdddS1IR4vaExiBVi81qv9`VGiL-Uq;-@GCHw>3at M$3(m4uH)1H0|TMr3;+NC literal 0 HcmV?d00001