- قسمت اول - معرفی و نصب
- قسمت دوم - آشنایی با Pipelineها
- قسمت سوم - آشنایی با Redirection
- قسمت چهارم - نوشتن اولین اسکریپت
- قسمت پنجم - اسکریپت بلاک و توابع
- قسمت ششم - ایجاد Cmdletها توسط #C
- قسمت هفتم - غنیسازی PowerShell
- قسمت هشتم - ماژولها
- قسمت نهم - آشنایی با Crescendo
- قسمت دهم - بررسی مشکلات به همراه پرچم فرمت
- قسمت یازدهم - یک مثال
- قسمت دوازدهم - آشنایی با GitHub Actions و بررسی یک مثال
- قسمت سیزدهم - ساخت یک Static Site Generator ساده توسط PowerShell و GitHub Actions
آیا امکان اجرای API روی IIS تنها برای یک سرور وجود دارد؟
کتابخانه «DNTCommon.Web.Core» به همراه روشی چندسکویی برای دریافت اطلاعات سرور هم هست. پس از دریافت اطلاعات، فقط کافی است یک AuthorizationFilter سفارشی را برای بررسی آن طراحی کنید:
public class RestrictServerAttribute : Attribute, IAuthorizationFilter { public void OnAuthorization(AuthorizationFilterContext context) { if (/* check server info */) { // Request came from an unauthorized host, return bad request context.Result = new BadRequestObjectResult("Host is not allowed"); } } }
و سپس آنرا به صورت یک فیلتر سراسری معرفی کنید تا به همهجا اعمال شود:
public void ConfigureServices(IServiceCollection services) { services.AddControllers(options => { options.Filters.Add(new RestrictServerAttribute()); }); }
با توجه به ابتدای آموزش، کدهای زیر را در ترمینال وارد میکنیم:
sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet/ trusty main" > /etc/apt/sources.list.d/dotnetdev.list' sudo apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893 sudo apt-get update
در خط دوم، کلید اختصاصی این پکیج جهت اعتبارسنجی معرفی میگردد که در سایت جاری قبلا به آن پرداخته شده است. در خط آخر هم مخازن موجود را به روزرسانی میکنیم تا آماده استفاده شود.
در سایت مایکروسافت از شما خواسته میشود که کد زیر را وارد کنید:
sudo apt-get install dotnet-dev-1.0.0-preview1-002702
sudo apt-get install dotnet-dev-1.0.0-preview2-003096
The following packages have unmet dependencies: dotnet-dev-1.0.0-preview1-002702 : Depends: dotnet-sharedframework-microsoft.netcore.app-1.0.0-rc2-3002702 but it is not going to be installed E: Unable to correct problems, you have held broken packages.
sudo dpkg -i libicu52_52.1-3ubuntu0.4_amd64.deb
sudo apt-get install dotnet-sharedframework-microsoft.netcore.app-1.0.0-rc2-3002702 sudo apt-get install dotnet-dev-1.0.0-preview2-003096
dotnet --version
1.0.0-preview2-003096
using System.Collections.Generic;
using BLL = DotNetTipsBLLLayer;//نام مستعار برای فضای نام using EmployeeDomain = DotNetTipsBLLLayer.Employee;//نام مستعار برای یک نوع داده
using (var sqlConnection = new SqlConnection()) { //کد }
using static System.Console; using static System.Math; namespace dotnettipsUsingStatic { class Program { static void Main(string[] args) { Write(" *** Cal Area *** "); int r = int.Parse(ReadLine()); var result = Pow(r, 2) * PI; Write($"Area is : {result}"); ReadKey(); } } }
enum Gender { Male, Female }
تا قبل از سی شارپ 6 برای استفادهی از نوع داده شمارشی بدین شکل عمل میکردیم:
var gender = Gender.Male;
و اکنون بازنویسی استفادهی ازEnum به کمک ویژگی جدید static using statement :
در قسمت معرفی فضاهای نام بدین شکل عمل میکنیم:
using static dotnettipsUsingStatic.Gender;
و در برنامه کافیست مستقیما نام اعضای Enum را ذکر کنیم .
var gender = Male;//تخصیص نوع داده شمارشی WriteLine($"Employee Gender is : {Male}");//استفاده مستقیم از نوع داده شمارشی
استفاده از ویژگی using static و متدهای الحاقی :
تا قبل از ارائه سی شارپ 6 اگر نیاز به استفادهی از یک متد الحاقی خاص همچون where در فضای نام System.Linq.Enumeable داشتیم میبایستی فضای نام System.Linq را به طور کامل اضافه میکردیم و راهی برای اضافه کردن یک فضای نام خاص درون فضای نام بزرگتر وجود نداشت.
اما با قابلیت جدید اضافه شده میتوانیم بخشی از یک فضای نام را اضافه کنیم:
متدهای استاتیک و متدهای الحاقی در زمان استفاده از ویژگی using static:
فرض کنید کلاس static ای بنام MyStaticClass داشته باشیم که متد Print1 و Print2 در آن تعریف شده باشند:
public static class MyStaticClass { public static void Print1(string parameter) { WriteLine(parameter); } public static void Print2(this string parameter) { WriteLine(parameter); } }
برای استفاده از متدهای تعریف شده به شکل زیر عمل میکنیم :
//فراخوانی تابع استاتیک Print1("Print 1");//روش اول MyStaticClass.Print1("Prtint 1");//روش دوم //فراخوانی متد الحاقی استاتیک MyStaticClass.Print2("Print 2"); "print 2".Print2();
ویژگیهای جدید ارائه شده در سی شارپ 6 برای افزایش خوانایی برنامهها و تمیزتر شدن کدها اضافه شدهاند. در مورد ویژگیهای ارائه شده در مقالهی جاری این نکته مهم است که گاهی قید کردن نام کلاسها خود سبب افزایش خوانایی کدها میشود .
var container = new UnityContainer();
container.RegisterType<ISomeService, SomeService>(new PerRequestLifetimeManager()); container.RegisterType<ISomeBusiness, SomeBusiness>(new PerRequestLifetimeManager()); container.RegisterType<ISomeController, SomeController>(new PerRequestLifetimeManager());
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
var someService=(ISomeService)DependencyResolver.Current.GetService(typeof(ISomeService)); var data=someService.GetData();
در لایه سرویس یک کلاس Service Factory داریم که قرار است همه سرویسها، برای برقراری ارتباط با یکدیگر از آن استفاده کنند.این کلاس معمولا در لایه سرویس به اشکال گوناگونی پیاده سازی میشود که کارش وهه سازی از Interfaceهای درخواستی است. اما برای یکپارچه کردن آن با Unity من آنرا به شکل زیر پیاده سازی کرده ام
public class ServiceFactory : MarshalByRefObject { static IUnityContainer uContainer = new UnityContainer(); public static Type DataContextType { get; set; } public static void Initialise(IUnityContainer unityContainer, Type dbContextType) { uContainer = unityContainer; DataContextType = dbContextType; uContainer.RegisterType(typeof(BaseDataContext), DataContextType, new HierarchicalLifetimeManager()); } public static T Create<T>() { return (T)Activator.CreateInstance<T>(); } public static T Create<T>(string fullTypeName) { return (T)System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(fullTypeName); } public static T Create<T>(Type entityType) { return (T)Activator.CreateInstance(entityType); } public static dynamic Create(Type entityType) { return Activator.CreateInstance(entityType); } public static T Get<T>() { return uContainer.Resolve<T>(); } public static object Get(Type type) { return uContainer.Resolve(type); } }
در این کلاس ما بجای ایجاد داینامیک آبجکتها، از Unity استفاده کردهایم. در همان ابتدا که برنامهی وب ما برای اولین بار اجرا میشود و بعد از Register کردن کلاسها، میتوانیم container را به صورت پارامتر سازنده به کلاس Service Factory ارسال کنیم. به این ترتیب برای استفاده از سرویسها در لایه Business از Unity بهره میبریم.
البته استفاده از Unity برای DataContext خیلی منطقی نیست و بهتر است نوع DataContext را در ابتدا بگیریم و هرجا نیاز داشتیم با استفاده از متد Create از آن وهله سازی بکنیم.
WAI-ARIA که برگرفته از Web Accessibility Initiative - Accessible Rich internet Application است به معنی برنامهی اینترنتی تعامل گرا با خاصیت دسترسی پذیری بالا میباشد و یک راهنماست که توسط کنسرسیوم وب (+ ) معرفی گشته است تا وب سایتها با رعایت این قوانین، دسترسی سایت خود را بالاتر ببرند. این قوانین به خصوص برای سایتهایی با محتوای پویا هستند که از فناوریهایی چون Ajax,Javascrip, HTML و دیگر فناوریهای مرتبط استفاده میکنند.
امروزه طراحان وب بیش از هر وقتی از فناوریهای سمت کلاینت چون جاوااسکریپت برای ساخت رابطهای کاربری استفاده میکنند که html به تنها قادر به ایجاد آنها نیست. یکی از تکنیکهای جاوااسکریپت، دریافت محتوای جدید و به روزآوری قسمتی از صفحات وب است بدون اینکه مجددا کل صفحه از وب سرور درخواست گردد که به این تکنیک Rich Internet Application هم میگویند. تا به اینجای کار هیچ مشکلی نیست و خوب هم هست؛ ولی مشکلی که در این بین وجود دارد این است که این نوع تکنیکها باعث از بین رفتن خاصیت دسترسی پذیری معلولین میگردند. معلولینی که از صفحه خوان ها استفاده میکنند یا به دلیل معلولیتهای خود قادر به حرکت دادن ماوس نیستند.
ARIA با استفاده از خصوصیتها Properties، نقشها Roles و وضعیتها States به طراحان برنامههای وب و سازندگان فناوریهای یاری رسان، اجازه میدهد که با ابزارهای کمکی معلولان ارتباط برقرار کنیم و یک صفحهی وب ساده را به یک صفحهی پویا تبدیل کنیم. ARIA تنها یک استاندارد برای وب نیست، بلکه یک فناوری چند پلتفرمه است که برای بازیهای رایانهای، موبایلها، دستگاههای سرگرمی و سلامتی و دیگر انواع برنامهها نیز تعریف شده است.
فریمورک ARIA
خود HTML به تنهایی نمیتواند نقشهای هر المان و ارتباط بین آنها را به درستی بیان کند و به این منظور ARIA به کمک میآید. با استفاده از نقشها میتوان هدف هر المان را مشخص کرد و با استفاده از خصوصیات ARIA نحوهی عملکرد آنها را تعریف کرد.
نقش ها
نقشها طبق مستندات کنسرسیوم وب بر 4 نوع هستند:
- Abstract Roles
- Widget Roles
- Document Roles
- Landmark Roles
Banner | این
قسمت که عموما برای اجزای مهمی مثل هدر سایت قرار میگیرد و شامل معرفی وب
سایت هست و در همهی صفحات وجود دارد که شامل لوگو، اطلاعات عمومی سایت و
اسپانسرها و ... میگردد و بسیار مهم است که تنها یکبار در صفحهی وب به کار
برود و تکرار آن پرهیز شود. |
Main | این
نقش به محتوای اصلی وب سایت اشاره میکند و نباید بیشتر از یکبار در هر صفحهی وب به کار برود و عموما بهتر است این خصوصیت در تگ div قرار گیرد:<div Role="main"></div> <main role="main">..... |
Navigation | اشاره به یک ناحیه پر از المانهای لینک برای ارتباط با صفحات دیگر |
Complementary | مشخص سازی ناحیهای که اطلاعات اضافی دربارهی محتوای اصلی سایت دارد؛ مانند بخش مقالات مرتبط، آخرین کامنتها و ... |
ContentInfo | این نقش که بیشتر برای فوتر مناسب است برای محتوایی به کار میرود که در آن به قوانین کپی رایت و ... اشاره میشود. |
form | برای اشاره به فرمها که دارای قسمتهای ورودی کاربر هستند. |
search | در صورتیکه فرمی دارید و از آن برای گزینهی جست و جو استفاده میکنید، از این نقش استفاده کنید. |
application | برای
اینکه وب سایت خود را به صورت یک وب اپلیکیشن معرفی کنید؛ تا یک صفحه وب معمولی،
استفاده میشود و برای وب سایتهای قدیمی یا با حالت سنتی توصیه نمیشود و
به برنامههای کمکیار معلولین میگوند که از حالت عادی به حالت
application سوئیچ کنند؛ پس با دقت بیشتری باید از این گزینه استفاده کرد. |
خصوصیتها و وضعیت ها
در حالیکه از نقشها برای معرفی هر المان یا تگ استفاده میکنید؛ خصویتها و وضعیتها به کاربر اطلاعات اضافی میدهند که چگونه ز آن استفاده کنند. برای معرفی خصوصیتها و وضعیتها از یک خصوصیت که با -aria شروع میشود استفاده کنید. از معروفترین آنها خصوصیت aria-required و وضعیت aria-checked میباشند، تا به ترتیب به کاربر اعلام کنید این تگ نیاز به پر شدن دارد، یا المانی نیاز به تغییر وضعیت انتخابی دارد.
نحوهی استفاده از آنها به شکل زیر است:
<div id="some-id" class="some-class" aria-live="assertive"><div>
ساخت ارتباطات میان المانها با خصوصیتهای ارتباطی
مثال شماره یک
<div role="main" aria-labelledby="some-id"> <h1 id="some-id">This Is A Heading</h1> Main content... </div>
مثال شماره دو
در این مثال هر گروهی از المانها یک برچسب و بعضی المانها یک برچسب اختصاصی دارند که توسط خصوصیت معرفی شده aria-labelldby کامل شدهاند:
<div id="billing">Billing Address</div> <div> <div id="name">Name</div> <input type="text" aria-labelledby="name billing"/> </div> <div> <div id="address">Address</div> <input type="text" aria-labelledby="address billing"/> </div>
مثال شماره سه
در این مثال گروهی از radio buttonها با برچسبشان ارتباط برقرار میکنند.
<div id="radio_label">My radio label</div> <ul role="radiogroup" aria-labelledby="radio_label"> <li role="radio">Item #1</li> <li role="radio">Item #2</li> <li role="radio">Item #3</li> </ul>
خصوصیتها و وضعیتهای aria را با چرخهی فعالیتهای صفحه به روز کنید
در صورتیکه چرخهی فعالیت صفحهی شما تغییر میکند و تگها نیاز به مقادیر جدیدی از aria دارند، حتما این مقادیر را هم به نسبت تغییراتی که در صفحه زخ میدهد، تغییر دهید تا وضعیت بحرانی برای کاربر به خصوص در حین کار با فرمها و ... پیش نیاید.
هر aria را دوباره استفاده نکنید
امروزه به خصوص با آمدن html5 و ویژگیهایی چون تگهای مفهومی، کار بسیار راحتتر شدهاست و مرورگر به طور خودکار میتواند aria را بر روی بعضی از المانها پیاده کند. به عنوان نمونه:
<form></form> <form role="form"></form>
امروزه شاهد پیشرفت فناوری در همهی عرصهها هستیم و همیشه این پیشرفتها ما را ذوق زده کردهاند، ولی یکی از بی نظیرترین استفادههای فناوری روز، استفاده در صنایع سلامتی است که نه تنها ما را ذوق زده میکند، بلکه از لحاظ احساسی هم ما را به وجد میآورند و جزء زیباترین نتایج فناوری میباشند. بسیاری از شرکتها چون گوگل در این راستا فعالیتهای زیادی کردهاند تا بتوانند سلامت جامعه را کنترل کنند، از ساخت لنز چشمی برای کنترل دیابت گرفته تا ساخت قاشق عذای خوری برای بیماران پارکینسون، ولی استفادههای ساده از مسائلی مانند بالا به افراد معلول این مژده را میدهد که ما آنها را فراموش نکردهایم.
در این بین به راحتی میتوان چندین نمونه از این ORMها را نام برد مثل IBatis , Hibernate ,Nhibernate و EF که از معروفترین آنها هستند.
من در حال حاضر قصد شروع یک پروژه اندرویدی را دارم و دوست دارم بجای استفادهی از Sqlitehelper، از یک ORM مناسب بهره ببرم که چند سوال برای من پیش میآید. آیا ORM ای برای آن تهیه شده است؟ اگر آری چندتا و کدامیک از آنها بهتر هستند؟ شاید در اولین مورد کتابخانهی Hibernate جاوا را نام ببرید؛ ولی توجه به این نکته ضروری است که ما در مورد پلتفرم موبایل و محدودیتهای آن صحبت میکنیم. یک کتابخانه همانند Hibernate مطمئنا برای یک برنامه اندروید چه از نظر حجم نهایی برنامه و چه از نظر حجم بزرگش در اجرا، مشکل زا خواهد بود و وجود وابستگیهای متعدد و دارا بودن بسیاری از قابلیتهایی که اصلا در بانکهای اطلاعاتی موبایل قابل اجرا نیست، باعث میشود این فریمورک انتخاب خوبی برای یک برنامه اندروید نباشد.
معیارهای انتخاب یک فریم ورک مناسب برای موبایل:
- سبک بودن: مهمترین مورد سبک بودن آن است؛ چه از لحاظ اجرای برنامه و چه از لحاظ حجم نهایی برنامه
- سریع بودن: مطمئنا ORMهای طراحی شدهی موجود، از سرعت خیلی بدی برخوردار نخواهند بود؛ اگر سر زبان هم افتاده باشند. ولی باز هم انتخاب سریع بودن یک ORM، مورد علاقهی بسیاری از ماهاست.
- یادگیری آسان و کانفیگ راحت تر.
OrmLight
این فریمورک مختص اندروید طراحی نشده ولی سبک بودن آن موجب شدهاست که بسیاری از برنامه نویسان از آن در برنامههای اندرویدی استفاده کنند. این فریم ورک جهت اتصالات JDBC و Spring و اندروید طراحی شده است.
نحوه معرفی جداول در این فریمورک به صورت زیر است:
@DatabaseTable(tableName = "users") public class User { @DatabaseField(id = true) private String username; @DatabaseField private String password; public User() { // ORMLite needs a no-arg constructor } public User(String username, String password) { this.username = username; this.password = password; } // Implementing getter and setter methods public String getUserame() { return this.username; } public void setName(String username) { this.username = username; } public String getPassword() { return this.password; } public void setPassword(String password) { this.password = password; } }
SugarORM
این فریمورک مختص اندروید طراحی شده است. یادگیری آن بسیار آسان است و به راحتی به یاد میماند. همچنین جداول مورد نیاز را به طور خودکار خواهد ساخت. روابط یک به یک و یک به چند را پشتیبانی میکند و عملیات CURD را با سه متد Save,Delete و Find که البته FindById هم جزء آن است، پیاده سازی میکند.
برای استفاده از این فریمورک نیاز است ابتدا متادیتاهای زیر را به فایل manifest اضافه کنید:
<meta-data android:name="DATABASE" android:value="my_database.db" /> <meta-data android:name="VERSION" android:value="1" /> <meta-data android:name="QUERY_LOG" android:value="true" /> <meta-data android:name="DOMAIN_PACKAGE_NAME" android:value="com.my-domain" />
public class User extends SugarRecord<User> { String username; String password; int age; @Ignore String bio; //this will be ignored by SugarORM public User() { } public User(String username, String password,int age){ this.username = username; this.password = password; this.age = age; } }
باقی عملیات آن از قبیل اضافه کردن یک رکورد جدید یا حذف رکورد(ها) به صورت زیر است:
User johndoe = new User(getContext(),"john.doe","secret",19); johndoe.save(); //ذخیره کاربر جدید در دیتابیس //حذف تمامی کاربرانی که سنشان 19 سال است List<User> nineteens = User.find(User.class,"age = ?",new int[]{19}); foreach(user in nineteens) { user.delete(); }
موقعیکه بحث کارآیی و سرعت پیش میآید نام GreenDAO هست که میدرخشد. طبق گفتهی سایت رسمی آن این فریمورک میتواند در ثانیه چند هزار موجودیت را اضافه و به روزرسانی و بارگیری نماید. این لیست حاوی برنامههایی است که از این فریمورک استفاده میکنند. جدول زیر مقایسهای است بین این کتابخانه و OrmLight که نشان میدهد 4.5 برابر سریعتر از OrmLight عمل میکند.
آموزش راه اندازی آن در اندروید استادیو، سورس آن در گیت هاب و مستندات رسمی آن.
Active Android
این کتابخانه از دو طریق فایل JAR و به شیوه maven قابل استفاده است که میتوانید روش استفادهی از آن را در این لینک ببینید و سورس اصلی آن هم در این آدرس قرار دارد. بعد از اینکه کتابخانه را به پروژه اضافه کردید، دو متادیتای زیر را که به ترتیب نام دیتابیس و ورژن آن هستند، به manifest اضافه کنید:
<meta-data android:name="AA_DB_NAME" android:value="my_database.db" /> <meta-data android:name="AA_DB_VERSION" android:value="1" />
public class MyActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActiveAndroid.initialize(this); //ادامه برنامه } }
@Table(name = "User") public class User extends Model { @Column(name = "username") public String username; @Column(name = "password") public String password; public User() { super(); } public User(String username,String password) { super(); this.username = username; this.password = password; } }
ORMDroid
از آن دست کتابخانههایی است که سادگی و کم حجم بودن شعار آنان است و سعی دارند تا حد ممکن همه چیز را خودکار کرده و کمترین کانفیگ را نیاز داشته باشد. حجم فعلی آن حدود 20 کیلوبایت بوده و نمیخواهند از 30 کیلوبایت تجاوز کند.
برای استفادهی از آن ابتدا دو خط زیر را جهت معرفی تنظیمات به manifest اضافه کنید:
<meta-data android:name="ormdroid.database.name" android:value="your_database_name" /> <meta-data android:name="ormdroid.database.visibility" android:value="PRIVATE||WORLD_READABLE||WORLD_WRITEABLE" />
ORMDroidApplication.initialize(someContext);
public class Person extends Entity { public int id; public String name; public String telephone; } //==================== Person p = Entity.query(Person.class).where("id=1").execute(); p.telephone = "555-1234"; p.save(); // یا Person person = Entity.query(Person.class).where(eql("id", id)).execute(); p.telephone = "555-1234"; p.save();
سورس آن در گیت هاب
در اینجا سعی کردیم تعدادی از کتابخانههای محبوب را معرفی کنیم ولی تعداد آن به همین جا ختم نمیشود. ORMهای دیگری نظیر AndRom و سایر ORM هایی که در این لیست معرفی شده اند وجود دارند.
نکته نهایی اینکه خوب میشود دوستانی که از این ORMهای مختص اندروید استفاده کرده اند؛ نظراتشان را در مورد آنها بیان کنند و مزایا و معایب آنها را بیان کنند.
پیاده سازی الگوی Decorator به کمک سیستم تزریق وابستگیهای NET Core.
مثال زیر را در نظر بگیرید که در آن یک سرویس تعریف شدهاست و در این بین استثنائی رخ دادهاست.
public interface ITaskService { void Run(); } public class MyTaskService : ITaskService { public void Run() { throw new InvalidOperationException("An exception from the MyTaskService!"); } }
using System; using Microsoft.Extensions.Logging; namespace CoreIocServices { public class MyTaskServiceDecorator : ITaskService { private readonly ILogger<MyTaskServiceDecorator> _logger; private readonly ITaskService _decorated; public MyTaskServiceDecorator( ILogger<MyTaskServiceDecorator> logger, ITaskService decorated) { _logger = logger; _decorated = decorated; } public void Run() { try { _decorated.Run(); } catch (Exception ex) { _logger.LogCritical(ex, "An unhandled exception has been occurred."); } } } }
مزیت اینکار، پیاده سازی اصل DRY یا Don't repeat yourself است. کاری که برای رفع این مشکل قرار است انجام دهیم، استفاده از یک تزئین کننده (محصور کننده)، کپسوله سازی اعمال تکراری و سپس اتصال آن به قسمتهای مختلف برنامه است. همچنین در این حالت اصل open closed principle نیز بهتر رعایت خواهد شد. از این جهت که کدهای تکراری برنامه به یک لایهی دیگر منتقل شدهاند و دیگر نیازی نیست برای تغییر آنها، کدهای قسمتهای اصلی برنامه را تغییر داد (کدهای برنامه باز خواهند بود برای توسعه و بسته برای تغییر).
پس از طراحی این تزئین کننده، اکنون نوبت به معرفی آن به سیستم تزریق وابستگیهای NET Core. است:
namespace CoreIocSample02 { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<MyTaskService>(); services.AddTransient<ITaskService>(serviceProvider => new MyTaskServiceDecorator( serviceProvider.GetService<ILogger<MyTaskServiceDecorator>>(), serviceProvider.GetService<MyTaskService>()) );
در اینجا هم میتوان در صورت نیاز اصل کلاس MyTaskService را بدون هیچ نوع تزئین کنندهای از سیستم تزریق وابستگیها دریافت کرد و یا اگر وهلهای از سرویس ITaskService را از آن درخواست کردیم، ابتدا شیء MyTaskServiceDecorator وهله سازی شده و سپس توسط آن یک نمونهی محصور شده و تزئین شدهی MyTaskService به فراخوان بازگشت داده خواهد شد.
ساده سازی معرفی تزئین کنندهها به سیستم تزریق وابستگیهای NET Core. به کمک Scrutor
در «قسمت هشتم - ساده سازی معرفی سرویسها توسط Scrutor» با کتابخانهی Scrutor آشنا شدیم. یکی دیگر از قابلیتهای آن، امکان ساده سازی تعریف تزئین کنندها است:
namespace CoreIocSample02 { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<ITaskService, MyTaskService>(); services.Decorate<ITaskService, MyTaskServiceDecorator>();
مثال 12: محاسبه کنید در سال 2012 و به ازای هر ماه مجزای آن، چه تعداد slots رزرو شدهاند؛ قسمت دوم.
این مثال را در قسمت قبل (مثال 6 آن) نیز بررسی کردیم. در اینجا میخواهیم در گزارش نهایی تولید شده، پس از اتمام ردیفهای یک ماه به ازای یک امکان خاص، جمع کل آن نیز درج شود و همچنین در پایان تمام ردیفها، جمع کل نهایی ذکر شود؛ چیزی شبیه به تصویر زیر که در آن 910، جمع کل slots ماه 8 است و 9191، جمع کل سال.
روش پیشنهادی حل این مساله استفاده از مفهومی به نام «GROUP BY ROLLUP» است:
SELECT facid, DATEPART(month, [StartTime]) AS month, sum(slots) AS slots FROM bookings WHERE starttime >= '2012-01-01' AND starttime < '2013-01-01' GROUP BY ROLLUP(facid, DATEPART(month, [StartTime])) ORDER BY facid, month;
ابتدا جمع slots را گروه بندی شده بر اساس هر ماه سال محاسبه میکنیم. این قسمت توسط LINQ to Entities قابل انجام است؛ همان مثال 6 قسمت قبل است.
سپس این اطلاعات که اکنون در سمت کلاینت (یعنی برنامهی ما) در حافظه موجود هستند، نیاز دارند به ازای هر گروه، یک جمع کل (sub total) و به ازای کل سال نیز یک جمع کل (grand total یا total) پیدا کنند.
ROLLUP(facid, month) اطلاعات تجمعی سلسه مراتبی پارامترهای ارسالی به آن را تولید میکند. یعنی (facid, month), (facid) و (). پیاده سازی LINQ to Objects این تابع را در اینجا میتوانید مشاهده کنید: Utils\GroupingExtensions.cs
بنابراین راه حل این مساله به صورت زیر خواهد بود:
var date1 = new DateTime(2012, 01, 01); var date2 = new DateTime(2013, 01, 01); var facilities = context.Bookings .Where(booking => booking.StartTime >= date1 && booking.StartTime < date2) .GroupBy(booking => new { booking.FacId, booking.StartTime.Month }) .Select(group => new { group.Key.FacId, group.Key.Month, TotalSlots = group.Sum(booking => booking.Slots) }) .OrderBy(result => result.FacId) .ThenBy(result => result.Month) .ToList() //This is new .GroupByWithRollup( item => item.FacId, item => item.Month, (primaryGrouping, secondaryGrouping) => new { FacId = primaryGrouping.Key, Month = secondaryGrouping.Key, TotalSlots = secondaryGrouping.Sum(item => item.TotalSlots) }, item => new { FacId = item.Key, Month = -1, TotalSlots = item.SubTotal(subItem => subItem.TotalSlots) }, items => new { FacId = -1, Month = -1, TotalSlots = items.GrandTotal(subItem => subItem.TotalSlots) });
در اینجا سلولهایی که اطلاعاتی ندارند، با منهای یک مشخص شدهاند؛ در گزارش اصلی با null مقدار دهی شده بودند.
مثال 13: به ازای نام هر کدام از امکانات موجود، جمع کل تعداد ساعات رزرو شدهی آنها را محاسبه کنید.
هر slot تنها نیم ساعت است و گزارش نهایی باید به همراه ستونهای facid, name, Total Hours باشد؛ مرتب شده بر اساس facid.
var items = context.Bookings .GroupBy(booking => new { booking.FacId, booking.Facility.Name }) .Select(group => new { group.Key.FacId, group.Key.Name, TotalHours = group.Sum(booking => booking.Slots) / 2M }) .OrderBy(result => result.FacId) .ToList();
مثال 14: گزارشی را از اولین رزرو کاربران پس از September 1st 2012، تهیه کنید.
این گزارش باید به همراه ستونهای surname, firstname, memid, starttime باشد؛ مرتب شده بر اساس memid.
var date1 = new DateTime(2012, 09, 01); var items = context.Bookings .Where(booking => booking.StartTime >= date1) .GroupBy(booking => new { booking.Member.Surname, booking.Member.FirstName, booking.Member.MemId }) .Select(group => new { group.Key.Surname, group.Key.FirstName, group.Key.MemId, StartTime = group.Min(booking => booking.StartTime) }) .OrderBy(result => result.MemId) .ToList();
مثال 15: گزارشی را از کاربران تهیه کنید که هر ردیف آن، به همراه تعداد کل کاربران باشد.
این گزارش باید به همراه ستونهای count, firstname, surname باشد؛ مرتب شده بر اساس joindate.
var members = context.Members .OrderBy(member => member.JoinDate) .Select(member => new { Count = context.Members.Count(), member.FirstName, member.Surname }) .ToList();
SELECT COUNT(*) FROM [Members] AS [m]; SELECT [m].[FirstName], [m].[Surname], @__Count_0 AS [Count] FROM [Members] AS [m] ORDER BY [m].[JoinDate];
باید بخاطر داشت که ID کاربران پشت سرهم نیست و همچنین این گزارش باید به همراه ستونهای row_number, firstname, surname باشد؛ مرتب شده بر اساس joindate.
هدف اصلی از این مثال، کار با مفهوم window functionها و تابع row_number است:
SELECT row_number() OVER (ORDER BY joindate) AS row_number, firstname, surname FROM members ORDER BY joindate;
var members = context.Members .OrderBy(member => member.JoinDate) .Select(member => new { member.FirstName, member.Surname }) .ToList() /* SELECT [m].[FirstName], [m].[Surname] FROM [Members] AS [m] ORDER BY [m].[JoinDate] */ // Now using LINQ to Objects .Select((member, index) => new { RowNumber = index + 1, member.FirstName, member.Surname }) .ToList();
مثال 17: کدامیک از امکانات موجود، بیشترین slots رزرو شده را دارد؟ قسمت دوم.
این مورد همان مثال 11 قسمت قبل است که پاسخ آنرا یافتیم (و از تکرار مجدد آن صرفنظر میکنیم) و هدف اصلی آن رسیدن به کوئری window function دار زیر است که تنها از طریق اجرای یک raw sql در EF-Core قابل اجرا است:
SELECT facid, total FROM (SELECT facid, sum(slots) AS total, rank() OVER (ORDER BY sum(slots) DESC) AS rank FROM bookings GROUP BY facid) AS ranked WHERE rank = 1;
مثال 18: به کاربران بر اساس تعداد ساعات رزرو آنها، امتیاز دهی (رتبه بندی) کنید.
این گزارش باید به همراه ستونهای firstname, surname, hours, rank باشد؛ مرتب شده بر اساس rank, surname.
هدف اصلی از این مثال، رسیدن به کوئری rank دار زیر است:
SELECT mems.firstname, mems.surname, ((sum(bks.slots) + 10) / 20) * 10 AS hours, rank() OVER (ORDER BY ((sum(bks.slots) + 10) / 20) * 10 DESC) AS rank FROM bookings AS bks INNER JOIN members AS mems ON bks.memid = mems.memid GROUP BY mems.firstname, mems.surname ORDER BY rank, mems.surname, mems.firstname;
var itemsQuery = context.Bookings .GroupBy(booking => new { booking.Member.FirstName, booking.Member.Surname }) .Select(group => new { group.Key.FirstName, group.Key.Surname, Hours = (group.Sum(booking => booking.Slots) + 10) / 20 * 10 }) .OrderByDescending(result => result.Hours) .ThenBy(result => result.Surname) .ThenBy(result => result.FirstName); var rankedItems = itemsQuery.Select(thisItem => new { thisItem.FirstName, thisItem.Surname, thisItem.Hours, Rank = itemsQuery.Count(mainItem => mainItem.Hours > thisItem.Hours) + 1 }) .ToList();
با این خروجی SQL نهایی:
مثال 19: سه امکانی را لیست کنید که بالاترین میزان فروش را داشتهاند.
این گزارش باید به همراه ستونهای name, rank باشد؛ مرتب شده بر اساس rank.
روش محاسبهی این گزارش با مثال قبلی یکی است (البته اینبار رتبه بندی بر اساس TotalRevenue است) و فقط در انتهای آن یک Where(result => result.Rank <= 3) را بیشتر دارد:
var facilitiesQuery = context.Bookings.Select(booking => new { booking.Facility.Name, Revenue = booking.MemId == 0 ? booking.Slots * booking.Facility.GuestCost : booking.Slots * booking.Facility.MemberCost }) .GroupBy(b => b.Name) .Select(group => new { Name = group.Key, TotalRevenue = group.Sum(b => b.Revenue) }) .OrderBy(result => result.TotalRevenue); var rankedFacilities = facilitiesQuery.Select(thisItem => new { thisItem.Name, thisItem.TotalRevenue, Rank = facilitiesQuery.Count(mainItem => mainItem.TotalRevenue > thisItem.TotalRevenue) + 1 }) .Where(result => result.Rank <= 3) .OrderBy(result => result.Rank) .ToList();
مثال 20: امکانات موجود را بر اساس میزان فروشی که دارند به گروههایی با تعداد مساوی high, average, low تقسیم بندی کنید.
این گزارش باید به همراه ستونهای name, revenue باشد؛ مرتب شده بر اساس revenue, name.
هدف اصلی از این گزارش کار با تابع ntile است که اطلاعات را بر اساس پارامتر ارسالی به آن تاجای ممکن به گروههای مساوی تقسیم میکند:
SELECT name, CASE WHEN class = 1 THEN 'high' WHEN class = 2 THEN 'average' ELSE 'low' END AS revenue FROM (SELECT facs.name AS name, ntile(3) OVER (ORDER BY sum(CASE WHEN memid = 0 THEN slots * facs.guestcost ELSE slots * membercost END) DESC) AS class FROM bookings AS bks INNER JOIN facilities AS facs ON bks.facid = facs.facid GROUP BY facs.name) AS subq ORDER BY class, name;
var facilities = context.Bookings.Select(booking => new { booking.Facility.Name, Revenue = booking.MemId == 0 ? booking.Slots * booking.Facility.GuestCost : booking.Slots * booking.Facility.MemberCost }) .GroupBy(b => b.Name) .Select(group => new { Name = group.Key, TotalRevenue = group.Sum(b => b.Revenue) }) .OrderByDescending(result => result.TotalRevenue) .ToList();
SELECT [f].[Name], SUM(CASE WHEN [b].[MemId] = 0 THEN CAST ([b].[Slots] AS DECIMAL (18, 6)) * [f].[GuestCost] ELSE CAST ([b].[Slots] AS DECIMAL (18, 6)) * [f].[MemberCost] END) AS [TotalRevenue] FROM [Bookings] AS [b] INNER JOIN [Facilities] AS [f] ON [b].[FacId] = [f].[FacId] GROUP BY [f].[Name] ORDER BY SUM(CASE WHEN [b].[MemId] = 0 THEN CAST ([b].[Slots] AS DECIMAL (18, 6)) * [f].[GuestCost] ELSE CAST ([b].[Slots] AS DECIMAL (18, 6)) * [f].[MemberCost] END) DESC;
var n = 3; var tiledFacilities = facilities.Select((item, index) => new { Item = item, Index = (index / n) + 1 }) .GroupBy(x => x.Index) .Select(g => g.Select(z => new { z.Item.Name, z.Item.TotalRevenue, Tile = g.Key, GroupName = g.Key == 1 ? "High" : (g.Key == 2 ? "Average" : "Low") }) .OrderBy(x => x.GroupName) .ThenBy(x => x.Name) ) .ToList(); var flatTiledFacilities = tiledFacilities.SelectMany(group => group) .Select(tile => new { tile.Name, Revenue = tile.GroupName }) .ToList();
مثال 21: چندماه طول میکشد تا هر کدام از امکانات موجود بر اساس فروشی که دارند، هزینهی مالکیت ابتدایی خود را کسب کنند.
این گزارش باید به همراه ستونهای name, months باشد؛ مرتب شده بر اساس name.
var facilities = context.Bookings.Select(booking => new { booking.Facility.Name, booking.Facility.InitialOutlay, booking.Facility.MonthlyMaintenance, Revenue = booking.MemId == 0 ? booking.Slots * booking.Facility.GuestCost : booking.Slots * booking.Facility.MemberCost }) .GroupBy(b => new { b.Name, b.InitialOutlay, b.MonthlyMaintenance }) .Select(group => new { group.Key.Name, RepayTime = group.Key.InitialOutlay / ((group.Sum(b => b.Revenue) / 3) - group.Key.MonthlyMaintenance) }) .OrderBy(result => result.Name) .ToList();
مثال 22: گزارش میانگین متحرک فروش کل هر کدام از روزهای August 2012 را برای یک بازهی 15 روزهی قبل، محاسبه کنید.
این گزارش باید به همراه ستونهای date, revenue باشد؛ مرتب شده بر اساس date. در این گزارش روزهای ماه 8 میلادی ردیف شده و به ازای هر ردیف، میانگین فروش 15 روز قبل از آن تاریخ، نمایش داده میشود. به همین جهت به آن میانگین متحرک نیز میگویند.
هدف اصلی از این گزارش، استفاده از توابع avg(revdata.rev) over است. اما چون نمیتوان از آنها در LINQ to Entities استفاده کرد، از روش دیگری که شامل جوین یک جدول با خودش است، استفاده میکنیم:
var startDate = new DateTime(2012, 08, 1); var endDate = new DateTime(2012, 08, 31); var period = 14; var dailyRevenueQuery = context.Bookings .Select(booking => new { StartDate = booking.StartTime.Date, // How to group by date (or TruncateTime) in EF-Core Revenue = booking.MemId == 0 ? booking.Slots * booking.Facility.GuestCost : booking.Slots * booking.Facility.MemberCost }) .GroupBy(b => b.StartDate) .Select(group => new { Date = group.Key, TotalRevenue = group.Sum(b => b.Revenue) });
اکنون که میزان کل فروش روزها را داریم، میخواهیم میانگین فروش 15 روز قبل شروع شدهی از از ابتدای ماه 8، تا انتهای آنرا محاسبه کنیم. برای اینکار نیاز است کوئری فوق را یکبار دیگر با خودش جوین کنیم تا از یک سر آن تاریخ هر روز و از طرف دیگر، میانگین 15 روز قبل، تولید شود:
var movingAvgs = dailyRevenueQuery .Select(dr1 => new { dr1.Date, MovingAvg = dailyRevenueQuery .Where(dr2 => dr2.Date <= dr1.Date && dr2.Date >= dr1.Date.AddDays(-period)) .Average(dr2 => dr2.TotalRevenue) }) .Where(result => result.Date >= startDate && result.Date <= endDate) .OrderBy(result => result.Date) .ToList();
کدهای کامل این قسمت را در اینجا میتوانید مشاهده کنید.
زبان برنامه نویسی Erlang
زبان Erlang در سال 1986 توسط شرکت Ericson سوئد به منظور استفاده در سرورهای switching تلفن ساخته شد. این زبان توسط تیمی به سرپرستی Joe Armstrong معرفی شد تا بتواند از برنامههای توزیعشده، مقاوم در برابر خطا، بلادرنگ و بیوقفه پشتیبانی کند. بعدها این زبان به شکل متنباز در اختیار عموم قرار گرفت. یکی از روشهای برنامه نویسی که توسط این برنامه میشه ازش استفاده کرد، روش تابعی (Functional Programming) هست. این روش قبلا وجود داشت و مدتی هم از مد افتاد، ولی با اومدن پردازندههای چند هسته ای استفاده از زبانهای برنامه نویسی که میشه با اونها تابعی نوشت از سر گرفته شد و حتی مایکروسافت در سال 2010 زبان برنامه نویسی #F رو معرفی کرد. یکی از قابلیتهای زبانهای تابعی سرعت اجرا شدن کدهای اونها هست که اونها رو از زبانهای امری مثل #C و Java جدا میکنه.
Scala هم یک زبان برنامه نویسی همه منظوره هست که ویژگی هایی رو از زبانهای برنامه نویسی شیء گرا داره و همچنین توسط اون میشه برنامه نویسی تابعی انجام داد. از اون به عنوان جانشینی برای جاوا یاد میکنند چون قابلتهای اضافه بر جاوا رو داره.