مطالب
ارسال انواع بی نام (Anonymous) بازگشتی توسط Entity framework به توابع خارجی
فرض کنید ساختار زیر را در مدل ساخته شده به وسیله‌ی Entity framework در پروژه‌ی خود داریم .



جدول Post با جدول GroupsDetail ارتباط یک به چند و در مقابل ان جدول GroupsDetail با جدول PostGroup ارتباط چند به یک دارد . به زبان ساده ما تعدادی گروه بندی برای  مطالب داریم (در جدول PostGroup) و میتوانیم برای هر مطلب تعدادی از گروه‌ها را در جدول GroupsDetail مشخص کنیم ...
فکر میکنم همین مقدار توضیح به اندازه‌ی کافی برای درک روابط مشخص شده در مدل رابطه ای مفروض کفایت کند.
حالا فرض کنید ما میخواهیم لیستی از عنوان مطالب موجود به همراه [نام] گروه‌های هر مطلب را داشته باشیم .
توجه شما رو به قطعه کد ساده‌ی زیر جلب میکنم:
var context = new Models.EntitiesConnection();
var query = context.Posts.Select(pst => new {
             id = pst.id, 
             Title = pst.Title, 
             GNames = pst.GroupsDetails.Select(grd => new { Name = grd.PostGroup.name }) 
             })
             .OrderByDescending(c => c.id)
             .ToList();
همانطور که شاهد هستید در قطعه کد بالا توسط خواص راهبری (Navigation Properties ) به صورت مستقیم نام گروه‌های ثبت شده برای هر مطلب در جدول GroupsDetail را از جدول  PostGroup استخراج کردیم . خب تا اینجا مسئله ای وجود نداشت ( البته با ORM‌ها این کار بسیار سهل و اسان شده است تا جایی که در حالت عادی برای همین کار باید کلی join نویسی کرد و .... در کل خدارو شکر که از دست کارهای تکراری و خسته کننده توسط این موجودات مهربان (ORM's) نجات یافتیم )...

خب میرسیم به ادامه‌ی مطلبمون . نتیجه‌ی این query چه خواهد بود ؟ کاملا واضح است که ما تعداد دلخواهی از فیلد‌ها رو برای واکشی مشخص کردیم پس در نتیجه نوع داده ای که توسط این query بازگشت داده خواهد شد یک لیست از یک نوع بی نام میباشد ... چگونه از نتیجه‌ی بازگشتی این query در صورت ارسال اون به عنوان یک پارامتر به یک تابع استفاده کنیم ؟ اگر ما تمامی فیلد‌های جدول Post را واکشی میکریم مقدار بازگشتی یک لیست از نوع Post بود که به راحتی قابل استفاده بود مثل مثال زیر

public bool myfunc(List<Post> query)
        {
            foreach (var item in query)
            {
            .....  string title =  item.Title;
            }
...return true;
        }
.
.
.

var context = new Models.EntitiesConnection();
var queryx = context.Posts.ToList();
.... myfunc(queryx );
اما حالا با یک لیست از یک نوع بی نام  رو به رو هستیم ... چند راه مختلف برای دسترسی به این گونه از مقادیر بازگشتی داریم .
1 : استفاده از Reflection برای دسترسی به فیلدهای مشخص شده .
2 : تعریف یک مدل کامل بر اساس فیلدهای مشخص شده‌ی بازگشتی و ارسال یک لیست از نوع تعریف شده به تابع . ( به نظرم این روش خسته کنندست و زیاد شکیل نیست چون برای هر query خاص مجبوریم یک مدل کلی تعریف کنیم و این باعث میشه تعداد زیادی مدل در نهایت داشته باشیم که استفاده‌ی زیادی از اونها نمیشه عملا )
3 : استفاده از یکی از روش‌های خلاقانه‌ی تبدیل نوع Anonymous‌ها .

روش سوم روش مورد علاقه‌ی من هست و انعطاف بالایی داره و خیلی هم شیکو مجلسیه ! در ضمن این روش که قراره ذکر بشه کاربردهای فراوانی داره که این مورد فقط یکی از اونهاست
خب بریم سر اصل مطلب . به کد زیر توجه کنید :

        public static List<T> CreateGenericListFromAnonymous<T>(object obj, T example)
        {
            return (List<T>)obj;
        }
        public static IEnumerable<T> CreateEmptyAnonymousIEnumerable<T>(T example)
        {
            return new List<T>(); ;
        }

////

        public bool myfunc(object query)
        {
            var cquery = CreateGenericListFromAnonymous(query, 
                              new { 
                                       id = 0, 
                                       Title = string.Empty, 
                                       GNames = CreateEmptyAnonymousIEnumerable(new { Name = string.Empty }) 
                              });
            foreach (var item in cquery)
            {
                string title = item.Title;
                foreach (var gname in item.GNames)
                {
                    string gn = gname.Name;
                }
.
.
.
            }
            return false;
        }

.
.
.
var context = new Models.EntitiesConnection();
var query = context.Posts.Select(pst => new
                              { 
                                   id = pst.id, 
                                   Title = pst.Title,
                                   GNames = pst.GroupsDetails.Select(grd => new { Name = grd.PostGroup.name }) 
                             }).OrderByDescending(c => c.id);
            myfunc(query.ToList());
در کد بالا به صورت تو در تو از انواع بی نام استفاده شده تا مطلب براتون خوب جا بیوفته . یعنی یک نوع بی نام که یکی از فیلدهای اون یک لیست از یک نوع بی نام دیگست ... خب میبینید که خیلی راحت با دو تابع ما تبدیل انواع بی نام رو به صورت inline انجام دادیم و ازش استفاده کردیم . بدون اینکه نیاز باشه ما یک مدل مجزا ایجاد کنیم ...
تابع CreateGenericListFromAnonymous : یک object رو میگیره و اون رو به یک لیست تبدیل میکنه بر اساس نوعی که به صورت inline براش مشخص میکنیم .
تابع CreateEmptyAnonymousIEnumerable یک لیست از نوع IEnumerable رو بر اساس نوعی که به صورت inline براش مشخص کردیم بر میگردونه . دلیل اینکه من در اینجا این تابع رو نوشتم این بود که ما در query یک فیلد با نام GNames داشتیم که مجموعه ای از نام گروه‌های هر مطلب بود که از نوع IEnumerable هستش . در واقع ما در اینجا نوع بی نامی داریم که یکی از فیلدهای اون یک لیست از یک نوع بی نام دیگست .  امیدوارم مطلب براتون جا افتاده باشه.
دوستان عزیز سوالی بود در قسمت نظرات مطرح کنید.  
نظرات مطالب
ASP.NET MVC #7
- Model ذکر شده در حلقه با M بزرگ است و نه کوچک.
- ذکر model@ با m کوچک برای تعریف Model در ابتدای View ضروری است.
- پس از تعریف کلاس‌های مدل برنامه، کامپایل را فراموش نکنید (خیلی از قسمت‌ها بر اساس Reflection کار می‌کنند و پس از کامپایل قابل دسترسی می‌شوند).
مطالب
بهبودهای کار با Lambdas در C# 9.0
C# 9.0 به همراه دو بهبود جزئی درباره‌ی کار با Lambdas است:
- امکان عدم ذکر بعضی از پارامترهای Lambdas
- امکان تعریف متدهای static anonymous


امکان عدم ذکر بعضی از پارامترهای Lambdas در C# 9.0

مثال زیر را در نظر بگیرید:
Action<object, EventArgs> handler1 = (object obj, EventArgs args) => ShowDialog();
در عبارت lambda تعریف شده، از پارامترهای obj و args استفاده نشده‌است. به همین جهت برای کاهش اخطارهای نمایش داده شده‌ی توسط کامپایلر در C# 9.0 می‌توان آن‌ها را به صورت discard parameters توسط _ معرفی کرد؛ به یکی از دو روش زیر:
Action<object, EventArgs> handler2 = (object _, EventArgs _) => ShowDialog();
// OR
Action<object, EventArgs> handler3 = (_, _) => ShowDialog();
که در یکی، نوع‌ها به همراه discard parameters ذکر شده‌اند و در دومی فقط discard parameters را داریم.

نمونه‌ی دیگر آن در حین تعریف رخ‌دادگردان‌ها است:
button1.Click += (s, e) => ShowDialog();
که اینبار پارامترهای استفاده نشده به صورت زیر قابل بیان هستند:
 button1.Click += (_, _) => ShowDialog();


امکان تعریف Static anonymous functions در C# 9.0

برای کاهش میزان تخصیص حافظه‌ی در حین کار با عبارات lambda ای که از متغیرهای محلی استفاده می‌کنند، اینبار در C# 9.0 می‌توان این عبارات را static تعریف کرد. برای نمونه مثال زیر را درنظر بگیرید:
namespace CS9Features
{
    public class LambdasTests
    {
        public void Test()
        {
            string text = "{0} is a good user !";
            PrintItems(item => string.Format(text, item));
        }

        private void PrintItems(Func<string, string> formatFunc)
        {
            foreach (var item in new[] { "User 1", "User 2" })
            {
                Console.WriteLine(formatFunc(item));
            }
        }
    }
}
در اینجا نحوه‌ی فرمت نهایی اطلاعات نمایش داده شده، توسط یک عبارت lambda تامین می‌شود. اتفاقی که در اینجا رخ می‌دهد، اصطلاحا capture شدن یک متغیر (text در اینجا) توسط یک anonymous function است (همان عبارت lambda نوشته شده). حاصل این capture شدن، ایجاد یک شیء موقتی برای مدیریت آن است که تخصیص حافظه و همچنین سربار عملیاتی اضافه‌ای را به همراه دارد. برای حذف این سربارها در C# 9.0 می‌توان متغیر استفاده شده را const تعریف کرد و سپس عبارت lambda تعریف شده را به صورت static نوشت:
const string text = "{0} is a good user !";
PrintItems(static item => string.Format(text, item));
با تعریف شدن یک عبارت lambda و یا یک anonymous method به صورت static که از تخصیص حافظه‌ی اضافی ایجاد شیء موقتی مدیریت کننده‌ی دسترسی به متغیر text جلوگیری می‌کند، اتفاقات زیر نیز رخ می‌دهند:
- این متد به عنوان static anonymous function شناخته می‌شود.
- دیگر نمی‌تواند دسترسی به حالت scope جاری را داشته باشد. بنابراین دیگر دسترسی به متغیرها، پارامترها و حتی شیء this را هم نخواهد داشت.
- می‌تواند با سایر اعضای استاتیک scope جاری کار کند.
- می‌تواند به تعاریف const مربوط به scope جاری، دسترسی داشته باشد.
نظرات مطالب
ارسال انواع بی نام (Anonymous) بازگشتی توسط Entity framework به توابع خارجی
خواهش میکنم قابل نداشت . دوست عزیز این مطلب واقعا پیچیده و تخصصی هستش من یک نمونه واستون نوشتم که کد رو براتون میزارم ولی برای فهم کاملش نیاز به اشنایی عمقی با ساختار دات نت و کار با رفلکشن داره که توضیحش در چند خط نمیگنجه بحثه یک کتاب کامله. این نمونه کد که نوشتم دقیقا همون چیزی هست که درخواست توضیحش رو دادید .
        public static List<T> CreateGenericListFromAnonymous<T>(object obj, T example)
        {
            var newquery = new List<T>();
            var constructor = typeof(T).GetConstructors(
            System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance
            ).OrderBy(c => c.GetParameters().Length).First();
            foreach (var item in ((IEnumerable<object>)obj))
            {
                var mapobj = new object[example.GetType().GetProperties().Count()];
                int counter = 0;
                foreach (var itemmap in example.GetType().GetProperties())
                {

                    object value = item.GetType().GetProperty(itemmap.Name).GetValue(item, null);
                    Type t = itemmap.PropertyType;
                    mapobj[counter] = Convert.ChangeType(value, t);
                    counter++;
                }
                newquery.Add((T)constructor.Invoke(mapobj));
            }

            return newquery;
        }


var context = new Models.EntitiesConnection();
var query = context.Posts.Select(pst => new
                              { 
                                   id = pst.id, 
                                   Title = pst.Title,
                                    Likes = pst.Likes,
                                    Unlikes = pst.Unlikes
                             }).OrderByDescending(c => c.id);

object test_custom_casting = CreateGenericListFromAnonymous(query.ToList(), new { id = 0, Title = string.Empty });
 همینطور که میبینید نتیجه‌ی بازگشتی از یک query مرجع یک list شامل فقط و فقط فیلدهایی هست که به صورت inline توسط انواع بی نام مشخص شده ! امیدوارم مفید واقع شده باشه . یا حق
مطالب
استفاده از shim و stub برای mock کردن در آزمون واحد
مقدمه:
از آنجایی که در این سایت در مورد shim و stub صحبتی نشده دوست داشتم مطلبی در این باره بزارم. در آزمون واحد ما نیاز داریم که یک سری اشیا را moq کنیم تا بتوانیم آزمون واحد را به درستی انجام دهیم. ما در آزمون واحد نباید وابستگی به لایه‌های پایین یا بالا داشته باشیم پس باید مقلدی از object هایی که در سطوح مختلف قرار دارند بسازیم.
شاید برای کسانی که با آزمون واحد کار کردند، به ویژه با فریم ورک تست Microsoft، یک سری مشکلاتی با mock کردن اشیا با استفاده از Mock داشته اند که حالا می‌خواهیم با معرفی فریم ورک‌های جدید، این مشکل را حل کنیم.
برای اینکه شما آزمون واحد درستی داشته باشید باید کارهای زیر را انجام دهید:
1- هر objectی که نیاز به mock کردن دارد باید حتما یا non-static باشد، یا اینترفیس داشته باشد.
2- شما احتیاج به یک فریم ورک تزریق وابستگی‌ها دارید که به عنوان بخشی از معماری نرم افزار یا الگوهای مناسب شی‌ءگرایی مطرح است، تا عمل تزریق وابستگی‌ها را انجام دهید.
3- ساختارها باید برای تزریق وابستگی در اینترفیس‌های object‌های وابسته تغییر یابند.

Shims و Stubs:
نوع stub همانند فریم ورک mock می‌باشد که برای مقلد ساختن اینترفیس‌ها و کلاس‌های non-sealed virtual یا ویژگی ها، رویدادها و متدهای abstract استفاده می‌شود. نوع shim می‌تواند کارهایی که stub نمی‌تواند بکند انجام دهد یعنی برای مقلد ساختن کلاس‌های static یا متدهای non-overridable استفاده می‌شود. با مثال‌های زیر می‌توانید با کارایی بیشتر shim و stub آشنا شوید.
یک پروژه mvc ایجاد کنید و نام آن را FakingExample بگذارید. در این پروژه کلاسی با نام CartToShim به صورت زیر ایجاد کنید:
namespace FakingExample
{
    public class CartToShim
    {
        public int CartId { get; private set; }
        public int UserId { get; private set; }
        private List<CartItem> _cartItems = new List<CartItem>();
        public ReadOnlyCollection<CartItem> CartItems { get; private set; }
        public DateTime CreateDateTime { get; private set; }
 
        public CartToShim(int cartId, int userId)
        {
            CartId = cartId;
            UserId = userId;
            CreateDateTime = DateTime.Now;
            CartItems = new ReadOnlyCollection<CartItem>(_cartItems);
        }
 
        public void AddCartItem(int productId)
        {
            var cartItemId = DataAccessLayer.SaveCartItem(CartId, productId);
            _cartItems.Add(new CartItem(cartItemId, productId));
        }
    }
}
و همچنین کلاسی با نام CartItem به صورت زیر ایجاد کنید:
public class CartItem
    {
        public int CartItemId { get; private set; }
        public int ProductId { get; private set; }
 
        public CartItem(int cartItemId, int productId)
        {
            CartItemId = cartItemId;
            ProductId = productId;
        }
    }
حالا یک پروژه unit test را با نام FakingExample.Tests اضافه کرده و نام کلاس آن را CartToShimTest بگذارید. یک reference از پروژه FakingExample تان به پروژه‌ی تستی که ساخته اید اضافه کنید. برای اینکه بتوانید کلاس‌های پروژه FakingExample را shim و یا stub کنید باید بر روی Reference پروژه تان راست کلیک کنید و گزینه Add Fakes Assembly را انتخاب کنید. وقتی این گزینه را می‌زنید، پوشه ای با نام Fakes در پروژه تست ایجاد شده و FakingExample.fakes در داخل آن قرار دارد همچنین در reference‌های پروژه تست، FakingExample.Fakes نیز ایجاد می‌شود.
اگر بر روی فایل fakes که در reference ایجاد شده دوبار کلیک کنید می‌توانید کلاس‌های CartItem و CartToShim را مشاهده کنید که هم نوع stub شان است و هم نوع shim آنها که در تصویر زیر می‌توانید مشاهده کنید.

ShimDataAccessLayer را که مشاهده می‌کنید یک متد SaveCartItem دارد که به دیتابیس متصل شده و آیتم‌های کارت را ذخیره می‌کند.

حالا می‌توانیم تست خود را بنویسیم. در زیر یک نمونه از تست را مشاهده می‌کنید:

[TestMethod]
        public void AddCartItem_GivenCartAndProduct_ThenProductShouldBeAddedToCart()
        {
            //Create a context to scope and cleanup shims
            using (ShimsContext.Create())
            {
                int cartItemId = 42, cartId = 1, userId = 33, productId = 777;
 
                //Shim SaveCartItem rerouting it to a delegate which 
                //always returns cartItemId
                Fakes.ShimDataAccessLayer.SaveCartItemInt32Int32 = (c, p) => cartItemId;
 
                var cart = new CartToShim(cartId, userId);
                cart.AddCartItem(productId);
 
                Assert.AreEqual(cartId, cart.CartItems.Count);
                var cartItem = cart.CartItems[0];
                Assert.AreEqual(cartItemId, cartItem.CartItemId);
                Assert.AreEqual(productId, cartItem.ProductId);
            }
        }
همانطور که در بالا مشاهده می‌کنید کدهای تست ما در اسکوپی قرار گرفته اند که محدوده shim را تعیین می‌کند و پس از پایان یافتن تست، تغییرات shim به حالت قبل بر می‌گردد. متد SaveCartItemInt32Int32 را که مشاهده می‌کنید یک متد static است و نمی‌توانیم با mock ویا stub آن را مقلد کنیم. تغییر اسم متد SaveCartItem به SaveCartItemInt32Int32 به این معنی است که متد ما دو ورودی از نوع Int32 دارد و به همین خاطر fake این متد به این صورت ایجاد شده است. مثلا اگر شما متد Save ای داشتید که یک ورودی Int و یک ورودی String داشت fake آن به صورت SaveInt32String ایجاد می‌شد.

به این نکته توجه داشته باشید که حتما برای assert کردن باید assert‌ها را در داخل اسکوپ ShimsContext قرار گرفته باشد در غیر این صورت assert شما درست کار نمی‌کند.

این یک مثال از shim بود؛ حالا می‌خواهم مثالی از یک stub را برای شما بزنم. یک اینترفیس با نام ICartSaver به صورت زیر ایجاد کنید:

public interface ICartSaver
    {
        int SaveCartItem(int cartId, int productId);
    }
برای shim کردن ما نیازی به اینترفیس نداشتیم اما برای استفاده از stub و یا Mock ما حتما به یک اینترفیس نیاز داریم تا بتوانیم object موردنظر را مقلد کنیم. حال باید یک کلاسی با نام CartSaver برای پیاده سازی اینترفیس خود بسازیم:
public class CartSaver : ICartSaver
    {
        public int SaveCartItem(int cartId, int productId)
        {
            using (var conn = new SqlConnection("RandomSqlConnectionString"))
            {
                var cmd = new SqlCommand("InsCartItem", conn);
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@CartId", cartId);
                cmd.Parameters.AddWithValue("@ProductId", productId);
 
                conn.Open();
                return (int)cmd.ExecuteScalar();
            }
        }
    }
حال تستی که با shim انجام دادیم را با استفاده از Stub انجام می‌دهیم:
[TestMethod]
        public void AddCartItem_GivenCartAndProduct_ThenProductShouldBeAddedToCart()
        {
            int cartItemId = 42, cartId = 1, userId = 33, productId = 777;
 
            //Stub ICartSaver and customize the behavior via a 
            //delegate, ro return cartItemId
            var cartSaver = new Fakes.StubICartSaver();
            cartSaver.SaveCartItemInt32Int32 = (c, p) => cartItemId;
 
            var cart = new CartToStub(cartId, userId, cartSaver);
            cart.AddCartItem(productId);
 
            Assert.AreEqual(cartId, cart.CartItems.Count);
            var cartItem = cart.CartItems[0];
            Assert.AreEqual(cartItemId, cartItem.CartItemId);
            Assert.AreEqual(productId, cartItem.ProductId);
        }
امیدوارم که این مطلب برای شما مفید بوده باشد.
اشتراک‌ها
آموزش QUnit فقط در 2 ساعت
QUnit یک فریم ورک قدرتمند و ساده برای آزمون واحد در جاوا اسکریپت است.
http://qunitjs.com/ 
آموزش QUnit فقط در 2 ساعت
مطالب دوره‌ها
تزریق وابستگی‌ها
خوب! بالاخره بعد از دو قسمت قبل، به بحث اصلی تزریق وابستگی‌ها رسیدیم. بحثی که بسیاری از برنامه نویس‌های تصور می‌کنند همان IoC است که پیشتر در مورد آن بحث شد.

تزریق وابستگی‌ها یا DI چیست؟

تزریق وابستگی‌ها یکی از انواع IoC بوده که در آن ایجاد و انقیاد (binding) یک وابستگی، در خارج از کلاسی که به آن نیاز دارد صورت می‌گیرد. روش‌های متفاوتی برای ارائه این وابستگی وهله سازی شده در خارج از کلاس به آن وجود دارد که در ادامه مورد بررسی قرار خواهند گرفت.
یک مثال: بسته غذایی را که با خود به سر کار می‌برید درنظر بگیرید. تمام اجزای مورد نیاز تشکیل دهنده یک بسته غذا عموما داخل آن قرار گرفته و حمل می‌شوند. حالت عکس آن زمانی است که در محل کار به شما غذا می‌دهند. این دقیقا همان حالتی است که تزریق وابستگی‌ها کار می‌کند؛ یک سری سرویس‌های خارجی، نیازهای کلاس جاری را برآورده می‌سازند.


در تصویر فوق یک طراحی مبتنی بر معکوس سازی کنترل‌ها را مشاهده می‌کنید. وابستگی‌های یک کلاس توسط اینترفیسی مشخص شده و سپس کلاسی وجود دارد که این وابستگی را پیاده سازی کرده است.
همچنین در این تصویر یک خط منقطع از کلاس MyClass به کلاس Dependency رسم شده است. این خط، مربوط به حالتی است که خود کلاس، مستقیما کار وهله سازی وابستگی مورد نیاز خود را انجام دهد؛ هر چند اینترفیسی نیز در این بین تعریف شده باشد. بنابراین اگر در بین کدهای این کلاس، چنین کدی مشاهده شد:
 IDependency dependency= new Dependency();
تعریف dependency از نوع IDependency به معنای معکوس سازی کنترل نبوده و عملا همان معماری سابق و متداول بکارگرفته شده است. برای بهبود این وضعیت، از تزریق وابستگی‌ها کمک خواهیم گرفت:


در اینجا یک کلاس دیگر به نام Injector اضافه شده است که قابلیت تزریق وابستگی‌ها را به کلاس نیازمند آن به روش‌های مختلفی دارا است. کار کلاس Injector، وهله سازی MyClass و همچنین وابستگی‌های آن می‌باشد؛ سپس وابستگی را دراختیار MyClass قرار می‌دهد.


تزریق وابستگی‌ها در سازنده کلاس

یکی از انواع روش‌های تزریق وابستگی‌ها، Constructor Injection و یا تزریق وابستگی‌ها در سازنده کلاس می‌باشد که متداول‌ترین نوع آن‌ها نیز به‌شمار می‌رود. در این حالت، وابستگی پس از وهله سازی، از طریق پارامترهای سازنده یک کلاس، در اختیار آن قرار می‌گیرند.
یک مثال:
public class Shopper
{
   private readonly ICreditCard _creditCard;
   public Shopper(ICreditCard creditCard)
   {
      _creditCard = creditCard;
   }
}
در اینجا شما یک کلاس خریدار را مشاهده می‌کنید که وابستگی مورد نیاز خود را به شکل یک اینترفیس از طریق سازنده کلاس دریافت می‌کند.
 ICreditCard creditCard = new MasterCard();
Shopper shopper = new Shopper(creditCard);
برای نمونه، وهله‌ای از مستر کارتی که  ICreditCard را پیاده سازی کرده باشد، می‌توان به سازنده این کلاس ارسال کرد. کار وهله سازی وابستگی و واژه کلیدی new به خارج از کلاس استفاده کننده از وابستگی منتقل شده است. بنابراین همانطور که ملاحظه می‌کنید، این مفاهیم آنچنان پیچیده نبوده و به همین سادگی قابل تعریف و اعمال هستند.


تزریق در خواص عمومی کلاس

Setter Injection و یا تزریق در خواص عمومی یک کلاس، یکی دیگر از روش‌های تزریق وابستگی‌ها است (setter در اینجا منظور همان get و set یک خاصیت می‌باشد).
در حالت تزریق وابستگی‌ها در سازنده کلاس، امکان وهله سازی آن کلاس بدون ارسال وابستگی‌ها به سازنده آن ممکن نیست؛ اما در اینجا خیر و امکان وهله سازی و استفاده از یک کلاس، پیش از اعمال وابستگی‌ها نیز وجود دارد. بنابراین امکان تغییر و تعویض وابستگی‌ها پس از وهله سازی کلاس نیز میسر است.
public class Shopper
{
   public ICreditCard CreditCard { get; set; }
}

ICreditCard creditCard = new MasterCard();
Shopper shopper = new Shopper();
shopper.CreditCard = creditCard;
نمونه‌ای از این روش را در مثال فوق مشاهده می‌کنید. در این کلاس، وابستگی مورد نیاز از طریق یک خاصیت عمومی دریافت شده است.


تزریق اینترفیس‌ها

تزریق اینترفیس‌ها نیز یکی دیگر از روش‌های تزریق وابستگی‌ها است؛ اما آنچنان استفاده گسترده‌ای برخلاف دو روش قبل نیافته است.
در این روش نه از سازنده کلاس استفاده می‌شود و نه از یک خاصیت عمومی. ابتدا یک اینترفیس که بیانگر ساختار کلاسی که قرار است تزریق شود ایجاد می‌گردد. سپس این اینترفیس را در کلاس وابستگی مورد نظر پیاده سازی خواهیم کرد. در این اینترفیس نیاز است متد خاصی تعریف شود تا کار تزریق وابستگی را انجام دهد.
یک مثال:
public interface IDependOnCreditCard
{
   void Inject(ICreditCard creditCard);
}

public class Shopper : IDependOnCreditCard
{
  private ICreditCard creditCard;
  public void Inject(ICreditCard creditCard)
  {
     this.creditCard = creditCard;
  }
}  

ICreditCard creditCard = new MasterCard();
Shopper shopper = new Shopper();
((IDependOnCreditCard)shopper).Inject(creditCard);
در اینجا یک اینترفیس به نام IDependOnCreditCard  تعریف گردیده و متد خاصی را به نام Inject تدارک دیده است که نوعی از کردیت کارد را دریافت می‌کند.
در ادامه کلاس خریدار، اینترفیس IDependOnCreditCard را پیاده سازی کرده است. به این ترتیب کلاس Injector تنها کافی است بداند تا این متد خاص را باید جهت تنظیم و تزریق وابستگی‌ها فراخوانی نماید. این روش نسبتا شبیه به روش Setter injection است.
این روش بیشتر می‌تواند جهت فریم ورک‌هایی که قابلیت یافتن کلیه کلاس‌های مشتق شده از IDependOnCreditCard  را داشته و سپس می‌دانند که باید متد Inject آن‌ها را فراخوانی کنند، مناسب است.


نکاتی که باید حین کار با تزریق وابستگی‌ها درنظر داشت

الف) حین استفاده از تزریق وابستگی‌ها، وهله سازی به تاخیر افتاده وابستگی‌ها میسر نیست. برای مثال زمانیکه یک وابستگی قرار است در سازنده کلاسی تزریق شود، وهله سازی آن باید پیش از نیاز واقعی به آن صورت گیرد. البته امکان استفاده از اشیاء Lazy دات نت 4 برای مدیریت این مساله وجود دارد اما در حالت کلی، دیگر همانند قبل و روش‌های متداول، وهله سازی تنها زمانیکه نیاز به آن وابستگی خاص باشد، میسر نیست. به همین جهت باید به تعداد وابستگی‌هایی که قرار است در یک کلاس استفاده شوند نیز جهت کاهش مصرف حافظه دقت داشت.
ب) یکی از مزایای دیگر تزریق وابستگی‌ها، ساده‌تر شدن نوشتن آزمون‌های واحد است. زیرا تهیه Mocks در این حالت کار با اینترفیس‌ها بسیار ساده‌تر است. اما باید دقت داشت، کلاسی که در سازنده آن حداقل 10 اینترفیس را به عنوان وابستگی دریافت می‌کند، احتمالا دچار مشکلاتی در طراحی و همچنین مصرف حافظه می‌باشد.
 
نظرات مطالب
مهارت‌های تزریق وابستگی‌ها در برنامه‌های NET Core. - قسمت دوم - الگوی Service Locator
یک نکته‌ی تکمیلی: طراحی یک کلاس ServiceLocator برای NET Core.

گاهی از اوقات مجبور به کار با کتابخانه‌هایی هستید که برای کار با تزریق وابستگی‌ها طراحی نشده‌اند. برای مثال این کتابخانه‌ها کلاسی را از شما دریافت می‌کنند، این کلاس را خودشان وهله سازی کرده و در نهایت استفاده خواهند کرد. چون وهله سازی این کلاس در اختیار شما نیست و همچنین کتابخانه‌ی فراخوان نیز از تزریق وابستگی‌های در سازنده‌ی کلاس دریافتی، پشتیبانی نمی‌کند، تنها راه حل باقیمانده، استفاده از الگوی Service Locator خواهد بود. برای این منظور می‌توانید از دو کلاس زیر کمک بگیرید:
using System;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;

namespace Utils
{
    public static class ServiceLocatorProvider
    {
        private static readonly Lazy<IServiceProvider> _serviceProviderBuilder =
            new Lazy<IServiceProvider>(GetServiceProvider, LazyThreadSafetyMode.ExecutionAndPublication);

        /// <summary>
        /// A lazy loaded thread-safe singleton
        /// </summary>
        public static IServiceProvider Current { get; } = _serviceProviderBuilder.Value;

        private static IServiceProvider GetServiceProvider()
        {
            var services = new ServiceCollection();
            ConfigureServices(services);
            return services.BuildServiceProvider();
        }

        private static void ConfigureServices(IServiceCollection services)
        {
            // TODO: add other services here ... services.AddSingleton ....
        }
    }

    public static class ServiceLocator
    {
        public static object GetService(Type serviceType)
        {
            return ServiceLocatorProvider.Current.GetService(serviceType);
        }

        public static TService GetService<TService>()
        {
            return ServiceLocatorProvider.Current.GetService<TService>();
        }

        public static object GetRequiredService(Type serviceType)
        {
            return ServiceLocatorProvider.Current.GetRequiredService(serviceType);
        }

        public static TService GetRequiredService<TService>()
        {
            return ServiceLocatorProvider.Current.GetService<TService>();
        }

        public static void RunScopedService<T, S>(Action<S, T> callback)
        {
            using (var serviceScope = ServiceLocatorProvider.Current.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<S>();

                callback(context, serviceScope.ServiceProvider.GetRequiredService<T>());
                if (context is IDisposable disposable)
                {
                    disposable.Dispose();
                }
            }
        }

        public static void RunScopedService<S>(Action<S> callback)
        {
            using (var serviceScope = ServiceLocatorProvider.Current.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<S>();
                callback(context);
                if (context is IDisposable disposable)
                {
                    disposable.Dispose();
                }
            }
        }

        public static T RunScopedService<T, S>(Func<S, T> callback)
        {
            using (var serviceScope = ServiceLocatorProvider.Current.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<S>();
                return callback(context);
            }
        }
    }
}
در اینجا باید متد ConfigureServices کلاس ServiceLocatorProvider را همانند قبل تنظیم و تعاریف سرویس‌های مدنظر خود را اضافه کنید. سپس در هر قسمتی از برنامه می‌توانید از متدهایی مانند ()<ServiceLocator.GetRequiredService<TService استفاده نمائید. در مورد متدهای RunScopedService آن در قسمت سوم بیشتر بحث شده‌است.
مطالب
مبانی TypeScript؛ Decorators
Decorators  یا تزئین کننده‌ها و ReflectDecorators، یکی از پیشنهادهای نگارش بعدی جاوا اسکریپت هستند (ECMAScript 2016) که هم اکنون قابلیت استفاده‌ی از آن‌ها در TypeScript وجود دارد.
جهت افزودن قابلیت‌های meta-programming به زبان‌های جاوا اسکریپت و TypeScript و همچنین تعریف annotations بر روی کلاس‌ها و اعضای کلاس، می‌توان از Decorators استفاده کرد. یک decorator، تعریف ویژه‌ای است که می‌تواند به تعریف یک کلاس، متد، خاصیت و یا پارامتر «متصل» شود و به صورت expression@ تعریف می‌گردد. این expression باید قابلیت فراخوانی به صورت یک متد را داشته باشد که در زمان اجرا فراخوانی خواهد شد. از Decorators در طراحی AngularJS 2 زیاد استفاده شده‌است.


نحوه‌ی فعال سازی Decorators با ES 5

این قابلیت فعلا در مرحله‌ی آزمایش به سر می‌برد؛ بنابراین برای فعال سازی آن نیاز است پارامترهای experimentalDecorators و emitDecoratorMetadata را به کامپایلر خط فرمان tsc و یا به خواص کامپایلر در فایل tsconfig.json اضافه کنید:
Command Line:
tsc --target ES5 --experimentalDecorators --emitDecoratorMetadata

tsconfig.json:
{
    "compilerOptions": {
        "target": "ES5",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true
    }
}


بررسی انواع Decorators

در اینجا یک مثال ساده از decoratorها را مشاهده می‌کنید:
 // A simple decorator
@decoratorExpression
class MyClass { }
و ساده‌ترین فرم پیاده سازی این decoratorExpression به صورت زیر است:
function decoratorExpression(target) {
   // Add a property on target
   target.annotated = true;
}
همانطور که ملاحظه می‌کنید، یک decorator در اصل یک function است که دسترسی به target ایی را که قرار است تزئین شود، میسر می‌کند. این قابلیت بسیار شبیه است به مفهومی به نام attributes و annotations در زبان #C.
در ادامه انواع و اقسام decoratorهای ممکن را با مثال‌هایی بررسی خواهیم کرد.


Class Decorator

یک class Decorator، پیش از تعریف یک کلاس اضافه می‌شود و هدف آن، اعمال تعاریفی به سازنده‌ی کلاس است و از آن می‌توان جهت تحت نظر قرار دادن تعاریف کلاس و یا تغییر یا حتی تعویض کلی تعاریف آن استفاده کرد. یک class Decorator را نمی‌توان در سایر فایل‌های تعاریف، مانند d.ts. قرار داد.
تنها آرگومانی که به یک class Decorator ارسال می‌شود، سازنده‌ی کلاسی است که به آن اعمال شده‌است. اگر متد class Decorator مقداری را برگرداند، سبب تعویض و جایگزینی تعاریف کلاس با مقدار باگشت داده شده، می‌شود.
در ادامه دو مثال را از class Decoratorها مشاهده می‌کنید:

مثال بدون پارامتر
 function ClassDecorator(
target: Function // The class the decorator is declared on
) {
    console.log("ClassDecorator called on: ", target);
}

@ClassDecorator
class ClassDecoratorExample {
}


اگر به تصویر فوق و خروجی نهایی آن دقت کنید، مشاهده می‌کنید که یک decorator برای اجرا، نیازی به وهله سازی از آن کلاس ندارد و اجرای آن دقیقا در زمان تعریف کلاس انجام می‌شود. این اجرا به معنای تزریق کدهای تزئین کننده، به تعاریف سازنده‌ی کلاس تعریف شده، پیش از وهله سازی از آن است.

مثال با پارامتر
 function ClassDecoratorParams(param1: number, param2: string) {
return function(
  target: Function // The class the decorator is declared on
  ) {
   console.log("ClassDecoratorParams(" + param1 + ", '" + param2 + "') called on: ", target);
}
}

@ClassDecoratorParams(1, "a")
@ClassDecoratorParams(2, "b")
class ClassDecoratorParamsExample {
}
با خروجی
 ClassDecoratorParams(2, 'b') called on: function ClassDecoratorParamsExample() {
}
ClassDecoratorParams(1, 'a') called on: function ClassDecoratorParamsExample() {
}
همانطور که در این مثال مشاهده می‌کنید، چندین decorator را نیز می‌توان به تعاریف یک کلاس اعمال کرد که به آن Decorator Composition نیز می‌گویند.


Property Decorator

یک Property Decorator دقیقا پیش از تعریف یک خاصیت اضافه می‌شود و نباید در سایر فایل‌های تعاریف جانبی قرار گیرد. زمانیکه متد expression آن در runtime فراخوانی می‌شود، دو پارامتر را دریافت خواهد کرد:
الف) برای static member ها، متد سازنده‌ی کلاس و برای  instance memberها، prototype کلاس را دریافت می‌کند.
ب) نام عضو.

اگر متد expression تعریف شده، مقداری را برگرداند، به عنوان Property Descriptor آن عضو بکارگرفته می‌شود.

در مثال ذیل که متد PropertyDecorator به خاصیت name اعمال شده‌است، دو پارامتر ارسالی به آن‌را بهتر می‌توان مشاهده کرد:
 function PropertyDecorator(
target: Object, // The prototype of the class
propertyKey: string | symbol // The name of the property
) {
   console.log("PropertyDecorator called on: ", target, propertyKey);
}

class PropertyDecoratorExample {
@PropertyDecorator
name: string;
}
با خروجی
 PropertyDecorator called on: PropertyDecoratorExample {} name


Method Decorator

یک Method Decorator باید درست پیش از تعریف یک متد، قرارگیرد و نباید در سایر کلاس‌ها تعاریف نوع‌های جانبی افزوده شود. هدف از آن می‌تواند بررسی، مشاهده و تغییر رفتار یک متد باشد. به متد expression آن، سه پارامتر ارسال می‌شوند:
الف) برای static member ها، متد سازنده‌ی کلاس و برای  instance memberها، prototype کلاس را دریافت می‌کند.
ب) نام عضو
ج) Property Descriptor عضو

اگر متد تعریف شده، خروجی را برگرداند به عنوان Property Descriptor آن متد استفاده خواهد شد.

در مثال ذیل، متد تزئین کننده‌ای به نام MethodDecorator تعریف شده‌است که سه پارامتر یاد شده را در زمان اجرا دریافت می‌کند:
 function MethodDecorator(
target: Object, // The prototype of the class
propertyKey: string, // The name of the method
descriptor: TypedPropertyDescriptor<any>
) {
console.log("MethodDecorator called on: ", target, propertyKey, descriptor);
}

class MethodDecoratorExample {
   @MethodDecorator
   method() {
   }
}
با خروجی
 MethodDecorator called on: MethodDecoratorExample { method: [Function] } method { value: [Function],
writable: true,
enumerable: true,
configurable: true }

و مثالی جهت محدود ساختن آن به یک سری متد خاص که یک پارامتر عددی را دریافت می‌کنند و یک خروجی عددی نیز دارند:
 function TypeRestrictedMethodDecorator(
target: Object, // The prototype of the class
propertyKey: string, // The name of the method
descriptor: TypedPropertyDescriptor<(num: number) => number>
) {
   console.log("TypeRestrictedMethodDecorator called on: ", target, propertyKey, descriptor);
}

class TypeRestrictedMethodDecoratorExample {
   @TypeRestrictedMethodDecorator
   method(num: number): number {
      return 0;
   }
}
با خروجی
 TypeRestrictedMethodDecorator called on: TypeRestrictedMethodDecoratorExample { method: [Function] } method { value: [Function],
writable: true,
enumerable: true,
configurable: true }

و مثالی از تزئین کننده‌های متدهای استاتیک یک کلاس که در این حالت، پارامتر target از نوع متد سازنده‌ی کلاس خواهد بود و نه prototype آن:
 function StaticMethodDecorator(
target: Function, // the function itself and not the prototype
propertyKey: string | symbol, // The name of the static method
descriptor: TypedPropertyDescriptor<any>
) {
   console.log("StaticMethodDecorator called on: ", target, propertyKey, descriptor);
}

class StaticMethodDecoratorExample {
   @StaticMethodDecorator
   static staticMethod() {
   }
}
با خروجی
 StaticMethodDecorator called on: function StaticMethodDecoratorExample() {
} staticMethod { value: [Function],
writable: true,
enumerable: true,
configurable: true }


Parameter Decorator

یک Parameter Decorator باید درست پیش از تعریف یک آرگومان متد، قرارگیرد و نباید در سایر کلاس‌ها تعاریف نوع‌های جانبی افزوده شود. به متد expression آن سه پارامتر ارسال می‌شوند:
الف) برای static member ها، متد سازنده‌ی کلاس و برای  instance memberها، prototype کلاس را دریافت می‌کند.
ب) نام عضو
ج) شماره ایندکس پارامتر مدنظر در متد

از خروجی متد تزئین کننده در اینجا صرفنظر می‌شود.

در ادامه مثالی را از نحوه‌ی تعریف یک تزئین کننده‌ی پارامترها را با سه آرگومان ویژه‌ی آن، مشاهده می‌کنید:
 function ParameterDecorator(
target: Function, // The prototype of the class
propertyKey: string | symbol, // The name of the method
parameterIndex: number // The index of parameter in the list of the function's parameters
) {
   console.log("ParameterDecorator called on: ", target, propertyKey, parameterIndex);
}

class ParameterDecoratorExample {
   method(@ParameterDecorator param1: string, @ParameterDecorator param2: number) {
   }
}
با خروجی
 ParameterDecorator called on: ParameterDecoratorExample { method: [Function] } method 1
ParameterDecorator called on: ParameterDecoratorExample { method: [Function] } method 0

یک سری مثال تکمیلی