[HttpPost, AjaxOnly, ValidateAntiForgeryToken, ValidateModelState] public virtual async Task<ActionResult> Create([Bind(Prefix = "Model")]MeetingCreateModel model) { await _service.CreateAsync(model).ConfigureAwait(false); return InformationNotification("Messages.Save.Success"); }
و به شکل زیر پیاده سازی CheckArgumentIsNull را در ApplicationDbContext انجام دادم:
public interface IUnitOfWork : IDisposable { DbSet<TEntity> Set<TEntity>() where TEntity : class; int SaveChanges(bool acceptAllChangesOnSuccess); int SaveChanges(); Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken()); Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()); void CheckArgumentIsNull(string v); }
void CheckArgumentIsNull(string arg) { if (arg == null) throw new ArgumentNullException(nameof(arg)); }
آیا کاری که کردم درست است؟
namespace Microsoft.Extensions.DependencyInjection { public interface IServiceCollection : ICollection<ServiceDescriptor>, IEnumerable<ServiceDescriptor>, IEnumerable, IList<ServiceDescriptor> { } }
ServiceProvider و مؤلفههای درونی آن، از یک مجموعه از ServiceDescriptorها برای برنامهی شما بر اساس سرویسهای ثبت شدهی توسط IServiceCollection استفاده میکنند. ServiceDescriptor حاوی اطلاعاتی در مورد سرویسهای ثبت شدهاست. اگر به کد منبع این کلاس برویم، میبینیم پنج Property اصلی دارد که با استفاده از آنها اطلاعات یک سرویس ثبت و نگهداری میشوند. با استفاده از این اطلاعات در هنگام اجرا ، DI Container به واکشی و ساخت نمونههایی از سرویس درخواستی اقدام میکند:
public Type ImplementationType { get; } public object ImplementationInstance { get; } public Func<IServiceProvider, object> ImplementationFactory { get; } public ServiceLifetime Lifetime { get; } public Type ServiceType { get; }
هر کدام از این Property ها کاربرد خاص خود را دارند:
- · ServiceType : نوع سرویسی را که میخواهیم ثبت شود، مشخص میکنیم ( مثلا اینترفیس IMessageService ) .
- · ImplementionType : نوع پیاده سازی سرویس مورد نظرمان را مشخص میکند ( مثلا کلاس MessageService ).
- · LifeTime : طول حیات سرویس را مشخص میکند. DI Container بر اساس این ویژگی، اقدام به ساخت و از بین بردن نمونههایی از سرویس میکند.
- · ImplementionInstance : نمونهی ساخته شدهی از سرویس است.
- · ImplementionFactory : یک Delegate است که چگونگی ساخته شدن یک نمونه از پیاده سازی سرویس را در خود نگه میدارد. این Delegate یک IServiceProvider را به عنوان ورودی دریافت میکند و یک object را بازگشت میدهد.
به صورت عادی، در سناریوهای معمول ثبت سرویسها درون IServiceCollection، نیازی به استفاده از ServiceDescriptor نیست؛ ولی اگر بخواهیم سرویسها را به روشهای پیشرفتهتری ثبت کنیم، مجبوریم که به صورت مستقیم با این کلاس کار کنیم.
می توانیم یک ServiceDesciriptor را به روشهای زیر تعریف کنیم:
var serviceDescriptor1 = new ServiceDescriptor( typeof(IMessageServiceB), typeof(MessageServiceBB), ServiceLifetime.Scoped); var serviceDescriptor2 = ServiceDescriptor.Describe( typeof(IMessageServiceB), typeof(MessageServiceBB), ServiceLifetime.Scoped); var serviceDescriptor3 = ServiceDescriptor.Singleton(typeof(IMessageServiceB), typeof(MessageServiceBB)); var serviceDescriptor4 = ServiceDescriptor.Singleton<IMessageServiceB, MessageServiceBB>();
همانطور که دیدیم، IServiceCollection در
واقع لیست و مجموعهای از اشیاء است که از نمونههای جنریک IServiceCollection ، IList ، IEnumerable و Ienumberabl ارث بری میکند؛ بنابراین میتوان از متدهای تعریف شدهی در این
اینترفیسها برای IServiceCollection نیز استفاده کرد. حالا ما برای اضافه کردن این سرویسهای جدید،
بدین طریق عمل میکنیم:
Services.Add(serviceDescriptor1);
استفاده از متدهای TryAdd()
به کد زیر نگاه کنید :
services.AddScoped<IMessageServiceB, MessageServiceBA>(); services.AddScoped<IMessageServiceB, MessageServiceBB>();
برای جلوگیری از این خطا میتوانیم از متدهای TryAddSingleton() ، TryAddScoped() و TryAddTransient() استفاده کنیم. این متدها درون فضای نام Microsoft.Extionsion.DependencyInjection.Extension قرار دارند.
عملکرد کلی این
متدها درست مثل متدهای Add() است؛ با این تفاوت که این متد ابتدا IServiceCollection را جستجو میکند و اگر برای type مورد نظر سرویسی ثبت نشده بود،
آن را ثبت میکند:
services.TryAddScoped<IMessageServiceB, MessageServiceBA>(); services.TryAddScoped<IMessageServiceB, MessageServiceBB>();
جایگذاری یک سرویس با نمونهای دیگر
گاهی اوقات میخواهیم یک پیاده سازی دیگر را بجای پیاده سازی فعلی، در DI Container ثبت کنیم. در این حالت از متد Replace() بر روی IServiceCollection برای این کار استفاده میکنیم. این متد فقط یک ServiceDescriptor را به عنوان پارامتر ورودی میگیرد:
services.Replace(serviceDescriptor3);
services.RemoveAll<IMessageServiceB>();
معمولا در پروژههای معمول خودمان نیازی به استفاده از Replace() و RemoveAll() نداریم؛ مگر اینکه بخواهیم پیاده سازی اختصاصی خودمان را برای سرویسهای درونی فریم ورک یا کتابخانههای شخص ثالث، بجای پیاده سازی پیش فرض، ثبت و استفاده کنیم.
AddEnumerable()
فرض کنید دارید برنامهی نوبت دهی یک کلینیک را مینویسید و به صورت پیش فرض از شما خواستهاند که هنگام صدور نوبت، این قوانین را بررسی کنید:
- هر شخص در هفته نتواند بیش از 2 نوبت برای یک تخصص بگیرد.
- اگر شخص در ماه بیش از 3 نوبت رزرو شده داشته باشد ولی مراجعه نکرده باشد، تا پایان ماه، امکان رزرو نوبت را نداشته باشد .
- تعداد نوبتهای ثبت شدهی برای پزشک در آن روز نباید بیش از تعدادی باشد که پزشک پذیرش میکند.
- و ...
یک روش معمول برای پیاده سازی این قابلیت، ساخت سرویسی برای ثبت نوبت است که درون آن متدی برای بررسی کردن قوانین ثبت نام وجود دارد. خب، ما این کار را انجام میدهیم. تستهای واحد و تستهای جامع را هم مینویسیم و بعد برنامه را انتشار میدهیم و همه چیز خوب است؛ تا اینکه مالک محصول یک نیازمندی جدید را میخواهد که در آن ما باید قانون زیر را در هنگام ثبت نوبت بررسی کنیم:
- نوبتهای ثبت شده برای یک شخص نباید دارای تداخل باشند.
در این حالت ما باید دوباره سرویس Register را باز کنیم و به متد بررسی کردن قوانین برویم و دوباره کدهایی را برای بررسی کردن قانون جدید بنویسیم و احتمالا کد ما به این صورت خواهد شد:
public class RegisterAppointmentService : RegisterAppointmentService { public Task<Result> RegisterAsync( PatientInfoDTO patientIfno , DateTimeOffset requestedDateTime , PhysicianId phusicianId ) { CheckRegisterantionRule(patientInfo); // code here } private Task CheckRegisterationRule(PatientInfoDTO patientInfo) { CheckRule1(patientInfo); CheckRule2(patientInfo); CheckRule3(patientInfo); } }
در این حالت باید به ازای هر قانون جدید، به متد CheckRegisterationRule برویم و به ازای هر قانون، یک متد private جدید را بسازیم. مشکل این روش این است که در این حالت ما مجبوریم با هر کم و زیاد شدن قانون، این کلاس را باز کنیم و آن را تغییر دهیم و با هر تغییر دوباره، تستهای واحد آن را دوباره نویسی کنیم. در یک کلام در کد بالا اصول Separation of Concern و Open/Closed Principle را رعایت نمیشود.
یک راهکار این است که یک
سرویس جداگانه را برای بررسی کردن قوانین بنویسیم و آن را به سرویس ثبت نوبت تزریق کنیم:
public class ICheckRegisterationRuleForAppointmentService : ICheckRegisterationRuleForAppointmentService { public Task CheckRegisterantionRule(PatientInfoDTO patientInfo) { CheckRule1(patientInfo); CheckRule2(patientInfo); CheckRule3(patientInfo); } } public class RegisterAppointmentService : IRegisterAppointmentService { private ICheckRegisterationRuleForAppointmentService _ruleChecker; public RegisterAppointmentService (RegisterAppointmentService ruleChecker) { _ruleChecker = ruleChecker; } public Task<Result> RegisterAsync( PatientInfoDTO patientIfno , DateTimeOffset requestedDateTime , PhysicianId phusicianId ) { _ruleChecker.CheckRegisterantionRule(patientInfo); // code here } }
با این کار وظیفهی چک کردن قوانین و وظیفهی ثبت و ذخیره سازی قوانین را از یکدیگر جدا کردیم؛ ولی همچنان در سرویس بررسی کردن قوانین، اصل Open/Closed رعایت نشدهاست. خب راه حل چیست !؟
یکی از راه حلهای موجود، استفاده از الگوی قوانین یا Rule Pattern است. برای اجرای این الگو، میتوانیم با تعریف یک اینترفیس کلی برای بررسی کردن قانون، به ازای هر قانون یک پیاده سازی اختصاصی را داشته باشیم:
interface IAppointmentRegisterationRule { Task CheckRule(PatientInfo patientIfno); } public class AppointmentRegisterationRule1 : IAppointmentRegisterationRule { public Task CheckRule(PatientInfo patientIfno) { Console.WriteLine("Rule 1 is checked"); return Task.CompletedTask; } } public class AppointmentRegisterationRule2 : IAppointmentRegisterationRule { public Task CheckRule(PatientInfo patientIfno) {
Console.WriteLine("Rule 2 is checked"); return Task.CompletedTask; } } public class AppointmentRegisterationRule3 : IAppointmentRegisterationRule { public Task CheckRule(PatientInfo patientIfno) { Console.WriteLine("Rule 3 is checked"); return Task.CompletedTask; } } public class AppointmentRegisterationRule4 : IAppointmentRegisterationRule { public Task CheckRule(PatientInfo patientIfno) { Console.WriteLine("Rule 4 is checked"); return Task.CompletedTask; } }
services.AddScoped<IAppointmentRegisterationRule, AppointmentRegisterationRule1>(); services.AddScoped<IAppointmentRegisterationRule, AppointmentRegisterationRule2>(); services.AddScoped<IAppointmentRegisterationRule, AppointmentRegisterationRule3>(); services.AddScoped<IAppointmentRegisterationRule, AppointmentRegisterationRule4>();
public class CheckRegisterationRuleForAppointmentService : ICheckRegisterationRuleForAppointmentService { private IEnumerable<IAppointmentRegisterationRule> _rules ; public CheckRegisterationRuleForAppointmentService(IEnumerable<IAppointmentRegisterationRule> rules) { _rules = rules; } public Task CheckRegisterantionRule(PatientInfoDTO patientInfo) { foreach(var rule in rules) { rule.CheckRule(patientInfo); } } }
کد بالا به
نظر کامل میآید ولی مشکلی دارد! اگر در DI Container برای IAppointmentRegisterationRule یک قانون را دو یا چند بار ثبت کنیم، در هر بار بررسی کردن قوانین، آن را به همان تعداد بررسی میکند و اگر این فرآیند منابع زیادی را به
کار میگیرد، میتواند عملکرد برنامهی ما را به هم بریزد. برای جلوگیری از این مشکل، از متد TryAddEnumerabl()
استفاده میکنیم که لیستی از ServiceDescriptor ها را میگیرد و هر serviceDescriptor را فقط یکبار ثبت میکند:
services.TryAddEnumerable(new[] { ServiceDescriptor.Scoped(typeof(IAppointmentRegisterationRule), typeof(AppointmentRegisterationRule1)), ServiceDescriptor.Scoped(typeof(IAppointmentRegisterationRule), typeof(AppointmentRegisterationRule2)), ServiceDescriptor.Scoped(typeof(IAppointmentRegisterationRule), typeof(AppointmentRegisterationRule3)), ServiceDescriptor.Scoped(typeof(IAppointmentRegisterationRule), typeof(AppointmentRegisterationRule4)), });
ASP.NET MVC #13
من الان واسه ثبت کاربر توی دیتابیس یه کلاس دارم به اسم CustomProfile که تمامی خصوصیتهای یک کاربر رو توی یه شیئ از این کلاس میریزم و بعد توی کدهای لایه مدل، این شیئ رو توسط کلاس Membership و ProfileBase خود دات نت توی پایگاه داده میریزیم. الان منظور شما اینه که من باید برای ویرایش یک کلاس CustomProfile دیگه با همون مشخصات بسازم و متد اعتبار سنجی برای Username اون رو یه متد دیگه قرار بدم ؟ ببینید کدهای من اینه:
public class CustomProfile { [Required(ErrorMessage="*")] [StringLength(50)] [System.Web.Mvc.Remote(action: "CheckUserName", controller: "User", HttpMethod = "POST", ErrorMessage = "این نام کاربری وجود دارد")] public string Username{ get; set; } [Required(ErrorMessage = "*")] public string Password{ get; set; } [Required(ErrorMessage = "*")] public string FirstName{ get; set; } [Required(ErrorMessage = "*")] public string LastName{ get; set; } [Required(ErrorMessage = "*")] [StringLength(10, ErrorMessage = "کدملی باید 10 رقم باشد")] public string NationalCode { get; set; } [Required(ErrorMessage = "*")] public string Address { get; set; } [RegularExpression(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*", ErrorMessage = "آدرس ایمیل معتبر نیست")] [System.Web.Mvc.Remote(action: "CheckEmail", controller: "User", AdditionalFields = "Username", HttpMethod = "POST", ErrorMessage = "این ایمیل وجود دارد")] public string Email{ get; set; } [StringLength(11, ErrorMessage = "تلفن باید 11 رقم باشد")] public string PhoneNo{ get; set; } [StringLength(11, ErrorMessage = "موبایل باید 11 رقم باشد")] public string MobileNo{ get; set; } public string[] Roles{ get; set; } public string LastActivityDate{ get; set; } public string LastLoginDate { get; set; } public string CreationDate { get; set; } public bool IsLockedOut{ get; set; } }
اینم کدViewها:
@model Sama.Models.CustomProfile @{ ViewBag.Title = "Create"; Layout = "~/Views/Shared/_Layout.cshtml"; } <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> @using (Html.BeginForm(actionName: "Create", controllerName: "User")) { @Html.ValidationSummary(true) <fieldset> <legend>کاربر جدید</legend> <div> <table> <tr> <td> <div> @Html.LabelFor(model => model.Username, "نام کاربری") </div> </td> <td> <div> @Html.EditorFor(model => model.Username) @Html.ValidationMessageFor(model => model.Username) </div> </td> </tr> <tr> <td> <div> @Html.LabelFor(model => model.Password, "کلمه عبور") </div> </td> <td> <div> @Html.PasswordFor(model => model.Password) @Html.ValidationMessageFor(model => model.Password) </div> </td> </tr> <tr> <td> <div> @Html.LabelFor(model => model.FirstName, "نام") </div> </td> <td> <div> @Html.EditorFor(model => model.FirstName) @Html.ValidationMessageFor(model => model.FirstName) </div> </td> </tr> <tr> <td> <div> @Html.LabelFor(model => model.LastName, "نام خانوادگی") </div> </td> <td> <div> @Html.EditorFor(model => model.LastName) @Html.ValidationMessageFor(model => model.LastName) </div> </td> </tr> <tr> <td> <div> @Html.LabelFor(model => model.NationalCode, "کد ملی") </div> </td> <td> <div> @Html.EditorFor(model => model.NationalCode) @Html.ValidationMessageFor(model => model.NationalCode) </div> </td> </tr> <tr> <td> <div> @Html.LabelFor(model => model.Email, "ایمیل") </div> </td> <td> <div> @Html.EditorFor(model => model.Email) @Html.ValidationMessageFor(model => model.Email) </div> </td> </tr> <tr> <td> <div> @Html.LabelFor(model => model.PhoneNo, "تلفن") </div> </td> <td> <div> @Html.EditorFor(model => model.PhoneNo) @Html.ValidationMessageFor(model => model.PhoneNo) </div> </td> </tr> <tr> <td> <div> @Html.LabelFor(model => model.MobileNo, "موبایل") </div> </td> <td> <div> @Html.EditorFor(model => model.MobileNo) @Html.ValidationMessageFor(model => model.MobileNo) </div> </td> </tr> <tr> <td> <div> @Html.LabelFor(model => model.Address, "آدرس") </div> </td> <td> <div> @Html.TextAreaFor(model => model.Address, 5, 30, null) @Html.ValidationMessageFor(model => model.Address) </div> </td> </tr> <tr> <td> نقشها </td> <td> @Helper.CheckBoxList("Roles", (List<SelectListItem>)ViewBag.Roles) </td> </tr> <tr> <td> </td> <td> <input type="submit" value="ذخیره" class="btn btn-primary" /> </td> </tr> </table> </div> </fieldset> }
حالا اگر من درست متوجه شده باشم باید برای Edit ، از یک ViewModel دیگه مثلا CustomProfileEdit استفاده کنم و اونجا متد اعتبار سنجی دیگه ای تعریف کنم. منظور شما همین بود ؟
203 00:16:38,731 --> 00:16:41,325 <i>Happy Christmas, your arse I pray God it's our last</i>
- عدم حذف تگها ، گاها برنامه نویسها از تگ هایی چون Bold,italic,underline,color استفاده میکنند که معدود برنامههایی آن را پشتیبانی کرده و تلویزیون و پلیرها هم که اصلا پشتیبانی نمیکنند و باعث میشود که متن روی تلویزیون مثل کد html ظاهر شود
- بعضی جملات دوبار روی صفحه ظاهر میشوند.
- تنها یک فایل را در هر زمان تبدیل میکند. مثلا اگر یک سریال چند قسمته داشته باشید، برای هر قسمت باید زیرنویس را انتخاب کرده و تبدیل کنید، در صورتی که میتوان دستور داد تمام زیرنویسهای داخل دایرکتوری را تبدیل کرد یا چند زیرنویس را برای این منظور انتخاب کرد.
نحوهی خواندن زیرنویس با کدنویسی
203 00:16:38,731 --> 00:16:41,325 <i>Happy Christmas, your arse I pray God it's our last</i>
private readonly static Regex regex_srt = new Regex(@"(?<sequence>\d+)\r\n(?<start>\d{2}\:\d{2}\:\d{2},\d{3}) --\> " + @"(?<end>\d{2}\:\d{2}\:\d{2},\d{3})\r\n(?<text>[\s\S]*?)\r\n\r\n", RegexOptions.Compiled); public string ToUnicode(string lines) { string subtitle= regex_srt.Replace(lines,delegate(Match m) { string text = m.Groups["text"].Value; //1.remove tags text = CleanScriptTags(text); //2.replace letters PersianReshape reshaper = new PersianReshape(); text = reshaper.reshape(text); string[] splitedlines = text.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); text = ""; foreach (string line in splitedlines) { //3.reverse tags text += ReverseText(reshaper.reshape(line))+Environment.NewLine ; } return string.Format("{0}\r\n{1} --> {2}\r\n", m.Groups["sequence"], m.Groups["start"].Value, m.Groups["end"]) + text + Environment.NewLine+Environment.NewLine ; } ); return subtitle; }
m.Groups["text"].Value
private static readonly Regex regex_tags = new Regex("<.*?>", RegexOptions.Compiled); private string CleanScriptTags(string html) { return regex_tags.Replace(html, string.Empty); }
PersianReshape reshaper = new PersianReshape(); text = reshaper.reshape(text); string[] splitedlines = text.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); text = ""; foreach (string line in splitedlines) { //3.reverse tags text += ReverseText(reshaper.reshape(line))+Environment.NewLine ; }
Dictionary<int ,int> letters=new Dictionary<int, int>(); //0=0x0 ,1=1x0 ,2=0x1 ,3=1x1 private void FillPrimaryTable() { //آ letters.Add(1570, 65153); //ا letters.Add(1575, 65166); //أ letters.Add(1571, 65155); //ب letters.Add(1576, 65167); //ت letters.Add(1578, 65173); //ث letters.Add(1579, 65177); //ج letters.Add(1580, 65181); ..... } Dictionary<int,byte> specialchars=new Dictionary<int, byte>(); private void SetSpecialChars() { //آ specialchars.Add(1570, 0); //ا specialchars.Add(1575, 0); //د2 specialchars.Add(1583, 1); //ذ2 specialchars.Add(1584, 1); //ر2 specialchars.Add(1585, 1); //ز2 specialchars.Add(1586, 1); //ژ specialchars.Add(1688, 1); //و2 specialchars.Add(1608, 1); //أ specialchars.Add(1571, 1); }
در آن متد هر بار یک حرف را انتخاب میکرد و حرف قبلی و بعدی آن را ارسال میکرد تا تابع CalculateIncrease آن را محاسبه کرده و کاراکتر نهایی را باز گرداند و به متغیر finalText اضافه میکرد. ولی در حین نوشتن، زمانی را به یاد آوردم که اندروید به تازگی آمده بود و هنوز در آن زمان از زبان فارسی پشتیبانی نمیکرد و حروف برنامههایی که مینوشتیم به صورت جدا از هم بود و همین مشکل را داشت که ما این مشکل را با استفاده از یک کلاس جاوا که دوست عزیزی آن را در اینجا به اشتراک گذاشته بود، حل میکردیم. پس به این صورت بود که از ادامهی نوشتن کلاس انصراف دادم و از یک کلاس دقیقتر و آماده استفاده کردم.
PersianReshape reshaper = new PersianReshape(); text = reshaper.reshape(text);
//3.reverse tags text = ReverseText(text);
string[] splitedlines = text.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); text = ""; foreach (string line in splitedlines) { //3.reverse tags text += ReverseText(reshaper.reshape(line))+Environment.NewLine ; }
private string Reverse(string text) { return Reverse(text,0,text.Length); } private string Reverse(string text,int start,int end) { if (end < start) return text; string reverseText = ""; for (int i = end-1; i >=start; i--) { reverseText += text[i]; } return reverseText; }
private string ReverseText(string text) { char[] chararray = text.ToCharArray(); string reverseText = ""; bool prefixcomp = false; bool postfixcomp = false; string prefix = ""; string postfix = ""; #region get prefix symbols for (int i = 0; i < chararray.Length; i++) { if (!prefixcomp) { char ch =(char) chararray.GetValue(i) ; if (ch< 130) { prefix += chararray.GetValue(i); } else { prefixcomp = true; break; } } } #endregion }
#region get postfix symbols for (int i = chararray.Length - 1; i >-1 ; i--) { if (!postfixcomp && prefix.Length!=text.Length) { char ch = (char)chararray.GetValue(i); if (ch < 130) { postfix += chararray.GetValue(i); } else { postfixcomp = true; break; } } } #endregion
#region reverse text reverseText = Reverse(text, prefix.Length, text.Length-postfix.Length); reverseText = unTagetdLettersRegex.Replace(reverseText, delegate(Match m) { return Reverse(m.Value); }); #endregion
private static readonly Regex unTagetdLettersRegex = new Regex(@"[A-Za-z0-9]+", RegexOptions.Compiled);
return prefix+ reverseText+postfix;
private static readonly Regex unTagetdLettersRegex = new Regex(@"[A-Za-z0-9]+", RegexOptions.Compiled); private string ReverseText(string text) { char[] chararray = text.ToCharArray(); string reverseText = ""; bool prefixcomp = false; bool postfixcomp = false; string prefix = ""; string postfix = ""; #region get prefix symbols for (int i = 0; i < chararray.Length; i++) { if (!prefixcomp) { char ch =(char) chararray.GetValue(i) ; if (ch< 130) { prefix += chararray.GetValue(i); } else { prefixcomp = true; break; } } } #endregion #region get postfix symbols for (int i = chararray.Length - 1; i >-1 ; i--) { if (!postfixcomp && prefix.Length!=text.Length) { char ch = (char)chararray.GetValue(i); if (ch < 130) { postfix += chararray.GetValue(i); } else { postfixcomp = true; break; } } } #endregion #region reverse text reverseText = Reverse(text, prefix.Length, text.Length-postfix.Length); reverseText = unTagetdLettersRegex.Replace(reverseText, delegate(Match m) { return Reverse(m.Value); }); #endregion return prefix+ reverseText+postfix; }
return string.Format("{0}\r\n{1} --> {2}\r\n", m.Groups["sequence"], m.Groups["start"].Value, m.Groups["end"]) + text + Environment.NewLine+Environment.NewLine ;
نمایی از برنامهی نهایی
روی پلیر یا تلویزیون
نکتهی نهایی: هنگام تست زیرنویس روی فیلم متوجه شدم پلیر خطوط بلند را که در صفحهی نمایش جا نمیشود، میشکند و به دو خط تقسیم میکند. ولی نکتهی خنده دار اینجا بود که خط اول را پایین میاندازد و خط دوم را بالا. برای همین این تکه کد را نوشتم و به طور جداگانه در گیت هاب هم قرار دادهام.
این تکه کد را هم بعد از
//1.remove tags text = CleanScriptTags(text);
text =StringUtils.ConvertToMultiLine(text);
کد متد ConvertToMultiline:
namespace Utils { public static class StringUtils { public static string ConvertToMultiLine(String text, int min = 30, int max = 40) { if (text.Trim() == "") return text; string[] words = text.Split(new string[] { " " }, StringSplitOptions.None); string text1 = ""; string text2 = ""; foreach (string w in words) { if (text1.Length < min) { if (text1.Length == 0) { text1 = w; continue; } if (w.Length + text1.Length <= max) text1 += " " + w; } else text2 += w + " "; } text1 = text1.Trim(); text2 = text2.Trim(); if (text2.Length > 0) { text1 += Environment.NewLine + ConvertToMultiLine(text2, min, max); } return text1; } } }
برنامه مورد نظر را به طور کامل میتوانید از اینجا یا اینجا به صورت فایل نهایی و هم سورس دریافت کنید.
C# 6 - String Interpolation
در C# 11، متن درون {} یک عبارت string interpolation، میتواند در طی چند سطر نیز تعریف شود و متن بین {} به صورت یک عبارت #C تفسیر میشود. در اینجا هر عبارت معتبر #C ای را میتوان بکار برد. در این حالت تعریف عبارات LINQ و pattern matching switch expressions سادهتر میشوند. برای مثال:
namespace CS11Tests; public static class NewLinesStringInterpolations { public static void Test() { var month = 8; var season = $"The season is {month switch { >= 1 and <= 3 => "spring", >= 4 and <= 6 => "summer", >= 7 and <= 9 => "autumn", 10 or 11 or 12 => "winter", _ => "Wrong month number", }}."; Console.WriteLine(season); int[] numbers = { 1, 2, 3, 4, 5, 6 }; var message = $"The reversed even values of {nameof(numbers)} are {string.Join(", ", numbers.Where(n => n % 2 == 0).Reverse())}."; Console.WriteLine(message); } }
ASP.NET MVC #24
مروری بر نمونه سؤالات ASP.NET MVC امتحانات مایکروسافت در چند سال اخیر
در قسمت آخر سری ASP.NET MVC بد نیست مروری داشته باشیم بر نمونه سؤالات امتحانات مایکروسافت؛ امتحانات 70-515 و 70-519 که در آنها تعدادی از سؤالات به ASP.NET MVC اختصاص دارند. در این سؤالات امکان انتخاب بیش از یک گزینه نیز وجود دارد.
1) شما در حال توسعه یک برنامهی ASP.NET MVC هستید. باید درخواست Ajax ایی از صفحهای صادر شده و خروجی زیر را از اکشن متدی دریافت کند:
["Adventure Works","Contoso"]
کدام نوع خروجی اکشن متد زیر را برای اینکار مناسب میدانید؟
a) AjaxHelper
b) XDocument
c) JsonResult
d) DataContractJsonSerializer
2) شما در حال طراحی یک برنامه ASP.NET MVC هستید. محتوای یک View باید بر اساس نیازمندیهای زیر تشکیل شود:
الف) ارائه محتوای رندر شده user controls/partial views به مرورگر
ب) کار انتخاب user controls/partial views مناسب در اکشن متد کنترلر باید انجام شود
استفاده از کدام روش زیر را توصیه میکنید؟
a) Use the Html.RenderPartial extension method
b) Use the Html.RenderAction extension method
c) Use the PartialViewResult class
d) Use the ContentResult class
3) در حین طراحی یک برنامه ASP.NET MVC، نیاز است منطق مدیریت استثناهای رخ داده و همچنین ثبت وقایع مرتبط را در یک مکان یا کلاس مرکزی مدیریت کنید. کدام روش زیر را پیشنهاد میدهید؟
a) استفاده از try/catch در تمام متدها
b) تحریف متد OnException در کنترلرها
c) مزین سازی تمام کنترلرها به ویژگی HandleError سفارشی شده
d) مزین سازی تمام کنترلرها به ویژگی HandleError پیش فرض
4) شما در حال توزیع برنامهی ASP.NET MVC خود جهت اجرا بر روی IIS 6.x هستید. چه ملاحظاتی را باید مدنظر داشته باشید تا برنامه به درستی کار کند؟
a) تنظیم IIS به نحویکه تمام درخواستها را بر اساس wildcard خاصی به aspnet_isapi.dll هدایت کند.
b) تنظیم IIS به نحویکه تمام درخواستها را بر اساس wildcard خاصی به aspnet_wp.exe هدایت کند.
c) تغییر برنامه به نحویکه تمام درخواستها را به یک HttpHandler خاص هدایت کند.
d) تغییر برنامه به نحویکه تمام درخواستها را به یک HttpModule خاص هدایت کند.
5) شما در حال توسعه برنامهی ASP.NET MVC هستید که در پوشه Views/Shared/DisplayTemplates آن، فایلی به نام score.cshtml به عنوان یک templated helper نمایش سفارشی اعداد صحیح تعریف شده است. مدل برنامه هم مطابق تعاریف زیر است:
public class Player
{
public String Name { get; set; }
public int LastScore { get; set; }
public int HighScore { get; set; }
}
در اینجا اگر نیاز باشد تا فایل score.cshtml یاد شده به صورت خودکار به خاصیت LastScore در حین فراخوانی متد HtmlHelper.DisplayForModel اعمال شود، چه روشی را پیشنهاد میدهید؟
a) فایل score.cshtml باید به LastScore.cshtml تغییر نام یابد.
b) فایل یاد شده باید از پوشه Views/Shared/DisplayTemplates به پوشه Views/Player/DisplayTemplates منتقل شود.
c) باید از ویژگی UIHint به همراه مقدار score جهت مزین سازی خاصیت LastScore استفاده کرد.
[UIHint("Score")]
[Display(Name="LastScore", ShortName="Score")]
6) شما در حال طراحی برنامهی ASP.NET MVC هستید که در آن متد Edit کنترلری باید تنها توسط کاربران اعتبارسنجی شده قابل دسترسی باشد. استفاده از کدام دو گزینه زیر را برای این منظور توصیه میکنید؟
a) [Authorize(Users = "")]
b) [Authorize(Roles = "")]
c) [Authorize(Users = "*")]
d) [Authorize(Roles = "*")]
7) قطعه کد HTML زیر را درنظر بگیرید:
<span id="ref">
<a name=Reference>Check out</a>
the FAQ on
<a href="http://www.contoso.com">
Contoso</a>'s web site for more information:
<a href="http://www.contoso.com/faq">FAQ</a>.
</span>
<a href="http://www.contoso.com/home">Home</a>
قصد داریم به کمک jQuery در span ایی با id مساوی ref، متن تمام لینکها را ضخیم کنیم. کدام گزینه زیر را پیشنهاد میدهید؟
a) $("#ref").filter("a[href]").bold();
b) $("ref").filter("a").css("bold");
c) $("a").css({fontWeight:"bold"});
d) $("#ref a[href]").css({fontWeight:"bold"});
یک روش ساده برای خلق انیمیشنهای CSS
using System; using System.Linq; public class Test { public static void Main() { var beatles = (new[] { new {id = 1, inst = "guitar", name = "john"}, new {id = 2, inst = "guitar", name = "george"}, new {id = 3, inst = "guitar", name = "paul"}, new {id = 4, inst = "drums", name = "ringo"}, new {id = 5, inst = "drums", name = "pete"} }); var result = beatles .GroupBy(g => g.inst) .Select(c => c.OrderBy(o => o.id).Select((v, i) => new { i, v }).ToList()) .SelectMany(c => c) .Select(c => new { c.v.id, c.v.inst, c.v.name, rn = c.i + 1 }) .ToList(); Console.WriteLine("id | inst \t| name \t| rn"); foreach (var row in result) { Console.WriteLine($"{row.id} | {row.inst}\t| {row.name} \t| {row.rn}"); } } }
id | inst | name | rn 1 | guitar| john | 1 2 | guitar| george | 2 3 | guitar| paul | 3 4 | drums| ringo | 1 5 | drums| pete | 2
public class Person { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } }
public class PeopleRepository { private List<Person> _people; public PeopleRepository() { _people = new List<Person> { new Person{ Id=1,Age=28,Name="Uthman"}, new Person{ Id=2,Age=27,Name="Vahid"}, new Person{ Id=3,Age=26,Name="Hadi"}, new Person{ Id=4,Age=25,Name="Saman"}, new Person{ Id=5,Age=20,Name="Sirwan"}, }; } public List<Person> GetAll() { return _people; } }
[Route("api/people")] public class PeopleController : Controller { PeopleRepository _repository; public PeopleController() { _repository = new PeopleRepository(); } [HttpGet("")] public IActionResult Get() { return Ok(_repository.GetAll()); } [HttpGet("{id:int}")] public IActionResult Get(int id) { return Ok(_repository.GetAll().FirstOrDefault(p => p.Id == id)); } [HttpPost] public IActionResult Post([FromBody]Person person) { return Ok(person); } [HttpPut("{id:int}")] public IActionResult Put(int id,[FromBody] Person person) { if (id != person.Id) return BadRequest(); return Ok(person); } [HttpDelete("{id:int}")] public IActionResult Delete(int id) { return Ok(); } [HttpPost("avatar")] public IActionResult Post(IFormFile file) { return Ok(); } }
GET http://localhost:59416/api/people
تا به اینجا توانستیم فقط با نوشتن آدرس API مورد نظر، آن را فراخوانی کنیم.
برای ارسال پارامترهایی در هدر درخواست فقط کافیست دقیقا در خط پایین آدرس، به صورت field-name:field-value، هر پارامتری را که میخواهید، به همراه درخواست ارسال کنید. برای نمونه برای API لیست افراد که در بالا تست کردیم، میتوانیم هدر را به صورت زیر تنظیم نماییم :
GET http://localhost:59416/api/people Content-Type: application/json
اجرای درخواست GET برای دریافت یک شخص خاص
GET http://localhost:59416/api/people/1
خروجی آن به صورت زیر میباشد
درخواست POST برای درج کاربر جدید:
POST http://localhost:59416/api/people content-type: application/json { "id": 10, "name": "ali", "age":37 }
بعد از هدرهای درخواست، یک خط خالی ایجاد کنید و پایینتر از خط خالی، میتوانید مقادیر body درخواست را وارد نمایید.
درخواست PUT برای آپدیت یک شخص :
PUT http://localhost:59416/api/people/3 content-type: application/json { "id": 3, "name": "ali", "age":37 }
درخواست DELETE برای حذف شخص:
DELETE http://localhost:59416/api/people/3 content-type: application/application/json
ارسال توکن اعتبارسنجی :
در صورتی که یک API نیاز به اعتبار سنجی دارد و باید توکن را به همراه درخواستی ارسال نمایید، میتوانید در هدر درخواست، همانند زیر، توکن را ارسال نمایید
GET http://localhost:59416/api/people content-type: application/json Authorization: Bearer token
آپلود فایل:
یکی از API هایی که در مثال ابتدای مقاله داشتیم، مربوط به آپلود فایل آواتار هست که از ورودی، یک IFormFile را به عنوان ورودی دریافت میکند. برای آپلود فایل به کمک افزونه Rest Client میتوانیم به صورت زیر فایل را ارسال نماییم
POST http://localhost:59416/api/people/avatar Content-Type: multipart/form-data; boundary=----MyBoundary ------MyBoundary Content-Disposition: form-data; name="file";filename="Studio" content-type: image/png < C:\Users\rahimi\Downloads\Studio.png ------MyBoundary--
قبل از آدرس فایل، وجود > ضروری میباشد.
فعال سازی دکمه Send Request به ازای هر آدرس:
اگر در یک فایل، چند آدرس را همانند عکس زیر داشته باشید، فقط یک دکمهی Send Request وجود خواهد داشت که کلیک بر روی آن منجر به فراخوانی اولین url میشود.
برای داشتن یک دکمه Send Request به ازای هر API، باید بین هر کدام از API ها، حداقل سه تا # قرار دهید.
### Get All People GET http://localhost:59416/api/people content-type: application/json ### Get Person GET http://localhost:59416/api/people/1 ### Create POST http://localhost:59416/api/people content-type: application/json { "id": 10, "name": "ali", "age":37 } ### Edit person PUT http://localhost:59416/api/people/3 content-type: application/json { "id": 3, "name": "ali", "age":37 } ### Delete person DELETE http://localhost:59416/api/people/3 content-type: application/application/json ### Upload Avatar POST http://localhost:59416/api/people/avatar Content-Type: multipart/form-data; boundary=----MyBoundary ------MyBoundary Content-Disposition: form-data; name="file";filename="Studio" content-type: image/png < C:\Users\rahimi\Downloads\Studio.png ------MyBoundary--
افزونهی Rest Client، فراتر از توضیحات این مقاله میباشد. در صورت علاقه و برای مطالعه بیشتر در مورد آن، میتوانید به لینک صفحه افزونه مراجعه نمایید.