Remote Tools for Visual Studio 2015 enables remote debugging,
remote testing, and unit testing on computers that don't have Visual
Studio installed.
This set of tools also enables the installation of a developer license,
and the deployment and performance profiling of Windows apps on
computers that are running Windows 8 or Windows 8.1.
پروژهها
OutputCache for ASP.NET Web API
برخلاف اکشنهای ASP.NET MVC که میتوانند با اتربیوت [OutputCache] دارای کش شوند، متدهای ASP.NET Web API را نمیتوان کش کرد.
فیلیپ ووگ در وبلاگش کد اتربیوتی نوشته است تا بتوان Web APIها را نیز کش کرد:
من این کد را توسعه داده ام به صورتی که ویژگیهایی مشابه کلاس OutputCacheAttribute پیدا کند.
نظرات اشتراکها
فرق بین TFS ،SVN و GIT در چیست؟
من از SVN استفاده میکنم و قصد تعویضش رو هم ندارم. SVN مانند Git توزیع شده نیست و ... برای کار من هم اهمیتی ندارد. سازگاری بسیار مناسبی با ویندوز دارد. برنامه Visual SVN Server و همچنین افزونه VisualSVN، کار کردن با SVN را تحت ویندوز و با VS.NET بسیار لذت بخش میکنند. تمام امکانات یک سورس کنترل عالی را هم دارد و من کم و کسری در آن نمیبینم.
اشتراکها
دوره توسعه asp.net mvc
در ادامه دوره مقدماتی mvc که قبلا در سایت به اشتراک گذاشته شده بود.دوره جدیدی رو مایکروسافت منتشر کرده با عنوان توسعه برنامههای MVC که سرفصلهای آن را در بخش زیر مشاهده میکنید:
Mod 01: Introduction to MVC 4 Mod 02: Developing ASP.NET MVC 4 Models Mod 03: Developing MVC 4 Controllers Mod 04: Developing ASP.NET MVC 4 Views Mod 05: Integrating JavaScript and MVC 4 Mod 06: Implementing Web APIs Mod 07: Deploying to Windows Azure Mod 08: Visual Studio 2013/MVC 5 Sneak Peek
هنگامی که دربارهی علم و یادگیری ماشینی فکر میکنیم، دو زبان برنامهنویسی بلافاصله به ذهن متبادر میشوند: پایتون و R. این دو زبان به شکل عمومی از بسیاری از الگوریتمهای یادگیری ماشین رایج، تکنیکهای پیشپردازش دادهها و خیلی بیشتر از اینها پشتیبانی میکنند؛ بنابراین برای -تقریباً- هر مسالهی یادگیری ماشینی مورد استفاده قرار میگیرند.
با اینحال، گاهی فرد یا شرکتی نمیتواند از پایتون یا R استفاده کند که میتواند به یکی از دلایل متعدد، از جمله وجود کد منبع در زبان دیگر یا نداشتن هیچ تجربهای در پایتون یا R باشد. یکی از محبوبترین زبانهای امروزی، #C است که برای بسیاری از کاربردها مورد استفاده قرار میگیرد. مایکروسافت برای استفاده از قدرت یادگیری ماشین در #C، یک بسته را به نام ML.NET ایجاد کرده که همهی قابلیتهای یادگیری ماشین پایه را فراهم میکند.
در این مقاله، به شما نشان خواهم داد که چگونه از ML.NET برای ایجاد یک مدل دستهبندی دوتایی بهره ببرید؛ قابلیتهای AutoML را مورد استفاده قرار داده و از یک مدل Tensorflow با ML.NET استفاده کنید. کد کامل مخصوص مدل دستهبندی دوتایی را میتوانید در GitHub بیابید.
افزودن ML.NET به پروژهی #C
اضافه کردن ML.NET به یک پروژهی #C یا #F آسان است. تنها کار لازم، اضافه کردن بستهی Microsoft.ML یا در برخی موارد، -بسته به نیازمندیهای پروژه- بستههای اضافی مانند: Microsoft.ML.ImageAnalytics, Microsoft.ML.TensorFlow یا Microsoft.ML.OnnxTransformer است.
بارگذاری در یک دیتاست و ایجاد pipeline دادهها
بارگذاری و پیشپردازش یک مجموعه داده در ML.NET کاملا ً متفاوت از زمانی است که با دیگر بستهها / چارچوبهای یادگیری ماشین کار میکنیم. چون ما نیاز داریم به طور واضح، ساختار دادهها را بیان کنیم. برای انجام این کار، فایلی به نام ModelInput.cs را درون یک پوشه به نام DataModels ایجاد کرده و داخل این فایل، همهی ستونهای مجموعه دادههای خود را ثبت خواهیم کرد. برای این مقاله، ما از مجموعه دادههای ردیابی کلاهبرداری کارت اعتباری استفاده میکنیم که میتواند آزادانه از Kaggle بارگیری شود. این مجموعهدادهها شامل ۳۱ ستون است. کلاس تراکنش (۰ یا ۱)، مقدار تراکنش، زمان تراکنش و نیز ۲۸ ویژگی بینام (anonymous).
using Microsoft.ML.Data; namespace CreditCardFraudDetection.DataModels { public class ModelInput { [ColumnName("Time"), LoadColumn(0)] public float Time { get; set; } [ColumnName("V1"), LoadColumn(1)] public float V1 { get; set; } [ColumnName("V2"), LoadColumn(2)] public float V2 { get; set; } [ColumnName("V3"), LoadColumn(3)] public float V3 { get; set; } [ColumnName("V4"), LoadColumn(4)] public float V4 { get; set; } [ColumnName("V5"), LoadColumn(5)] public float V5 { get; set; } [ColumnName("V6"), LoadColumn(6)] public float V6 { get; set; } [ColumnName("V7"), LoadColumn(7)] public float V7 { get; set; } [ColumnName("V8"), LoadColumn(8)] public float V8 { get; set; } [ColumnName("V9"), LoadColumn(9)] public float V9 { get; set; } [ColumnName("V10"), LoadColumn(10)] public float V10 { get; set; } [ColumnName("V11"), LoadColumn(11)] public float V11 { get; set; } [ColumnName("V12"), LoadColumn(12)] public float V12 { get; set; } [ColumnName("V13"), LoadColumn(13)] public float V13 { get; set; } [ColumnName("V14"), LoadColumn(14)] public float V14 { get; set; } [ColumnName("V15"), LoadColumn(15)] public float V15 { get; set; } [ColumnName("V16"), LoadColumn(16)] public float V16 { get; set; } [ColumnName("V17"), LoadColumn(17)] public float V17 { get; set; } [ColumnName("V18"), LoadColumn(18)] public float V18 { get; set; } [ColumnName("V19"), LoadColumn(19)] public float V19 { get; set; } [ColumnName("V20"), LoadColumn(20)] public float V20 { get; set; } [ColumnName("V21"), LoadColumn(21)] public float V21 { get; set; } [ColumnName("V22"), LoadColumn(22)] public float V22 { get; set; } [ColumnName("V23"), LoadColumn(23)] public float V23 { get; set; } [ColumnName("V24"), LoadColumn(24)] public float V24 { get; set; } [ColumnName("V25"), LoadColumn(25)] public float V25 { get; set; } [ColumnName("V26"), LoadColumn(26)] public float V26 { get; set; } [ColumnName("V27"), LoadColumn(27)] public float V27 { get; set; } [ColumnName("V28"), LoadColumn(28)] public float V28 { get; set; } [ColumnName("Amount"), LoadColumn(29)] public float Amount { get; set; } [ColumnName("Class"), LoadColumn(30)] public bool Class { get; set; } } }
در اینجا یک فیلد را برای هر یک از ستونهای داخل مجموعه دادهمان ایجاد میکنیم. نکتهی مهم، تعیین شاخص (Index)، نوع و ستون، به شکل صحیح است. حالا که دادههای ما مدلسازی شدهاند، باید قالب و شکل دادههای خروجی خود را مدل کنیم. این کار میتواند به روشی مشابه با کدهای بالا انجام شود.
using Microsoft.ML.Data; namespace CreditCardFraudDetection.DataModels { public class ModelOutput { [ColumnName("PredictedLabel")] public bool Prediction { get; set; } public float Score { get; set; } } }
ما در اینجا ۲ فیلد داریم. فیلد score نشاندهندهی خروجی به شکل درصد است؛ در حالیکه فیلد prediction از نوع بولی است. اکنون که هر دو داده ورودی و خروجی را مدلسازی کردهایم، میتوانیم دادههای واقعی خود را با استفاده از روش مونتکارلو بارگذاری کنیم.
IDataView trainingDataView = mlContext.Data.LoadFromTextFile<ModelInput>( path: dataFilePath, hasHeader: true, separatorChar: ',', allowQuoting: true, allowSparse: false);
ساخت و آموزش مدل
برای ایجاد و آموزش مدل، نیاز به ایجاد یک pipeline داریم که شامل پیشپردازش دادههای مورد نیاز و الگوریتم آموزش است. برای این مجموعه دادهی خاص، انجام هر پیشپردازش بسیار دشوار است زیرا ۲۸ ویژگی بینام دارد. بنابراین تصمیم گرفتم که آن را ساده نگه دارم و تنها همهی ویژگیها را الحاق کنم (این کار باید در ML.NET انجام شود).
var dataProcessPipeline = mlContext.Transforms.Concatenate("Features", new[] { "Time", "V1", "V2", "V3", "V4", "V5", "V6", "V7", "V8", "V9", "V10", "V11", "V12", "V13", "V14", "V15", "V16", "V17", "V18", "V19", "V20", "V21", "V22", "V23", "V24", "V25", "V26", "V27", "V28", "Amount" });
برای مدل، الگوریتم LightGBM را انتخاب میکنم. این الگوریتم در واقع در Microsoft.ML از ابتدا وجود ندارد؛ بنابراین شما باید Microsoft.ML.LightGbm را نصب کنید تا قادر باشید از آن استفاده کنید.
// Choosing algorithm var trainer = mlContext.BinaryClassification.Trainers.LightGbm(labelColumnName: "Class", featureColumnName: "Features"); // Appending algorithm to pipeline var trainingPipeline = dataProcessPipeline.Append(trainer);
اکنون میتوانیم مدل را با متد Fit، آموزش داده سپس با استفاده از mlContext.model.save ذخیره کنیم:
ITransformer model = trainingPipeline.Fit(trainingDataView);mlContext.Model.Save(model , trainingDataView.Schema, <path>);
ارزیابی مدل
حالا که مدل ما آموزش دیده است، باید عملکرد آن را بررسی کنیم. سادهترین راه برای انجام این کار، استفاده از اعتبارسنجی متقاطع (cross-validation) است. ML.Net به ما روشهای اعتبارسنجی متقاطع را برای انواع مختلف دادههای مختلف، ارایه میدهد. از آنجا که مجموعه دادههای ما یک مجموعه داده دستهبندی دودویی است، ما از روش mlContext.BinaryClassification.CrossValidateNonCalibrated برای امتیازدهی به مدل خود استفاده خواهیم کرد:
var crossValidationResults = mlContext.BinaryClassification.CrossValidateNonCalibrated(trainingDataView, trainingPipeline, numberOfFolds: 5, labelColumnName: "Class");
انجام پیشبینی
پیش بینی دادههای جدید با استفاده از ML.NET واقعاً سرراست و راحت است. ما فقط باید یک PredictionEngine، نمایشی دیگر را از مدل خود که به طور خاص، برای استنباط ساخته شده است، ایجاد کنیم و متد Predict آن را به عنوان یک شی ModelInput فراخوانی کنیم.
var predEngine = mlContext.Model.CreatePredictionEngine<ModelInput, ModelOutput>(mlModel); ModelInput sampleData = new ModelInput() { time = 0, V1 = -1.3598071336738, ... }; ModelOutput predictionResult = predEngine.Predict(sampleData); Console.WriteLine($"Actual value: {sampleData.Class} | Predicted value: {predictionResult.Prediction}");
Auto-ML
نکته جالب دیگر در مورد ML.NET اجرای عالی Auto ML است. با استفاده از Auto ML فقط با مشخص کردن اینکه روی چه مشکلی کار میکنیم و ارائه دادههای خود، میتوانیم راهحلهای اساسی و پایهی یادگیری ماشین را بسازیم.
برای شروع کار با ML خودکار در ML.NET، باید Visual Studio Extension - ML.NET Model Builder (Preview) را بارگیری کنیم. این کار را میتوان از طریق تب extensions انجام داد.
پس از نصب موفقیت آمیز افزونه، با کلیک راست روی پروژهی خود در داخل Solution Ex میتوانیم از Auto ML استفاده کنیم.
برای شروع کار با ML خودکار در ML.NET، باید Visual Studio Extension - ML.NET Model Builder (Preview) را بارگیری کنیم. این کار را میتوان از طریق تب extensions انجام داد.
پس از نصب موفقیت آمیز افزونه، با کلیک راست روی پروژهی خود در داخل Solution Ex میتوانیم از Auto ML استفاده کنیم.
با این کار پنجره Model Builder باز میشود. سازندهی مدل، ما را در روند ساخت یک مدل یادگیری ماشین راهنمایی میکند.
برای کسب اطلاعات در مورد چگونگی گذراندن مراحل مختلف، حتماً آموزش رسمی شروع کار را در سایت مایکروسافت، بررسی کنید. بعد از تمام مراحل، Model Builder به طور خودکار کد را تولید میکند.
استفاده از یک مدل پیشآموزشدادهشدهی تنسورفلو (pre-trained)
نکتهی جالب دیگر در مورد ML.NET این است که به ما امکان استفاده از مدلهای Tensorflow و ONNX را برای استنباط ( inference ) میدهد. برای استفاده از مدل Tensorflow باید Microsoft.ML.TensorFlow را با استفاده از NuGet نصب کنیم. پس از نصب بستههای لازم، میتوانیم با فراخوانی متد Model.LoadTensorFlowModel، یک مدل Tensorflow را بارگذاری کنیم. پس از آن، باید متد ScoreTensorFlowModel را فراخوانی کرده و نام لایهی ورودی و خروجی را به آن ارسال کنیم.
private ITransformer SetupMlnetModel(string tensorFlowModelFilePath) { var pipeline = _mlContext.<preprocess-data> .Append(_mlContext.Model.LoadTensorFlowModel(tensorFlowModelFilePath) .ScoreTensorFlowModel( outputColumnNames: new[]{TensorFlowModelSettings.outputTensorName }, inputColumnNames: new[] { TensorFlowModelSettings.inputTensorName }, addBatchDimensionInput: false)); ITransformer mlModel = pipeline.Fit(CreateEmptyDataView()); return mlModel; }
اطلاعات بیشتر در مورد نحوه استفاده از مدل Tensorflow در ML.NET:
در قسمتهای قبلی (^ و ^ و ^) با Text Template در Visual Studio آشنا شدید. این قسمت برای تکمیل بحث در مورد ابزاری که Microsoft از آن در برنامههای خود از جمله Visual Studio جهت تولید کدهای اتوماتیک استفاده مینماید، صحبت خواهیم کرد.
قبل از آن بد نیست که بدانید چرا این ابزار T4 نام گرفته !
T4 مخفف Text Template Transformation Toolkit میباشد (TTTT). شکل زیر مراحل اجرای یک کد Text Template را توسط T4 نشان میدهد:
برای آزمایش، یک فایل متنی کنار فایل TextTransform.exe با نام Text2.tt ایجاد نمایید و کد زیر را در داخل آن بنویسید:
این مثال بعد از اجرا، لیست تمام Process های جاری سیستم را به همراه Id آنها، چاپ مینماید.
برای تولید فایل خروجی، دستور زیر را در cmd.exe اجرا کنید :
نکته: اگر User شما به این پوشه دسترسی ندارد و کاربر Admin نیستید احتمالا به مشکل بر میخورد. میتوانید فایل TextTransform.exe را در مکان دیگری قرار دهید و دستور را از آن محل اجرا کنید و یا برای پوشهی مذکور دسترسی ایجاد نمایید.
اگر میخواهید بیشتر در مورد معماری T4 بدانید بهتر است مقاله زیر را مطالعه کنید:
قبل از آن بد نیست که بدانید چرا این ابزار T4 نام گرفته !
T4 مخفف Text Template Transformation Toolkit میباشد (TTTT). شکل زیر مراحل اجرای یک کد Text Template را توسط T4 نشان میدهد:
Program Files (x86)\Common Files\microsoft shared\TextTemplating\10.0
برای اطلاع از نحوه کار با TextTransform.exe خارج از محیط Visual Studio بهتر است دستور زیر را در cmd.exe اجرا کنید تا راهنمای استفاده و پارامترهای اختیاری آن را مشاهده نمایید:
TextTransform.exe –h
برای آزمایش، یک فایل متنی کنار فایل TextTransform.exe با نام Text2.tt ایجاد نمایید و کد زیر را در داخل آن بنویسید:
<#@ template debug="true" hostspecific="false" language="C#" #> <#@ output extension=".txt" #> <#@ import namespace="System.Diagnostics" #> Report In : <#= DateTime.Now #> <# Process[] Procs = Process.GetProcesses(); for (int i = 0; i < Procs.Length; i++) { string Pstr = Procs[i].ProcessName + " -|- " + Procs[i].Id + Environment.NewLine ; #><#= Pstr #><# } #>
برای تولید فایل خروجی، دستور زیر را در cmd.exe اجرا کنید :
TextTransform.exe -out Report1.txt Text2.tt
توجه کنید که فایل Text2.tt را کنار فایل TextTransform.exe قرار دهید و بعد از اجرای دستور بالا، باید خروجی در فایل Text2.tt در همان مسیر ایجاد گردد.نکته: اگر User شما به این پوشه دسترسی ندارد و کاربر Admin نیستید احتمالا به مشکل بر میخورد. میتوانید فایل TextTransform.exe را در مکان دیگری قرار دهید و دستور را از آن محل اجرا کنید و یا برای پوشهی مذکور دسترسی ایجاد نمایید.
اگر میخواهید بیشتر در مورد معماری T4 بدانید بهتر است مقاله زیر را مطالعه کنید:
http://www.olegsych.com/2008/05/t4-architecture/
نکته دیگر این که برای Visual Studio، ابزارهایی جهت بهبود کار با Text Templateها وجود دارند که با جستجوی T4 Editor، نمونههایی از آنها را خواهید یافت. tangible T4 Editor نمونه ای از این Pluginها میباشد که به Visual Studio افزوده میگردد و یا یک پروژه Open Source هم برای آشنایی بسیار بیشتر با T4 در t4toolbox.codeplex.com وجود دارد که میتوانید مشاهده کنید.حتما با FxCop که برای آنالیز اسمبلیهای برنامههای دات نتی بکار میرود آشنایی دارید. شبیه به این مورد به صورت افزونهای برای Visual studio 2008 team system نیز موجود است. فقط کافی است Microsoft® Visual Studio Team System 2008 Database Edition GDR R2 را نصب کرده و یک پروژه دیتابیس جدید را شروع کنید (نوع database wizard که یک دیتابیس کامل را import میکند). سپس در برگه build تیک مربوط به code analysis را قرار دهید (شکل 1) و یکبار پروژه را build کنید. به این صورت در پنجره خروجی، اشکالات کدهای T-SQL شما گوشزد میشود (شکل 3). اینکار را با استفاده از منوی Data نیز میتوان انجام داد (شکل 2).
شکل 1- فعال سازی تحلیل و بررسی کد
شکل 2- اجرای تحلیل و بررسی کد
شکل 3- یک نمونه خروجی حاصل از تحلیل و بررسی کد
در این مقاله سعی داریم تا سرعت یافت و جستجوی Viewهای متناظر با هر اکشن را در View Engine، با پیاده سازی قابلیت Caching نتیجه یافت آدرس فیزیکی viewها در درخواستهای متوالی، افزایش دهیم تا عملا بازده سیستم را تا حدودی بهبود ببخشیم.
طی مطالعاتی که بنده بر روی سورس MVC داشتم، به صورت پیش فرض، در زمانیکه پروژه در حالت Release اجرا میشود، نتیجه حاصل از یافت آدرس فیزیکی ویوهای متناظر با اکشن متدها در Application cache ذخیره میشود (HttpContext.Cache). این امر سبب اجتناب از عمل یافت چند باره بر روی آدرس فیزیکی ویوها در درخواستهای متوالی ارسال شده برای رندر یک ویو خواهد شد.
نکته ای که وجود دارد این هست که علاوه بر مفید بودن این امر و بهبود سرعت در درخواستهای متوالی برای اکشن متدها، این عمل با توجه به مشاهدات بنده از سورس MVC علاوه بر مفید بودن، تا حدودی هزینه بر هم هست و هزینهای که متوجه سیستم میشود شامل مسائل مدیریت توکار حافظه کش توسط MVC است که مسائلی مانند سیاستهای مدیریت زمان انقضاء مداخل موجود در حافظهی کش اختصاص داده شده به Lookup Cahching و مدیریت مسائل thread-safe و ... را شامل میشود.
همانطور که میدانید، معمولا تعداد ویوها اینقدر زیاد نیست که Caching نتایج یافت مسیر فیزیکی view ها، حجم زیادی از حافظه Ram را اشغال کند پس با این وجود به نظر میرسد که اشغال کردن این میزان اندک از حافظه در مقابل بهبود سرعت، قابل چشم پوشی است و سیاستهای توکار نامبرده فقط عملا تاثیر منفی در روند Lookup Caching پیشفرض MVC خواهند گذاشت. برای جلوگیری از تاثیرات منفی سیاستهای نامبرده و عملا بهبود سرعت Caching نتایج Lookup آدرس فیزیکی ویوها میتوانیم یک لایه Caching سطح بالاتر به View Engine اضافه کنیم .
خوشبختانه تمامی View Engineهای MVC شامل Web Forms و Razor از کلاس VirtualPathProviderViewEngine مشتق شدهاند که نکته مثبت که توسعه Caching اختصاصی نامبرده را برای ما مقدور میکند. در اینجا خاصیت ( Property ) قابل تنظیم ViewLocationCache از نوع IViewLocationCache هست .
بنابراین ما یک کلاس جدید ایجاد کرده و از اینترفیس IViewLocationCache مشتق میکنیم تا به صورت دلخواه بتوانیم اعضای این اینترفیس را پیاده سازی کنیم .
خوب؛ بنابر این اوصاف، من کلاس یاد شده را به شکل زیر پیاده سازی کردم :
و به صورت زیر میتوانید از آن استفاده کنید:
نکته: فقط به یاد داشته باشید که اگر View جدیدی اضافه کردید یا یک View را حذف کردید، برای جلوگیری از بروز مشکل، حتما و حتما اگر پروژه در مراحل توسعه بر روی IIS قرار دارد app domain را ریاستارت کنید تا حافظه کش مربوط به یافتها پاک شود (و به روز رسانی) تا عدم وجود آدرس فیزیکی View جدید در کش، شما را دچار مشکل نکند.
طی مطالعاتی که بنده بر روی سورس MVC داشتم، به صورت پیش فرض، در زمانیکه پروژه در حالت Release اجرا میشود، نتیجه حاصل از یافت آدرس فیزیکی ویوهای متناظر با اکشن متدها در Application cache ذخیره میشود (HttpContext.Cache). این امر سبب اجتناب از عمل یافت چند باره بر روی آدرس فیزیکی ویوها در درخواستهای متوالی ارسال شده برای رندر یک ویو خواهد شد.
نکته ای که وجود دارد این هست که علاوه بر مفید بودن این امر و بهبود سرعت در درخواستهای متوالی برای اکشن متدها، این عمل با توجه به مشاهدات بنده از سورس MVC علاوه بر مفید بودن، تا حدودی هزینه بر هم هست و هزینهای که متوجه سیستم میشود شامل مسائل مدیریت توکار حافظه کش توسط MVC است که مسائلی مانند سیاستهای مدیریت زمان انقضاء مداخل موجود در حافظهی کش اختصاص داده شده به Lookup Cahching و مدیریت مسائل thread-safe و ... را شامل میشود.
همانطور که میدانید، معمولا تعداد ویوها اینقدر زیاد نیست که Caching نتایج یافت مسیر فیزیکی view ها، حجم زیادی از حافظه Ram را اشغال کند پس با این وجود به نظر میرسد که اشغال کردن این میزان اندک از حافظه در مقابل بهبود سرعت، قابل چشم پوشی است و سیاستهای توکار نامبرده فقط عملا تاثیر منفی در روند Lookup Caching پیشفرض MVC خواهند گذاشت. برای جلوگیری از تاثیرات منفی سیاستهای نامبرده و عملا بهبود سرعت Caching نتایج Lookup آدرس فیزیکی ویوها میتوانیم یک لایه Caching سطح بالاتر به View Engine اضافه کنیم .
خوشبختانه تمامی View Engineهای MVC شامل Web Forms و Razor از کلاس VirtualPathProviderViewEngine مشتق شدهاند که نکته مثبت که توسعه Caching اختصاصی نامبرده را برای ما مقدور میکند. در اینجا خاصیت ( Property ) قابل تنظیم ViewLocationCache از نوع IViewLocationCache هست .
بنابراین ما یک کلاس جدید ایجاد کرده و از اینترفیس IViewLocationCache مشتق میکنیم تا به صورت دلخواه بتوانیم اعضای این اینترفیس را پیاده سازی کنیم .
خوب؛ بنابر این اوصاف، من کلاس یاد شده را به شکل زیر پیاده سازی کردم :
public class CustomViewCache : IViewLocationCache { private readonly static string s_key = "_customLookupCach" + Guid.NewGuid().ToString(); private readonly IViewLocationCache _cache; public CustomViewCache(IViewLocationCache cache) { _cache = cache; } private static IDictionary<string, string> GetRequestCache(HttpContextBase httpContext) { var d = httpContext.Cache[s_key] as IDictionary<string, string>; if (d == null) { d = new Dictionary<string, string>(); httpContext.Cache.Insert(s_key, d, null, Cache.NoAbsoluteExpiration, new TimeSpan(0, 15, 0)); } return d; } public string GetViewLocation(HttpContextBase httpContext, string key) { var d = GetRequestCache(httpContext); string location; if (!d.TryGetValue(key, out location)) { location = _cache.GetViewLocation(httpContext, key); d[key] = location; } return location; } public void InsertViewLocation(HttpContextBase httpContext, string key, string virtualPath) { _cache.InsertViewLocation(httpContext, key, virtualPath); } }
protected void Application_Start() { ViewEngines.Engines.Clear(); var ve = new RazorViewEngine(); ve.ViewLocationCache = new CustomViewCache(ve.ViewLocationCache); ViewEngines.Engines.Add(ve); ... }
نکته: فقط به یاد داشته باشید که اگر View جدیدی اضافه کردید یا یک View را حذف کردید، برای جلوگیری از بروز مشکل، حتما و حتما اگر پروژه در مراحل توسعه بر روی IIS قرار دارد app domain را ریاستارت کنید تا حافظه کش مربوط به یافتها پاک شود (و به روز رسانی) تا عدم وجود آدرس فیزیکی View جدید در کش، شما را دچار مشکل نکند.
در ادامه مطلب تغییر اندازه تصاویر #1 ، در این پست میخواهیم نحوه تغییر اندازه تصاویر را در زمان درخواست کاربر بررسی کنیم.
پس از ایجاد هندلر، فایل کد آن مانند زیر خواهد بود، ما باید کدهای خود را در متد ProcessRequestبنویسیم.
خوب برای نوشتن کد در این مرحله ما باید چند کار انجام دهیم.
1- گرفتن پارامترهای ورودی کاربر جهت تغییر سایز از طریق روشهای انتقال مقادیر بین صفحات (در اینجا استفاده از Query String ).
2-بازیابی تصویر از دیتابیس یا از دیسک به صورت یک آرایه بایتی.
3- تغییر اندازه تصویر مرحله 2 و ارسال تصویر به خروجی.
در این هندلر ما چند متد اضافه کردیم.
پ.ن : هرچند میتوانستیم کد هارا بهبود داده و خیلی بهینهتر بنویسیم اما هدف فقط اشنایی با عمل تغییر اندازه تصویر در زمان اجرا بود، امیدوارم اساتید من ببخشن.
نظرات اقای موسوی تا حدودی اعمال شد و در پست تغییراتی انجام شد.
موفق وموید باشید
در پست قبلی بررسی کردیم که کاربر میتواند در دوحالت تصاویر دریافتی از کاربران سایت را تغییر اندازه دهد، یکی در زمان ذخیره سازی تصاویر بود و دیگری در زمانی که کاربر درخواست نمایش یک تصویر را دارد.
خوب ابتدا فرض میکنیم برای نمایش تصاویر چند حالت داریم مثلا کوچک، متوسط، بزرگ و حالت واقعی (اندازه اصلی).
البته دقت نمایید که این طبقه بندی فرضی بوده و ممکن است برای پروژههای مختلف این طبقه بندی متفاوت باشد. (در این پست قصد فقط اشنایی با تغییر اندازه تصاویر است و شاید کد به درستی refactor نشده باشد).
برای تغییر اندازه تصاویر در زمان اجرا یکی از روش ها، میتواند استفاده از Handler باشد. خوب برای ایجاد Handler ابتدا در پروژه وب خود بروی پروژه راست کلیک کرده، و گزینه New Item را برگزینید، و در پنجره ظاهر شده مانند تصویر زیر گزینه Generic Handler را انتخاب نمایید.
خوب ابتدا فرض میکنیم برای نمایش تصاویر چند حالت داریم مثلا کوچک، متوسط، بزرگ و حالت واقعی (اندازه اصلی).
البته دقت نمایید که این طبقه بندی فرضی بوده و ممکن است برای پروژههای مختلف این طبقه بندی متفاوت باشد. (در این پست قصد فقط اشنایی با تغییر اندازه تصاویر است و شاید کد به درستی refactor نشده باشد).
برای تغییر اندازه تصاویر در زمان اجرا یکی از روش ها، میتواند استفاده از Handler باشد. خوب برای ایجاد Handler ابتدا در پروژه وب خود بروی پروژه راست کلیک کرده، و گزینه New Item را برگزینید، و در پنجره ظاهر شده مانند تصویر زیر گزینه Generic Handler را انتخاب نمایید.
پس از ایجاد هندلر، فایل کد آن مانند زیر خواهد بود، ما باید کدهای خود را در متد ProcessRequestبنویسیم.
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace PWS.UI.Handler { /// <summary> /// Summary description for PhotoHandler /// </summary> public class PhotoHandler : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; context.Response.Write("Hello World"); } public bool IsReusable { get { return false; } } } }
خوب برای نوشتن کد در این مرحله ما باید چند کار انجام دهیم.
1- گرفتن پارامترهای ورودی کاربر جهت تغییر سایز از طریق روشهای انتقال مقادیر بین صفحات (در اینجا استفاده از Query String ).
2-بازیابی تصویر از دیتابیس یا از دیسک به صورت یک آرایه بایتی.
3- تغییر اندازه تصویر مرحله 2 و ارسال تصویر به خروجی.
using System; using System.Data.SqlClient; using System.Diagnostics; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Globalization; using System.IO; using System.Web; namespace PWS.UI.Handler { /// <summary> /// Summary description for PhotoHandler /// </summary> public class PhotoHandler : IHttpHandler { /// <summary> /// بازیابی تصویر اصلی از بانک اطلاعاتی /// </summary> /// <param name="photoId">کد تصویر</param> /// <returns></returns> private byte[] GetImageFromDatabase(int photoId) { using (var connection = new SqlConnection("ConnectionString")) { using (var command = new SqlCommand("Select Photo From tblPhotos Where Id = @PhotoId", connection)) { command.Parameters.Add(new SqlParameter("@PhotoId", photoId)); connection.Open(); var result = command.ExecuteScalar(); return ((byte[])result); } } } /// <summary> /// بازیابی فایل از دیسک /// </summary> /// <param name="photoId">با فرض اینکه نام فایل این است</param> /// <returns></returns> private byte[] GetImageFromDisk(string photoId /* or somting */) { using (var sourceStream = new FileStream("Original File Path + id", FileMode.Open, FileAccess.Read)) { return StreamToByteArray(sourceStream); } } /// <summary> /// Streams to byte array. /// </summary> /// <param name="inputStream">The input stream.</param> /// <returns></returns> /// <exception cref="System.ArgumentException"></exception> static byte[] StreamToByteArray(Stream inputStream) { if (!inputStream.CanRead) { throw new ArgumentException(); } // This is optional if (inputStream.CanSeek) { inputStream.Seek(0, SeekOrigin.Begin); } var output = new byte[inputStream.Length]; int bytesRead = inputStream.Read(output, 0, output.Length); Debug.Assert(bytesRead == output.Length, "Bytes read from stream matches stream length"); return output; } /// <summary> /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler" /> interface. /// </summary> /// <param name="context">An <see cref="T:System.Web.HttpContext" /> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param> public void ProcessRequest(HttpContext context) { // Set up the response settings context.Response.ContentType = "image/jpeg"; context.Response.Cache.SetCacheability(HttpCacheability.Public); context.Response.BufferOutput = false; // مرحله اول int size = 0; switch (context.Request.QueryString["Size"]) { case "S": size = 100; //100px break; case "M": size = 198; //198px break; case "L": size = 500; //500px break; } byte[] changedImage; var id = Convert.ToInt32(context.Request.QueryString["PhotoId"]); byte[] sourceImage = GetImageFromDatabase(id); // یا //byte[] sourceImage = GetImageFromDisk(id.ToString(CultureInfo.InvariantCulture)); //مرحله 2 if (size != 0) //غیر از حالت واقعی تصویر { changedImage = Helpers.ResizeImageFile(sourceImage, size, ImageFormat.Jpeg); } else { changedImage = (byte[])sourceImage.Clone(); } // مرحله 3 if (changedImage == null) return; context.Response.AddHeader("Content-Length", changedImage.Length.ToString(CultureInfo.InvariantCulture)); context.Response.BinaryWrite(changedImage); } public bool IsReusable { get { return false; } } } }
1- متد GetImageFromDatabase: این متد یک کد تصویر را گرفته و آن را از بانک اطلاعاتی بازیابی میکند. (در صورتی که تصویر در بانک ذخیره شده باشد)
2- متد GetImageFromDisk: این متد نام تصویر (با فرض اینکه یک عدد میباشد) را به عنوان پارامتر گرفته و آنرا بازیابی میکند (در صورتی که تصویر در دیسک ذخیره شده باشد.)
3- متد StreamToByteArray: زمانی که تصویر از فایل خوانده میشود به صورت Stream است این متد یک Stream را گرفته و تبدیل به یک آرایه بایتی میکند.
در نهایت در متد ProcessRequestتصویر خوانده شده با توجه به پارامترهای ورودی تغییر اندازه داده شده و در نهایت به خروجی نوشته میشود.
برای استفاده این هندلر، کافی است در توصیر خود به عنوان مسیر رشته ای شبیه زیر وارد نمایید:
2- متد GetImageFromDisk: این متد نام تصویر (با فرض اینکه یک عدد میباشد) را به عنوان پارامتر گرفته و آنرا بازیابی میکند (در صورتی که تصویر در دیسک ذخیره شده باشد.)
3- متد StreamToByteArray: زمانی که تصویر از فایل خوانده میشود به صورت Stream است این متد یک Stream را گرفته و تبدیل به یک آرایه بایتی میکند.
در نهایت در متد ProcessRequestتصویر خوانده شده با توجه به پارامترهای ورودی تغییر اندازه داده شده و در نهایت به خروجی نوشته میشود.
برای استفاده این هندلر، کافی است در توصیر خود به عنوان مسیر رشته ای شبیه زیر وارد نمایید:
PhotoHandler.ashx?PhotoId=10&Size=S مانند <img src='PhotoHandler.ashx?PhotoId=10&Size=S' alt='تصویر ازمایشی' />
نظرات اقای موسوی تا حدودی اعمال شد و در پست تغییراتی انجام شد.
موفق وموید باشید
- زمانیکه یک DLL قابل بارگذاری نیست یعنی یکسری از وابستگیهای آن نصب نشدند. این لیست را هم باید نصب کنید:
Windows dependencies
- وابستگی ویندوز سرور آن علاوه بر نصب NET Core.، نصب Visual C++ Redistributable for Visual Studio 2015 است. بستهای را که در اینجا لینک دادند، قدیمی است. آدرس بستهی به روز و جدید آن (با شماره نگارش 14.0.24215 ^)
Windows dependencies
- وابستگی ویندوز سرور آن علاوه بر نصب NET Core.، نصب Visual C++ Redistributable for Visual Studio 2015 است. بستهای را که در اینجا لینک دادند، قدیمی است. آدرس بستهی به روز و جدید آن (با شماره نگارش 14.0.24215 ^)