آموزش معماری سه لایه در سی شارپ (#C)

آموزش معماری سه لایه در سی شارپ (C#)

 

پیاده‌سازی یک مثال با معماری سه لایه در عمل

در این بخش می‌خواهیم مثال موجود در بخش ۱۸-۶ از فصل ۶ را مجدداً با معماری سه لایه پیاده‌سازی کنیم. همانطور که به یاد دارید، مثال ۱۸-۶ طراحی و پیاده‌سازی یک دفترچه تلفن بوده است که در آن از بانک اطلاعاتی sql server استفاده کردیم. در مثال این بخش مجدداً همان مثال را با معماری سه لایه پیاده‌سازی می‌کنیم و به منظور تنوع در استفاده از فضای نام و کلاس‌ها از بانک اطلاعتی Ms Access استفاده می‌کنیم.

ابتدا نرم افزار Ms Access را اجرا کرده و یک بانک اطلاعاتی بنام Phonebook ایجاد کنید. سپس با انتخاب گزینه Table Design از منوی create همانند شکل زیر یک جدول شامل چهار فیلد بسازید و فیلد تلفن را به عنوان کلید اصلی تعریف کنید(شکل ۳-۷).

 

پیاده‌سازی یک مثال با معماری سه لایه در عمل

شکل ۳-۷) ساخت جدول person برای پایگاه داده phonebook در access

 

جدول فوق را بنام Person نام‌گذاری کنید، پس از ذخیره بر روی نام جدول دوبار کلیک کرده و چند نمونه داده همانند شکل زیر وارد کنید.

 

نمونه داده

شکل ۴-۷) داده‌های جدول person

 

در ادامه وارد visual studio شوید و یک برنامه جدید بنام N_Layer_PhoneBook ایجاد کنید و برروی نام برنامه در پنجره Solution Explorer کلیک راست کنید و برای هر یک از لایه‌ها یک پوشه به شکل زیر بسازید.

 

Solution Explorer

شکل ۵-۷) ساخت پوشه جدید برای لایه‌ها در Visual Studio

 

پس از ایجاد سه پوشه برای هر یک از لایه‌ها باید پنجره Solution Explorer برنامه به شکل زیر تبدیل شده باشد.

 

پنجره Solution Explorer

شکل ۶-۷) پنجره solution explorer پس از ساخت پوشه

 

ایجاد لایه Data

روی پوشه DAL راست کلیک کرده و با انتخاب گزینه New Item همانند شکل زیر و با انتخاب گزینه Dataset در پنجره باز شده، یک dataset بنام dsPhonebook در پوشه DAL ایجاد کنید(شکل ۷-۷).

 

ایجاد لایه Data

شکل ۷-۷) ساخت dataset در پوشه DAL

 

پس از ایجاد dataset با کلیک راست کردن برروی صفحه dataset و انتخاب گزینه DataTable همانند شکل زیر شمای یک جدول را برروی دیتاست ایجاد کنید. نام جدول ایجاد شده را Person بگذارید(شکل ۸-۷).

 

شمای یک جدول

شکل ۸-۷) ساخت جدول در dataset

 

برروی جدول Person کلیک راست کرده و ستون‌های مورد نظر را با انتخاب گزینه Column مطابق شکل زیر به جدول Person اضافه کنید.

 

جدول Person

شکل ۹-۷) اضافه کردن ستون به جدول Person

 

در ادامه برروی پوشه DAL کلیک راست کرده و با انتخاب گزینه New Item همانند روش قبل و با انتخاب گزینه Class در پنجره باز شده کلاس های زیر را در پوشه DAL ایجاد کنید.

 

۱- یک کلاس به نام ConnectionManager با روش فوق به این پوشه اضافه کنید.

این کلاس برای مدیریت شی connection مورد استفاده قرار می‌گیرد. کلاس افزوده شده را به شکل زیربرنامه‌نویسی کنید.

 

کلاس به نام ConnectionManager

همانگونه که مشاهده می‌کنید به دلیل استفاده از بانک اطلاعاتی Ms Access، فضای نام System.Data.OleDb را به برنامه ضمیمه کرده‌ایم. کار این کلاس ایجاد شی اتصال، باز کردن شی اتصال و بستن آن می‌باشد. دقت کنید شی اتصال بصورت static تعریف شده است، که تنها به ازاء تمام اشیائی که از این کلاس تعریف می‌شوند تنها یک شی اتصال موجود باشد.

 

۲- یک کلاس به نام BaseAdapter به پوشه DAL اضافه کنید و کلاس ایجاد شده را به شکل زیر برنامه‌نویسی کنید.

 

کلاس به نام BaseAdapter

این کلاس پایه ذخیره‌سازی در بانک اطلاعاتی و واکشی داده‌هاست و به نحوی طراحی شده است که برای ذخیره‌سازی و واکشی داده‌های هر موجودیتی مناسب باشد و بدون تغییر در هر برنامه‌ای می‌تواند در لایه DAL قرار گیرد. همانگونه که مشاهده می‌کنید، این کلاس به شکل یک کلاس قالب(Template Class) تعریف شده است. پارامتر T این کلاس از نوع DataTable است تا بتواند هر نوع جدول داده‌ای را بجای پارامتر T در نظر بگیرد. در نتیجه این کلاس پایه‌ای می‌تواند برای ذخیره‌سازی و واکشی داده‌های هر جدولی بکار رود. این کلاس شامل متدهای زیر می‌باشد:

• متد Select_All: این متد یک رشته پرس‌وجو از پارامتر ورودی دریافت کرده، داده‌های معادل رشته پرس و جو را واکشی کرده و بصورت یک DataTable برمی‌گرداند.

سوال ۱: چرا این متد بصورتvirtual تعریف شده است؟

سوال ۲: گزینه protected در تعریف این متد به چه معناست؟

• Select_By: این متد یک رشته پرس‌وجو و یک مجموعه از پارامترهای مورد نیاز رشته پرس‌وجو را از پارامتر ورودی دریافت کرده، داده‌های معادل رشته پرس‌وجو را واکشی کرده و بصورت یک Datatable برمی‌گرداند.

• متد Execute_Command_NonQuery: این متد برای اجرای تمام دستورات sql که خروجی ندارند طراحی شده است. این متد یک رشته پرس‌وجو و یک مجموعه از پارامترهای مورد نیاز رشته پرس‌وجو را از پارامتر ورودی دریافت کرده، رشته پرس‌وجو را اجرا کرده و نتیجه اجرا برروی داده‌ی بانک اطلاعاتی اعمال می‌گردد. در صورتی که عمل درخواست شده انجام شود مقدار true، و در غیر اینصورت false بر‌می‌گردد. از این متد برای درج، حذف، و بروزرسانی داده ها در بانک اطلاعاتی استفاده می‌گردد.

سوال ۳: چرا این کلاس بصورت abstract تعریف شده است؟

 

۳- یک کلاس به نام PersonAdapter به پوشه DAL اضافه کنید و کلاس ایجاد شده را به شکل زیر برنامه‌نویسی کنید.

 

کلاس به نام PersonAdapter

این کلاس برای مدیریت ذخیره و بازیابی اشیائی از نوع کلاس Person پیاده‌سازی شده است، کلاس BaseAdapter را به ارث می‌برد و دارای تمام اجزاء کلاس BaseAdapter است. این کلاس شامل ۶ جزء داده‌ای رشته‌ای می‌باشد که این رشته‌ها حاوی پرس‌وجوهای مورد نیاز اشیایی از نوع شخص می‌باشند و همچنین حاوی ۶ متد به شرح زیر است:

• متد Insert: این متد اطلاعات یک شخص را از پارامتر ورودی دریافت کرده، اطلاعات دریافت شده را به مجموعه‌ای از نوع اشیاء پارامتر تبدیل می‌کند. سپس به کمک متد Execute_Command_NonQuery که از کلاس پایه به ارث برده شده است، اطلاعات را در بانک اطلاعاتی درج می‌کند.

• متد Update_By_Tel: این متد اطلاعات جدید یک شخص را از پارامتر ورودی دریافت کرده، اطلاعات دریافت شده را به مجموعه‌ای از نوع اشیاء پارامتر تبدیل می‌کند. سپس به کمک متد Execute_Command_NonQuery که از کلاس پایه به ارث برده شده است، اطلاعات را در بانک اطلاعاتی بروزرسانی می‌کند. دقت کنید که در این متد ویرایش اطلاعات براساس شماره تلفن شخص انجام می‌پذیرد.

• متد Delete_By_Tel: این متد تلفن یک شخص را از پارامتر ورودی دریافت کرده، اطلاعات دریافت شده را به مجموعه ای از نوع اشیاء پارامتر تبدیل می‌کند و سپس به کمک متدExecute_Command_NonQuery که از کلاس پایه به ارث برده شده است، اطلاعات را از بانک اطلاعاتی حذف می کند. دقت کنید که در این متد حذف اطلاعات بر حسب شماره تلفن شخص انجام می‌پذیرد.

• متد Select_AllPerson: این متد اطلاعات تمامی رکوردهای موجود در بانک اطلاعاتی را بصورت یک شی جدول داده‌ای برمی‌گرداند. همچنین این متد به کمک متدSelect_All به ارث برده شده از کلاس پایه، تمامی رکوردهای موجود در بانک اطلاعاتی را بصورت یک شی DataTable برمی‌گرداند.

• متد Select_AllPerson_By_Tel: این متد تلفن یک شخص را از پارامتر ورودی دریافت کرده، اطلاعات دریافت شده را به مجموعه‌ای از نوع اشیاء پارامتر تبدیل می‌کند. سپس به کمک متد Select_By به ارث برده شده از کلاس پایه، مشخصات صاحب تلفن ورودی را از بانک اطلاعاتی واکشی کرده و بصورت یک شی DataTable برمی‌گرداند.

• متد Select_AllPerson_Like_By: این متد نام فیلد مورد جستجو و مقدار آن را از پارامتر ورودی دریافت کرده، اطلاعات دریافت شده را به مجموعه‌ای از نوع اشیاء پارامتر تبدیل می‌کند. سپس به کمک متد Select_By که از کلاس پایه به ارث برده شده است، نتایج جستجو را از بانک اطلاعاتی واکشی کرده و بصورت یک شی DataTable برمی‌گرداند. همچنین دقت کنید که در این متد از ویژگی Like زبان پرس‌وجوی sql استفاده شده است.

سوال ۴: چرا رشته‌های حاوی پرس‌وجو در این کلاس بصورت const تعریف شده‌اند؟

لایه Data با ایجاد این کلاس پایان یافت. دقت کنید که از کلاس‌های این لایه تنها می‌توان در لایه Business استفاده کرد. نمودار کلاس لایه Data به شکل زیر می‌باشد.

 

نمودار کلاس لایه Data

شکل ۱۰-۷) نمودار کلاس برای لایه Data در نرم افزار دفترچه تلفن 

 

(Business Logic – B. L) Business ایجاد لایه 

وظیفه این لایه انجام پردازش‌های منطقی نرم افزار است. در این لایه منطق کاری نرم افزار پیاده‌سازی شده است. این لایه فاقد هر گونه امکانات نمایش اطلاعات به کاربر است. فرمان تغییرات در داده ها نظیر ذخیره کردن، حذف کردن، بروز رسانی و نظایر آن توسط این لایه انتقال داده می‌شود. نهایتاً بسته به نحوه طراحی نرم افزار، پیامی در رابطه با اجرای موفقیت آمیز یا عدم اجرای فرمان کاربر، توسط لایهUI به کاربر نمایش داده می‌شود. برای پیاده‌سازی این لایه دو مجموعه کلاس در نظر گرفته‌ایم:

• مجموعه اول کلاس‌هایی برای ارتباط با لایه بالاتر یعنی لایه UI. این کلاس‌ها را در زیرپوشه بنام Entities در لایه BL قرار می‌دهیم. وظیفه این کلاس‌ها دریافت داده‌های ورودی از لایه لایه UI، انجام پردازش‌های منطقی نرم افزار برروی داده‌ها و همچنین ارسال داده‌ها و نتایج به عنوان خروجی به لایه نمایش.

• مجموعه دوم کلاس‌هایی برای ارتباط با لایه پایین‌تر یعنی لایه DAL می‌باشد. وظیفه این کلاس‌ها ارتباط با لایه پایین‌تر و انجام پردازش‌های منطقی مورد نیاز برروی داده‌هایی است که باید به لایه DAL ارسال شوند یا از لایه DAL واکشی شده‌اند. این کلاس‌ها را در زیر پوشه‌ای بنام Providers در لایه BL قرار می‌دهیم.

ابتدا با کلیک راست کردن برروی پوشه BL و در پنجره Solution Explorer دو زیر پوشه به شکل زیر ایجاد کنید(شکل ۱۱-۷).

 

پنجره Solution Explorer

شکل ۱۱-۷) پنجره Solution Explorer پس از ساختن زیرپوشه

 

در ادامه برروی پوشه Provider کلیک راست کرده و با انتخاب گزینه New Item همانند روش قبل و با انتخاب گزینه Interface و Class در پنجره باز شده اجزاء زیر را در پوشه Provider ایجاد کنید.

۱- یک Interface به نام IProvider با روش فوق به این پوشه اضافه کنید.

این واسط کمک می‌کند تا تمام موجودیت‌های این لایه به یک صورت مدیریت شوند. واسط افزوده شده را به شکل زیر برنامه‌نویسی کنید.

 

Interface به نام IProvider

همانگونه که در کد مشاهد می‌کنید، این واسط به شکل الگو ایجاد شده است و درنتیجه برای تمام موجودیت‌ها قابل استفاده است. T در این الگو نام کلاس موجویت مورد نظر است و TPK تایپ کلید اصلی موجودیت مذکور می باشد.

۲- یک Class به نام PersonProvider به زیرپوشه Provider در لایه BL اضافه کنید و به شکل زیر آن‌را برنامه‌نویسی کنید.

 

Class به نام PersonProvider

در این کلاس به کمک اشیایی از نوع کلاس PersonAdapter با لایه DAL ارتباط برقرار می‌شود و همچنین با اشیائی از نوع کلاس PersonEntity با لایه UI ارتباط برقرار می‌گردد.

۳- آخرین کلاس لایه BL کلاسی بنام PersonEntity خواهد بود که اشیائی از نوع این کلاس برای ارتباط با لایه بالاتر یعنی لایه UIبکار می‌روند. ابتدا یک کلاس بنام PersonEntity به زیرپوشه Entities در لایه BL اضافه کنید و به شکل زیر آن‌را برنامه‌نویسی کنید.

 

کلاس بنام PersonEntity

در این حالت نمودار کلاس لایه Data به شکل زیر می‌باشد.

 

نمودار کلاس لایه Data

 شکل ۱۲-۷) نمودار کلاس برای لایه Data

 

ایجاد لایه Peresentaion 

لایه کاربر در اصل همان ظاهر برنامه است که کاربران با آن ارتباط برقرار می‌کنند تا نیازهای خود را به سیستم جهت اعمال تغییرات یا گرفتن گزارش ارائه دهند. این لایه بطور کلی از قوانین موجود در سیستم مجزا می‌باشد. کاربران تنها لیستی از داده‌ها را می‌بینند و یا داده‌های جدید را به این لایه ارائه می‌دهند. نمایش داده‌ها به کاربر نهایی واجازه دادن به آن‌ها برای ارتباط داشتن با داده ها، اصلی‌ترین وظیفه این لایه است. در برنامه دفترچه تلفن لایه UI تنها یک فرم مبتی بر ویندوز است که کاربر بوسیله آن می‌تواند با دفترچه تلفن و داده‌های درون آن ارتباط برقرار کند. برای ایجاد لایه UI ابتدا یک فرم بنام Frmmain به شکل زیر در لایه UI به برنامه اضافه کنید. فرم را به شکل زیر طراحی کنید و کنترل‌های فرم را همانند مثال ۱۸_۶ نام‌گذاری نمایید(شکل ۱۳-۷).

 

ایجاد لایهPeresentaion (UI)

شکل ۱۳-۷) ظاهر برنامه دفترچه تلفن

 

در لایه UI نیاز داریم تا به کلاس‌های موجود در لایه BL دسترسی داشته باشیم. بنابراین به پنجره کد فرم ایجاد شده بروید و فضاهای نام(BL.Providers وBL.Entities) ایجاد شده در لایه BL را با دستور using و همانند شکل زیر به این لایه اضافه کنید.

 

دستور using

برای ارتباط لایه UI با لایه BL کافیست تا در لایه کاربر یک شی بنام P1 از نوع کلاس PersonProvider موجود در لایه BL تعریف کرده و کار را آغاز کنیم. برای این منظور کد فوق را به شکل زیر تغییر دهید.

 

 ارتباط لایه UI با لایه BL

برای کدنویسی این فرم به ۵ زیربرنامه نیاز داریم. کد زیربرنامه‌ها و توضیح آن‌ها به شرح زیر می‌باشد. زیربرنامه‌های زیر را به پنجره کد فرم فوق اضافه کنید.

 

• زیربرنامه Show_CurrentPerson:

این زیربرنامه یک شماره ردیف از پارامتر ورودی دریافت کرده و در صورت معتبر بودن ردیف مذکور در کنترل DataGridView آنرا به رکورد جاری تبدیل کرده و اطلاعات رکورد جاری در جعبه متن‌های سمت چپ فرم نمایش داده می‌شود.

 

زیربرنامه Show_CurrentPerson

• زیربرنامه Show_All:

این زیربرنامه اطلاعات کل افراد را در کنترل DataGridView نمایش داده و با فراخوانی زیربرنامه Show_CurrentPerson(0); نفر اول را به رکورد جاری تبدیل می‌کند.

 

زیربرنامه Show_All

• زیربرنامه New_Person:

این زیربرنامه جعبه متن‌های سمت چپ فرم را خالی می‌کند و آماده دریافت اطلاعات جدید می‌گردد. فرض بر این است که خاصیت ReadOnly جعبه متن‌ها True است و بصورت پیش فرض نمی‌توان داده‌ها را تغییر داد یا اطلاعات جدیدی وارد کرد، مگر اینکه کاربر دکمه New یا Edit را انتخاب کرده باشد که در این صورت خاصیت ReadOnly کنترل‌های مورد نظر را False می‌کنیم تا قابل نوشتن گردند.

 

زیربرنامه New_Person:

• زیربرنامه Save_New_Person:

این زیربرنامه یک شی از نوع PersonEntityایجاد کرده و آنرا با اطلاعات فرد جدید مقداردهی می‌کند؛ همچنین با فراخوانی متد Add از شی P1، آنرا به لایه BL فرستاده تا اطلاعات شخص جدید پس از پردازش‌های منطقی مورد نیاز در بانک اطلاعات دائمی گردند.

 

زیربرنامه Save_New_Person

• زیربرنامه Edit_Person:

این زیربرنامه برای بروز رسانی اطلاعات ویرایش شده در رکورد جاری برروی فرم ایجاد شده است. در این زیربرنامه ابتدا یک شی از نوع کلاس PersonEntity ایجاد می‌کنیم و به کمک این موجودیت، اطلاعات جدید فرد جاری را بروزرسانی می‌کنیم. در نهایت زیربرنامه Show_All را اجرا می‌کنیم تا اطلاعات جدید برروی فرم بروز رسانی شده و در کنترل DataGridView نمایش داده شود.

 

 زیربرنامه Edit_Person

در ادامه رخدادهای مورد نیاز از هریک از کنترل‌های موجود برروی فرم را به شکل زیر برنامه‌نویسی می‌کنیم.

-رخداد Load فرم را به شکل زیر کدنویسی کنید. این کد بدین منظور است که با بازشدن فرم، اطلاعات همه افراد در کنترل DataGridView نمایش داده شوند.

 

رخداد Load فرم  

-رخداد کلیک دکمه‌های Next، Prevoius، Firstو Last را به شکل زیر کدنویسی کنید.

 

رخداد کلیک دکمه‌های Next، Prevoius، Firstو Last 

سوال ۵: DGV1.CurrentCell.RowIndex چیست، چه کاربردی دارد، چه مقادیری می‌گیرد؟

سوال ۶: DGV1.RowCount چیست؟ چه کاربردی دارد،؟ چه مقادیری می‌گیرد؟

-رخداد کلیک دکمه New را به شکل زیر کدنویسی کنید.

 

رخداد کلیک دکمه New  

– رخداد کلیک دکمه Save را به شکل زیر کدنویسی کنید.

 

رخداد کلیک دکمه Save

– رخداد کلیک دکمه Edit را به شکل زیر برنامه‌نویسی کنید. در این رخداد چک می‌شود تا اگر متن دکمه کلمه Edit است، یعنی دفعه اول است که دکمه کلیک می‌شود و بنابراین فرم آماده ویرایش می‌شود؛ در غیر اینصورت یعنی دفعه دوم است که کلیک می‌شود و پس از ویرایش اطلاعات است. بنابراین با فراخوانی زیربرنامه Edit_Person(); اطلاعات ویرایش شده را بروزرسانی می‌کند و فرم را به شکل اولیه برمی‌گرداند.

 

Edit_Person();

– رخداد کلیک دکمه Del را به شکل زیر برنامه‌نویسی کنید. در انتهای این رخداد با فراخوانی زیربرنامه Show_All() اطلاعات کل افراد مجدداً واکشی شده و در کنترل DataGridView نمایش داده می‌شوند تا فرد حذف شده از لیست افراد برروی فرم نیز حذف گردد.

 

DataGridView

سوال ۷: فراخوانی زیربرنامه show_All(); در رخداد فوق چه کاربردی دارد؟

– رخداد CellClick از کنترل DataGridView را به شکل زیر کدنویسی کنید. این رخداد برای این منظور است که با کلیک کردن برروی هر سطر از کنترل DataGridView اطلاعات سطر مذکور به رکورد جاری تبدیل شده و اطلاعات آن در جعبه متن‌های سمت چپ فرم نمایش داده شود.

 

رخداد CellClick

سوال ۸: e.RowIndex چیست؟ چه کاربردی دارد؟ چه مقادیری می‌گیرد؟

– رخداد کلیک دکمه search را به شکل زیر کدنویسی کنید.

 

رخداد کلیک دکمه search

سوال ۹: چرا به متن جستجو رشته “%” را اضافه کرده‌ایم؟

– رخداد TextChanged از جعبه متن txtsearch را به شکل زیر کدنویسی کنید. در این رخداد تنها رخداد کلیک دکمه Search را فراخوانی کرده‌ایم و در نتیجه با تغییر متن جستجو بطور اتوماتیک نتیجه نمایش داده می‌شود و دیگر نیازی به کلیک کردن برروی دکمه Search ندارد.

 

رخداد TextChanged

سوال ۱۰: چرا با خالی شدن جعبه متن txtsearch اطلاعات تمام افراد نمایش داده می‌شود؟

– رخداد کلیک دکمه Save را به شکل زیر کدنویسی کنید.

 

رخداد کلیک دکمه Save

 با اتمام برنامه‌نویسی لایه UI کل برنامه‌نویسی پایان یافت. برنامه را اجرا کنید و نمونه خروجی را مطابق شکل ۱۴-۷ مشاهد نمایید:

 

 نمونه خروجی

شکل ۱۴-۷) نمونه‌ای از اجرای برنامه دفترچه تلفن

 

دقت کنید که بزرگترین مزیت برنامه‌نویسی سه لایه مدیریت و نگهداری ساده برنامه است و تغییرات در هر لایه به سایر لایه‌ها و بخش‌های برنامه انتشار پیدا نمی‌کند. به عنوان مثال اگر بخواهیم دفترچه تلفن تحت وب داشته باشیم، کافیست لایه UI را تغییر داده و بجای فرم ویندوز از یک فرم وب استفاده کنیم و برنامه‌نویسی مجدد نیاز نیست. همچنین برای تغییر نوع بانک اطلاعاتی بهSql Server کافیست تا نوع بانک اطلاعاتی در لایه DAL را تغییر دهیم و بدین ترتیب سایر بخش‌های برنامه تغییر نمی‌کند. این موضوع در پروژه های بزرگ و حرفه ای بسیار حائز اهمیت است.

 

متن فوق بخش هایی از کتاب «آموزش مباحث ویژه در c#.net» نوشته مهندس رشید شجاعی می باشد.

کلیه مباحث مطرح شده در متن در آموزش ویدئویی زیر که توسط خود ایشان تدریس شده است، مورد بررسی قرار گرفته است:

مجموعه آموزش های کاربردی برنامه نویسی #C (سی شارپ) — کلیک کنید (+)

 

1 پاسخ
  1. safiyeh
    safiyeh says:

    پاسخ سوال دو:

    اگر از نوع protected استفاده نشود متدهای تعریف شده در این کلاس توسط کلاس هایی که از آن مشتق شده اند قابل استفاده نمی باشد و به آنها دسترسی نداریم.

    پاسخ دادن

ترک بک و پینگ بک

ارسال یک پاسخ

در گفتگو ها شرکت کنید.

پاسخ دهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *