کتابخانه bootstrap-photo-gallery
کتابخانه Vertical-Responsive-Menu
کتابخانه GridList
آموزش Responsive کردن یک قالب
اصول طراحی یک سیستم افزونه پذیر به کمک StructureMap
تهیه قرارداد
یک پروژهی Class library به نام PluginsBase را به Solution جاری اضافه کنید. به آن اینترفیس قرار داد پلاگینهای برنامه خود را اضافه نمائید. برای مثال:
namespace PluginsBase { public interface IPlugin { string Name { get; } void Run(); } }
تهیه سه پلاگین جدید
به Solution جاری سه پروژهی مجزای Class library با نامهای plugin1 تا 3 را اضافه کنید. در ادامه به هر پلاگین، ارجاعی را به اسمبلی PluginsBase، برای دریافت قرارداد پیاده سازی منطق پلاگین، اضافه نمائید. هدف این است که اینترفیس IPlugin، در این اسمبلیها قابل دسترسی شود.
هر پلاگین هم دارای برای مثال کدهایی مانند کد ذیل خواهد بود که در آن صرفا نام آنها به 2 و 3 تنظیم میشود.
using PluginsBase; namespace Plugin1 { public class Plugin1Main : IPlugin { public string Name { get { return "Test 1"; } } public void Run() { // todo: ... } } }
کپی خودکار پلاگینها به پوشهی مخصوص آنها
به پروژهی WinFormsWithPluginSupport مراجعه کنید. در پوشهی bin\debug آن یک پوشهی جدید به نام Plugins ایجاد نمائید. بدیهی است هربار که پلاگینهای برنامه تغییر کنند نیاز است اسمبلیهای نهایی آنها را به این پوشه کپی نمائیم. اما راه بهتری نیز وجود دارد. به خواص هر کدام از پروژههای پلاگین مراجعه کرده و برگهی Build events را باز کنید.
در اینجا قسمت post-build event را به نحو ذیل تغییر دهید:
Copy "$(ProjectDir)$(OutDir)$(TargetName).*" "$(SolutionDir)WinFormsWithPluginSupport\bin\debug\Plugins"
به این ترتیب هربار که پلاگین جاری کامپایل شود، پس از آن به صورت خودکار به پوشهی plugins تعیین شده، کپی میشود و دیگر نیازی به کپی دستی نخواهد بود.
تنظیم فوق، تنها اسمبلی اصلی پروژه را به پوشهی bin\debug\plugins کپی میکند. اگر میخواهید تمام فایلها کپی شوند، از تنظیم ذیل استفاده کنید:
Copy "$(ProjectDir)$(OutDir)*.*" "$(SolutionDir)WinFormsWithPluginSupport\bin\debug\Plugins"
اضافه کردن وابستگیهای اصلی پروژهی WinForms
در ادامه بستهی نیوگت StructureMap را به پروژهی WinForms از طریق دستور ذیل اضافه کنید:
PM> install-package structuremap
تعریف محل ثبت پلاگینها
روشهای متفاوتی برای کار با StructureMap وجود دارد. یکی از آنها تعریف کلاسی است مشتق شده از کلاس Registry آن به نحو ذیل:
using System.IO; using System.Windows.Forms; using PluginsBase; using StructureMap.Configuration.DSL; using StructureMap.Graph; namespace WinFormsWithPluginSupport.Core { public class PluginsRegistry : Registry { public PluginsRegistry() { this.Scan(scanner => { scanner.AssembliesFromPath( path: Path.Combine(Application.StartupPath, "plugins"), // یک اسمبلی نباید دوبار بارگذاری شود assemblyFilter: assembly => { return !assembly.FullName.Equals(typeof(IPlugin).Assembly.FullName); }); scanner.AddAllTypesOf<IPlugin>().NameBy(item => item.FullName); }); } } }
یک نکتهی مهم
در قسمت assemblyFilter تعیین کردهایم که اسمبلی تکراری PluginBase بارگذاری نشود. چون این اسمبلی هم اکنون به برنامهی WinForms ارجاع دارد. رعایت این نکته جهت رفع تداخلات آتی بسیار مهم است. همچنین این فایل در پوشهی Plugins نیز نباید حضور داشته باشد وگرنه شاهد بارگذاری افزونهها نخواهید بود.
سپس نیاز به وهله سازی Container آن و معرفی این کلاس PluginsRegistry میباشد:
using System; using System.Threading; using StructureMap; namespace WinFormsWithPluginSupport { public static class IocConfig { private static readonly Lazy<Container> _containerBuilder = new Lazy<Container>(defaultContainer, LazyThreadSafetyMode.ExecutionAndPublication); public static IContainer Container { get { return _containerBuilder.Value; } } private static Container defaultContainer() { return new Container(x => { x.AddRegistry<PluginsRegistry>(); }); } } }
تنظیمات ابتدایی WinForms برای دسترسی به امکانات StructureMap
به فرم اصلی برنامه مراجعه کرده و به سازندهی آن IContainer را اضافه کنید. از این اینترفیس جهت دسترسی به پلاگینهای برنامه استفاده خواهیم کرد.
using System.Windows.Forms; using StructureMap; namespace WinFormsWithPluginSupport { public partial class FrmMain : Form { private readonly IContainer _container; public FrmMain(IContainer container) { _container = container; InitializeComponent(); } } }
using System; using System.Windows.Forms; namespace WinFormsWithPluginSupport { static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(IocConfig.Container.GetInstance<FrmMain>()); } } }
بارگذاری و اجرای افزونهها
دو دکمهی Run و ReLoad را به فرم اصلی برنامه با کدهای ذیل اضافه کنید:
using System.Linq; using System.Windows.Forms; using PluginsBase; using StructureMap; using WinFormsWithPluginSupport.Core; namespace WinFormsWithPluginSupport { public partial class FrmMain : Form { private readonly IContainer _container; public FrmMain(IContainer container) { _container = container; InitializeComponent(); } private void BtnRun_Click(object sender, System.EventArgs e) { var plugins = _container.GetAllInstances<IPlugin>().ToList(); foreach (var plugin in plugins) { plugin.Run(); } } private void BtnReload_Click(object sender, System.EventArgs e) { _container.EjectAllInstancesOf<IPlugin>(); _container.Configure(x => x.AddRegistry<PluginsRegistry>() ); } } }
همچنین در متد ReLoad نحوهی بارگذاری مجدد این پلاگینها را در صورت نیاز مشاهده میکنید.
اگر برنامه را اجرا کردید و پلاگینی بارگذاری نشد، به دنبال اسمبلیهای تکراری بگردید. برای مثال PluginsBase نباید هم در پوشهی اصلی اجرایی برنامه حضور داشته باشد و هم در پوشهی پلاگینها.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید
WinFormsWithPluginSupport.zip
public class ProductViewModel { public int ProductId { get; set; } public string Name { get; set; } public string Description { get; set; } public string ImageUrl { get; set; } public decimal UnitPrice { get; set; } }
public record ProductDTO(int Id, string Name, string Description);
public record ProductDTO { public int Id { get; init; } public string Name { get; init; } } var dto = new ProductDTO { Id = 1, Name = "some name" };
public class Product : DataObject<Product> { public Product(int id) { Id = id; InitializeFromDatabase(); } private void InitializeFromDatabase() { DataHelpers.LoadFromDatabase(this); } public int Id { get; private set; } // other properties and methods }
public class Product { public Product(int id) { Id = id; } private Product() { // required for EF } public int Id { get; private set; } // other properties and methods }
- برای اجرای وظایف خود به فریم ورک ثالثی وابسته نیست.
- به کلاس پایهای ( Base class) نیاز ندارد.
- وابستگی به متد استاتیکی ندارد.
- می تواند در هر جایی از پروژه، نمونه سازی شود.
- اصل Persistence Ignorant را بیشتر رعایت کرده، نه بطور کامل؛ چون یک سازنده دارد که به کتابخانهی ثالثی نیازمند است (سازندهی بدون پارامتر که مورد نیاز EF میباشد).
رفع مشکلات:
در قسمت قبل با ذکر یک مثال و بیان مشکلات آن از دیدگاه اصول Defensive Code قصد داشتیم که مساله را روشنتر کنیم. مواردی که در
قسمت قبل ذکر شدند، به سادهترین شکل ممکن بیان
شدند و شما به راحتی با بررسی این موارد و تفکر در کدهای خود، میتوانید
این موارد را در کدی که خودتان مینویسید رعایت کنید. حل پیچیدگیهای موجود در کد قبل، با در نظر گرفتن اصول مذکور و اصولهای طراحی مختلف میتواند به روشهای مختلفی انجام گیرد. برای مثال میتوان برای هر یک از کارهایی که کد مثال قبل انجام میدهد، یک کلاس مجزا ایجاد نمود و اصول مذکور را در آن رعایت کرد. درنهایت این
کلاسها را در قالب یک Class Library دسته بندی کرد.
Predictability:
در ادامه قصد داریم در مورد قابلیت پیش بینی و فواید رعایت اصول آن در کد، بحث کنیم. به صورت کلی یک متد، یکسری پارامترها را به عنوان ورودی دریافت میکند؛ یک عملیات خاص را بر روی پارامترهای ورودی انجام میدهد و در نهایت، در صورت لزوم یک مقدار را بر میگرداند. پارامترهای ورودی میتوانند از سمت کاربر و از یک سورس خارجی وارد شوند و یا میتوانند از یک متد دیگر به این متد ارسال شوند. اولین مرحله برای ایجاد قابلیت Predictability این است که یکسری گاردها را به کد خود اضافه کنید تا به وسیلهی آنها پارامترهای ورودی را بررسی کنید و فقط اجازهی ورود، ورودیهای معتبر را بدهیم. برای مثال کد ذیل را درنظر بگیرد.
پارامترهای ورودی این کد با مستطیل قرمز رنگ مشخص شده اند. حال ما سعی داریم با قرار دادن یکسری گارد برای پارامترهای ورودی، از ورود مقادیر ناخواسته جلوگیری کنیم.
بر اساس اصول (GIGO (Garbage in-Garbage out در برنامه نویسی متدی که ورودیهای نامعتبر به آن پاس داده شوند، خروجیهای نامعتبری هم پس خواهد داد. بنابراین برای جلوگیری از این مسئله باید از ورود ورودیهای نامعتبر به متدها جلوگیری کرد. گاردها از ورود مقادیر نامعتبر به متدها جلوگیری خواهند کرد و در نتیجه خروجی مناسب و قابل پیش بینی از متد گرفته خواهد شد. برای جلوگیری از ورود دادههای نامعتبر، باید با استفاده از این دستورات که در ابتدای متد قرار داده میشوند، از ورود دادههای نامعتبر جلوگیری کرد. به این دستورات Guard Clauses گفته میشود. غیر از این مساله، کاهش دادن تعداد پارامترها و قراردادن قانونی برای تعیین اولویت پارامترهای متدها (برای مثال با توجه به اهمیت) میتواند به افزایش Predictability متدها بسیار کمک کند. با پیروی کردن از این اصول ساده شما میتوانید میزان خطاهایی که از پارامترهای ورودی منشاء میگیرند را کاهش دهید.
اجازه دهید با یک مثال؛ مسالهی بالا را تشریح کنیم. برای مثال یک برنامهی کوچک نوشتهایم؛ برای شمردن گام ها. در این برنامه تعداد قدمهای هدف و تعداد قدمهای برداشته شدهی امروز تعیین میشوند و سپس هدف، بر حسب درصد بیان خواهد شد.
با استفاده از این Application میخواهیم مفاهیمی را که بیان کردیم، به صورت کاربردی
نمایش دهیم. کدی این محاسبه را برای ما انجام میدهد، در ذیل نمایش داده شده و
در قالب یک متد تعیین شده است.
private decimal CalculatePercentOfGoalSteps (string goalSteps, string actualSteps) { return (Convert.ToDecimal(actualSteps) / Convert.ToDecimal(goalSteps)) * 100; }
این متد دارای دو پارامتر از نوع string می باشد و نتیجه هم در قالب یک مقدار decimal بازگشت داده خواهد شد. این جمله کلیتی از متد را بیان خواهد کرد. نحوهی فراخوانی این متد هم در کد ذیل آورده شد است.
private void Calculate_Click(object sender, EventArgs e) { var result =CalculatePercentOfGoalSteps (stepGoalForTodayTxt.Text, numberOfStepsForToday.Text); lblResult.Text = "شما به" + result + "% از هدف تان رسیده اید"; }
حال Application را اجرا کرده و نتیجه کار را مشاهده میکنیم. برای مثال شکل ذیل:
در این مثال با توجه به مقادیر وارد شده، به 40 درصد از هدف مورد نظر رسیدهایم. اما هدف از بیان این مثال، این نیست که مشخص گردد که ما چقدر به هدفمان نزدیک شدهایم. بلکه هدف مسایل دیگری است. در نظر بگیرید که بجای 5000، صفر را وارد کنید. در این حالت با یک Exception روبرو میشویم:
همانطور که در شکل بالا مشاهده میکنید، خطای Divide by zero رخ داده است. برای رفع این خطا و جلوگیری از رخداد این خطا، میتوان کد
ذیل را پیشنهاد داد.
private decimal CalculatePercentOfGoalSteps(string goalSteps, string actualSteps) { decimal result =0; var goalStepsCount = Convert.ToDecimal(goalSteps); if (goalStepsCount>0) { result = (Convert.ToDecimal(actualSteps) / goalStepsCount) * 100; } return result; }
با تغییر کد به این صورت مشکل Exception بالا حل میشود، اما باز هم مشکل دیگری وجود دارد. فرض کنید همانند شکل ذیل textbox اول را خالی کنیم و بعد از آن سعی در محاسبه داشته باشیم،
باز هم یک Exception دیگر
علت بوجود آمدن این مشکل این است که ما در کد امکان خالی بودن پارامترهای متد را در نظر نگرفتهایم و پیش بینیهای لازم صورت نگرفته است بنابراین دستور Convert .با مشکل مواجه شد. برای حل این مشکل میتوان به جای Convert از decimal.Tryparse استفاده کرد.
private decimal CalculatePercentOfGoalSteps(string goalSteps, string actualSteps) { decimal result = 0; decimal goalStepsCount = 0; decimal.TryParse(goalSteps, out goalStepsCount); decimal actualStepsCount = 0; decimal.TryParse(actualSteps, out actualStepsCount); if (goalStepsCount>0) { result = (actualStepsCount / goalStepsCount) * 100; } return result; }
با انجام دادن این کارها از بروز خطاهایی که ناشی از ورودیهای نامعتبر در کد هستند، جلوگیری کردیم. اما آیا این پایان کار است؟ خیر با استفاده کردن از این روش ما توانستهایم که از بروز خطا در برنامه جلوگیری کنیم. اما مشکلی که این روش دارد این است که کاربر متوجه نمیشود که چه زمانی برنامه دچار مشکل شده است. کاری که ما انجام میدهیم این است که برای تمامی حالات خطا، مقدار صفر را بر میگردانیم.
برای اینکه بتوانیم این کد به راحتی debug کنیم باید از مفهوم Fail Fast استفاده کنیم . این مفهوم قابلیتی را در کد ایجاد میکند که در صورتی که کد، دادههای نامعتبری را دریافت کرد، سریعا اجرای آن متوقف میشود و همزمان نیز اطلاعاتی در مورد خطا در اختیار کاربر قرار میدهد. برای این منظور با قرار دادن یکسری Guard Clauses، کد بالا را همانند شکل ذیل تغییر خواهیم داد.
private decimal CalculatePercentOfGoalSteps(string goalSteps, string actualSteps) { decimal goalStepsCount = 0; decimal actualStepsCount = 0; /// اطمینان حاصل میکنند که پارامترهای ورودی دارای مقدار هستند if (string.IsNullOrWhiteSpace(goalSteps)) throw new ArgumentException("مقدار هدف باید وارد شود", "goalSteps"); if (string.IsNullOrWhiteSpace(actualSteps)) throw new ArgumentException("مقدار واقعی باید وارد شود", "goalSteps"); ///اطمینان حاصل میکنند که مقادیر وارد شده حتما عددی هستند if (!decimal.TryParse(goalSteps, out goalStepsCount)) throw new ArgumentException("مقدار هدف باید عددی باشد", goalSteps); if(!decimal.TryParse(actualSteps, out actualStepsCount)) throw new ArgumentException("مقدار واقعی باید عددی باشد", actualSteps); ///اطمینان حاصل میکند که مقدار متغیر نباید صفر باشد if (goalStepsCount <= 0) throw new ArgumentException("مقدار هدف نباید صفر و یا کمتر از صفر باشد", "goalStepsCount"); return (actualStepsCount / goalStepsCount) * 100; }
ایجاد کردن این تغییرات در متد باعث افزایش خوانایی
کد میشود و هدف متد را روشنتر بیان خواهد کرد. اضافه کردن این کدها به دلیل اینکه
تمامی شرایط تست را تعیین خواهیم کرد Test-ability کد را بالا میبرد. اضافه کردن کدهای بالا به برنامه کمک خواهد کرد که
شرایط خطا در برنامه به درستی هندل شود و به طبع آن تصمیمات مناسبی گرفته شود و در
نهایت Predictability متدها و کل برنامه را افزایش میهد.
آشنایی با نحوه ایجاد یک IoC Container
IoC Container چیست؟
IoC Container، فریم ورکی است برای انجام تزریق وابستگیها. در این فریم ورک امکان تنظیم اولیه وابستگیهای سیستم وجود دارد. برای مثال زمانیکه برنامه از یک IoC Container، نوع اینترفیس خاصی را درخواست میکند، این فریم ورک با توجه به تنظیمات اولیهاش، کلاسی مشخص را بازگشت خواهد داد.
IoC Containerهای قدیمیتر، برای انجام تنظیمات اولیه خود از فایلهای کانفیگ استفاده میکردند. نمونههای جدیدتر آنها از روشهای Fluent interfaces برای مشخص سازی تنظیمات خود بهره میبرند.
زمانیکه از یک IOC Container در کدهای خود استفاده میکنید، مراحلی چند رخ خواهند داد:
الف) کد فراخوان، از IOC Container، یک شیء مشخص را درخواست میکند. عموما اینکار با درخواست یک اینترفیس صورت میگیرد؛ هرچند محدودیتی نیز نداشته و امکان درخواست یک کلاس از نوعی مشخص نیز وجود دارد.
ب) در ادامه IOC Container به لیست اشیاء قابل ارائه توسط خود نگاه کرده و در صورت وجود، وهله سازی شیء درخواست شده را انجام و نهایتا شیء مطلوب را بازگشت خواهد داد.
در این بین زنجیرهی وابستگیهای مورد نیاز نیز وهله سازی خواهند شد. برای مثال اگر وابستگی اول به وابستگی دوم برای وهله سازی نیاز دارد، کار وهله سازی وابستگیهای وابستگی دوم نیز به صورت خودکار انجام خواهند شد. (این موردی است که بسیاری از تازه واردان به این بحث تا یکبار آنرا امتحان نکنند باور نخواهند کرد!)
ج) سپس کد فراخوان وهله دریافتی را مورد پردازش قرار داده و سپس شروع به استفاده از متدها و خواص آن خواهد نمود.
در تصویر فوق محل قرارگیری یک IOC Container را مشاهده میکنید. یک IOC Container در مورد تمام وابستگیهای مورد نیاز، اطلاعات لازم را دارد. همچنین این فریم ورک در مورد کلاسی که قرار است از وابستگیهای سیستم استفاده نماید نیز مطلع است؛ به این ترتیب میتواند به صورت خودکار در زمان وهله سازی آن، نوعهای وابستگیهای مورد نیاز آنرا در اختیارش قرار دهد.
برای مثال در اینجا MyClass، وابستگی مشخص شده در سازنده خود را به نام IDependency از IOC Container درخواست میکند. سپس این IOC Container بر اساس تنظیمات اولیه خود، یکی از وابستگیهای A یا B را بازگشت خواهد داد.
آغاز به کار ساخت یک IOC Container نمونه
در ابتدا کدهای آغازین مثال بحث جاری را در نظر بگیرید:
using System; namespace DI01 { public interface ICreditCard { string Charge(); } public class Visa : ICreditCard { public string Charge() { return "Charging with the Visa!"; } } public class MasterCard : ICreditCard { public string Charge() { return "Swiping the MasterCard!"; } } public class Shopper { private readonly ICreditCard creditCard; public Shopper(ICreditCard creditCard) { this.creditCard = creditCard; } public void Charge() { var chargeMessage = creditCard.Charge(); Console.WriteLine(chargeMessage); } } }
var shopper = new Shopper(new Visa()); shopper.Charge();
using System; using System.Collections.Generic; using System.Linq; namespace DI01 { public class Resolver { //کار ذخیره سازی و نگاشت از یک نوع به نوعی دیگر در اینجا توسط این دیکشنری انجام خواهد شد private Dictionary<Type, Type> dependencyMap = new Dictionary<Type, Type>(); /// <summary> /// یک نوع خاص از آن درخواست شده و سپس بر اساس تنظیمات برنامه، کار وهله سازی /// نمونه معادل آن صورت خواهد گرفت /// </summary> public T Resolve<T>() { return (T)Resolve(typeof(T)); } private object Resolve(Type typeToResolve) { Type resolvedType; // ابتدا بررسی میشود که آیا در تنظیمات برنامه نگاشت متناظری برای نوع درخواستی وجود دارد؟ if (!dependencyMap.TryGetValue(typeToResolve, out resolvedType)) { //اگر خیر، کار متوقف خواهد شد throw new Exception(string.Format("Could not resolve type {0}", typeToResolve.FullName)); } var firstConstructor = resolvedType.GetConstructors().First(); var constructorParameters = firstConstructor.GetParameters(); // در ادامه اگر این نوع، دارای سازندهی بدون پارامتری است // بلافاصله وهله سازی خواهد شد if (!constructorParameters.Any()) return Activator.CreateInstance(resolvedType); var parameters = new List<object>(); foreach (var parameterToResolve in constructorParameters) { // در اینجا یک فراخوانی بازگشتی صورت گرفته است برای وهله سازی // خودکار پارامترهای مختلف سازنده یک کلاس parameters.Add(Resolve(parameterToResolve.ParameterType)); } return firstConstructor.Invoke(parameters.ToArray()); } public void Register<TFrom, TTo>() { dependencyMap.Add(typeof(TFrom), typeof(TTo)); } } }
var resolver = new Resolver(); //تنظیمات اولیه resolver.Register<Shopper, Shopper>(); resolver.Register<ICreditCard, Visa>(); //تزریق وابستگیها و وهله سازی var shopper = resolver.Resolve<Shopper>(); shopper.Charge();
ابتدا کار تعاریف نگاشتهای اولیه انجام میشود. در این صورت زمانیکه متد Resolve فراخوانی میگردد، نوع درخواستی آن به همراه سازنده دارای آرگومانی از نوع ICreditCard وهله سازی شده و بازگشت داده خواهد شد. سپس با در دست داشتن یک وهله آماده، متد Charge آنرا فراخوانی خواهیم کرد.
بررسی نحوه استفاده از Microsoft Unity به عنوان یک IoC Container
Unity چیست؟
Unity یک فریم ورک IoC Container تهیه شده توسط مایکروسافت میباشد که آنرا به عنوان جزئی از Enterprise Library خود قرار داده است. بنابراین برای دریافت آن یا میتوان کل مجموعه Enterprise Library را دریافت کرد و یا به صورت مجزا به عنوان یک بسته نیوگت نیز قابل تهیه است.
برای این منظور در خط فرمان پاورشل نیوگت در VS.NET دستور ذیل را اجرا کنید:
PM> Install-Package Unity
پیاده سازی مثال خریدار توسط Unity
همان مثال قسمت قبل را درنظر بگیرید. قصد داریم اینبار بجای IoC Container دست سازی که تهیه شد، پیاده سازی آنرا به کمک MS Unity انجام دهیم.
using Microsoft.Practices.Unity; namespace DI02 { class Program { static void Main(string[] args) { var container = new UnityContainer(); container.RegisterType<ICreditCard, MasterCard>(); var shopper = container.Resolve<Shopper>(); shopper.Charge(); } } }
مطابق کدهای فوق، ابتدا تنظیمات IoC Container انجام شده است. به آن اعلام کردهایم که در صورت نیاز به ICreditCard، نوع MasterCard را یافته و وهله سازی کن. با این تفاوت که Unity هوشمندتر بوده و سطر مربوط به ثبت کلاس Shoper ایی را که در قسمت قبل انجام دادیم، در اینجا حذف شده است.
سپس به این IoC Container اعلام کردهایم که نیاز به یک وهله از کلاس خریدار داریم. در اینجا Unity کار وهله سازیهای خودکار وابستگیها و تزریق آنها را در سازنده کلاس خریدار انجام داده و نهایتا یک وهله قابل استفاده را در اختیار ادامه برنامه قرار خواهد داد.
یک نکته:
به صورت پیش فرض کار تزریق وابستگیها در سازنده کلاسها به صورت خودکار انجام میشود. اگر نیاز به Setter injection و مقدار دهی خواص کلاس وجود داشت میتوان به نحو ذیل عمل کرد:
container.RegisterType<ICreditCard, MasterCard>(new InjectionProperty("propertyName", 5));
مدیریت طول عمر اشیاء در Unity
توسط یک IoC Container میتوان یک وهله معمولی از شیءایی را درخواست کرد و یا حتی طول عمر این وهله را به صورت Singleton معرفی نمود (یک وهله در طول عمر کل برنامه). در Unity اگر تنظیم خاصی اعمال نشود، هربار که متد Resolve فراخوانی میگردد، یک وهله جدید را در اختیار ما قرار خواهد داد. اما اگر پارامتر متد RegisterType را با وهلهای از ContainerControlledLifetimeManager مقدار دهی کنیم:
container.RegisterType<ICreditCard, MasterCard>(new ContainerControlledLifetimeManager());
حالت پیش فرض مورد استفاده، بدون ذکر پارامتر متد RegisterType، مقدار TransientLifetimeManager میباشد.