بررسی in Memory OLTP for Database
new Vue({ el: '#app', template: '<div>Hello DNT</div>' });
new Vue({ el: '#app', data() { return { blogTitle: 'DNT' } }, render: function (createElement) { return createElement('h1', this.blogTitle) } });
Vue.component('child', { template: '<div>Hello DNT users</div>' });
Vue.component('child', { props: ['text'], template: `<div> {{ text }} </div>` }); new Vue({ el: '#app', data() { return { message: 'Hello DNT!' } } });
<child :text="message"></child>
Vue.component('blogPost', { props: { post: { type: Object, required: true } }, template: `<div> <h1>{{ post.title }}</h1> <p>{{ post.body }}</p> </div>` });
[Vue warn]: Invalid prop: type check failed for prop "post". Expected Object, got String. found in ---> <BlogPost> <Root>
Vue.component('blogPost', { props: { post: { type: Object, required: true, validator: obj => { const titleIsValid = typeof obj.title === 'string'; const bodyIsValid = typeof obj.body === 'string'; const isValid = titleIsValid && bodyIsValid; if (!isValid) { console.warn("prop is not valid"); return false; } return true; } } }, template: `<div> <h1>{{ post.title }}</h1> <p>{{ post.body }}</p> </div>` });
Vue.component('blogPost', { props: { post: { type: Object, validator: obj => { const titleIsValid = typeof obj.title === 'string'; const bodyIsValid = typeof obj.body === 'string'; const isValid = titleIsValid && bodyIsValid; if (!isValid) { console.warn("prop is not valid"); return false; } return true; }, default: function() { return { title: 'Vue is fun!', body: 'Vue is fun..................' } } } }, template: `<div> <h1>{{ post.title }}</h1> <p>{{ post.body }}</p> </div>` });
data: function () { return { stars: 5, hover: 5 } },
var dt = { stars: 5, hover: 5 }; Vue.component('blogPost', { data: function() { return dt; }, props: // as before..., template: `<div class="blog-post"> <h1>{{ post.title }}</h1> <p>{{ post.body }}</p> <div class="star-wrap"> <span v-for="n in 5" class="star" :class="{ full: hover >= n+1 }" @click="stars = n+1" @mouseover="hover = n+1" @mouseout="hover = stars" ></span> </div> </div>` });
برای رفع این مشکل کافی است به اینصورت دیتا را تعریف کنیم:
Vue.component('blogPost', { data: function() { return { stars: 5, hover: 5 } }, props: // as before..., template: // as before });
تغییر دیتا درون کامپوننتها
تا اینجا توانستیم از کامپوننت والد دادههایی را به کامپوننتهای فرزند ارسال کنیم. اکنون میخواهیم قابلیت تغییر دیتای تعریف شدهی درون کامپوننت والد را درون کامپوننتها نیز داشته باشیم:
Vue.component('child', { props: ['message'], methods: { changeName() { this.message = "New Name!..." } }, template: '#child-template' }); new Vue({ el: '#app', data() { return { name: 'DNT!' } } });
تمپلیت کامپوننت فوق نیز به صورت x-template درون DOM تعریف شده است:
<script type="text/x-template" id="child-template"> <div> <p>{{ message }}</p> <button @click="changeName">Change Name</button> </div> </script>
فراخوانی کامپوننت نیز به اینصورت میباشد:
<div id="app"> <child :message="name"></child> </div>
همانطور که مشاهده میکنید، دیتای name را از طریق ویژگی message توانستهایم به کامپوننت child ارسال کنیم. درون تمپلیت آن نیز یک دکمه را برای تغییر مقدار این ویژگی تعریف کردهایم. تغییر این ویژگی نیز یک assignment ساده است. اما اگر بر روی دکمهی Change Name کلیک کنید، هشدار زیر را درون کنسول مشاهده خواهید کرد:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "message" found in ---> <Child> <Root>
دلیل آن نیز مشخص است؛ زیرا با تغییر این ویژگی، کامپوننت والد از وجود تغییرات مطلع نشدهاست و فقط این تغییرات، درون کامپوننت child صورت گرفتهاست. برای اطلاعرسانی کامپوننت والد میتوانیم از یک ایونت ویژه استفاده کنیم و کامپوننت والد را از وجود تغییرات مطلع کنیم:
changeName() { this.message = "New Name!...", this.$emit("change-name", this.message); }
برای تگ child نیز این ایونت را اضافه خواهیم کرد:
<child :message="name" @change-name="name = $event"></child>
در اینحالت با تغییر ویژگی message، مقدار دیتای name نیز بلافاصله تغییر پیدا خواهد کرد.
Slots
Slot یک روش عالی برای جایگزینی محتوای درون یک کامپوننت است. فرض کنید میخواهیم کامپوننتمان به صورت زیر باشد:
<modal> Hello </modal>
در اینحالت باید درون تمپلیت مکان قرارگیری Hello را تعیین کنیم. اینکار را میتوانیم با قرار دادن تگ slot انجام دهیم:
Vue.component('modal', { template: ` ... <div class="modal-body"> <slot></slot> </div> ... ` });
اکنون هر محتوایی که درون تگ modal قرار گیرد، در قسمت slot نمایش داده خواهد شد. این نوع slot به صورت پیشفرض میباشد. در واقع میتوانیم slotها را نیز نامگذاری کنیم. به عنوان مثال یک slot برای عنوان modal، یک slot برای بدنه modal و یک slot دیگر برای فوتر modal تعریف کنیم:
Vue.component('modal', { template: ` <div class="modal fade" id="detailsModal" tabindex="-1" role="dialog" aria-labelledby="detailsModalLabel" aria-hidden="false"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="detailsModalLabel"> <slot name="title"></slot> </h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <slot name="body"></slot> </div> <div class="modal-footer"> <slot name="footer"></slot> </div> </div> </div> </div> ` });
اکنون میتوانیم محتوای مورد نظر را برای قرارگیری درون slotها تعیین کنیم:
<modal> <template slot="title">Title</template> <template slot="body">Lorem ipsum dolor sit amet.</template> <template slot="footer"> <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> <button type="button" class="btn btn-primary">Save changes</button> </template> </modal>
در این بین با توجه به اینکه دات نت پشتیبانی توکاری از SQL Server دارد، اتصال و استفاده از توانمندیهای آن نیاز به کتابخانه جانبی خاصی ندارد. اما برای کار با بانکهای اطلاعاتی دیگر نیاز خواهد بود تا پروایدر ADO.NET آنها را تهیه و به برنامه اضافه کنیم.
چهار نمونه از منابع داده پیش فرضی که در متد MainTableDataSource قابل تعریف هستند به شرح زیر میباشند:
public void SqlDataReader(string connectionString, string sql, params object[] parametersValues) //.mdb or .accdb files public void AccessDataReader(string filePath, string password, string sql, params object[] parametersValues) public void OdbcDataReader(string connectionString, string sql, params object[] parametersValues)
AccessDataReader قابلیت اتصال به بانکهای اطلاعاتی اکسس جدید (فایلهای accdb) و اکسس قدیم (فایلهای mdb) را دارد.
OdbcDataReader یک پروایدر عمومی است که از روز اول دات نت به همراه آن بوده است. برای مثال جهت اتصال به بانکهای اطلاعاتی فاکسپرو میتواند مورد استفاده قرار گیرد.
اما ... برای مابقی بانکهای اطلاعاتی چطور؟
برای سایر بانکهای اطلاعاتی، منبع داده عمومی زیر تدارک دیده شده است:
public void GenericDataReader(string providerName, string connectionString, string sql, params object[] parametersValues)
یک نکته:
در تمام منابع داده فوق، امکان نوشتن کوئریهای پارامتری نیز پیش بینی شده است. فقط باید دقت داشت که پارامترهای معرفی شده باید با @ شروع شوند که یک نمونه از آنرا در مثال جاری ملاحظه خواهید نمود.
در ادامه نحوه تهیه گزارش از یک بانک اطلاعاتی SQLite را توسط PdfReport بررسی خواهیم کرد:
using System; using PdfRpt.Core.Contracts; using PdfRpt.Core.Helper; using PdfRpt.FluentInterface; namespace PdfReportSamples.SQLiteDataReader { public class SQLiteDataReaderPdfReport { public IPdfReportData CreatePdfReport() { return new PdfReport().DocumentPreferences(doc => { doc.RunDirection(PdfRunDirection.RightToLeft); doc.Orientation(PageOrientation.Portrait); doc.PageSize(PdfPageSize.A4); doc.DocumentMetadata(new DocumentMetadata { Author = "Vahid", Application = "PdfRpt", Keywords = "Test", Subject = "Test Rpt", Title = "Test" }); }) .DefaultFonts(fonts => { fonts.Path(AppPath.ApplicationPath + "\\fonts\\irsans.ttf", Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\verdana.ttf"); }) .PagesFooter(footer => { footer.DefaultFooter(DateTime.Now.ToString("MM/dd/yyyy")); }) .PagesHeader(header => { header.DefaultHeader(defaultHeader => { defaultHeader.RunDirection(PdfRunDirection.RightToLeft); defaultHeader.ImagePath(AppPath.ApplicationPath + "\\Images\\01.png"); defaultHeader.Message("گزارش جدید ما"); }); }) .MainTableTemplate(template => { template.BasicTemplate(BasicTemplate.SilverTemplate); }) .MainTablePreferences(table => { table.ColumnsWidthsType(TableColumnWidthType.Relative); table.NumberOfDataRowsPerPage(5); }) .MainTableDataSource(dataSource => { dataSource.GenericDataReader( providerName: "System.Data.SQLite", connectionString: "Data Source=" + AppPath.ApplicationPath + "\\data\\blogs.sqlite", sql: @"SELECT [url], [name], [NumberOfPosts], [AddDate] FROM [tblBlogs] WHERE [NumberOfPosts]>=@p1", parametersValues: new object[] { 10 } ); }) .MainTableSummarySettings(summarySettings => { summarySettings.OverallSummarySettings("جمع کل"); summarySettings.PreviousPageSummarySettings("نقل از صفحه قبل"); summarySettings.PageSummarySettings("جمع صفحه"); }) .MainTableColumns(columns => { columns.AddColumn(column => { column.PropertyName("rowNo"); column.IsRowNumber(true); column.CellsHorizontalAlignment(HorizontalAlignment.Center); column.IsVisible(true); column.Order(0); column.Width(1); column.HeaderCell("ردیف"); }); columns.AddColumn(column => { column.PropertyName("url"); column.CellsHorizontalAlignment(HorizontalAlignment.Center); column.IsVisible(true); column.Order(1); column.Width(2); column.HeaderCell("آدرس"); column.ColumnItemsTemplate(template => { template.Hyperlink(foreColor: System.Drawing.Color.Blue, fontUnderline: true); }); }); columns.AddColumn(column => { column.PropertyName("name"); column.CellsHorizontalAlignment(HorizontalAlignment.Center); column.IsVisible(true); column.Order(2); column.Width(2); column.HeaderCell("نام"); }); columns.AddColumn(column => { column.PropertyName("NumberOfPosts"); column.CellsHorizontalAlignment(HorizontalAlignment.Center); column.IsVisible(true); column.Order(3); column.Width(2); column.HeaderCell("تعداد مطلب"); column.ColumnItemsTemplate(template => { template.TextBlock(); template.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj)); }); column.AggregateFunction(aggregateFunction => { aggregateFunction.NumericAggregateFunction(AggregateFunction.Sum); aggregateFunction.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj)); }); }); columns.AddColumn(column => { column.PropertyName("AddDate"); column.CellsHorizontalAlignment(HorizontalAlignment.Center); column.IsVisible(true); column.Order(4); column.Width(2); column.HeaderCell("تاریخ ثبت"); column.ColumnItemsTemplate(template => { template.TextBlock(); template.DisplayFormatFormula(obj => obj == null ? string.Empty : PersianDate.ToPersianDateTime((DateTime)obj) /*((DateTime)obj).ToString("dd/MM/yyyy HH:mm")*/); }); }); }) .MainTableEvents(events => { events.DataSourceIsEmpty(message: "There is no data available to display."); }) .Export(export => { export.ToExcel(); }) .Generate(data => data.AsPdfFile(AppPath.ApplicationPath + "\\Pdf\\RptSqlDataReaderSample.pdf")); } } }
توضیحات:
- در مثال فوق نحوه استفاده از یک بانک اطلاعاتی SQLite را ملاحظه میکنید. این بانک اطلاعاتی نمونه در پوشه bin\data سورس به روز شده پروژه موجود است.
dataSource.GenericDataReader( providerName: "System.Data.SQLite", connectionString: "Data Source=" + AppPath.ApplicationPath + "\\data\\blogs.sqlite", sql: @"SELECT [url], [name], [NumberOfPosts], [AddDate] FROM [tblBlogs] WHERE [NumberOfPosts]>=@p1", parametersValues: new object[] { 10 } );
در مرحله بعد به کمک GenericDataReader میتوان به این پروایدر دسترسی یافت. همانطور که ملاحظه میکنید یک کوئری پارامتری با مقدار پارامتر مساوی 10 جهت تهیه گزارش، تعریف شده است.
همچنین باید دقت داشت که اگر پروژه جاری شما مبتنی بر دات نت 4 است، نیاز خواهید داشت چند سطر زیر را به فایل config برنامه اضافه نمائید تا با SQLite مشکلی نداشته باشد:
<?xml version="1.0"?> <configuration> <startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> </startup> </configuration>
- در حین معرفی ستون AddDate، نحوه نمایش و تبدیل تاریخ دریافتی که با فرمت DateTime است را به تاریخ شمسی ملاحظه میکنید. متد PersianDate.ToPersianDateTime در فضای نام PdfRpt.Core.Helper قرار دارد. توسط DisplayFormatFormula، فرصت خواهید داشت مقدار متناظر با سلول در حال رندر را پیش از نمایش، به هر نحو دلخواهی فرمت کنید.
- در ستون url از قالب نمایشی پیش فرض Hyperlink، برای نمایش اطلاعات فیلد جاری به صورت یک لینک قابل کلیک استفاده شده است.
یک نکته:
ذکر قسمت MainTableColumns و تمام تعاریف مرتبط با آن در PdfReports اختیاری است. به این معنا که میتوانید قسمت گزارش سازی و تعاریف گزارشات برنامه خود را پویا کنید (شبیه به حالت auto generate columns در گریدهای معروف). کوئریهای SQL متناظر با گزارشات را در بانک اطلاعاتی ذخیره کنید و به گزارش ساز فوق ارسال نمائید. حاصل یک گزارش جدید است.
ارسال عکس به stimulsoft و ایجاد گزارش
StiImage stiImg = new StiImage(); stiImg.Image = img;
مقدمه ای کاربردی بر Machine Learning
آیا میدانستید که Facebook در حال حاضر بیشتر از 400 میلیون کاربر فعال دارد که حدود 200 میلیون از این کاربران هر روز از این سرویس استفاده میکنند؟ آماری که این سرویس داده به این صورت است که تا سال 2010، کاربرانش در هر روز حدود 16 بیلیون دقیقه از وقت خودشان را به استفاده از این سرویس اختصاص دادهاند. در هر هفته کاربران این سرویس حدود 6 بیلیون مطلب را که شامل عکس و متن بوده را به اشتراک گذاشتهاند. هر ماه بیشتر از 3 بیلیون عکس توسط این سرویس Upload شدهاست. کاربرانش روزانه بیشتر از 1 بیلیون عکس را توسط این سرویس مشاهده کردهاند. این سرویس در ثانیه حدود 50 میلیون عملیات را انجام میدهد!
آیا میدانستید که سرویس Twitter در حال حاضر 350 میلیون کاربر فعال دارد که حدود 100 میلیون از این کاربران روزانه از این سرویس استفاده میکنند و هر روز کاربران این سرویس 500 میلیون توییت را ارسال میکنند. این سرویس در فینال جام جهانی حدود 618,725 توییت را در یک دقیقه دریافت کردهاست.
و یا سرویس Telegram حدود 100 میلیون کاربر فعال دارد که بصورت متوسط در هر روز 220 هزار کاربر جدید در آن ثبت نام میکنند. کاربران این سرویس روزانه 15 بیلیون پیام را ارسال میکنند و 700 میلیون عکس را به اشتراک میگذارند.
چه سروری به تنهایی میتواند این آمار و ارقام را پوشش دهد؟ هزینه خرید و نگهداری آن چقدر است؟ چقدر باید هزینه کنیم تا این سرور از دسترس خارج نشود؟ یک سرور به تنهایی چه راهکاری را میتواند ارائه دهد که هیچوقت از دسترس خارج نشود؟
بهتر است بدانید که سرویس Facebook روی بیش از 60,000 سرور ارائه میشود! چه سروری به تنهایی میتواند کارآیی 60,000 سرور را داشته باشد؟
چه نوع پایگاه داده ای که روی یک سرور سرویس ارائه میدهد، قادر است روزانه به بیشتر از 1 تریلیون درخواست، پاسخ دهد؟ اصلا چه سروری به تنهایی قادر است این حجم داده را که هر روز رو به افزایش است، نگهداری کند؟
بهتر است بدانید پایگاه داده سرویسهای شرکت Apple، روی بیش از 75,000 Node قرار دارند که روزانه حدود 10 پتابایت داده ذخیره میکنند. یا Netflix که از 2,500 Node استفاده میکند و روزانه 420 ترابایت داده را در قالب 1 تریلیون درخواست دریافت میکند یا Easou که پایگاه داده آن روی 270 Node قرار دارد و روزانه 300 ترابایت داده را در قالب 800 میلیون درخواست دریافت میکند! اینها اعداد و ارقامی نیستند که ما بتوانیم با SqlServerی که روی یک سرور قرار دارد، پوشش دهیم.
چه تعداد سرویس را در کشورمان میشناسید که در زمان بالا رفتن تعداد درخواست از دسترس خارج میشوند؟ چه تعداد سرویس را میشناسید که تنها راهکاری را که میتوان در این زمان برای آنها ارائه داد، ارتقاء سخت افزار آنهاست؟ پس از مدتی با بالا رفتن تعداد کاربران، دوباره سخت افزار را ارتقاء میدهیم. تا کجا باید این کار را تکرار کنیم؟ چقدر هزینه کنیم برای سخت افزاری که ممکن است به هر دلیلی در هر لحظه از دسترس خارج شود؟ آیا با توجه به آمار تعداد کاربران، تعداد درخواست و حجم دادهی سرویسهایی که میشناسید و قابل مقایسه با آمار ذکر شده است، باز هم از دسترس خارج میشوند؟
در سری مقالات Distributed systems in .NET من سعی میکنم شما را با خصوصیات و اصطلاحات موجود در سیستمهای توزیع شده آشنا کنم و ابزارهایی را که در NET. برای پیاده سازی این نوع سیستمها وجود دارد، به شما معرفی کنم و با هم ببینیم که این نوع سیستمها چه راهکاری را برای رفع نیازمندیهای ما ارائه میدهند.
نمونهای از طراحی یک سیستم توزیع شده