ما هي طرق Dependency Injection
-
في هذا الدرس بنتعلم طرق تنفيذ Dependency Injection ، وهي الطرق الي من خلالها بتم تعريف Dependency Injection في ملف Startup في ConfigureServices وهذه الطرق هي :
- ()AddTransient
- ()AddScoped
- ()AddSingleton
طيب تمام نوخذ فكره سريع لكل نوع :
AddTransient
طريقة AddTransient تنشئ instance من Implementation type في كل مرة تقوم فيها باستخدام dependency.
AddScoped
لا يقوم أسلوب AddScoped دائمًا بإنشاء instance جديد لimplementation class. يعيد استخدام instance من Implementation type الناشئ من نفس HTTP. يشترك في نفس object.
AddSingleton
طريقة AddSingleton تنشئ instance جديدًا ل implementation type الأول فقط. ثم يعيد استخدامه لكل طلب لاحق.
تمام في Net Core. هناك 3 اختلافات لكل من طرق AddTransient و AddScoped و AddSingleton:
<service, implType>()
ينشئ هذا الاختلاف instance لنوع التنفيذ لكل dependency. في القسم الموضح أعلاه استخدمناDependency Injection (DI)
<service>()
يستخدم هذا الاختلاف لتسجيل Single Type object. استخدمنا هذه الطريقة في الدرس السابق Dependency Injection for Single.
<service>(factoryFunc)
يستخدم هذا الاختلاف factory function التي سيتم استدعاؤها لإنشاء implementation objects.
تمام.
استخدام AddTransient Method
تخبر طريقة ()AddTransient ال service provider بإنشاء instance جديد ل implementation type في كل مرة يتم تطبيق التبعية فيها.
لقد تم استخدام هذه الطريقة AddTransient بالفعل في Startup class في الدروس السابقة:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<Connections>(Configuration.GetSection("ConnectionStrings"));
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddTransient<ICourses, CourseRepository>();
services.AddTransient<IStorage, StorageRepository>();
services.AddTransient<CourseSum>();
services.AddSingleton<TotalStudents>();
services.AddControllersWithViews();
}
بالنسبة CourseRepository class ، هناك شيئان يجب ملاحظتهما:
• 1. في constructor CoursesController نجد أنها تعتمد على ICourses وCourseSum
public CoursesController(ICourses repo, CourseSum Csum)
{
_CoursesRepository = repo;
_CourseSum = Csum;
}
• 2. تعتمد CourseSum class على ICourses.
public CourseSum(CourseRepository repo)
{
CourseRepository = repo;
}
في مثل هيك حاله سيقوم service provider بإنشاء 2 instances من CourseRepository بسبب استخدام طريقة ()AddTransient . ثم سيتم إنشاء instance واحد لحل dependency في CoursesController لتفعيل تبعية فئة CourseSum.
بعد ذلك ، في class CourseRepository.cs ، أضف الكود التالي الذي يعيد GUID من نوع string override :
using StudentsAcademy.Interface;
using StudentsAcademy.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StudentsAcademy.Repository
{
public class CourseRepository : ICourses
{
private string guid = System.Guid.NewGuid().ToString();
public override string ToString()
{
return guid;
}
private IStorage storage;
public CourseRepository(IStorage repo)
{
storage = repo;
new List<CoursesModel> {
new CoursesModel { CourseNumber = "0001",CourseName="Math",CourseDescription="Math Desc",Capacity=20, Price = 115 },
new CoursesModel { CourseNumber = "0002",CourseName="Sciences",CourseDescription="Sciences Desc",Capacity=30, Price = 120 },
new CoursesModel { CourseNumber = "0003",CourseName="Physics",CourseDescription="Physics Desc",Capacity=25, Price = 140 }
}.ForEach(p => AddCourse(p));
}
public IEnumerable<CoursesModel> _CoursesModel => storage.Courses;
public CoursesModel this[string name] => storage[name];
public void AddCourse(CoursesModel Courses) => storage[Courses.CourseName] = Courses;
public void DeleteCourse(CoursesModel Courses) => storage.Remove(Courses.CourseNumber);
}
}
الهدف من GUID في تحديد نسخة instance التابع ل class CourseRepository.cs.
انتقل بعد ذلك إلى Course Controller وفي Index action قم بإضافة متغيرين ViewBag. سيحتوي متغير ViewBag الأول على GUID المستلم من Repository class object بينما يحتوي متغير ViewBag الثاني على GUID المستلم من ProductSum class object.
using Microsoft.AspNetCore.Mvc;
using StudentsAcademy.Interface;
using StudentsAcademy.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StudentsAcademy.Controllers
{
public class CoursesController : Controller
{
private ICourses _CoursesRepository;
private CourseSum _CourseSum;
public CoursesController(ICourses repo, CourseSum Csum)
{
_CoursesRepository = repo;
_CourseSum = Csum;
}
public IActionResult Index()
{
ViewBag.HomeControllerGuid = _CoursesRepository.ToString();
ViewBag.TotalGuid = _CourseSum.CourseRepository.ToString();
return View(_CoursesRepository._CoursesModel);
}
}
}
الآن قم بتشغيل التطبيق الخاص بك وسترى قيم GUID من متغيرين ViewBag مختلفة. يحدد هذا أن service provider قام بتكوين 2 instances من فئة Repository class.
الصورة ادناه توضح القيم المختلفة ل GUIDs

استخدام طريقة AddScoped
لا يقوم أسلوب AddScoped دائمًا بإنشاء instance جديد implementation class. يعيد استخدام instance of the Implementation للطلب الناشئ عن طلبات HTTP نفسها ، أي يشارك نفس الكائن object بين تلك الطلبات.
تعمل طريقة ()AddScoped على اخبار service provider بمشاركة single object لجميع المكونات التي تعالج الطلب.
انتقل إلى طريقة ()ConfigureServices ل Startup class وقم بتغيير طريقة AddTransient إلى AddScoped لكائن ICourses.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<Connections>(Configuration.GetSection("ConnectionStrings"));
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
//services.AddTransient<ICourses, CourseRepository>();
services.AddScoped<ICourses, CourseRepository>();
services.AddTransient<IStorage, StorageRepository>();
services.AddTransient<CourseSum>();
services.AddSingleton<TotalStudents>();
services.AddControllersWithViews();
}
أعد تشغيل التطبيق الخاص بك وسترى أن كلا من GUIDS لهما نفس القيمة. هذا يعني أن Service Provider قد أنشأ object واحدًا فقط من CourseRepository class والتي تتم مشاركتها بين "CoursesController" وفئة "CourseSum".

أعد تحميل الصفحة وسترى إنشاء Guid جديد ، لأنه يتم بدء طلب HTTP جديد وبالتالي يتم إنشاء object جديد إلى CourseRepository class.
استخدام AddSingleton
تعمل طريقة ()AddSingleton على اخبار Service Provider أن يضمن استخدام single object فقط لجميع الطلبات لنوع معين.
تمام وحتى نعمل ذلك، انتقل إلى ()ConfigureServices واستخدم طريقة ()AddSingleton لتنفيذ تبعية نوع ICourses:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<Connections>(Configuration.GetSection("ConnectionStrings"));
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
//services.AddTransient<ICourses, CourseRepository>();
//services.AddScoped<ICourses, CourseRepository>();
services.AddSingleton<ICourses, CourseRepository>();
services.AddTransient<IStorage, StorageRepository>();
services.AddTransient<CourseSum>();
services.AddSingleton<TotalStudents>();
services.AddControllersWithViews();
}
أعد تشغيل التطبيق الخاص بك وسترى أن قيم GUID متماثلة ، قم بتحديث الصفحة ولكن ستلاحظ أن قيم GUID لا تتغير.
انظر إلى الصورة أدناه التي توضح ذلك:

تعمل طريقة AddSingleton على انشاء instance جديد من فئة ICourses للمرة الأولى فقط وتتم مشاركتها لكل طلب.
حقن الإجراء مع FromServices
يمكن أن يكون تنفيذ التبعية من خلال constructor of the Controller مكلفًا لأن التبعية يتم استدعاءها وتنفيذها في كل مرة يتم فيها إنشاء Controller، وأيضًا لأنه لا تحتاج جميع طرق الإجراء action methods إلى كائن نوع التنفيذ implementation type object.
إذن هنا يأتي جزء التبعية Action Injection الذي يتم تعريفه من خلال parameters of Action methods. لذا فإن action method المعينة فقط هي التي تؤدي إلى بدء implementation type. هذا يعني أن التبعية لا يتم استدعاءها وتنفيذها في كل مرة يتم فيها استدعاء Controller.
في Action Injection ، يتم تطبيق السمة [FromServices] على parameters.
في CoursesController ، قم بتغيير Index Action method لإضافة parameter من نوع CourseSum وإضافة [FromServices] attribute إليها. قم أيضًا بإزالة CourseSum attribute من constructor في controller.
الكود التالي يوضح هذه التغيرات
using Microsoft.AspNetCore.Mvc;
using StudentsAcademy.Interface;
using StudentsAcademy.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StudentsAcademy.Controllers
{
public class CoursesController : Controller
{
private ICourses _CoursesRepository;
//private CourseSum _CourseSum;
public CoursesController(ICourses repo)
{
_CoursesRepository = repo;
// _CourseSum = Csum;
}
public IActionResult Index([FromServices] CourseSum Csum)
{
ViewBag.HomeControllerGuid = _CoursesRepository.ToString();
ViewBag.TotalGuid = Csum.ICourses.ToString();
// ViewBag.Total = _CourseSum.Total;
return View(_CoursesRepository._CoursesModel);
}
}
}
الآن سيتم استدعاء CourseSum فقط عندما يتم استدعاء Index Action Method وليس عند استدعاء Controller.
استخدام Factory Function (factoryFunc)
يُستخدم الاختلاف Factory Function (factoryFunc) هذا لتسجيل factory function التي سيتم استدعاؤها لإنشاء implementation objects. لذا يمكنك استخدامه لإنشاء منطقك الخاص لإخبار التطبيق بموعد استخدام التبعيات.
تمام وحتي نفهم الموضوع بشكل احسن نعمل مثال.
نبدا بانشاء class جديد باسم ClassRoomsRepository.cs داخل مجلد Repository وأضف الكود التالي:
using StudentsAcademy.Interface;
using StudentsAcademy.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StudentsAcademy.Repository
{
public class ClassRoomsRepository : ICourses
{
private Dictionary<string, CoursesModel> _coursesModel;
public ClassRoomsRepository()
{
_coursesModel = new Dictionary<string, CoursesModel>();
new List<CoursesModel> {
new CoursesModel { CourseNumber = "0001",CourseName="Math",CourseDescription="Math Desc",Capacity=20, Price = 115 },
new CoursesModel { CourseNumber = "0002",CourseName="Sciences",CourseDescription="Sciences Desc",Capacity=30, Price = 120 },
new CoursesModel { CourseNumber = "0003",CourseName="Physics",CourseDescription="Physics Desc",Capacity=25, Price = 140 }
}.ForEach(p => AddCourse(p));
}
public IEnumerable<CoursesModel> _CoursesModel => _coursesModel.Values;
public CoursesModel this[string name] => _coursesModel[name];
public void AddCourse(CoursesModel Courses) => _coursesModel[Courses.CourseName] = Courses;
public void DeleteCourse(CoursesModel Courses) => _coursesModel.Remove(Courses.CourseNumber);
}
}
انتقل الآن إلى Startup class وأضف Factory function في ConfigureServices لاستدعاء وتنفيذ تبعية ICourses.
الكود التالي يوضح ذلك
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using StudentsAcademy.Services;
using StudentsAcademy.Middleware;
using StudentsAcademy.Share;
using Microsoft.EntityFrameworkCore;
using StudentsAcademy.Interface;
using StudentsAcademy.Repository;
namespace StudentsAcademy
{
public class Startup
{
private IWebHostEnvironment env;
public Startup(IConfiguration configuration, IWebHostEnvironment hostEnv)
{
Configuration = configuration;
env = hostEnv;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ICourses>(provider =>
{
if (env.IsDevelopment())
{
var x = provider.GetService<CourseRepository>();
return x;
}
else
{
return new ClassRoomsRepository();
}
});
services.Configure<Connections>(Configuration.GetSection("ConnectionStrings"));
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
//services.AddTransient<ICourses, CourseRepository>();
//services.AddScoped<ICourses, CourseRepository>();
//services.AddSingleton<ICourses, CourseRepository>();
services.AddTransient<IStorage, StorageRepository>();
services.AddTransient<CourseSum>();
services.AddSingleton<TotalStudents>();
services.AddControllersWithViews();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseStatusCodePages();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
if ((Configuration.GetSection("Middleware")?.GetValue<bool>("EnableResponseEditingMiddleware")).Value)
{
app.UseMiddleware<ResponseEditingMiddleware>();
}
if ((Configuration.GetSection("Middleware")?.GetValue<bool>("EnableRequestEditingMiddleware")).Value)
{
app.UseMiddleware<RequestEditingMiddleware>();
}
if ((Configuration.GetSection("Middleware")?.GetValue<bool>("EnableShortCircuitMiddleware")).Value)
{
app.UseMiddleware<ShortCircuitMiddleware>();
}
if ((Configuration.GetSection("Middleware")?.GetValue<bool>("EnableContentMiddleware")).Value)
{
app.UseMiddleware<ContentMiddleware>();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
يتحقق الكود مما إذا كانت المشروع قيد التطوير development، ثم يقوم بإنشاء instance جديد لفئة CourseRepository وإلا يتم إنشاء instance جديد ل class ClassRoomsRepository.
لاحظ أنه لكي تتمكن طريقة ConfigureServices من الوصول إلى environment ، يجب عليك إضافة constructor ل Startup class. يجب أن يتم إضافة parameter لهذا constructor من النوع IWebHostEnvironment.
private IWebHostEnvironment env;
public Startup(IWebHostEnvironment hostEnv) => env = hostEnv;
وبهذه الطريقة تم تطبيق factoryFunc
اترك تعليقك