using System.IO; using System.Text.Encodings.Web; using Microsoft.AspNetCore.Html; namespace Sample { /// <summary> /// Html Helper Extensions /// </summary> public static class HtmlHelperExtensions { /// <summary> /// Convert IHtmlContent/TagBuilder to string /// </summary> public static string GetString(this IHtmlContent content) { using (var writer = new StringWriter()) { content.WriteTo(writer, HtmlEncoder.Default); return writer.ToString(); } } } }
پیاده سازی یک کلاس مبدل سه مرحله دارد:
- مرحله اول :ساخت کلاس ValueConverter
- مرحله دوم : تعریف آن به عنوان یک منبع یا ریسورس
- مرحله سوم : استفاده از آن در عملیات بایند کردن Binding
در مرحله اول، نحوه پیاده سازی کلاس ValueConverter به شکل زیر است:
public class BoolToVisibilityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { // Do the conversion from bool to visibility } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { // Do the conversion from visibility to bool } }
در متد تبدیل باید مقداری را که کنترل نیاز دارد، بر اساس مقادیر کلاس، ایجاد و بازگشت دهید.
در مرحلهی دوم نحوه تعریف مبدل ما در پنجره XAML به صورت زیر میباشد:
<Window x:Class="test.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:نامی دلخواه="clr-namespace:فضای نامی که کلاس مبدل در آن قرار دارد" > <Window.Resources> <نام دلخواهی که در بالا تعریف کرده اید: ClassNameنام کلاس x:Key="کلید این آیتم در ریسورس"/> </Window.Resources>
پس الان باید خط چهارم برای ما روشن باشد؛ فضای نام جدیدی را در برنامه خودمان ایجاد کردهایم که این تگ به آن اشاره میکند و نام دلخواهی هم برای اشاره به این فضای نام برایش در نظر گرفتهایم. هر موقع در برنامه این نام دلخواه تعیین شده قرار گیرد، یعنی اشاره به این فضای نام که در قسمت Window.resource خط هشتم تعریف شده است.
در خط هشتم، یک ریسورس (منبع) را به برنامه معرفی کردهایم:
ریسورسها برای ذخیره سازی دادهها در سطح یک کنترل، سطح محلی در یک پنجره، یا سطح عمومی در کل پروژه به کار میروند. محدودیتی در ذخیره دادهها وجود ندارد و هر چقدر که دوست دارید میتوانید داده به آن پاس کنید. این دادهها میتوانند یک سری اطلاعات ذخیره شده در یک ساختار ساده تا یک ساختار سلسه مراتبی از کنترلها باشند. ریسورسها به شما این اجازه را میدهند تا دادهها را در یک مکان ذخیره کرده و آنها را در یک یا چندجا مورد استفاده قرار دهید.
از آن جا که مباحث ریسورسها را در یک مقالهی جداگانه بررسی میکنیم، فقط به ذکر نکات بالا جهت کد فعلی بسنده خواهیم کرد و ادامهی آن را در یک مقاله دیگر مورد بررسی قرار میدهیم.
هر ریسورس دارای یک نام یا یک کلید است که با خصوصیت x:key تعریف میشود.
ریسورس بالا یک کلاس را که در فضای نام دلخواهی قرار دارد، تعریف میکند و یک کلید هم به آن انتساب میدهد.
مرحلهی سوم معرفی ریسورس به عملیات Binding است:
{Binding نام پراپرتی کلاس, Converter={StaticResource کلید آیتم مربوطه در ریسورس}, ConverterParameter=پارامتری که به کلاس مبدل پاس میشود}
قصد داریم یک مبدل برای فیلد جنیست درست کنیم. از آنجا که این فیلد Boolean است و خصوصیت IsChecked یک RadioButton هم Boolean است، میتوان یک ارتباط مستقیم را ایجاد کرد. ولی مشکل در اینجا هست که True برای مذکر است و false برای مؤنث. در نتیجه تنها Radiobutton مربوطه به جنس مذکر به این حالت پاسخ میدهد و از آنجا که برای جنس مونث false در نظر گرفته شده است، انتخاب آن هم false خواهد بود. پس باید در مبدل، مقداری که کنترل میخواهد را شناسایی کرده و اگر مقدار با آن برابر بود، True را بازگردانیم. مقداری که هر کنترل درخواست میکند را از طریق پارامتر به تابع مبدل ارسال میکنیم. radiobutton مذکر، مقدار True را به عنوان پارامتر ارسال میکند و radiobutton مونث هم مقدار false را به عنوان پارامتر ارسال میکند. اگر تابع مبدل را ببینید، این مقدارها با پارامترها همخوانی دارند، True در غیر این صورت false بر میگرداند.
مرحله اول تعریف کلاس ValueConveter:
using System; using System.Globalization; using System.Windows; using System.Windows.Data; namespace test.ValueConverters { public class GenderConverter: IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var ParameterString = parameter as string; if (ParameterString == null) return DependencyProperty.UnsetValue; bool bparam; bool test = bool.TryParse(parameter.ToString(), out bparam); if (test) { return ((bool)value).Equals(bparam); } return DependencyProperty.UnsetValue; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } }
برای تعریف این مبدل در محیط XAML به صورت زیر اقدام میکنیم:
<Window x:Class="test.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:valueConverters="clr-namespace:test.ValueConverters" Title="MainWindow" Height="511.851" Width="525"> <Window.Resources> <valueConverters:GenderConverter x:Key="GenderConverter"/> </Window.Resources> </window>
نکته: در صورتی که بعد از تعریف ریسورس با خطای زیر روبرو شدید و محیط طراحی Design را از دست دادید یکبار پروژه را بیلد کنید تا مشکل حل شود.
The name "GenderConverter" does not exist in the namespace "clr-namespace:test.ValueConverters".
اکنون در عملیات بایندینگ دو کنترل اینگونه مینویسیم:
<RadioButton GroupName="Gender" IsChecked="{Binding Gender, Converter={StaticResource GenderConverter}, ConverterParameter=True}" Name="RdoMale" >Male</RadioButton> <RadioButton GroupName="Gender" IsChecked="{Binding Gender, Converter={StaticResource GenderConverter}, ConverterParameter=False}" Name="RdoFemale" Margin="0 5 0 0" >Female</RadioButton>
<RadioButton GroupName="Gender" IsChecked="{Binding Gender}" Name="RdoMale" >Male</RadioButton>
جهت یادآوری نگاهی به کلاس برنامه میاندازیم:
public enum FieldOfWork { Actor=0, Director=1, Producer=2 } public class Person : INotifyPropertyChanged { public bool Gender { get; set; } public string ImageName { get; set; } public string Country { get; set; } public DateTime Date { get; set; } public FieldOfWork FieldOfWork { get; set; } private string _name; public string Name { get { return _name; } set { _name = value; OnPropertyChanged(); } }
public FieldOfWork FieldOfWork { get; set; }
کد کلاس مبدل را به صورت زیر مینویسیم:
using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; namespace test.ValueConverters { public class EnumConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var ParameterString = parameter as string; if (ParameterString == null) return DependencyProperty.UnsetValue; if (Enum.IsDefined(value.GetType(), value) == false) return DependencyProperty.UnsetValue; object paramvalue = Enum.Parse(value.GetType(), ParameterString); return paramvalue.Equals(value); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } }
کد قسمت ریسورس را با کلاس جدید به روز میکنیم:
<Window.Resources> <valueConverters:GenderConverter x:Key="GenderConverter"/> <valueConverters:EnumConverter x:Key="EnumConverter"></valueConverters:EnumConverter> </Window.Resources>
<CheckBox Name="ChkActor" IsChecked="{Binding FieldOfWork, Converter={StaticResource EnumConverter}, ConverterParameter=Actor}" >Actor/Actress</CheckBox> <CheckBox Name="ChkDirector" IsChecked="{Binding FieldOfWork, Converter={StaticResource EnumConverter}, ConverterParameter=Director}" >Director</CheckBox> <CheckBox Name="ChkProducer" IsChecked="{Binding FieldOfWork, Converter={StaticResource EnumConverter}, ConverterParameter=Producer}" >Producer</CheckBox>
public static Person GetPerson() { return new Person() { Name = "Leo", Gender =true, ImageName ="man.jpg", Country = "Italy", FieldOfWork = test.FieldOfWork.Actor, Date = DateTime.Now.AddDays(-3) }; }
برنامه را اجرا کنید تا نتیجه کار را ببینید. باید چک باکس Actor تیک خورده باشد. میتوانید منبع داده را تغییر داده تا نتیجه کار را ببینید.
بگذارید فیلد FieldOfWork را به حالت قبلی یعنی IList برگردانیم. در بسیاری از اوقات ما چند گزینه از یک Enum را انتخاب میکنیم، مثل داشتن چند سطح دسترسی یا چند سمت کاری و ...
کلاس را به همراه کد GetPerson به شکل زیر تغییر میدهیم:
public enum FieldOfWork { Actor=0, Director=1, Producer=2 } public class Person : INotifyPropertyChanged { public bool Gender { get; set; } public string ImageName { get; set; } public string Country { get; set; } public DateTime Date { get; set; } public IList<FieldOfWork> FieldOfWork { get; set; } private string _name; public string Name { get { return _name; } set { _name = value; OnPropertyChanged(); } } public static Person GetPerson() { return new Person { Name = "Leo", Gender = true, ImageName = "man.jpg", Country = "Italy", FieldOfWork = new FieldOfWork[] { test.FieldOfWork.Actor, test.FieldOfWork.Producer }, Date = DateTime.Now.AddDays(-3) }; } }
دو Enum بازیگر و تهیه کننده را انتخاب کردهایم، پس در زمان اجرا باید این دو گزینه انتخاب شوند.
کد مبدل را به صورت زیر مینویسیم:
using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; namespace test.ValueConverters { public class EnumList : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var ParameterString = parameter as string; if (ParameterString == null) return DependencyProperty.UnsetValue; var enumlist= (IList) value; if(enumlist==null && enumlist.Count<1) return DependencyProperty.UnsetValue; if (Enum.IsDefined(enumlist[0].GetType(), ParameterString) == false) return DependencyProperty.UnsetValue; /* foreach (var item in enumlist) { object paramvalue = Enum.Parse(item.GetType(), ParameterString); bool result = item.Equals(paramvalue); if (result) return true; } return false; */ return (from object item in enumlist let paramvalue = Enum.Parse(item.GetType(), ParameterString) select item.Equals(paramvalue)).Any(result => result); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } }
در حلقهای که به شکل توضیح درآمده، همه آیتمهای مربوطه در لیست را بررسی کرده و اگر آیتمی برابر پارامتر باشد، True بر میگرداند و در صورتی که حلقه به اتمام برسد و آیتم پیدا نشود، مقدار False را برمیگرداند. این حلقه از آن جهت به شکل توضیح درآمده است که کد Linq آن در زیر نوشته شده است.
تعریف کلاس بالا در ریسورس:
<Window.Resources> <valueConverters:GenderConverter x:Key="GenderConverter"/> <valueConverters:EnumConverter x:Key="EnumConverter"></valueConverters:EnumConverter> <valueConverters:EnumList x:Key="EnumList"></valueConverters:EnumList> </Window.Resources>
<CheckBox Name="ChkActor" IsChecked="{Binding FieldOfWork, Converter={StaticResource EnumList}, ConverterParameter=Actor}" >Actor/Actress</CheckBox> <CheckBox Name="ChkDirector" IsChecked="{Binding FieldOfWork, Converter={StaticResource EnumList}, ConverterParameter=Director}" >Director</CheckBox> <CheckBox Name="ChkProducer" IsChecked="{Binding FieldOfWork, Converter={StaticResource EnumList}, ConverterParameter=Producer}" >Producer</CheckBox>
نتیجهی کار باید به شکل زیر باشد:
فیلد جنسیت و زمینه کاری از تابع مبدل به دست آمده است.
بدیهی است خروجیهای بالا برای کنترل هایی است که مقدار Boolean را میپذیرند و برای سایر کنترلها باید با کمی تغییر در کد و نوع برگشتی که تحویل خروجی متد مبدل میشود، دهید.دانلود فایلهای این قسمت
یکی از مواردی که عموما در برنامه نویسی با آن سر و کار داریم، parse اطلاعات با فرمتهای مختلف است. از CSV تا XML تا ... JSON .
در مورد کار با XML در دات نت فریم ورک، فضاهای نام مرتبط زیادی وجود دارند؛ برای مثال System.Xml.Linq و System.Xml . همچنین یک روش دیگر هم برای کار با اطلاعات XML ایی در دات نت وجود دارد. میشود کلاس معادل یک فایل XML را تولید و سپس اطلاعات آنرا به این کلاس نگاشت کرد. اطلاعات بیشتر : (^). این برنامه کار خود مایکروسافت است.
در مورد JSON از دات نت سه و نیم به بعد کارهایی صورت گرفته مانند : (^). اما آنچنان دلچسب نیست. جهت رفع این خلاء کتابخانهی سورس باز و بسیار کاملی در این زمینه به نام JSON.NET تهیه شده که از این آدرس قابل دریافت است: (^)
و خبر خوب اینکه امکان تهیه کلاسهای معادل اطلاعات JSON ایی هم مدتیاست توسط برنامه نویسهای مستقل تهیه شده است. یا میتوان از امکانات توکار دات نت استفاده کرد یا از کتابخانههایی مانند JSON.NET یا از هیچکدام! میتوان یک راست کل اطلاعات JSON ایی دریافتی را به یک یا چند کلاس معادل آن نگاشت کرد:
- پروژه سورس باز JSON C# Class Generator
- و یا یک ابزار آنلاین مشابه: json2csharp
«بررسی مفاهیم معکوس سازی وابستگیها و ابزارهای مرتبط با آن»
«اصول طراحی SOLID»
«مطالعهی بیشتر»
تزریق وابستگیها (یا Dependency injection = DI) به معنای ارسال نمونهای/وهلهای از وابستگی (یک سرویس) به شیء وابستهی به آن (یک کلاینت) است. در فرآیند تزریق وابستگیها، یک کلاس، وهلههای کلاسهای دیگر مورد نیاز خودش را بجای وهله سازی مستقیم، از یک تزریق کننده دریافت میکند. بنابراین بجای نوشتن newها در کلاس جاری، آنها را به صورت وابستگیهایی در سازندهی کلاس تعریف میکنیم تا توسط یک IoC Container تامین شوند. در اینجا به فریم ورکهایی که کار وهله سازی این وابستگیها را انجام میدهند، IoC Container و یا DI container میگوییم (IoC = inversion of control ).
چندین نوع تزریق وابستگیها وجود دارند که دو حالت زیر، عمومیترین آنها است:
الف) تزریق در سازندهی کلاس: لیست وابستگیهای یک کلاس، به عنوان پارامترهای سازندهی آن ذکر میشوند.
ب) تزریق در خواص یا Setter injection: کلاینت خواصی get و set را به صورت public معرفی میکند و سپس IoC Container با وهله سازی آنها، وابستگیهای مورد نیاز را تامین خواهد کرد.
تزریق وابستگیها در ASP.NET Core
برخلاف نگارشهای قبلی ASP.NET، این نگارش جدید از ابتدا با دید پشتیبانی کامل از DI طراحی شدهاست و این مفهوم، در سراسر اجزای آن به صورت یکپارچهای پشتیبانی میشود. همچنین به همراه یک minimalistic DI container توکار نیز هست .
این IoC Container توکار از 4 حالت طول عمر ذیل پشتیبانی میکند:
- instance: در هربار نیاز به یک وابستگی خاص، تنها یک وهله از آن در اختیار مصرف کننده قرار میگیرد و در اینجا شما هستید که مسئول تعریف نحوهی وهله سازی این شیء خواهید بود (برای بار اول).
- transient: هربار که نیاز به وابستگی خاصی بود، یک وهلهی جدید از آن توسط IoC Container تولید و ارائه میشود.
- singleton: در این حالت تنها یک وهله از وابستگی درخواست شده در طول عمر برنامه تامین میشود.
- scoped: در طول عمر یک scope خاص، تنها یک وهله از وابستگی درخواست شده، در اختیار مصرف کنندهها قرار میگیرد. برای مثال مرسوم است که به ازای یک درخواست وب، تنها یک وهله از شیءایی خاص در اختیار تمام مصرف کنندههای آن قرار گیرد (single instance per web request).
طول عمر singleton، برای سرویسها و کلاسهای config مناسب هستند. به این ترتیب به کارآیی بالاتری خواهیم رسید و دیگر نیازی نخواهد بود تا هر بار این اطلاعات خوانده شوند. حالت scoped برای وهله سازی الگوی واحد کار و پیاده سازی تراکنشها مناسب است. برای مثال در طی یک درخواست وب، یک تراکنش باید صورت گیرد.
حالت scoped در حقیقت نوع خاصی از حالت transient است. در حالت transient صرفنظر از هر حالتی، هربار که وابستگی ویژهای درخواست شود، یک وهلهی جدید از آن تولید خواهد شد. اما در حالت scoped فقط یک وهلهی از وابستگی مورد نظر، در بین تمام اشیاء وابستهی به آن، در طول عمر آن scope تولید میشود.
بنابراین در برنامههای وب دو نوع singleton برای معرفی کلاسهای config و نوع scoped برای پیاده سازی تراکنشها و همچنین بالابردن کارآیی برنامه در طی یک درخواست وب (با عدم وهله سازی بیش از اندازهی از کلاسهای مختلف مورد نیاز)، بیشتر از همه به کار برده میشوند.
یک مثال کاربردی: بررسی نحوهی تزریق یک سرویس سفارشی به کمک IoC Container توکار ASP.NET Core
مثال جاری که بر اساس ASP.NET Core Web Application و با قالب خالی آن ایجاد شدهاست، دارای نام فرضی Core1RtmEmptyTest است. در همین پروژه بر روی پوشهی src، کلیک راست کرده و گزینهی Add new project را انتخاب کنید و سپس یک پروژهی جدید از نوع NET Core -> Class library. را به آن، با نام Core1RtmEmptyTest.Services اضافه کنید (تصویر فوق).
در ادامه کلاس نمونهی سرویس پیامها را به همراه اینترفیس آن، با محتوای زیر به آن اضافه کنید:
namespace Core1RtmEmptyTest.Services { public interface IMessagesService { string GetSiteName(); } public class MessagesService : IMessagesService { public string GetSiteName() { return "DNT"; } } }
انجام اینکار معادل است با افزودن یک سطر ذیل به فایل project.json پروژه:
{ "dependencies": { // same as before "Core1RtmEmptyTest.Services": "1.0.0-*" },
public void Configure( IApplicationBuilder app, IHostingEnvironment env, IMessagesService messagesService)
public void Configure( IApplicationBuilder app, IHostingEnvironment env, IMessagesService messagesService) { app.Run(async context => { var siteName = messagesService.GetSiteName(); await context.Response.WriteAsync($"Hello {siteName}"); }); }
System.InvalidOperationException No service for type 'Core1RtmEmptyTest.Services.IMessagesService' has been registered. at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType) at Microsoft.AspNetCore.Hosting.Internal.ConfigureBuilder.Invoke(object instance, IApplicationBuilder builder) System.Exception Could not resolve a service of type 'Core1RtmEmptyTest.Services.IMessagesService' for the parameter 'messagesService' of method 'Configure' on type 'Core1RtmEmptyTest.Startup'. at Microsoft.AspNetCore.Hosting.Internal.ConfigureBuilder.Invoke(object instance, IApplicationBuilder builder) at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<IMessagesService, MessagesService>(); }
پس از اینکار، مجددا برنامه را اجرا کنید. اکنون این خروجی باید مشاهده شود:
و به این معنا است که اکنون IoC Cotanier توکار ASP.NET Core، میداند زمانیکه به IMessagesService رسید، چگونه باید آنرا وهله سازی کند.
چه سرویسهایی به صورت پیش فرض در IoC Container توکار ASP.NET Core ثبت شدهاند؟
در ابتدای متد ConfigureServices یک break point را قرار داده و برنامه را در حالت دیباگ اجرا کنید:
همانطور که ملاحظه میکنید، به صورت پیش فرض 16 سرویس در اینجا ثبت شدهاند که تاکنون با دو مورد از آنها کار کردهایم.
امکان تزریق وابستگیها در همه جا!
در مثال فوق، سرویس سفارشی خود را در متد Configure کلاس آغازین برنامه تزریق کردیم. نکتهی مهم اینجا است که برخلاف نگارشهای قبلی ASP.NET MVC (یعنی بدون نیاز به تنظیمات خاصی برای قسمتهای مختلف برنامه)، میتوان این تزریقها را در کنترلرها، در میان افزارها، در فیلترها در ... همه جا و تمام اجزای ASP.NET Core 1.0 انجام داد و دیگر اینبار نیازی نیست تا نکتهی ویژهی نحوهی تزریق وابستگیها در فیلترها یا کنترلرهای ASP.NET MVC را یافته و سپس اعمال کنید. تمام اینها از روز اول کار میکنند. همینقدر که کار ثبت سرویس خود را در متد ConfigureServices انجام دادید، این سرویس در سراسر اکوسیستم ASP.NET Core، قابل دسترسی است.
نیاز به تعویض IoC Container توکار ASP.NET Core
قابلیت تزریق وابستگیهای توکار ASP.NET Core صرفا جهت برآورده کردن نیازمندیهای اصلی آن طراحی شدهاست و نه بیشتر. بنابراین توسط آن قابلیتهای پیشرفتهای را که سایر IoC Containers ارائه میدهند، نخواهید یافت. برای مثال تعویض امکانات تزریق وابستگیهای توکار ASP.NET Core با StructureMap این مزایا را به همراه خواهد داشت:
• امکان ایجاد child/nested containers (پشتیبانی از سناریوهای چند مستاجری)
• پشتیبانی از Setter Injection
• امکان انتخاب سازندهای خاص (اگر چندین سازنده تعریف شده باشند)
• سیم کشی خودکار یا Conventional "Auto" Registration (برای مثال اتصال اینترفیس IName به کلاس Name به صورت خودکار و کاهش تعداد تعاریف ابتدای برنامه)
• پشتیبانی توکار از Lazy و Func
• امکان وهله سازی از نوعهای concrete (یا همان کلاسهای معمولی)
• پشتیبانی از مفاهیمی مانند Interception و AOP
• امکان اسکن اسمبلیهای مختلف جهت یافتن اینترفیسها و اتصال خودکار آنها (طراحیهای افزونه پذیر)
روش تعویض IoC Container توکار ASP.NET Core با StructureMap
جزئیات این جایگزین کردن را در مطلب «جایگزین کردن StructureMap با سیستم توکار تزریق وابستگیها در ASP.NET Core 1.0» میتوانید مطالعه کنید.
یا میتوانید از روش فوق استفاده کنید و یا اکنون قسمتی از پروژهی رسمی استراکچرمپ در آدرس https://github.com/structuremap/structuremap.dnx جهت کار با NET Core. طراحی شدهاست. برای کار با آن نیاز است این مراحل طی شوند:
الف) دریافت بستهی نیوگت StructureMap.Dnx
برای این منظور بر روی گره references کلیک راست کرده و گزینهی manage nuget packages را انتخاب کنید. سپس در برگهی browse آن، StructureMap.Dnx را جستجو کرده و نصب نمائید (تیک مربوط به انتخاب pre releases هم باید انتخاب شده باشد):
انجام این مراحل معادل هستند با افزودن یک سطر ذیل به فایل project.json برنامه:
{ "dependencies": { // same as before "StructureMap.Dnx": "0.5.1-rc2-final" },
پس از نصب بستهی StructureMap.Dnx، به کلاس آغازین برنامه مراجعه کرده و این تغییرات را اعمال کنید:
public class Startup { public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddDirectoryBrowser(); var container = new Container(); container.Configure(config => { config.Scan(_ => { _.AssemblyContainingType<IMessagesService>(); _.WithDefaultConventions(); }); //config.For<IMessagesService>().Use<MessagesService>(); config.Populate(services); }); container.Populate(services); return container.GetInstance<IServiceProvider>(); }
سپس وهلهای از IServiceProvider، توسط استراکچرمپ تامین شده و بازگشت داده میشود تا بجای IoC Container توکار ASP.NET Core استفاده شود.
در این مثال چون در متد Scan، کار بررسی اسمبلی لایه سرویس برنامه با قراردادهای پیش فرض استراکچرمپ انجام شدهاست، دیگر نیازی به سطر تعریف config.For نیست. در اینجا هرگاه IName ایی یافت شد، به کلاس Name متصل میشود (name هر نامی میتواند باشد).
ASP.NET MVC #17
سلام
من ViewModel استفاده کردم و ViewModelها را هم در یک فایل مجزا و داخل پروژه DataLayer تعریف کرده ام ، با توضیحات شما ViewModel خود یک پروژه مجزا مثل DataLayer بشود، صحیحتر است و یا در خود پروژه اصلی تعریف شود؟
.NET 8 Preview 4 is now available and includes many great new improvements to ASP.NET Core.
Here’s a summary of what’s new in this preview release:
Blazor
Streaming rendering with Blazor components
Handling form posts with Blazor SSR
Route to named elements in Blazor
Webcil packaging for Blazor WebAssembly apps
API authoring
Expanded support for form binding in minimal APIs
API project template includes .http file
Native AOT
Logging and exception handling in compile-time generated minimal APIs
ASP.NET Core top-level APIs annotated for trim warnings
Reduced app size with configurable HTTPS support
Worker Service template updates
Additional default services configured in the slim builder
API template JSON configuration changes
Support for JSON serialization of compiler-generated IAsyncEnumerable unspeakable types
Authentication and authorization
Identity API endpoints
Improved support for custom authorization policies with IAuthorizationRequirementData
ASP.NET Core metrics
For more details on the ASP.NET Core work planned for .NET 8 see the full ASP.NET Core roadmap for .NET 8 on GitHub.
آشنایی با NHibernate - قسمت اول
ابتدا به تشریح کلاس ترسیم بیضی (Ellipse) میپردازیم.
using System.Drawing; namespace PWS.ObjectOrientedPaint.Models { /// <summary> /// Ellipse Draw /// </summary> public class Ellipse : Shape { #region Constructors (2) /// <summary> /// Initializes a new instance of the <see cref="Ellipse" /> class. /// </summary> /// <param name="startPoint">The start point.</param> /// <param name="endPoint">The end point.</param> /// <param name="zIndex">Index of the z.</param> /// <param name="foreColor">Color of the fore.</param> /// <param name="thickness">The thickness.</param> /// <param name="isFill">if set to <c>true</c> [is fill].</param> /// <param name="backgroundColor">Color of the background.</param> public Ellipse(PointF startPoint, PointF endPoint, int zIndex, Color foreColor, byte thickness, bool isFill, Color backgroundColor) : base(startPoint, endPoint, zIndex, foreColor, thickness, isFill, backgroundColor) { ShapeType = ShapeType.Ellipse; } /// <summary> /// Initializes a new instance of the <see cref="Ellipse" /> class. /// </summary> public Ellipse() { ShapeType = ShapeType.Ellipse; } #endregion Constructors #region Methods (1) // Public Methods (1) /// <summary> /// Draws the specified g. /// </summary> /// <param name="g">The g.</param> public override void Draw(Graphics g) { if (IsFill) g.FillEllipse(BackgroundBrush, StartPoint.X, StartPoint.Y, Width, Height); g.DrawEllipse(Pen, StartPoint.X, StartPoint.Y, Width, Height); base.Draw(g); } #endregion Methods } }
کلاس دایره (Circle) از کلاس بالا (Ellipse) ارث بری دارد که کد آن را در زیر مشاهده مینمایید.
using System; using System.Drawing; namespace PWS.ObjectOrientedPaint.Models { /// <summary> /// Circle /// </summary> public class Circle : Ellipse { #region Constructors (2) /// <summary> /// Initializes a new instance of the <see cref="Circle" /> class. /// </summary> /// <param name="startPoint">The start point.</param> /// <param name="endPoint">The end point.</param> /// <param name="zIndex">Index of the z.</param> /// <param name="foreColor">Color of the fore.</param> /// <param name="thickness">The thickness.</param> /// <param name="isFill">if set to <c>true</c> [is fill].</param> /// <param name="backgroundColor">Color of the background.</param> public Circle(PointF startPoint, PointF endPoint, int zIndex, Color foreColor, byte thickness, bool isFill, Color backgroundColor) { float x = 0, y = 0; float width = Math.Abs(endPoint.X - startPoint.X); float height = Math.Abs(endPoint.Y - startPoint.Y); if (startPoint.X <= endPoint.X && startPoint.Y <= endPoint.Y) { x = startPoint.X; y = startPoint.Y; } else if (startPoint.X >= endPoint.X && startPoint.Y >= endPoint.Y) { x = endPoint.X; y = endPoint.Y; } else if (startPoint.X >= endPoint.X && startPoint.Y <= endPoint.Y) { x = endPoint.X; y = startPoint.Y; } else if (startPoint.X <= endPoint.X && startPoint.Y >= endPoint.Y) { x = startPoint.X; y = endPoint.Y; } StartPoint = new PointF(x, y); var side = Math.Max(width, height); EndPoint = new PointF(x + side, y + side); ShapeType = ShapeType.Circle; Zindex = zIndex; ForeColor = foreColor; Thickness = thickness; BackgroundColor = backgroundColor; IsFill = isFill; } /// <summary> /// Initializes a new instance of the <see cref="Circle" /> class. /// </summary> public Circle() { ShapeType = ShapeType.Circle; } #endregion Constructors #region Methods (1) // Public Methods (1) /// <summary> /// Points the in sahpe. /// </summary> /// <param name="point">The point.</param> /// <param name="tolerance">The tolerance.</param> /// <returns> /// <c>true</c> if [has point in sahpe] [the specified point]; otherwise, <c>false</c>. /// </returns> public override bool HasPointInSahpe(PointF point, byte tolerance = 5) { float width = Math.Abs(EndPoint.X+tolerance - StartPoint.X-tolerance); float height = Math.Abs(EndPoint.Y+tolerance - StartPoint.Y-tolerance); float diagonal = Math.Max(height, width); float raduis = diagonal / 2; float dx = Math.Abs(point.X - (X + Width / 2)); float dy = Math.Abs(point.Y - (Y + height / 2)); return (dx + dy <= raduis); } #endregion Methods } }
در متد HasPointInShape با استفاده از فرمول دایره تعیین میشود که آیا نقطه پارامتر ورودی متد در داخل دایره واقع شده است یا خیر (جهت انتخاب شکل برای جابجایی یا تغییر اندازه).
موفق و موید باشید
در ادامه مطالب قبل:
پیاده سازی پروژه نقاشی (Paint) به صورت شی گرا 1#
پیاده سازی پروژه نقاشی (Paint) به صورت شی گرا 2#
پیاده سازی پروژه نقاشی (Paint) به صورت شی گرا 3#
پیاده سازی پروژه نقاشی (Paint) به صورت شی گرا 4#
پیاده سازی پروژه نقاشی (Paint) به صورت شی گرا 5#