در یکی از پروژه هایی که چندین سال پیش نوشته شده بود برای قفل نرم افزار از کدی شبیه به زیر استفاده شده بود :
Math.Sinh(123456.12).ToString();
برای رفع آن همانطور که در مطلب فوق نیز عنوان شده، با استفاده از کد زیر قابل اصلاح میباشد :
Math.Sinh(123456.12).ToString(CultureInfo.InvariantCulture);
من هنوز در این بحث گزارش گیری کاملا مبتدی هستم. چون میخواستم تازه کارم رو شروع کنم دنبال انتخاب انتخاب نرم افزار مناسب برای گزارشگیری C# در محیط ویندوز بودم. یک سری مطالب کپی شده و تکراری در اینترنت رو دیدم، دوست داشتم نظر تخصصی و شخصی خودتون رو بدونم. من اول دنبال کریستال بودم، ولی بعدش دیدم که ظاهرا استیمول هم نرم افزار معروفیه، میشه کمکم کنید؟
بی نهایت ممنون از سایت خوبتون
پایان پروژه ASP.NET Ajax Control Toolkit !
تمام این موارد که مبفرمایید درست، من خودم از طرفداران استقاده از jQuery هستم اما یک مسئله برای من هنوز وجود دارد اون هم RAD است. در یکی از کتابخانه های آژاکسی برای asp.net مثل Anthem.Net
این مسئله خیلی خوب رعایت شده و کار توسعه نرم افزار مبنتی بر استفاده از آژاکس به سرعت انجام میشه. این برای من هنوز سواله با jQuery چطور می توان به این سرعت نرم افزار های Full-Ajax را توسعه داد.
اعمال غیر همزمان و چند ریسمانی
زمانیکه از یک API غیرهمزمان برای مدیریت چنین عملیاتی استفاده میشود، ترد جاری را در این حالت در خواب فرو میبرد. برای اینکه کار بیشتری برای انجام وجود ندارد. همچنین با اینکه کلاینت درخواستی را ارسال میکند یا پاسخی را دریافت، برای مدیریت کل عملیات در اکثر اوقات نیازی به تردها ندارد. این سخت افزار شبکهی نصب شده در سیستم است که عمدهی کار را انجام میدهد و نه برنامه. زمانیکه برنامه درخواست ارسال اطلاعاتی را بر روی شبکه ارائه میدهد، درایور سخت افزار شبکه است که به سخت افزار مرتبط فرمان میدهد چه اطلاعاتی را باید ارسال کند. اکثر اینگونه سخت افزارها قادرند اطلاعات را خارج از حافظهی اصلی سیستم دریافت کنند. در اینجا درایور تنها باید به سخت افزار عنوان کند، چه اطلاعاتی را و به کجا باید ارسال کند. بنابراین CPU تنها در طی ارسال این فرمان است که مشغول میباشد و نه خارج از آن و این زمان اصلا در مقایسه با زمان ارسال اطلاعات توسط سخت افزار مرتبط، طولانی نیست. CPU مجددا زمانی درگیر خواهد شد که سخت افزار شبکه، اطلاعاتی را دریافت کرده است و باز هم این زمان در مقایسه با زمان دریافت اطلاعات توسط سخت افزار شبکه بسیار کوتاه است.
اغلب کارهای IO به همین شکل هستند. شبیه به همین روند در حالت دسترسی به سخت دیسک وجود دارد. مدت زمانیکه CPU به دیسک کنترلر اعلام میکند چه اطلاعاتی را نیاز دارد در مقایسه با مدت زمانیکه دیسک کنترلر این اطلاعات را واقعا بارگذاری میکند، بسیار ناچیز است.
نمونهی دیگر آن کار با بانکهای اطلاعاتی است. در اغلب اوقات برنامهی ما صرفا یک درخواست را به بانک اطلاعاتی ارائه میدهد و اصل عملیات در جایی دیگر و توسط موتور بانک اطلاعاتی، خارج از برنامه پردازش میگردد.
بنابراین جهت پردازش یک پروسهی خاص، در بسیاری از مراحل آن تنها یک ترد کافی است و هدف اصلی اعمال غیرهمزمان، کاهش تعداد تردهایی است که برنامه جهت پردازش عملیاتی خاص، نیاز دارد. این نوع الگوریتمها طوری طراحی شدهاند تا تردها تنها زمانی بکار گرفته شود که واقعا CPU قرار است کار خاصی را انجام دهد و نه برای مثال زمانیکه دیسک کنترلر یا سخت افزار شبکه مشغول به کار هستند (و ویندوز به صورت توکار دارای یک چنین API ایی هست). این مساله در سمت کلاینت، سبب خواهد شد تا ترد UI آزاد شود و بتواند به درخواستهای رسیده کاربر بهتر پاسخ دهد. همچنین این مساله در سمت سرور نیز بسیار مفید است، زیرا برنامه قادر خواهد شد تا به تعداد بیشتری از درخواستها به صورت همزمان پاسخ دهد. زیرا با کاهش تعداد تردهای درگیر، مقیاس پذیری سیستم افزایش مییابد.
using (var store = new DocumentStore { Url = "http://localhost:8080" }.Initialize()) { using (var session = store.OpenSession()) { store.DatabaseCommands.PutAttachment(key: "file/1", etag: null, data: System.IO.File.OpenRead(@"D:\Prog\packages.config"), metadata: new RavenJObject { { "Description", "توضیحات فایل" } }); var question = new Question { By = "users/Vahid", Title = "Raven Intro", Content = "Test....", FileId = "file/1" }; session.Store(question); session.SaveChanges(); } }
RavenFS یک فایل سیستم مجازی توزیع شدهاست و برای فایلهای بزرگ چند گیگابایتی به طور بهینهای طراحی گردیدهاست تا کارآیی بانک اطلاعاتی را بالا ببرد و وجود فایلهای تکراری، از بین برود. این سیستم جدید شامل سیستم پیش فرض ایندکس گذاری میباشد و به شما این اجازه را میدهد تا بر روی متادیتاهای یک فایل از قبیل حجم، تاریخ آخرین نگارش و حتی متادیتاهای اختصاصی که شما در حین ذخیره سازی به آن اضافه میکنید، به جستوجو بپردازد. این سیستم جدید همچنین این امکان را به شما میدهد تا این اطلاعات را بین Nodeها، با کمترین میزان انتقالات جابجا کنید و دسترسی سریعتری را بین نودهای مختلف داشته باشید.
برای ذخیره سازی یک فایل ابتدا باید یک FileStore را همانند آنچه که برای DocumentStore داشتید تعریف کنید. Url که شامل همان رشته اتصالی بوده و DefaultFileSystem هم همانند DefaultDatabase که نام دیتابیس در آن ذکر میشد، در اینجا نام فایل سیستم ذکر میگردد:
var fileStore = new FilesStore() { Url = "http://localhost:8080", DefaultFileSystem = "SampleFs" }; fileStore.Initialize();
بعد از آن باید از طریق Store جدید یک سشن ایجاد شود و فایل مورد نظر را در قالب یک استریم بخوانیم:
var session = fileStore.OpenAsyncSession(); var stream = File.OpenRead("D:\\Apocalypse.Now.Redux.1979.BDRip.YIFY.mkv");
توجه داشته باشید که برای کار با فایل سیستم، همه متدهای session به صورت غیرهمزمان بوده و متد همزمانی وجود ندارد. سپس در مرحله بعد میخواهم متادیتاهای شخصی نیز به آن اضافه کنیم:
var metadata = new RavenJObject { {"User", "users/1345"}, {"Director","Francis Ford Coppola" }, {"Year","1979" } };
با استفاده از شیء RavenObject میتوانیم در قالب کلید و مقدار، مقادیر خود را ذخیره کنیم و بعد از آن همه موارد بالا که شامل فایل هدر، استریم و متادیتای اختصاصی است را رجیستر کنیم. اگر هم چندین فایل داریم میتوانید آنها را هم در همینجا رجیستر کنید:
session.RegisterUpload("mkv/sample.mkv", stream, metadata);
در مرحله بعدی تغییرات را تایید و عملیات آپلود آغاز میگردد:
await session.SaveChangesAsync();
همانطور که میبینید تمامی متدهای کاربردی این سشن به طور غیرهمزمان طراحی شدهاند.
کلیه عملیاتی که در بالا انجام شد:
var fileStore = new FilesStore() { Url = "http://localhost:8080", DefaultFileSystem = "SampleFs" }; fileStore.Initialize(); var session = fileStore.OpenAsyncSession(); var stream = File.OpenRead("D:\\Apocalypse.Now.Redux.1979.BDRip.YIFY.mkv"); var metadata = new RavenJObject { {"User", "users/1345"}, {"Director","Francis Ford Coppola" }, {"Year","1979" } }; session.RegisterUpload("Mkv/sample.mkv", stream, metadata); await session.SaveChangesAsync();
حالا اگر به نسخه سرور ravenDb مراجعه کنید میبینید که فایل طبق فایل هدر داده شده قرار گرفته است و اطلاعات مربوط به آن ذخیره شده است:
{ "User": "users/1345", "Country": "Iran", "City": "Kashan", "Raven-Synchronization-History": [ { "Version": 4, "ServerId": "42d0cccb-103d-4bf0-9f3d-6f635b1c8ba4" }, { "Version": 5, "ServerId": "42d0cccb-103d-4bf0-9f3d-6f635b1c8ba4" } ], "Raven-Synchronization-Version": "6", "Raven-Synchronization-Source": "42d0cccb-103d-4bf0-9f3d-6f635b1c8ba4" }
برای خواندن هم به شیوه زیر عمل میکنیم:
از طریق Store ایجاد شده، یک سشن جدید را باز میکنیم و فایل مورد نظر را از طریق یکی از متادیتاهای تعریف شده بازیابی میکنیم:
var fileStore = new FilesStore() { Url = "http://localhost:8080", DefaultFileSystem = "SampleFs" }; fileStore.Initialize(); var session = fileStore.OpenAsyncSession(); var file = await session.Query() .WhereEquals("Year", "1979") .FirstOrDefaultAsync();
var stream = await session.DownloadAsync("mkv/"+file.Name);
سپس استریم را روی دیسک سخت دخیره یا به هر مکانی که مد نظر است ارسال میکنیم:
var fs = File.Create("D:\\file2.mkv"); stream.CopyTo(fs); fs.Flush(); fs.Close();
await stream.CopyToAsync(fs);
سپس کل کد بازیابی را به شکل زیر مینویسیم:
var fileStore = new FilesStore() { Url = "http://localhost:8080", DefaultFileSystem = "SampleFs" }; fileStore.Initialize(); var session = fileStore.OpenAsyncSession(); var file = await session.Query() .WhereEquals("Year", "1979") .FirstOrDefaultAsync(); var stream = await session.DownloadAsync("mkv/"+file.Name); var fs = File.Create("D:\\file2.mkv"); await stream.CopyToAsync(fs);
تفاوت Desktop Application با Web Application
در تصویر بالا هری و سالی، یک کپی از مخزن موجود را گرفته و سپس هر کدام جداگانه بر روی کپیهای خودشان مشغول به کار میشوند. سپس سالی کارش را زودتر به اتمام رسانده و مخزن را به روز میکند. بعد از آن، هری هم کارش به پایان میرسد و قصد به روز کردن مخزن را دارد ولی سیستم به او اجازه این کار را نمیدهد؛ چون این مخزن آن مخزن نیست که هری قبلا از آن کپی گرفته است. آن مخزن بعد از به روزرسانی سالی تغییر یافته است. پس او مجبور است تا تغییرات جدید مخزن را دریافت کرده و کپی خودش را به روز کند. پس از آن میتواند کپی خودش را بر روی مخزن اعمال کند (با فرض اینکه تغییرات جدید هیچ تصادمی با تغییراتی که روی کپی خودش اعمال کرده است ندارند).
سناریو بالا با احتساب وجود تصادم
اگر همین سناریوی بالا را فرض کنیم که تغییراتی که هری روی فایلها داده است همان تغییراتی است که سالی قبلا روی مخزن اصلی روی همان فایلها اعمال کرده است، آیا در این حالت دریافت به روزرسانیهای جدید باعث ایجاد تصادم میشود؟
هری درخواست ادغام آخرین تغییرات مخزن را با کپی خودش میکند. از آنجا که فایل A تصادم دارد یک فلگ flag از این وضعیت میگیرد. حال هری میتواند تفاوتهای ایجاد شده را ببیند و بین آنها یکی را انتخاب کند. در این وضعیت هری همپوشانیهای کدها را برطرف میکند و شاید هم بحثی در مورد این تصادم با سالی داشته باشد تا بهترین تغییر کد انتخاب گردد و نهایتا به روشی کاملا امن و مطمئن، با مخزن اصلی ادغام میشود.
پی نوشت : نرم افزارها نمیتوانند موضوع تصادم را به طور خودکار اعمال کنند. از آنجا که نیاز به تصمیم گیری و درک هوشمند دارد این کار به صورت انسانی باید بررسی گردد.