The term 'enable-migrations' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, veri fy that the path is correct and try again. At line:1 char:18 + enable-migrations <<<< + CategoryInfo : ObjectNotFound: (enable-migrations:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException
• NotNull/NotEmpty
• Matches (regex)
• InclusiveBetween (range)
• CreditCard
• EqualTo (cross-property equality comparison)
• MaxLength
• MinLength
• Length
اما باید دقت داشت که سایر قابلیتهای این کتابخانه، قابلیت ترجمهی به قواعد unobtrusive java script validation ندارند؛ مانند:
- استفادهی از شرطها توسط متدهای When و یا Unless (برای مثال اگر خاصیت A دارای مقدار 5 بود، آنگاه ورود اطلاعات خاصیت B باید اجباری شود)
- تعریف قواعد اعتبارسنجی سفارشی، برای مثال توسط متد Must
- تعریف RuleSetها (برای مثال تعیین کنید که از یک مدل، فقط تعدادی از خواص آن اعتبارسنجی شوند و نه تمام آنها)
در یک چنین حالتهایی باید کاربر اطلاعات کامل فرم را به سمت سرور ارسال کند و در post-back صورت گرفته، نتایج اعتبارسنجی را دریافت کند.
روش توسعهی اعتبارسنجی سمت کلاینت کتابخانهی FluentValidation جهت سازگاری آن با unobtrusive java script validation
باتوجه به اینکه قواعد سفارشی کتابخانهی FluentValidation، معادل unobtrusive java script validation ای ندارند، در ادامه مثالی را جهت تدارک آنها بررسی میکنیم. برای این منظور، معادل مطلب «ایجاد ویژگیهای اعتبارسنجی سفارشی در ASP.NET Core 3.1 به همراه اعتبارسنجی سمت کلاینت آنها» را بر اساس ویژگیهای کتابخانهی FluentValidation پیاده سازی میکنیم.
1) ایجاد اعتبارسنج سمت سرور
میخواهیم اگر مقدار عددی خاصیتی بیشتر از مقدار عددی خاصیت دیگری بود، اعتبارسنجی آن با شکست مواجه شود. به همین منظور با پیاده سازی یک PropertyValidator سفارشی، LowerThanValidator را به صورت زیر تعریف میکنیم:
using System; using FluentValidation; using FluentValidation.Validators; namespace FluentValidationSample.Models { public class LowerThanValidator : PropertyValidator { public string DependentProperty { get; set; } public LowerThanValidator(string dependentProperty) : base($"باید کمتر از {dependentProperty} باشد") { DependentProperty = dependentProperty; } protected override bool IsValid(PropertyValidatorContext context) { if (context.PropertyValue == null) { return false; } var typeInfo = context.Instance.GetType(); var dependentPropertyValue = Convert.ToInt32(typeInfo.GetProperty(DependentProperty).GetValue(context.Instance, null)); return int.Parse(context.PropertyValue.ToString()) < dependentPropertyValue; } } public static class CustomFluentValidationExtensions { public static IRuleBuilderOptions<T, int> LowerThan<T>( this IRuleBuilder<T, int> ruleBuilder, string dependentProperty) { return ruleBuilder.SetValidator(new LowerThanValidator(dependentProperty)); } } }
- همچنین جهت سهولت کار فراخوانی آن، یک متد الحاقی جدید به نام LowerThan نیز در اینجا تعریف شدهاست.
- البته اگر قصد اعتبارسنجی سمت سرور را ندارید، میتوان در متد IsValid، مقدار true را بازگشت داد.
2) ایجاد متادیتای مورد نیاز جهت unobtrusive java script validation در سمت سرور
در ادامه نیاز است ویژگیهای data-val خاص unobtrusive java script validation را توسط FluentValidation ایجاد کنیم:
using FluentValidation; using FluentValidation.AspNetCore; using FluentValidation.Internal; using FluentValidation.Resources; using FluentValidation.Validators; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; namespace FluentValidationSample.Models { public class LowerThanClientValidator : ClientValidatorBase { private LowerThanValidator LowerThanValidator { get { return (LowerThanValidator)Validator; } } public LowerThanClientValidator(PropertyRule rule, IPropertyValidator validator) : base(rule, validator) { } public override void AddValidation(ClientModelValidationContext context) { MergeAttribute(context.Attributes, "data-val", "true"); MergeAttribute(context.Attributes, "data-val-LowerThan", GetErrorMessage(context)); MergeAttribute(context.Attributes, "data-val-LowerThan-dependentproperty", LowerThanValidator.DependentProperty); } private string GetErrorMessage(ClientModelValidationContext context) { var formatter = ValidatorOptions.MessageFormatterFactory().AppendPropertyName(Rule.GetDisplayName()); string messageTemplate; try { messageTemplate = Validator.Options.ErrorMessageSource.GetString(null); } catch (FluentValidationMessageFormatException) { messageTemplate = ValidatorOptions.LanguageManager.GetStringForValidator<NotEmptyValidator>(); } return formatter.BuildMessage(messageTemplate); } } }
<input dir="rtl" type="number" data-val="true" data-val-lowerthan="باید کمتر از Age باشد" data-val-lowerthan-dependentproperty="Age" data-val-required="'سابقه کار' must not be empty." id="Experience" name="Experience" value="">
3) تعریف مدل کاربران و اعتبارسنجی آن
مدلی که در این مثال از آن استفاده شده، یک چنین تعریفی را دارد و در قسمت اعتبارسنجی خاصیت Experience آن، از متد الحاقی جدید LowerThan استفاده شدهاست:
public class UserModel { [Display(Name = "نام کاربری")] public string Username { get; set; } [Display(Name = "سن")] public int Age { get; set; } [Display(Name = "سابقه کار")] public int Experience { get; set; } } public class UserValidator : AbstractValidator<UserModel> { public UserValidator() { RuleFor(x => x.Username).NotNull(); RuleFor(x => x.Age).NotNull(); RuleFor(x => x.Experience).LowerThan(nameof(UserModel.Age)).NotNull(); } }
4) افزودن اعتبارسنجهای تعریف شده به تنظیمات برنامه
پس از تعریف LowerThanValidator و LowerThanClientValidator، روش افزودن آنها به تنظیمات FluentValidation به صورت زیر است:
namespace FluentValidationSample.Web { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews().AddFluentValidation( fv => { fv.RegisterValidatorsFromAssembly(Assembly.GetExecutingAssembly()); fv.RegisterValidatorsFromAssemblyContaining<RegisterModelValidator>(); fv.RunDefaultMvcValidationAfterFluentValidationExecutes = false; fv.ConfigureClientsideValidation(clientSideValidation => { clientSideValidation.Add( validatorType: typeof(LowerThanValidator), factory: (context, rule, validator) => new LowerThanClientValidator(rule, validator)); }); } ); }
5) تعریف کدهای جاوا اسکریپتی مورد نیاز
پیش از هرکاری، اسکریپتهای فایل layout برنامه باید چنین تعریفی را داشته باشند:
<script src="~/lib/jquery/dist/jquery.min.js"></script> <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script> <script src="~/lib/jquery-validation/dist/jquery.validate.js"></script> <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script> <script src="~/js/site.js" asp-append-version="true"></script>
$.validator.unobtrusive.adapters.add('LowerThan', ['dependentproperty'], function (options) { options.rules['LowerThan'] = { dependentproperty: options.params['dependentproperty'] }; options.messages['LowerThan'] = options.message; }); $.validator.addMethod('LowerThan', function (value, element, parameters) { var dependentProperty = '#' + parameters['dependentproperty']; var dependentControl = $(dependentProperty); if (dependentControl) { var targetvalue = dependentControl.val(); if (parseInt(targetvalue) > parseInt(value)) { return true; } return false; } return true; });
6) آزمایش برنامه
@using FluentValidationSample.Models @model UserModel @{ ViewData["Title"] = "Home Page"; } <div dir="rtl"> <form asp-controller="Home" asp-action="RegisterUser" method="post"> <fieldset class="form-group"> <legend>ثبت نام</legend> <div class="form-group row"> <label asp-for="Username" class="col-md-2 col-form-label text-md-left"></label> <div class="col-md-10"> <input dir="rtl" asp-for="Username" class="form-control" /> <span asp-validation-for="Username" class="text-danger"></span> </div> </div> <div class="form-group row"> <label asp-for="Age" class="col-md-2 col-form-label text-md-left"></label> <div class="col-md-10"> <input dir="rtl" asp-for="Age" class="form-control" /> <span asp-validation-for="Age" class="text-danger"></span> </div> </div> <div class="form-group row"> <label asp-for="Experience" class="col-md-2 col-form-label text-md-left"></label> <div class="col-md-10"> <input dir="rtl" asp-for="Experience" class="form-control" /> <span asp-validation-for="Experience" class="text-danger"></span> </div> </div> <div class="form-group row"> <label class="col-md-2 col-form-label text-md-left"></label> <div class="col-md-10 text-md-right"> <button type="submit" class="btn btn-info col-md-2">ارسال</button> </div> </div> </fieldset> </form> </div>
کار با Kendo UI DataSource
<script type="text/javascript"> $(function () { // var r = "12"; var productsDataSource = new kendo.data.DataSource({ transport: { read: { url: "@Url.Action("GetProducts", "Home")", dataType: "json", contentType: 'application/json; charset=utf-8', type: 'GET', data: { param1: "dfvdf", param2: "val2" } // ارسال اطلاعات اضافی و سفارشی به سرور در حین درخواست }, create: { url: "@Url.Action("PostProduct","Home")", contentType: 'application/json; charset=utf-8', type: "POST" }, update: { url:// function (product) { "@Url.Action("UpdateProduct","Home")",//, +product.Id; //}, contentType: 'application/json; charset=utf-8', type: "PUT" }, destroy: { url: function (p) { return "@Url.Action("DeleteProduct","Home")/" + p.Id; }, contentType: 'application/json; charset=utf-8', type: "DELETE" }, parameterMap: function (options) { return kendo.stringify(options); } }, schema: { parse: function (data) { return data; }, data: "Data", total: "Total", model: { id: "Id", // define the model of the data source. Required for validation and property types. fields: { "Id": { type: "number", editable: false }, //تعیین نوع فیلد برای جستجوی پویا مهم است "Name": { type: "string", validation: { required: true }, editable: true }, "Discription": { type: "string", }, "Title": { type: "string", editable: false }, "GroupName": { type: "string", }, "Link": { type: "string" } } }, batch: false, }, error: function (e) { alert(e.errorThrown.stack); }, pageSize: 5, sort: { field: "Id", dir: "desc" } }); $("#report-grid").kendoGrid({ dataSource: productsDataSource, autoBind: true, scrollable: false, pageable: true, sortable: true, columns: [ { field: "Id", title: "#" }, { field: "Name", title: "Product" } ] }); }); </script>
AngularJS #4
html
<div ng-app="myApp" id="ng-app"> <div ng-controller="MenuCtrl" style="width:300px"> <div style="height:200px;overflow:auto;"> <div ng-repeat="menu in menu" > <div style="float:right;cursor:pointer;" ng-click="remove(menu.ID,$index);">X</div> <a href="#"> <img style="width:32px;" ng-src="/Content/user.gif" alt="{{menu.Title}}"> </a> <div> <h4>{{menu.Title}}</h4> {{menu.Url}} </div> </div> </div> <form action="/Menu/Add" method="post"> <div> <label for="Title">عنوان</label> <input id="Title" type="text" name="Title" ng-model="menu.Title" placeholder="عنوان" /> </div> <div> <label for="Url">آدرس</label> <input id="Url" type="text" name="Url" ng-model="menu.Url" placeholder="آدرس" /> </div> <div> <label for="ParentID">والد</label> <input id="ParentID" type="text" name="ParentID" ng-model="menu.ParentID" placeholder="والد" /> </div> <button type="button" ng-click="addmenu()">ذخیره</button> </form> </div> </div>
var app = angular.module('myApp', ['ngAnimate']); app.controller('MenuCtrl', function ($scope, $http) { $scope.menu = {}; $http.get('/Menu/GetAll').success(function (data) { $scope.menu = data; }) $scope.addmenu= function () { $http.post("/Menu/Add", $scope.menu).success(function () { $scope.menus.push({ Title: $scope.menu.Title, Url: $scope.menu.Url, ParentID: $scope.menu.ParentID }); $scope.menu = {}; }); }; $scope.remove = function (ID, index) { $http.post("/Menu/Remove", { ID: ID }).success(function () { $scope.menu.splice(index, 1); }); }; });
public class MenuController : Controller { // // GET: /Menu/ MyContext _db = new MyContext(); public ActionResult GetAll() { var menu = _db.Menus.ToList(); var result = JsonConvert.SerializeObject(menu, Formatting.Indented, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, }); return Content(result); } public ActionResult Add(Menu menu) { _db.Menus.Add(menu); _db.SaveChanges(); return Json("1"); } public ActionResult Remove(int id) { var selectedMenu = new Menu { ID = id }; _db.Menus.Attach(selectedMenu); _db.Menus.Remove(selectedMenu); _db.SaveChanges(); return Json("1"); } public ActionResult Index() { return View(); } }
مدیریت رجیستری در #C
رجیستری یک پایگاه دادهی سیستمی است که برنامهها، اجزای سیستم و اطلاعات پیکربندی در آن ذخیره و بازیابی میشود. دادههای ذخیره شده در رجیستری مطابق با نسخه ویندوز فرق میکنند. نرمافزارها برای بازیابی، تغییر و پاک کردن رجیستری از API های مختلفی استفاده میکنند. خوشبختانه .NET نیز امکانات لازم برای مدیریت رجیستری را فراهم کرده است.
در صورت رخداد خطا در رجیستری، امکان خراب شدن ویندوز وجود دارد در نتیجه با احتیاط عمل کنید و قبل از هر کاری از رجیستری پشتیبان تهیه نمایید. قبل از شروع به کدنویسی قدری با ساختار رجیستری آشنا شویم تا در ادامه قادر به درک مفاهیم باشیم.
ساختار رجیستری
رجیستری اطلاعات را در ساختار درختی نگاه میدارد. هر گره در درخت، یک کلید ( key ) نامیده میشود. هر کلید میتواند شامل چندین زیرکلید ( subkey ) و چندین مقدار ( value ) باشد. در برخی موارد، وجود یک کلید تمام اطلاعاتی است که نرم افزار بدان نیاز دارد و در برخی موارد، برنامه کلید را باز کرده و مقادیر مربوط به آن کلید را میخواند. یک کلید میتواند هر تعداد مقدار داشته باشد و مقادیر به هر شکلی میتوانند باشند. هر کلید شامل یک یا چند کاراکتر است. نام کلیدها نمیتوانند کاراکتر “\” را داشته باشند. نام هر زیرکلید یکتاست و وابسته به کلیدی است که در سلسله مراتب، بلافاصله بالای آن میآید. نام کلیدها باید انگلیسی باشند اما مقادیر را به هر زبانی میتوان نوشت. در زیر یک نمونه از ساختار رجیستری را مشاهده میکنید که در نرمافزار registry editor به نمایش در آمده است.
هر کدام از درختهای زیر my computer یک کلید است. HKEY_LOCAL_MACHINE دارای زیرکلیدهایی مثل HARDWARE ، SAM و SECURITY است. هر مقدار شامل یک اسم، نوع و دادههای درون آن است. برای مثال MaxObjectNumber از مقادیر زیرکلید HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\VIDEO است. دادههای درون هر مقدار میتواند از انواع باینری، رشتهای و عددی باشد؛ برای مثال MaxObjectNumber یک عدد ۳۲ بیتی است.
محدودیتهای فنی برای نوع و اندازهی اطلاعاتی که در رجیستری ذخیره میگردد، وجود دارد. برنامهها باید اطلاعات اولیه و پیکربندی را در رجیستری نگه دارند وسایر دادهها را در جای دیگر ذخیره کنند. معمولا دادههای بیشتر از یک یا دو کیلوبایت باید در یک فایل ذخیره شوند و با استفاده از یک کلید در رجیستری به آن فایل رجوع کرد. برای حفظ فضای ذخیره سازی باید دادههای شبیه به هم در یک ساختار جمع آوری گردند و ساختار را به عنوان یک مقدار ذخیره کرد؛ به جای آن که هر عضو ساختار را به عنوان یک کلید ذخیره کرد. ذخیره سازی اطلاعات به صورت باینری این امکان را میدهد که اطلاعات را در یک مقدار ذخیره کنید.
اطلاعات رجیستری در پیج فایل ( Page File ) ذخیره میشوند. پیج فایل ناحیهای از حافظه RAM است که میتواند در زمانی که استفاده نمیشود به Hard منتقل شود. اندازهی پیج فایل به وسیلهی مقدار PagedPoolSize در کلید HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management مطابق با جدول زیر تنظیم میگردد.
مقدار | توضیحات |
0×00000000 | سیستم یک مقدار بهینه را تعیین میکند |
0x1–0x20000000 | یک اندازه مشخص برحسب بایت که در این بازه باشد |
0xFFFFFFFF | سیستم بیشترین مقدار ممکن را تشخیص میدهد |
کلیدهای از پیش تعریف شده
یک برنامه قبل از آن که اطلاعاتی را در رجیستری درج کند باید یک کلید را باز کند. برای باز کردن یک کلید میتوان از سایر کلیدهایی که باز هستند، استفاده کرد. سیستم کلیدهایی را از پیش تعریف کرده که همیشه باز هستند. در ادامه کلیدهای از پیش تعریف شده را قدری بررسی میکنیم.
HKEY_CLASSES_ROOT
زیرشاخههای این کلید، انواع اسناد و خصوصیات مربوط به آنها را مشخص میکنند. این شاخه نباید در یک سرویس یا برنامهای که کاربران متعدد دارد، مورد استفاده قرار گیرد.
HKEY_CURRENT_USER
زیرشاخههای این کلید، تنظیمات مربوط به کاربر جاری را مشخص میکنند. این تنظیمات شامل متغیرهای محیطی، اطلاعات دربارهی برنامهها، رنگها، پرینترها، ارتباطات شبکه و تنظیمات برنامههاست. به طور مثال مایکروسافت اطلاعات مربوط به برنامههای خود را در کلید HKEY_CURRENT_USER\Software\Microsoft ذخیره میکند. هر کدام از برنامهها یک زیرکلید در کلید مزبور را به خود اختصاص دادهاند. این شاخه نیز نباید در یک سرویس یا برنامهای که کاربران متعدد دارد، مورد استفاده قرار گیرد.
HKEY_LOCAL_MACHINE
زیرشاخههای این کلید، وضعیت فیزیکی کامپیوتر را مشخص میکنند که شامل حافظهی سیستم، سختافزار و نرمافزارهای نصب شده بر روی سیستم، اطلاعات پیکربندی، تنظیمات ورود به سیستم، اطلاعات امنیتی شبکه و اطلاعات دیگر سیستم است.
HKEY_USERS
زیرشاخههای این کلید، پیکربندی کاربران پیش فرض، جدید، جاری سیستم و به طور کلی همهی کاربران را مشخص میکند.
HKEY_CURRENT_CONFIG
زیرشاخههای این کلید، اطلاعاتی درباره وضعیت سختافزار کامپیوتر در اختیار ما میگذارند. در واقع این کلید نام مستعاری برای کلید HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Hardware Profiles\Current است که در ویندوزهای قبل از ۳.۵۱ NT وجود نداشته است.
کندوهای رجیستری
یک کندو ( Hive ) یک گروه از کلیدها، زیرکلیدها و مقادیر در رجیستری است که یک مجموعه از فایلهای پشتیبان را به همراه دارد. در هنگام بوت ویندوز، اطلاعات از این فایلها استخراج میشوند. شما هم چنین میتوانید با استفاده از Import در منوی فایل registry editor به صورت دستی این کار را انجام دهید. زمانی که ویندوز را خاموش میکنید، اطلاعات کندوها در فایلهای پشتیبان نوشته میشوند. شما میتوانید این کار را به طور دستی با Export در منوی فایل registry editor نیز انجام دهید.
فایلهای پشتیبان همه کندوها به جز HKEY_CURRENT_USER در شاخهی Windows Root\System32\config قرار دارند. فایلهای پشتیبان HKEY_CURRENT_USER در شاخهی System Root\Documents and Settings\Username قرار دارند. پسوند فایلها در این شاخهها، نوع دادههایی که در بر دارند را نشان میدهند. در جدول زیر برخی کندوها و فایلهای پشتیبانشان آمده است.
کندوی رجیستری | فایلهای پشتیبان |
HKEY_CURRENT_CONFIG | System, System.alt, System.log, System.sav |
HKEY_CURRENT_USER | Ntuser.dat, Ntuser.dat.log |
HKEY_LOCAL_MACHINE\SAM | Sam, Sam.log, Sam.sav |
HKEY_LOCAL_MACHINE\Security | Security, Security.log, Security.sav |
HKEY_LOCAL_MACHINE\Software | Software, Software.log, Software.sav |
HKEY_LOCAL_MACHINE\System | System, System.alt, System.log, System.sav |
HKEY_USERS\.DEFAULT | Default, Default.log, Default.sav |
دسته بندی اطلاعات
قبل از قرار دادن اطلاعات در رجیستری باید آنها را به دو دسته اطلاعات کامپیوتر و اطلاعات کاربر تقسیم کرد. با این تقسیم بندی، چندین کاربر میتوانند از یک برنامه استفاده کنند و یا اطلاعات را بر روی شبکه قرار دهند. زمانی که یک برنامه نصب میشود، باید اطلاعات کامپیوتری خود را در شاخه فرضی HKEY_LOCAL_MACHINE\Software\MyCompany\MyProduct\1.0 به گونهای تعریف کند که نام شرکت، نام محصول و نسخه برنامه به خوبی مشخص گردند و هم چنین اطلاعات مربوط به کاربران را در شاخه فرضی HKEY_CURRENT_USER\Software\MyCompany\MyProduct\1.0 نگاه دارد.
باز کردن، ساختن و بستن کلیدها
قبل از آن که بتوانیم یک اطلاعات را در رجیستری درج کنیم، باید یک کلید بسازیم و یا یک کلید موجود را باز کنیم. یک برنامه همیشه به یک کلید به عنوان زیرکلیدی از یک کلید باز رجوع میکند. کلیدهای از پیش تعریف شده همیشه باز هستند.
کلاسهای تعریف شده برای کار با رجیستری در فضانام Microsoft.Win32 قرار دارند. کلاس Microsoft.Win32.Registry مربوط به کلاسهای از پیش تعریف شده و کلاس Microsoft.Win32.RegistryKey برای کار با رجیستری است. برای باز کردن یک کلید از متد RegistryKey.OpenSubKey استفاده میکنیم. به یاد داشته باشید که کلیدهای از پیش تعریف شده همیشه باز هستند و نیازی به باز کردن ندارند. برای ساختن یک کلید از متد RegistryKey.CreateSubKey استفاده میکنیم. دقت کنید زیرکلیدی که میخواهید بسازید، باید به یک کلید باز رجوع کند. برای خاتمه دسترسی به یک کلید، باید آن را ببندیم. برای بستن یک کلید از متد RegistryKey.Close استفاده میکنیم.
اکنون که با ساختار رجیستری و کلاسهای مربوطه در .NET برای کار با رجیستری آشنا شدیم، به کدنویسی میپردازیم.
ساختن یک زیرکلید جدید
برای ساختن یک زیرکلید جدید از متد RegistryKey.CreateSubKey به صورت زیر استفاده میکنیم.
public RegistryKey CreateSubKey( string subkey);
subkey نام و مسیر کلیدی که میخواهید بسازید را مشخص میکند که معمولا به فرم فرضی key name\Company Name\Application Name\version است. این متد یک زیرکلید را برمیگرداند و در صورت بروز خطا مقدار null را برمیگرداند و یک exception را فرا میخواند. خطا به دلایلی چون عدم داشتن مجوز، وجود نداشتن مسیر درخواستی و غیره رخ میدهد. برای بررسی exception ها میتوانید از بلوک try-catch استفاده کنید.
RegistryKey MyReg = Registry .CurrentUser.CreateSubKey( "SOFTWARE\\SomeCompany\\SomeApp\\SomeVer" );
برای دست یابی به کلیدهای از پیش تعریف شده از کلاس Registry مطابق جدول زیر استفاده میکنیم.
فیلد | کلید |
ClassesRoot | HKEY_CLASSES_ROOT |
CurrentUser | HKEY_CURRENT_USER |
LocalMachine | HKEY_LOCAL_MACHINE |
Users | HKEY_USERS |
CurrentConfig | HKEY_CURRENT_CONFIG |
باز کردن زیرکلید موجود
برای باز کردن یک زیرکلید موجود از متد RegistryKey.OpenSubKey به دو صورت استفاده میکنیم.
public RegistryKey OpenSubKey( string name); public RegistryKey OpenSubKey( string name, bool writable);
RegistryKey MyReg = Registry .CurrentUser.OpenSubKey( "SOFTWARE\\SomeCompany\\SomeApp\\SomeVer" , true );
مثال فوق کلید مشخص شده را در شاخهی HKEY_CURRENT_USER و در حالت ویرایش باز میکند.
خواندن اطلاعات از رجیستری
اگر یک شیء RegistryKey سالم داشته باشید میتوانید به مقادیر و اطلاعات درون مقادیر آن دسترسی داشته باشید. برای دست یابی به اطلاعات درون یک مقدار مشخص در کلید از متد RegistryKey.GetValue به دو صورت استفاده کنیم.
public object GetValue( string name); public object GetValue( string name, object defaultValue);
نوشتن اطلاعات در رجیستری
برای نوشتن اطلاعات در یک مقدار از متد RegistryKey.SetValue به صورت زیر استفاده میکنیم.
public void SetValue( string name, object value);
بستن یک کلید
زمانی که دیگر با کلید کاری ندارید و میخواهید تغییرات در رجیستری ثبت گردد باید فرآیندی به نام flushing را انجام دهید. برای انجام این کار به راحتی از متد RegistryKey.Close استفاده کنید.
RegistryKey MyReg = Registry .CurrentUser.CreateSubKey( "SOFTWARE\\SomeCompany\\SomeApp\\SomeVer" ); int nSomeVal = ( int )MyReg.GetValue( "SomeVal" , 0); MyReg.SetValue( "SomeValue" , nSomeVal + 1); MyReg.Close();
پاک کردن یک کلید
برای پاک کردن یک زیرکلید از متد RegistryKey.DeleteSubKey به دو صورت استفاده میکنیم.
public void DeleteSubKey( string subkey); public void DeleteSubKey( string subkey, bool throwOnMissingSubKey);
پاک کردن کل یک درخت
برای پاک کردن کل یک درخت با همهی کلیدهای فرزند و مقادیر آنها از متد RegistryKey.DeleteSubKeyTree به دو صورت استفاده میکنیم.
public void DeleteSubKeyTree( string subkey); public void DeleteSubKeyTree( string subkey, bool throwOnMissingSubKey);
پاک کردن یک مقدار
برای پاک کردن یک مقدار از متد RegistryKey.DeleteValue به دو صورت زیر استفاده میکنیم.
public void DeleteValue( string name); public void DeleteValue( string name, bool throwOnMissingValue);
لیست کردن زیرکلیدها
برای به دست آوردن یک لیست از همه زیرکلیدهای یک شیء RegistryKey از متد RegistryKey.GetSubKeyNames به صورت زیر استفاده میکنیم که یک آرایه رشتهای از نام زیرکلیدها را برمیگرداند.
public string [] GetSubKeyNames();
لیست کردن نام مقادیر
برای به دست آوردن یک لیست از همه مقادیری که در یک شیء RegistryKey وجود دارند از متد RegistryKey.GetValueNames به صورت زیر استفاده میکنیم که یک آرایه رشتهای از نام مقادیر را برمیگرداند.
public string [] GetSubKeyNames();
ثبت تغییرات به صورت دستی
برای ثبت تغییرات یا به اصطلاح فلاش کردن به صورت دستی میتوانید از متد RegistryKey.Flush به صورت زیر استفاده نمایید. زمانی که از RegistryKey.Close استفاده میکنید فرآیند فلاش کردن به طور اتوماتیک انجام میگیرد.
public void Flush();
TypeScript 4.3 منتشر شد
Separate Write Types on Properties
override and the --noImplicitOverride Flag
Template String Type Improvements
ECMAScript #private Class Elements
ConstructorParameters Works on Abstract Classes
Contextual Narrowing for Generics
Always-Truthy Promise Checks
static Index Signatures
.tsbuildinfo Size Improvements
Lazier Calculations in --incremental and --watch Compilations
Import Statement Completions
Editor Support for @link Tags
Go-to-Definition on Non-JavaScript File Paths
Breaking Changes
What’s Next?
- یک راه حل آن، تزریق «private injector: Injector» است و دریافت وهلهی سرویس به صورت (HttpClient)this.injector.get.
- راه حل دوم، دریافت وابستگی مورد نیاز از طریق یک پارامتر (و نه توسط تزریق وابستگیها در سازندهی کلاس):
loadClientConfig(httpClient: HttpClient): Promise<any> {
{ provide: APP_INITIALIZER, useFactory: (config: AppConfigService, httpClient: HttpClient) => () => config.loadClientConfig(httpClient), deps: [AppConfigService, HttpClient], multi: true },
data: { permission: { permittedRoles: ["Admin", "User"] } as AuthGuardPermission }, canActivate: [AuthGuard]
core.js:1427 ERROR Error: Uncaught (in promise): Error: StaticInjectorError[Location]: StaticInjectorError[Location]: NullInjectorError: No provider for Location! Error: StaticInjectorError[Location]: StaticInjectorError[Location]: NullInjectorError: No provider for Location!
از آنجاکه نهایت اندازهی یک سند BSON نمیتواند بیشتر از 16 مگابایت باشد، قابلیتی به نام GridFS ایجاد شدهاست تا بتوان فایلهای باینری را در آن ذخیره کرد. GridFS شامل دو بخش مختلف برای ذخیره اطلاعات یک فایل باینری است:
- fs.chunks که برای ذخیره اطلاعات قطعههای یک فایل باینری به کار میرود.
- fs.files که برای ذخیره اطلاعات و متادیتاها به کار میرود.
قبل از هر چیزی باید بدانید که کتابخانه مربوط به GridFs در یک پکیج جداگانه عرضه شده است و باید آن از طریق nuget نصب کنیم:
install-package MongoDB.Driver.GridFS
برای آپلود یک فایل باینری به داخل سیستم از کد زیر استفاده میکنیم:
var client = new MongoClient(); var db = client.GetDatabase("publisher"); IGridFSBucket bucket = new GridFSBucket(db);
byte[] source=File.ReadAllBytes(@"D:\Untitled.png"); var options = new GridFSUploadOptions { ChunkSizeBytes = 64512, // 63KB Metadata = new BsonDocument { { "CoverType", "Front" }, { "copyrighted", true } } };
مورد دومی که مقداردهی شدهاست، متادیتاها هستند و این قابلیت را داریم که پرس و جوی خود را بر اساس آنها نیز فیلتر کنیم. این خصوصیت مقدار دریافتی از جنس BsonDocument را دریافت میکند. ولی اگر شما برای فایل خود، کلاس اختصاصی برای متادیتاها در نظر گرفتهاید میتوانید از یک Extension Method به نام ToBsonDocument استفاده کنید و شیء خود را به این نوع تبدیل کنید:
var options = new GridFSUploadOptions { ChunkSizeBytes = 64512, // 63KB Metadata = metaData.ToBsonDocument() };
در نهایت آن را آپلود میکنیم:
var id = bucket.UploadFromBytes("GoneWithTheWind", source, options);
نکته: اگر در یک کلاس، چند فیلد از جنس ObjectId دارید، مونگو در بین تشخیص شناسه اصلی سند و شناسه تصاویر، با توجه به نام خصوصیتها و غیره، تا حد زیادی هوشمند عمل میکند. ولی اگر خواستید صریحا شناسه اصلی را ذکر کنید و آن را متمایز از بقیه نشان دهید، میتوانید از خصوصیت BsonId در بالای نام فیلد ID استفاده کنید:
[BsonId] public ObjectId Id { get; set; }
جهت خواندن فایل آپلود شده، تنها کافی است از طریق شناسهی دریافتی در مرحلهی آپلود، اقدام نماییم:
var bytes = bucket.DownloadAsBytes(id);
نکته: تمام متدهای آپلود و دانلود دیتا، هم به صورت آرایه ای از بایتها و هم به صورت استریم میتوانند مورد استفاده قرار بگیرند و به ازای هر کدام، متدهای همزمان و غیرهمزمان نیز موجود هستند.
اگر قصد دارید بر اساس نام داده شده، فایل را دریافت کنید، ممکن است که چندین فایل، تحت یک نام ذخیره شده باشند که میتوانید در حالتهای مختلفی این تصاویر را واکشی نمایید:
0 : فایل اصلی
1: اولین نسخه فایل
2: دومین نسخه فایل
و الی آخر...
1-: جدیدترین نسخه فایل (مقدار پیش فرض)
2-: نسخه ماقبل جدیدترین نسخه فایل
و الی آخر...
منظور از نسخه، فایلهایی با نامی موجود و از قبل ذخیره شده هستند که نسخه جدیدی از فایل قبلی بوده و فایل اول، فایل اصلی محسوب میشود.
برای درک بهتر مسئله، من تصاویر زیر را به ترتیب از سمت راست به سمت چپ، تحت یک نام، وارد سیستم میکنم:
var client = new MongoClient(); var db = client.GetDatabase("publisher"); IGridFSBucket bucket = new GridFSBucket(db); var image=bucket.DownloadAsBytesByName("City of Glass-cover"); File.WriteAllBytes(@"D:\a.jpg",image);
برای مقداردهی خواص بالا به شکل زیر عمل میکنیم:
var client = new MongoClient(); var db = client.GetDatabase("publisher"); IGridFSBucket bucket = new GridFSBucket(db); var options = new GridFSDownloadByNameOptions { Revision = 0 }; var image=bucket.DownloadAsBytesByName("City of Glass-cover",options); File.WriteAllBytes(@"D:\a.jpg",image);
برای بازگردانی تصاویر از طریق مقادیر موجود در متادیتا، باید از کلاس ویژهای به نام GridFSFileInfo استفاده کنیم. در اینجا هم همانند روزهای اول، از کلاس بیلدر جهت ایجاد شرط استفاده میکنیم:
var client = new MongoClient(); var db = client.GetDatabase("publisher"); IGridFSBucket bucket = new GridFSBucket(db); var filter = Builders<GridFSFileInfo>.Filter.Gte(x => x.Length , 600); var sort = Builders<GridFSFileInfo>.Sort.Descending(x => x.UploadDateTime); var options =new GridFSFindOptions() { Limit = 3, Sort = sort }; var cursor = bucket.Find(filter, options); var list = cursor.ToList();
برای یافتن تصاویر بر اساس متادیتاهای تعریف شده، از کد زیر استفاده میکنیم:
var filter = Builders<GridFSFileInfo>.Filter.Eq("metadata.CoverType","Front");
تغییر نام تصاویر
جهت ویرایش یک نام فایل از طریق متدهای زیر اقدام مینماییم:
bucket.Rename(id, newFilename); //یا در حالت غیرهمزمان await bucket.RenameAsync(id, newFilename);
حذف تصویر
bucket.Delete(id); //یا await bucket.DeleteAsync(id);
برای حذف کل bucket از طریق کد زیر اقدام مینماییم:
bucket.Drop(); //یا await bucket.DropAsync();
using MvcApplication3.Models; ... namespace MvcApplication3.Controllers { public class StudentController : Controller { public ActionResult Index() { //var data = new StudentsList(); return View(); } public ActionResult DataList() { var data = new StudentsList(); return PartialView("Pv_DataList", data); } #region Edit [HttpGet] public ActionResult Edit(int? id) { var data = new StudentsList().FirstOrDefault(p => p.Id == id); return PartialView("Pv_Edit", data); } [HttpPost] [AjaxOnly] [ValidateAntiForgeryToken] public ActionResult Edit(StudentModel model) { Thread.Sleep(1000); if (this.ModelState.IsValid) { return Json("ok", JsonRequestBehavior.AllowGet); } return Json("error"); } #endregion } }
@using MvcApplication3.Models @{ ViewBag.Title = "Student Index"; } <style> #div_StudentEditDialogContainer { padding: 15px; background-color: silver; border: 1px solid gray; -webkit-border-radius: pxpx; -moz-border-radius: pxpx; border-radius: 10px; display: none; position: absolute; -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.3); -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.4); box-shadow: 0 1px 3px rgba(0,0,0,0.5); } </style> <h2>Student Index</h2> <div id="div_StudentListViewContainer"> @{ Html.RenderAction("DataList", "Student");} </div> <div id="div_StudentEditDialogContainer"> @{ Html.RenderAction("Edit", "Student");} </div> <div id="div_StudentAddDialogContainer"> </div> <div id="div_StudentRemoveDialogContainer"> </div> <div id="div_StudentSearchDialogContainer"> </div> @section JavaScript{ <script type="text/javascript"> $(document).ready(function () { $("#div_StudentListViewContainer table input[type='submit']").click(function (e) { //show edit dialog e.preventDefault(); var id = $(this).parent().parent().attr('data-studentid'); var url = '@Url.Action("Edit", "Student")'; $.ajax({ type: "GET", url: url, data: { id: id }, beforeSend: function () { //$(waitingPanel).css("display", "block"); }, success: function (html) { if (html == "nodata") { $("#div_StudentEditDialogContainer").html("دانشجویی با این مشخصات یافت نشد!"); } else { $("#div_StudentEditDialogContainer").css("display", "block"); $("#div_StudentEditDialogContainer").html("").append(html); } }, complete: function () { //$(waitingPanel).css("display", "none"); } }); }); }); </script> }
@using MvcApplication3.Models @model StudentModel @{ var postUrl = Url.Action(actionName: "Edit", controllerName: "Student"); } @using (Html.BeginForm(actionName: "Edit", controllerName: "Student", method: FormMethod.Post, htmlAttributes: new { id = "frm_studentEdit" })) { @Html.ValidationSummary(true) @Html.AntiForgeryToken() <div class="editor-label"> @Html.LabelFor(model => model.Id) </div> <div class="editor-field"> @Html.EditorFor(model => model.Id) @Html.ValidationMessageFor(model => model.Id) </div> <div class="editor-label"> @Html.LabelFor(model => model.Code) </div> <div class="editor-field"> @Html.EditorFor(model => model.Code) @Html.ValidationMessageFor(model => model.Code) </div> <div class="editor-label"> @Html.LabelFor(model => model.FullName) </div> <div class="editor-field"> @Html.EditorFor(model => model.FullName) @Html.ValidationMessageFor(model => model.FullName) </div> <div class="editor-label"> @Html.LabelFor(model => model.BirthDate) </div> <div class="editor-field"> @Html.EditorFor(model => model.BirthDate) @Html.ValidationMessageFor(model => model.BirthDate) </div> <div class="editor-label"> @Html.LabelFor(model => model.IsMale) </div> <div class="editor-field"> @Html.EditorFor(model => model.IsMale) @Html.ValidationMessageFor(model => model.IsMale) </div> <p> <input type="submit" id="btn_Save" value="ذخیره" /> <input type="submit" id="btn_Cancel" value="انصراف" /> </p> } <script type="text/javascript"> $(function () { $("#btn_Save").click(function (e) { e.preventDefault(); var button = $(this); $("#frm_studentEdit").PostMvcFormAjax({ postUrl: '@postUrl', loginUrl: '/login', beforePostHandler: function () { //غیرفعال سازی دکمه ارسال button.attr('disabled', 'disabled'); button.val("..."); }, completeHandler: function (data) { //فعال سازی مجدد دکمه ارسال button.removeAttr('disabled'); button.val("ذخیره"); }, errorHandler: function () { alert('خطایی رخ داده است'); } }); }); $("#btn_Cancel").click(function (e) { e.preventDefault(); var button = $(this); $(".editDialog").parent("div").css("display", "none"); }); }); </script>