- طراحی دیتابیس یا بانک اطلاعاتی بر پایه چند زبانه بودن و بررسی سناریوهای مختلف.
- نکاتی که باید در ساخت سایتهای چند زبانه به آنها دقت کرد.
- شیوهی تشخیص و تغییر زبان سایت
- معرفی چند کامپوننت وب، برای مباحث چند زبانه
طراحی مدل دیتابیس
اولین کار برای داشتن یک سایت چند زبانه، این است که یک مدل صحیح و مناسب را برای دیتابیس خود انتخاب کنید. یکی از اولین روشهایی که به ذهن هر فردی میرسد این است که برای هر ستون متنی که قرار است چند زبانه باشد، به تعداد زبانها برایش یک ستون در نظر بگیریم. یعنی برای جدول مقالات که قرار است در سه زبان فارسی و انگلیسی و عربی باشد، سه ستون برای عنوان مقاله و سه ستون نیز برای متن آن داشته باشیم. تصویر زیر نمونهای از این مدل را نشان میدهد.
مزایا:
- پیاده سازی آسان
معایب:
- در این روش با زیاد شدن هر زبان، تعداد ستونها افزایش مییابد که باعث میشود طراحی مناسبی نداشته باشد.
- در ضمن این مورد باید توسط برنامه نویس مرتبا اضافه گردد یا اینکه برنامه نویس این امکان را در سیستم قرار دهد که مدیر سایت بتواند در پشت صحنه کوئری افزودن ستون را ایجاد کند که باید جدول مرتبا مورد alter گرفتن قرار بگیرد.
- ممکن است همیشه برای هر زبانی مطلبی قرار نگیرد و این مورد باعث میشود بی جهت فضایی برای آن در نظر گرفته شود.
پی نوشت: با اینکه امروزه بحث فیلدهای sparse Column وجود دارد ولی این فیلدها در هر شرایطی مورد استفاده قرار نمیگیرند وبیشتر متعلق به زمانی است که میدانیم آن فیلد به شدت کم مورد استفاده قرار میگیرد.
پی نوشت دوم : در صورتی که فیلد شما مانند متن مقاله که عموما از نوع داده (varchar(max است استفاده میکنید و در صورتی که زبان مورد استفاده قرار نگیرد در خیلی از اوقات بی جهت فیلدهای Blob ساخته اید که بهینه سازی آن را نیز باید در نظر بگیرید.
ID | کد |
Language | زبان |
ISO | کد دو رقمی آن زبان |
Flag | پرچم آن کشور |
مزایا:
- پیاده سازی آسان
معایب:
- ایجاد رکوردهای تکراری، هر مقاله برای بعضی از اطلاعاتش که چند زبانه نیستند دادههای تکراری خواهد داشت.
- هر مقاله یک مقالهی جدا شناخته میشود و ارتباطی میان آنان نخواهد بود. بدین ترتیب توانایی ایجاد گزارشهایی چون هر گروه از مقاله و دسته بندی آنها از بین خواهد رفت. در ضمن مدیر عموما در یک سیستم مدیریتی میخواهد تنها یک لینک را به یک مقاله بدهد و سایت بنا به تشخیص در زبان مزبور، یکی از این مقالات را به کاربر نمایش دهد؛ نه اینکه مرتبا مدیر برای هر زبان، لینکی را مهیا کند و در این حالت چنین چیزی ممکن نخواهد بود.
- در یک سیستم فروشگاهی همانند تصویر بالا کار هم سختتر میشود و هر رکورد، یک محصول جدا شناخته میشود و ویرایشها هم برای هر کدام باید جداگانه صورت بگیرد که در عمل این طرح را رد میکند.
سومین راه حل این است که سه جدول ایجاد کنیم:
یک. جدول زبانها (که بالاتر ایجاد شده بود)
دو . جدول نام مقاله به همراه اطلاعات پایه و فیلدها بی نیاز به چند زبانه بودن
سه : یک جدول که هر دو ستون آن کدهای کلید دو جدول بالا را دارند و فیلدهای چند زبانه در آن وجود دارند.
جدول پایه
ID | کد |
Name | نام مقاله |
CreationDate | تاریخ ایجاد |
Writer | نویسنده |
Visibilty | وضعیت نمایش |
LanguageCode | کد زبان |
ArticleID | کد مقاله |
CreationDate | تاریخ ایجاد |
Visibility | وضعیت نمایش مقاله |
Title | عنوان مقاله |
ContentText | متن مقاله |
در جدول پایه یک مقاله ایجاد میشود که اطلاعات عمومی همه مقالات را دارد و حتی خصوصیت وضعیت نمایش آن، روی همهی مقالات با هر زبانی تاثیر میگذارد. در جدول دو، هر مقاله یک رکورد دارد که کد زبان و کد مقاله برای آن یک کلید ترکیبی به حساب میآیند. پس، از هر مقاله یک یا چند زبان خواهیم داشت. همچنین دارای فیلدهایی با وضعیت مخصوص به خود هم هستند؛ مثل فیلد وضعیت نمایش مقاله که فقط برای این مقاله با این زبان کاربرد دارد.
مزایا:
- گزارش گیری آسان برای هر دسته مقاله با زبانهای مختلف و ارتباط و یکپارچگی
- آسان در افزودن زبان.
معایب:
- ایجاد کوئریهای پیچیدهتر و جوین دار که به نسبت روشهای قبلی کوئریها پیچیدهتر شده اند.
- کدنویسی زیادتر.
استفاده از ساختارهای XML یا JSON برای ذخیره سازی اطلاعات چند زبانه مانند ساختارهای زیر:
XML<Articles> <Article> this is english text </Article> <Article> این یک متن فارسی است </Article> </Articles> یا <Articles> <en-us> this is english text </en-us> <fa-ir> این یک متن فارسی است </fa-ir> </Articles>
"Articles":["en-us':{"title":"this is english text","content":" english content"},"fa-ir":{"title":"متن فارسی","content":"محتوای فارسی"}]
از مزایای این روش ذخیرهی همه دادهها در یک ستون و یک جدول است و نیازی به ستونهای اضافه یا جداول اضافه نیست ولی معایب این روش استفاده از کوئریهای پیچیدهتر جهت ارتباط و خواندن است.
استفاده از بانکهای اطلاعاتی NO SQL
در این بانکها دیگر درگیر تعداد ستونها و جنس آنها نیستیم و میتوانیم برای هر مقاله یا محصول، هر تعداد زبان و یا فیلد را که میخواهیم، در نظر بگیریم و اضافه کنیم. برای آشنایی بیشتر با این نوع بانکها و انواع آن، مقالات مربوط به nosql را در سایت دنبال کنید.
نکاتی که در یک سایت چند زبانه باید به آنها توجه کرد.
یک . زبان آن صفحه را معرفی کنید: این کار هم به موتورهای جست و جو برای ثبت سایت شما کمک میکند و هم برای معلولین که از ابزارهای صفحه خوان استفاده میکنند، کمک بزرگی است. در این روش، صفحه خوانها و دستگاههای خط بریل که زبان صفحه را تشخیص نمیدهند با خواندن کد زبان میتوانند زبان صفحه را تشخیص دهند. با استفاده از خط زیر میتوانید زبان اصلی صفحهی خود را تنظیم نمایید:
<html lang="en">
اگر از XHTML استفاده میکنید خاصیت زیر را فراموش نکنید. دریافت W3C Validation بدون آن امکان پذیر نخواهد بود.
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<blockquote lang=”fr”> <p>Le plus grand faible des hommes, c'est l'amour qu'ils ont de la vie.</p> </blockquote>
سه. لینک ها : اگر دارید در صفحهای لینک به جایی میدهید که متفاوت از زبان شماست، حتما باید زبان صفحه یا سایت مقصد را مشخص کنید. مثلا لینک زیر برای صفحهای است که از یک زبان غیر فرانسوی به یک صفحهی با زبان فرانسوی هدایت میشود:
<a href="" hreflang="fr">French</a>
همچنین اگر متن لینک شما هم به زبان فرانسوی باشد خیلی خوب میشود که آن را هم بیان کنید و از خاصیت lang و هم hreflang همزمان استفاده کنید:
<a href="" hreflang="fr">Francais</a>
پنج. انکودینگ صفحه را مشخص کنید: برای اینکه نحوهی رمزگذاری و رمزگشایی حروف و نمادها مشخص گردد، باید انکودینگ تنظیم شود و حتی برای بعضی از موتورهای جست و جو که ممکن است با وب سایت شما به مشکل بر بخورند. امروزه بیشتر از صفحات یونیکد استفاده میشود که سطح وسیعی از کاراکترها را پشتیبانی میکند.
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
HTML5
<meta charset="UTF-8">
شش. اندازهی فونت: موقعی که یک سایت چند زبانه را طراحی میکنید این نکته خیلی مهم هست که بدانید اندازه فونتهای زبان پیش فرض، برای باقی زبانها مناسب نیستند. به عنوان مثال ممکن است اندازه فونتی برای زبانهای انگلیسی، فرانسوی و آلمانی مناسب باشد ولی برای زبانهای فارسی و عربی و چینی و ... مناسب نباشد و خواندن آن سخت شود. به همین جهت یکی از راههای حل این مشکل استفاده از قالب css است که وابسته به خصوصیت lang ای است که شما برای صفحه و هر المان یا تگی که از این خصوصیت استفاده میکند، تعیین کردهاید.
:lang(en) { font-size: 85%; font-family: arial, verdana, sans-serif; } :lang(zh) { font-size: 125%; font-family: helvetica, verdana, sans-serif; }
خط زیر تعیین میکند که از استایل اول استفاده شود:
<html lang="en">
<html lang="zh">
<body class="english"> or <body class="chinese">
و استایل:
.english { font-size: 85%; font-family: arial, verdana, sans-serif; } .chinese { font-size: 125%; font-family: helvetica, verdana, sans-serif; }
هشت : زمان را نیز تغییر دهید: یکی از مواردی که در کمتر سایت چند زبانهای به چشم میخورد و به نظر بنده میتواند بسیار مهم باشد این است که time zone منطقهی هر زبان را بدانید. به عنوان مثال برای مقالهی خود، تاریخ ایجاد را به صورت UTC ذخیره کنید و سپس نمایش را بر اساس زبان یا حتی بهتر و دقیقتر از طریق IP کشور مربوطه به دست آورید. برای کاربران ثبت نام شده این تاریخ میتواند دقیقتر باشد همانند انجمنهای وی بولتین.
شیوههای تشخیص زبان سایت
یکی از راههای تشخیص زبان این است که موقعی که برای اولین بار کاربری به سایت مراجعه میکند، زبان مورد نظرش را سوال کنید و این اطلاعات را در یک کوکی بدون تاریخ انقضاء ذخیره کنید تا در دفعات بعدی آن را بررسی نمایید.
دومین راه، استفاده از IP کاربر مراجعه کننده است تا بر اساس آن زبان مورد نظر را انتخاب کنید.
در سومین شیوه که اغلب استفاده میشود، زبان سایت به طور پیش فرض بر روی یک زبان خاص که بهتر است انگلیسی باشد تنظیم شده است و سپس کاربر از طریق یک منو یا ابزارهای موجود در سایت، زبان سایت را تغییر دهد.
پی نوشت: فراموش نگردد که امکان تغییر زبان همیشه برای کاربر مهیا باشد و طوری نباشد که کاربر در آینده نتواند زبان سایت را تغییر دهد؛ حتی اگر تشخیص خودکار سایت برای زبان فعال باشد.
پلاگینها و ابزارهای مدیریت زبانپی نوشت: در روشهای بالا بهتر است همان مرتبهی اول اطلاعات را در یک کوکی ذخیره کنید تا مراحل پیگیری راحتتر و آسانتر شود.
دومین ابزار که بیشتر برای انتخاب کشور میباشد و من خودم در بخش مدیریتی سیستمها از آن استفاده میکنم، ابزار CountrySelector است. این پلاگین قابلیت جست و جو زبان را همزمان با تایپ کاربر نیر داراست. اسامی کشورها به صورت انگلیسی شروع شده و به زبان آن کشور در داخل پرانتز خاتمه مییابند و پرچم هر کشور نیز در کنار آن قرار دارد. کار کردن با آن بسیار راحت بوده و مستنداتش به طور کامل کار با آن را توضیح میدهد.
پلاگین بعدی International Telephone Input است که پیاده سازی پلاگین بالا میباشد. برای مواردی مفید است که شما نیاز دارید کد تلفنی کشوری را انتخاب کنید.
در مقالههای زیر که در سایت جاری است در مورد Globalization و به خصوص استفاده از ریسورسها مطالب خوبی بیان شده است:
قسمت بیست و دوم آموزش MVC که مبحث Globalization را دنبال میکند.
قسمت اول از شش قسمت مباحث Globalization که دنبالهی آن را میتوانید در مقالهی خودش دنبال کنید.
ChatGPT مزخرفه!
ChatGPT is bullshit
Recently, there has been considerable interest in large language models: machine learning systems which produce human-like text and dialogue. Applications of these systems have been plagued by persistent inaccuracies in their output; these are often called “AI hallucinations”. We argue that these falsehoods, and the overall activity of large language models, is better understood as bullshit in the sense explored by Frankfurt (On Bullshit, Princeton, 2005): the models are in an important way indifferent to the truth of their outputs. We distinguish two ways in which the models can be said to be bullshitters, and argue that they clearly meet at least one of these definitions. We further argue that describing AI misrepresentations as bullshit is both a more useful and more accurate way of predicting and discussing the behaviour of these systems.
در مقالهی قبل توانستیم یک سری
از مدلهای مربوط به وبلاگ را آماده کنیم. در ادامه به تکمیل آن و همچین
آغاز تهیهی مدلهای مربوط به اخبار و پیغام خصوصی میپردازیم.
همکاران این قسمت:
سلمان معروفی
مدل گزارش دهی
/// <summary> /// Repersents a Report template for every cms section /// </summary> public class Report { #region Ctor /// <summary> /// Create one instance for <see cref="Report"/> /// </summary> public Report() { ReportedOn = DateTime.Now; Id = SequentialGuidGenerator.NewSequentialGuid(); } #endregion #region Properties /// <summary> /// gets or sets identifier for Report /// </summary> public virtual Guid Id { get; set; } /// <summary> /// gets or sets reason of report /// </summary> public virtual string Reason { get; set; } /// <summary> /// gets or sets section that is reported /// </summary> public virtual ReportSection Section { get; set; } /// <summary> /// gets or sets sectionid that is reported /// </summary> public virtual long SectionId { get; set; } /// <summary> /// gets or sets type of report /// </summary> public virtual ReportType Type{ get; set; } /// <summary> /// gets or sets report's datetime /// </summary> public virtual DateTime ReportedOn { get; set; } /// <summary> /// indicate this report is read by admin /// </summary> public virtual bool IsRead { get; set; } #endregion #region NavigationProperties /// <summary> /// gets or sets id of user that is reporter /// </summary> public virtual long ReporterId { get; set; } /// <summary> /// gets or sets id of user that is reporter /// </summary> public virtual User Reporter { get; set; } #endregion } /// <summary> /// Represents Report Section /// </summary> public enum ReportSection { News, Poll, Announcement, ForumTopic, BlogComment, BlogPost, NewsComment, PollComment, AnnouncementComment, ForumPost, User, ... } /// <summary> /// Represents Type of Report /// </summary> public enum ReportType { Spam, Abuse, Advertising, ... }
قصد داریم در این سیستم به کاربران خاصی دسترسی گزارش دادن در بخشهای مختلف را بدهیم. این دسترسیها در بخش تنظیمات سیستم قابل تغییر خواهند بود (برای مثال براساس امتیاز ، براساس تعداد پست و ... ) . این امکان میتواند برای مدیریت سیستم مفید باشد.
برای سیستم گزارش دهی به مانند سیستم امتیاز دهی عمل خواهیم کرد. در کلاس Report، خصوصیت ReportSection از نوع دادهی شمارشی میباشد که در بالا تعریف آن نیز آماده است و مشخص کنندهی بخشهایی میباشد که لازم است امکان گزارش دهی داشته باشند. خصوصیت Type هم که از نوع شمارشی ReportType میباشد، مشخص کنندهی نوع گزارشی است که داده شده است.
علاوه بر نوع گزارش، میتوان دلیل گزارش را هم ذخیره کرد که برای این منظور خصوصیت Reason در نظر گرفته شدهاست. خصوصیت IsRead هم برای مدیریت این گزارشات در پنل مدیریت در نظر گرفته شده است. اگر در مقالهی قبل دقت کرده باشید، متوجه وجود خصوصیتی به نام ReportsCount در کلاس BaseContent و BaseComment خواهید شد که برای نشان دادن تعداد گزارشهایی است که برای آن مطلب یا نظر داده شده است، استفاده میشود.
کلاس پایه فایلهای ضمیمه
/// <summary> /// Represents a base class for every attachment /// </summary> public abstract class BaseAttachment { #region Ctor public BaseAttachment() { Id = SequentialGuidGenerator.NewSequentialGuid(); AttachedOn = DateTime.Now; } #endregion #region Properties /// <summary> /// sets or gets identifier for attachment /// </summary> public virtual Guid Id { get; set; } /// <summary> /// sets or gets name for attachment /// </summary> public virtual string FileName { get; set; } /// <summary> /// sets or gets type of attachment /// </summary> public virtual string ContentType { get; set; } /// <summary> /// sets or gets size of attachment /// </summary> public virtual long Size { get; set; } /// <summary> /// sets or gets Extention of attachment /// </summary> public virtual string Extension { get; set; } /// <summary> /// sets or gets bytes of data /// </summary> //public byte[] Data { get; set; } /// <summary> /// sets or gets Creation Date /// </summary> public virtual DateTime AttachedOn { get; set; } /// <summary> /// gets or sets counts of download this file /// </summary> public virtual long DownloadsCount { get; set; } /// <summary> /// gets or sets datetime that is modified /// </summary> public virtual DateTime ModifiedOn { get; set; } /// <summary> /// gets or sets section that this file attached there /// </summary> public virtual AttachmentSection Section { get; set; } /// <summary> /// gets or sets information of user agent /// </summary> public virtual string Agent { get; set; } #endregion #region NavigationProperties /// <summary> /// sets or gets identifier of attachment's owner /// </summary> public virtual long OwnerId { get; set; } /// <summary> /// sets or gets identifier of attachment's owner /// </summary> public virtual User Owner { get; set; } #endregion } public enum AttachmentSection { News, Announcement, ForumTopic, Conversation, BlogComment, NewsComment, PollComment, AnnouncementComment, ForumPost, BlogPost, Group, ... }
کلاس بالا اکثر خصوصیات لازم برای مدل Attachment ما را در خود دارد. قصد داریم از ارث بری TPH برای مدیریت فایلهای ضمیمه استفاده کنیم. در سیستم بستهی ما، تنها کاربران احراز هویت شده میتوانند فایل ضمیمه کنند و برای همین منظور OwnerId را که همان ارسال کنندهی فایل میباشد، به صورت Nullable در نظر نگرفتهایم.
یک سری از مشخصات که نیاز به توضیح اضافی ندارند، ولی خصوصیت AttachmentSection که از نوع شمارشی AttachmentSection است، برای دسترسی راحت کاربر به فایلهای ارسالی خود در پنل کاربری در نظر گرفته شده است. برای بخشهای (وبلاگ - اخبار - نظرسنجیها - آگهیها - انجمن) که نیاز به Privacy خاصی نیست و احراز هویت کفایت میکند، مدل زیر را در نظر گرفته ایم:
مدل فایلهای ضمیمه عمومی
/// <summary> /// Repersent the attachment for file /// </summary> public class Attachment : BaseAttachment { }
/// <summary> /// Represents one news item /// </summary> public class NewsItem : BaseContent { #region Ctor /// <summary> /// create one instance of <see cref="NewsItem"/> /// </summary> public NewsItem() { Rating = new Rating(); PublishedOn = DateTime.Now; } #endregion #region Properties /// <summary> /// indicating that this news show on sidebar /// </summary> public virtual bool ShowOnSideBar { get; set; } /// <summary> /// indicate this NewsItem is approved by admin if NewsItem.Moderate==true /// </summary> public virtual bool IsApproved { get; set; } #endregion #region NavigationProperties /// <summary> /// gets or sets newsitem's Reviews /// </summary> public ICollection<NewsComment> Comments { get; set; } #endregion }
کلاس بالا نشان دهندهی اشتراکهای ما خواهند بود. این مدل ما هم از کلاس پایهی BaseContent بحث شده در مقالهی قبل، ارث بری کرده و علاوه بر آن دو خصوصیت دیگر تحت عنوان IsApproved برای اعمال مدیریتی در نظر گرفته شده است (اگر در بخش تنظیمات سیستم اخبار، مدیریت تصمیم گرفته باشد تا اخبار جدید به اشتراک گذاشته شده با تأیید مدیریتی منتشر شوند) و خصوصیت ShowOnSideBar هم به عنوان یک تنظیم مدیریتی برای خبر خاصی در نظر گرفته شده که لازم است به صورت sticky در سایدبار نمایش داده شود.
برای اخبار نیز امکان ارسال نظر خواهیم داشت که برای این منظور لیستی از مدل زیر (NewsComment) در مدل بالا تعریف شده است .
مدل نظرات اخبار
public class NewsComment : BaseComment { #region Ctor public NewsComment() { Rating = new Rating(); CreatedOn = DateTime.Now; } #endregion #region NavigationProperties /// <summary> /// gets or sets body of blog NewsItem's comment /// </summary> public virtual long? ReplyId { get; set; } /// <summary> /// gets or sets body of blog NewsItem's comment /// </summary> public virtual NewsComment Reply { get; set; } /// <summary> /// gets or sets body of blog NewsItem's comment /// </summary> public virtual ICollection<NewsComment> Children { get; set; } /// <summary> /// gets or sets NewsItem that this comment sent to it /// </summary> public virtual NewsItem NewsItem { get; set; } /// <summary> /// gets or sets NewsItem'Id that this comment sent to it /// </summary> public virtual long NewsItemId { get; set; } #endregion }
/// <summary> /// Indicate one conversation /// </summary> public class Conversation { #region Ctor /// <summary> /// create one instance of <see cref="Conversation"/> /// </summary> public Conversation() { Id = SequentialGuidGenerator.NewSequentialGuid(); SentOn = DateTime.Now; } #endregion #region Properties /// <summary> /// gets or sets identifier of record /// </summary> public virtual Guid Id { get; set; } /// <summary> /// represents this conversaion is seen /// </summary> public virtual bool IsRead { get; set; } /// <summary> /// gets or sets subject of this conversation /// </summary> public virtual string Subject { get; set; } /// <summary> /// gets or sets Date that this record added /// </summary> public virtual DateTime SentOn { get; set; } /// <summary> /// indicate this record deleted by sender /// </summary> public virtual bool DeletedBySender { get; set; } /// <summary> /// indicate this record deleted by receiver /// </summary> public virtual bool DeletedByReceiver { get; set; } /// <summary> /// gets or sets Messagescount that Unread by sender of this conversation /// </summary> public virtual int UnReadSenderMessagesCount { get; set; } /// <summary> /// gets or sets Messagescount that Unread by receiver of this conversation /// </summary> public virtual int UnReadReceiverMessagesCount { get; set; } /// <summary> /// gets or sets Messagescount of this conversation for increase performance /// </summary> public virtual int MessagesCount { get; set; } #endregion #region NavigationProperties /// <summary> /// gets or sets if of user that start this conversation /// </summary> public virtual long SenderId { get; set; } /// <summary> /// gets or sets user that start this conversation /// </summary> public virtual User Sender { get; set; } /// <summary> /// gets or sets id of user that is recipient /// </summary> public virtual long ReceiverId { get; set; } /// <summary> /// gets or sets user that is recipient /// </summary> public virtual User Receiver { get; set; } /// <summary> /// get or set Messages of this conversation /// </summary> public virtual ICollection<ConversationReply> Messages { get; set; } /// <summary> /// get or set Attachments that attached in this conversation /// </summary> public virtual ICollection<ConversationAttachment> Attachments { get; set; } #endregion
مدل بالا نشان دهندهی گفتگوی بین دو کاربر میباشد. هر گفتگو امکان دارد با موضوع خاصی ایجاد شود و مسلما یک کاربر بهعنوان دریافت کننده و کاربر دیگری بعنوان ارسال کننده خواهد بود. برای این منظور خصوصیات Receiver و Sender که از نوع User هستند را در این کلاس در نظر گرفتهایم.
خصوصیات DeletedBySender و DeletedByReceiver هم برای این در نظر گفته شدهاند که اگر یک طرف این گفتگو خواهان حذف آن باشد، برای آن کاربر حذف نرم انجام دهیم و فعلا برای کاربر مقابل قابل دسترسی باشد.
UnReadSenderMessagesCount و UnReadReceiverMessagesCount هم برای بالا بردن کارآیی سیستم در نظر گفته شدهاند و در واقع تعداد پیغامهای خوانده نشده در یک گفتگو به صورت متمایز برای هر دو طرف، ذخیره میشود. هر گفتگو شامل یکسری پیغام رد و بدل شده خواهد بود که بدین منظور لیستی از ConversationReplyها را در مدل بالا تعریف کردهایم.
در هر گفتگو یکسری فایل هم ممکن است ضمیمه شود ، برای این منظور هم یک لیستی از کلاس ConversationAttachment در مدل گفتگو تعریف شده است که در ادامه پیاده سازی کلاس ConversationAttachment را هم خواهیم دید.
مدل ConversationReply به شکل زیر میباشد:
/// <summary> /// Represents One Reply to Conversation /// </summary> public class ConversationReply { #region Ctor /// <summary> /// create one instance of <see cref="ConversationReply"/> /// </summary> public ConversationReply() { Id = SequentialGuidGenerator.NewSequentialGuid(); SentOn = DateTime.Now; } #endregion #region Properties /// <summary> /// gets or sets identifier of record /// </summary> public virtual Guid Id { get; set; } /// <summary> /// represents this conversaionReply is seen /// </summary> public virtual bool IsRead { get; set; } /// <summary> /// gets or sets body of this conversationReply /// </summary> public virtual string Body { get; set; } /// <summary> /// gets or sets Date that this record added /// </summary> public virtual DateTime SentOn { get; set; } #endregion #region NavigationProperties /// <summary> /// gets or sets Parent's Id Of this ConversationReply /// </summary> public virtual Guid? ParentId { get; set; } /// <summary> /// gets or sets Parent Of this ConversationReply /// </summary> public virtual ConversationReply Parent { get; set; } /// <summary> /// get or set Children Of this ConversationReply /// </summary> public virtual ICollection<ConversationReply> Children { get; set; } /// <summary> /// gets or sets if of user that start this conversationReply /// </summary> public virtual long SenderId { get; set; } /// <summary> /// gets or sets user that start this conversationReply /// </summary> public virtual User Sender { get; set; } /// <summary> /// gets or sets Conversation that this message sent in it /// </summary> public virtual Conversation Conversation{ get; set; } /// <summary> /// gets or sets Id of Conversation that this message sent in it /// </summary> public virtual Guid ConversationId { get; set; } #endregion }
مدل بالا نشان دهندهی پیغامهای داده شده در یک گفتگو با موضوعی خاص میباشد. ساختار درختی آن هم برای ایجاد امکان جواب دهی برای پیغامها در نظر گرفته شده است (الزامی نیست). هر پیغام در یک گفتگو ارسال شده و یک ارسال کننده نیز دارد که برای این منظور به ترتیب دو خصوصیت Conversation از نوع کلاس Conversation و Sender از نوع User در نظر گرفتهایم.
با توجه به وجود Privacy در گفتگو نیاز است تا مدل فایل ضمیمه بخش گفتگوها به شکل زیر باشد:
/// <summary> /// Represents the attachment That attached in Conversation /// </summary> public class ConversationAttachment : BaseAttachment { #region NavigationProperties public virtual Conversation Conversation { get; set; } public virtual Guid? ConversationId { get; set; } #endregion }
همانطور که کمی بالاتر بحث شد، قصد اعمال ارث بری TPH را برای مدیریت فایلهای ضمیمه داریم. برای این منظور مدل بالا نیز از کلاس BaseAttachment ارث بری کرده و دو خصوصیت اضافه هم برای اعمال ارتباط یک به چند با گفتگو خواهد داشت. توجه کنید که ConversationId به صورت Nullable تعریف شدهاست.
نتیجه این قسمت
استفاده از Chart.js در Blazor
This is a Blazor library that wraps Chart.js. You can use it in both client- and server-side projects.