راهنمای آموزشی رایگان entity framework
چندتا سئوال مفهومی در مورد EF دارم.
از بین روشهای model first ، database First و poco ، شما کدام رو توصیه می کنید و از کدام استفاده می کنید؟
از چه روشی برای حفظ داده های DB ، بعد از تغییر model استفاده می کنید.چون به طور پیش فرض DB دوباره ساخته می شود.
اگه لطف کنید کمی در مورد معماری لایه های برنامه هایی که از EF استفاده می کنند هم صحبت کنید متشکر میشم.
پروژه DNTFrameworkCore که قصد پشتیبانی از آن را دارم، یک زیرساخت سبک وزن و توسعه پذیر با پشتیبانی از طراحی چند مستاجری، با تمرکز بر کاهش زمان و افزایش کیفیت توسعه سیستمهای تحت وب مبتنی بر ASP.NET Core، توسعه داده شده است.
اهدافی که این زیرساخت دنبال میکند
- ارائه ساختارهای مشترک بین پروژههای مختلف از جمله Cross-Cutting Concernها و ...
- دنبال کردن اصل DRY به منظور متمرکز شدن صرف برروی منطق تجاری سیستم نه انجام و حل یکسری مسائل تکراری
- کاهش زمان توسعه و اختصاص زمان بیشتر برای نوشتن آزمونهای واحد منطق تجاری
- کاهش باگ و جلوگیری از پخش شدن باگهای پیاده سازی در سراسر سیستم
- کاهش زمان آموزش نیروهای جدید برای ملحق شدن به تیم تولید شما با حداقل دانش طراحی و برنامه نویسی شیء گرا
- ارائه راهکاری یکپارچه برای توسعه پذیر بودن منطق تجاری پیاده سازی شده از طریق در معرض دید قرار دادن یکسری «Extensibility Point» با استفاده از رویکرد Event-Driven
امکانات این زیرساخت در زمان نگارش مطلب جاری
- مکانیزم اعتبارسنجی خودکار ورودیهای متدهای مرتبط با منطق تجاری
- مکانیزم به روز رسانی یک AggregateRoot، به همراه موجودیتهای وابسته به آن (سناریوهای Master-Detail)
- مکانیزم Eventing، به منظور آگاهی از تغییرات و رخدادهای یک AggregateRoot خاص
- مکانیزم مدیریت خودکار تراکنشها
- مکانیزم شماره گذاری و تولید خودکار کد منحصر به فرد برای موجودیتهای سیستم
- اعمال مفاهیم Functional Programming برای مدیریت خطاهای قابل پیش بینی و همچنین مواجهه با حالتهای شکست متدها
- مکانیزم اعمال دسترسیهای پویا
- پیاده سازی سرویس CRUD پایه، مبتنی بر EF Core
- پیاده سازی کنترلر CRUD پایه، مبتنی بر ASP.NET Core Web API
- پروایدر Logging به منظور ذخیره سازی لاگ سیستم در بانک اطلاعاتی، با استفاده از EF Core
- مکانیزم Auditing به منظور ذخیره اطلاعات آماری از وضعیت انجام شدن متدها به همراه ورودی ها، خروجی و همچنین موفقیت یا عدم موفقیت آنها در بانک اطلاعاتی با پروایدر مبتنی بر EF Core
- مکانیزم ذخیره سازی کلیدهای موقتی تولید شده Data Protecction API در بانک اطلاعاتی با استفاده از EF Core
- مکانیزم Configuration به منظور ذخیره سازی و خواندن تنظیمات مبتنی بر Name-Value در بانک اطلاعاتی با استفاده از EF Core
- مکانیزم Hooks به منظور توسعه پذیر کردن DbContext مبتنی EF Core به همراه تعدادی Hook پیش فرض تعریف شده در زیرساخت
- مکانیزم ردیابی تغییرات
- امکان طراحی چند مستاجری به همراه مکانیزم فیلتر خودکار اطلاعات با امکان غیرفعال کردن آن مبتنی بر EF Core
- مکانیزم حذف نرم به همراه فیلتر خودکار اطلاعات حذف شده با امکان غیرفعال کردن آن مبتنی بر EF Core
- بسته نیوگت DNTFrameworkCore.FluentValidation به عنوان Adapter کتابخانه FluentValidation با مکانیزم اعتبارسنجی خودکار ورودیهای متدها
- کتابخانه DNTFrameworkCore.Cqrs به عنوان ابزار کمکی برای اعمال الگوی معماری CQRS (به زودی)
- امکان انجام کارهای طولانی در پس زمینه
- لاگ تغییرات موجودیتها یا Entity History (به زودی)
نحوه استفاده از بستههای نیوگت مرتبط
PM> Install-Package DNTFrameworkCore -Version 1.0.0
services.AddDNTFramework() .AddDataAnnotationValidation() .AddModelValidation() .AddValidationOptions(options => { /*options.IgnoredTypes.Add(typeof());*/ }) .AddMemoryCache() .AddAuditingOptions(options => { // options.Enabled = true; // options.EnabledForAnonymousUsers = false; // options.IgnoredTypes.Add(typeof()); // options.Selectors.Add(new NamedTypeSelector("SelectorName", type => type == typeof())); }).AddTransactionOptions(options => { // options.Timeout=TimeSpan.FromMinutes(3); //options.IsolationLevel=IsolationLevel.ReadCommitted; });
متدهای الحاقی بالا برای ثبت سرویسها و تنظیمات مرتبط با مکانیزمهای اعتبارسنجی خودکار، مدیریت تراکنشها، لاگ آماری، Eventing و سایر امکانات ذکر شده، در IoC Container توکار ASP.NET Core استفاده خواهند شد.
PM> Install-Package DNTFrameworkCore.EntityFramework -Version 1.0.0
services.AddDNTUnitOfWork<ProjectDbContext>();
بسته نیوگت بالا شامل پیاده سازی مبتنی بر EF Core برای واسطهای تعریف شده در بسته نیوگت DNTFrameworkCore، میباشد؛ از جمله آن میتوان به CrudService پایه اشاره کرد. متد الحاقی AddDNTUnitOfWork برای ثبت و معرفی واسطهای IUnitOfWork و ITransactionProvider به عنوان مهیا کننده تراکنش به همراه پیاده سازهای آنها و همچنین ثبت یک سری Hook تعریف شده برای ردیابی تغییرات، در سیستم تزریق وابستگی، استفاده خواهد شد.
همچنین با نصب بسته بالا، امکان استفاده از مهیا کننده Logging با امکان ذخیره سازی در بانک اطلاعاتی را خواهید داشت:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseDefaultServiceProvider((context, options) => { options.ValidateScopes = context.HostingEnvironment.IsDevelopment(); }) .ConfigureLogging((hostingContext, logging) => { logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); logging.AddConsole(); logging.AddDebug(); logging.AddEntityFramework<ProjectDbContext>(options => options.MinLevel = LogLevel.Warning); }) .UseStartup<Startup>();
متد جنریک الحاقی AddEntityFramework برای ثبت مهیا کننده مذکور استفاده میشود.
PM> Install-Package DNTFrameworkCore.Web -Version 1.0.0
بسته نیوگت بالا شامل یکسری سرویس برای اعمال دسترسیهای پویا، CrudController مبتنی بر ASP.NET Core Web API، فیلتر مدیریت سراسری خطاهای برنامه و سایر امکاناتی که در ادامه مقالات با جزئیات بیشتری بررسی خواهیم کرد، میباشد. برای ثبت سرویسهای تعریف شده میتوانید از متد الحاقی AddDNTCommonWeb و به منظور تغییر محل ذخیره سازی کلیدهای موقت رمزنگاری مرتبط با Data Protection API و انتقال آنها به بانک اطلاعاتی، استفاده کنید.
services.AddDNTCommonWeb() .AddDNTDataProtection();
نکته: برای انتقال کلیدهای موقت رمزنگاری به بانک اطلاعاتی، نیاز است تا از متد الحاقی زیر که در بسته نیوگت DNTFrameworkCore.EntityFramework موجود میباشد، به شکل زیر استفاده کنید:
services.AddDNTProtectionRepository<ProjectDbContext>();
PM> Install-Package DNTFrameworkCore.EntityFramework.SqlServer -Version 1.0.0
بسته بالا از امکانات مخصوص SqlServer برای اعمال قفل منطقی برای مدیریت مباحث همزمانی استفاده میکند؛ همچنین PreUpdateHook مرتبط با تولید خودکار کد منحصر به فرد، در این کتابخانه پیاده سازی شده است. به شکل زیر میتوانید سرویسهای مرتبط با آن را به سیستم تزریق وابستگیهای معرفی کنید:
services.AddDNTNumbering(options => { options.NumberedEntityMap[typeof(Task)] = new NumberedEntityOption { Start = 100, Prefix = "Task-", IncrementBy = 5 }; });
به عنوان مثال برای شماره گذاری موجودیت Task، لازم است تنظیمات مرتبط آن را به شکل بالا به سیستم شماره گذاری معرفی کنید.
اگر قصد استفاده از کتابخانه FluentValidation را داشته باشید، میبایست بسته زیر را نیز نصب کنید:
PM> Install-Package DNTFrameworkCore.FluentValidation -Version 1.0.0
برای ثبت و معرفی Adapter مرتبط، به سیستم اعتبارسنجی خودکار معرفی شده، لازم است از طریق متد الحاقی AddFluentModelValidation به شکل زیر اقدام کنید:
services.AddDNTFramework() .AddDataAnnotationValidation() .AddModelValidation() .AddFluentModelValidation() .AddValidationOptions(options => { /*options.IgnoredTypes.Add(typeof());*/ }) .AddMemoryCache() .AddAuditingOptions(options => { // options.Enabled = true; // options.EnabledForAnonymousUsers = false; // options.IgnoredTypes.Add(typeof()); // options.Selectors.Add(new NamedTypeSelector("SelectorName", type => type == typeof())); }).AddTransactionOptions(options => { // options.Timeout=TimeSpan.FromMinutes(3); //options.IsolationLevel=IsolationLevel.ReadCommitted; });
برای شروع پروژه جدید، نصب این بستهها کفایت میکند. اگر نیاز به طراحی MultiTenancy دارید، بسته زیر را برای شناسایی Tenant جاری و از این قبیل کارها نیز میبایست نصب کنید:
PM> Install-Package DNTFrameworkCore.Web.MultiTenancy -Version 1.0.0
services.AddMultiTenancy<TenantResolver>();
app.UseMultiTenancy();
services.Scan(scan => scan .FromCallingAssembly() .AddClasses(classes => classes.AssignableTo<ISingletonDependency>()) .AsMatchingInterface() .WithSingletonLifetime() .AddClasses(classes => classes.AssignableTo<IScopedDependency>()) .AsMatchingInterface() .WithScopedLifetime() .AddClasses(classes => classes.AssignableTo<ITransientDependency>()) .AsMatchingInterface() .WithTransientLifetime() .AddClasses(classes => classes.AssignableTo(typeof(IDomainEventHandler<>))) .AsImplementedInterfaces() .WithTransientLifetime()); foreach (var descriptor in services.Where(s => typeof(IApplicationService).IsAssignableFrom(s.ServiceType)) .ToList()) { services.Decorate(descriptor.ServiceType, (target, serviceProvider) => ProxyGenerator.CreateInterfaceProxyWithTargetInterface( descriptor.ServiceType, target, serviceProvider.GetRequiredService<ValidationInterceptor>(), (IInterceptor) serviceProvider.GetRequiredService<TransactionInterceptor>())); }
متغیرهای استاتیک و برنامههای ASP.NET
صحبت شما درست است ولی در این مورد این مشکلاتی که گفتید ایجاد نمیشود. عرض کردم که قبل از اینکه Thread بخواهد به کاربر 2 سپرده شود DataContext نابود شده است. معماری من کمی متفاوت است. فکر میکنم اگر بخواهیم بحث کنیم باید بر سر معماری بحث کنیم.
پاسخ اینکه آیا این معماری درست کار میکند یا نه در این جواب است که آیا امکان دارد دو Thread همزمان به یک شی که دارای ویژگی ThreadStatic است دسترسی پیدا کنند؟ اگر جواب خیر است پس همه چیز مرتب است (التبه کمی توضیح دارد).
و همچنین بله صحبت شما در مورد BLL درست است اما گاهی برای پروژههای کوچک بهتر است لایهها با یک شبه BLL که داخل DAL قرار دارد صحبت کنند. البته فکر میکنم نمیتوانم به درستی بیان کنم.
اما در کل شما به یک نکته خیلی خوب اشاره کردید و آن هم امنیت است. چک نکردم اما فکر میکنم در صورتی که «برنامهنویس خراب کاری کند» ممکن است این سیستم امنیت خوبی نداشته باشد. سعی میکنم برای این موضوع فکری کنم. اگر این مورد جدی باشد و اشتباهات برنامهنویس بتواند موجب مشکل شود حتماً از این روش صرف نظر کرده و از همان HttpContext.Current استفاده خواهم کرد.
دیکته من هرگز خوب نبوده و نیست. منظورم از اسرار، رازها نبوده منظورم پافشاری بوده (اصرار) :)
ممنون.
با پیشرفت بیشتر تکنولوژی وب در سالهای اخیر و رشد کاربران فضای اینترنتی، خدمات و پیچیدگیهای بیشتری به نرم افزارها اضافه شده و به همین دلیل استفاده از میکروسرویسها بجای حالت قدیمی مونولوتیک (یک برنامه همه کاره) طرفداران بیشتری پیدا کردهاست. در این حالت برنامه به قسمتهای خرد و مجزایی تبدیل شده و هر پروژه ساختار و تکنولوژی مخصوص به خود را مدیریت میکند و در این بین با استفاده روشهای متفاوتی به ایجاد ارتباط با یکدیگر میپردازند .
مشکلی که در این حالت میتواند رخ دهد، زیاد شدن مسیرهای متفاوت برای اتصال به هر یک از سرویسها و سختتر شدن به روزرسانی این مسیرها میباشد. به همین دلیل در این بخش، نیاز به ابزاری میباشد تا بتوان از طریق آن، مسیردهی سادهای را ایجاد کرد و در پشت صحنه مسیردهیهای متفاوتی را کنترل نمود. با ایجاد چنین ابزاری در واقع شما API Gateway ایجاد نمودهاید. یکی از معروفترین کتابخانههای این حوزه، Ocelot میباشد. کار با این ابزار بسیار ساده بوده و امکانات بسیار زیاد و قدرتمندی را فراهم مینماید.
برای اینکار ابتدا سه پروژه را میسازیم که موارد زیر را شامل میگردد:
پروژه اول نوع Api : با دریافت Id در اکشنمتد مورد نظر، شیء user بازگردانده میشود:
public class User { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string UserName { get; set; } public static List<User> GetUsers() { return new List<User>() { new() { Id = 1, FirstName = "علی", LastName = "یگانه مقدم", UserName = "yeganehaym" }, new () { Id = 2, FirstName = "وحید", LastName = "نصیری", UserName = "VahidN" }, }; } }
[ApiController] [Route("/api/[controller]/{id?}")] public class UserController : ControllerBase { [HttpGet] public User GetUser(int id) { var users = Users.User.GetUsers(); var user = users.FirstOrDefault(x => x.Id == id); return user; } }
پروژه دوم نوع Api : دریافت لیستی از محصولات:
public class Product { public int Id { get; set; } public string Name { get; set; } public int Price { get; set; } public int Quantity { get; set; } public static List<Product> GetProducts() { return new List<Product>() { new() { Id = 1, Name = "LCD", Price = 20000, Quantity = 10 }, new() { Id = 1, Name = "Mouse", Price = 320000, Quantity = 15 }, new() { Id = 1, Name = "Keyboard", Price = 50000, Quantity = 25 }, }; } }
[ApiController] [Route("api/[controller]")] public class ProductController : ControllerBase { [HttpGet] public List<Product> GetProducts() { return Product.GetProducts(); } }
پروژه سوم همان ApiGateway هست و همینکه یک پروژهی وب خالی باشد، کفایت میکند. در این پروژه Ocelot را نصب نموده و سپس فایلی با نام ocelot.json را با محتوای زیر به ریشهی پروژه همانند فایلهای appsettings.json اضافه میکنیم:
{ "Routes":[ { "DownstreamPathTemplate":"/api/User/{id}", "DownstreamScheme":"https", "DownstreamHostAndPorts":[ { "Host":"localhost", "Port":"7279" } ], "UpstreamPathTemplate":"/GetUser/{id}", "UpstreamHttpMethod":[ "GET" ]}, { "DownstreamPathTemplate":"/api/Product", "DownstreamScheme":"https", "DownstreamHostAndPorts":[ { "Host":"localhost", "Port":"7261" } ], "UpstreamPathTemplate":"/Products", "UpstreamHttpMethod":[ "GET" ] } ] }
این فایلها شامل دو قسمتUpStream و DownStream میشوند. آپاستریمها در واقع آدرسی است که شما قصد اتصال به آنرا دارید و قسمت داوناستریم، سرویس مقصدی است که ocelot باید درخواست شما را به سمت آن ارسال نماید. بهعنوان مثل شما با ارسال درخواستی به آدرس Products ، در پشت صحنه به آدرس localhost:7261/api/product ارسال میگردد. بدین صورت سیستم نهایی تنها به یک دامنه و آدرس منسجم ارسال شده، ولی در پشت صحنه این آدرسها ممکن است به تعداد زیادی سرویس در آدرسهای متفاوتی ارسال گردند.
جهت راه اندازی نهایی، کد زیر را به فایل Program.cs اضافه میکنیم:
builder.Services.AddOcelot();
app.UseOcelot();
پس از اضافه کردن پیکربندی و middleware آن، کد زیر را نیز جهت شناسایی فایل ocelot به فایل Program.cs نیز اضافه مینماییم:
builder.Configuration.SetBasePath(builder.Environment.ContentRootPath) .AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
همچنین
در صورت تمایل میتوانید کد را به شکل زیر هم نوشته تا بتوانید تنظیمات متفاوتی را برای محیط اجرایی متفاوتی ایجاد نمایید:
builder.Configuration.SetBasePath(builder.Environment.ContentRootPath) .AddJsonFile("ocelot.json", optional: false, reloadOnChange: true) .AddJsonFile($"ocelot.{builder.Environment.EnvironmentName}.json", optional: false, reloadOnChange: true);
هر
سه برنامه را با هم اجرا نمایید و با استفاده از برنامهی PostMan درخواستی را برای هر یک از موارد مورد نظر /Products و /GetUser/{1,2} به سمت پروژه ApiGateway
ارسال نمایید.
Ocelot موارد دیگری از قبیل تنظیم Load Balancer بین سرویس ها، اتصال به سرویسهای Service Discoveryچون Consul یا یوریکا و کش کردن و ... را نیز فراهم مینماید.
عملیات کشینگ
جهت بحث کشینگ، ابتدا بسته زیر را اضافه نمایید:
Install-Package Ocelot.Cache.CacheManager
سپس
پیکربندی ابتدایی را به شکل زیر تغییر دهید:
builder.Services.AddOcelot() .AddCacheManager(x => x.WithDictionaryHandle());
در ادامه در فایل Ocelot جیسون،
برای هر بخشی که مدنظر شماست تا کشی را انجام دهد، کد زیر اضافه نمایید:
"FileCacheOptions":{ "TtlSeconds":30, "Region":"custom" }
TtlSeconds : مدت
زمان کش به ثانیه
Region : یک عبارت رشتهای همانند یک عنوان یا نام که بعدا میتوانید از طریق api ها به آن متصل شوید و عملیاتی چون خالی کردن کش را صادر نمایید.
حال برای بخش محصولات این تنظیمات ذکر میگردد:
{ "Routes":[ { "DownstreamPathTemplate":"/api/User/{id}", "DownstreamScheme":"https", "DownstreamHostAndPorts":[ { "Host":"localhost", "Port":"7279" } ], "UpstreamPathTemplate":"/GetUser/{id}", "UpstreamHttpMethod":[ "GET" ] }, { "DownstreamPathTemplate":"/api/Product", "DownstreamScheme":"https", "DownstreamHostAndPorts":[ { "Host":"localhost", "Port":"7261" } ], "UpstreamPathTemplate":"/Products", "UpstreamHttpMethod":[ "GET" ], "FileCacheOptions":{ "TtlSeconds":30, "Region":"custom" } } ] }
برای اینکه متوجه عملکرد آن شوید یک نقطه توقف را در اکشن دریافت محصول قرار دهید و سپس برنامه را در حالت دیباگ اجرا نمایید. در مرتبه اول باید نقطه توقف بتواند اجرای کد را به شما نمایش دهد ولی تا 30 ثانیه آینده هر چقدر از طریق Postman درخواستی را ارسال نمایید نقطه توقف اجرا نخواهد گردید، ولی نتیجهی قبل برای شما ارسال خواهد شد.
این مورد را برای بخش کاربران هم انجام دهید و میبینید که برای هر userId و هر شکل Url، یک پاسخ منحصر به فرد، دریافت و کش خواهد شد.
جلوگیری از درخواستهای بیش از حد
یکی دیگر از ویژگیهای Ocelot، جلوگیری از درخواست بیش از حد میباشد. به همین علت ابتدا کد زیر را به هر درخواستی که مدنظر شماست اضافه نمایید:
"RateLimitOptions":{ "ClientWhitelist":[ ], "EnableRateLimiting":true, "Period":"5s", "PeriodTimespan":1, "Limit":1, "HttpStatusCode":429 }
WhiteClients : برای مشخص کردن کلاینتهایی که نباید اعمال محدودیت روی آنها صورت بگیرد.
EnableRateLimiting : این مورد باعث فعالسازی آن میگردد.
Period: مدت زمانیکه حداکثر تعداد درخواست باید در آن بازه صورت بگیرد. به ترتیب برای ثانیه، دقیقه، ساعت و روز حروف s - m - h و d استفاده میگردد.
PeriodTimespan: بعد از محدود شدن، بعد از چه مدتی دوباره بتواند درخواستی را ارسال نماید. در اینجا بعد از محدودیت ارسال درخواست، بعد از یک ثانیه مجدد اجازه ارسال درخواست باز میگردد.
Limit: در بازه زمانی مشخص شده چند درخواست مورد قبول واقع میشود و بعد از آن دیگر اجازه ارسال درخواست را نخواهد داشت.
HttpStatusCode: در صورت فیلتر شدن درخواستهای رسیده، چه کد وضعیتی باید برگردانده شود که عدد 429 به معنای Too Many Request میباشد.
با تنظیمات بالا هر کلاینت میتواند در 5 ثانیه، نهایتا یک درخواست را ارسال نماید و با ارسال بقیه درخواستها، Ocelot بجای هدایت درخواست به سرویس مربوطه، کد وضعیت 429 را باز میگرداند و یک ثانیه بعد از گذشت 5 ثانیه میتواند مجددا درخواست خود را ارسال نماید.
در نهایت به یک فایل مشابه زیر میرسیم:
{ "Routes":[ { "DownstreamPathTemplate":"/api/User/{id}", "DownstreamScheme":"https", "DownstreamHostAndPorts":[ { "Host":"localhost", "Port":"7279" } ], "UpstreamPathTemplate":"/GetUser/{id}", "UpstreamHttpMethod":[ "GET" ], "FileCacheOptions":{ "TtlSeconds":30, "Region":"custom" } }, { "DownstreamPathTemplate":"/api/Product", "DownstreamScheme":"https", "DownstreamHostAndPorts":[ { "Host":"localhost", "Port":"7261" } ], "UpstreamPathTemplate":"/Products", "UpstreamHttpMethod":[ "GET" ], "RateLimitOptions":{ "ClientWhitelist":[ ], "EnableRateLimiting":true, "Period":"5s", "PeriodTimespan":1, "Limit":1, "HttpStatusCode":429 } } ], "DangerousAcceptAnyServerCertificateValidator": true }
برای تست آن با استفاد از PostMan مرتبا به آدرس Products/ درخواست ارسال نمایید.
فایل پروژه : Ocelot.zip
Live Connect
XP Connect
NPRuntime
در پایان سال 2004 بسیاری از شرکتهای اصلی ارائه کننده مرورگرهای وب که از NPAPI استفاده میکردند، به این توافق رسیدند که از فناوری NPRuntime به عنوان یک اکستنشن (افزونه) برای تامین اسکریپت نویسی NPAPI استفاده کنند. اسکریپت نویسی این فناوری مستقل از دیگر فناوریهایی چون جاوا و XPCOM است و توسط گوگل (در آن زمان)، فایرفاکس، سافاری و اپرا (در آن زمان) پشتیبانی میشود.
PPAPI
این فناوری در 12 آگوست 2009 معرفی شد. Pepper Plugin API مجموعه ای از تغییرات و اصلاحات روی NPAPI است که باعث انعطاف پذیری و امنیت بیشتر میشود. این افزونه جهت پیاده سازی آسان اجرای پلاگین در یک پروسهی جداگانه است. هدف این پروژه تهیه یک فریمورک برای ایجاد پلاگینهای مستقل یا Cross-Platform میباشد.
- اجرای جداگانه از پروسهی مرورگر
- تعریف رویدادهای استاندارد و رسم تصاویر دو بعدی
- تهیه گرافیک سه بعدی
- ریجستری پلاگین
- استاندارد سازی برای استفاده از سیستم رندر مرورگر Compositing Process
- تعریف مفهوم یا معنای واحد از NPAPI بین تمامی مروگرها
مشاهده یا دانلود کدهای مقاله
تزریق وابستگی چیست؟
تزریق وابستگی (DI) تکنیکی برای دستیابی به اتصال شل بین اشیاء و همکاران اشیاء و وابستگیهای بین آنها میباشد. یک شیء برای انجام وظایف خود، بجای اینکه اشیاء همکار خود را به صورت مستقیم نمونه سازی کند، یا از ارجاعات استاتیک استفاده نماید، میتواند از اشیائی که برایش تامین شدهاست، استفاده کند. در اغلب موارد کلاسها، وابستگیهای خود را از طریق سازندهی خود درخواست میکنند، که به آنها اجازه میدهد اصل وابستگی صریح را رعایت کنند (Explicit Dependencies Principle). این روش را «تزریق در سازنده» مینامند.
از آنجا که در طراحی کلاسها با استفاده از DI، نمونه سازی مستقیم، توسط کلاسها و به صورت Hard-coded انجام نمیگیرد، وابستگی بین اشیاء کم شده و پروژهای با اتصالات شل به دست میآید. با این کار اصل وابستگی معکوس (Dependency Inversion Principle) رعایت میشود. بر اساس این اصل، ماژولهای سطح بالا نباید به ماژولهای سطح پایین خود وابسته باشند؛ بلکه هر دو باید به کلاسهایی انتزاعی وابسته باشند. اشیاء بجای ارجاع به پیاده سازیهای خاص کلاسهای همکار خود، کلاسهای انتزاعی، معمولاٌ اینترفیس آنها را درخواست میکنند و هنگام نمونه سازی از آنها (داخل متد سازنده) کلاس پیاده سازی شده برایشان تامین میشود. خارج کردن وابستگیهای مستقیم از کلاسها و تامین پیاده سازیهای این اینترفیسها به صورت پارامترهایی برای کلاسها، یک مثال از الگوی طراحی استراتژی (Strategy design pattern) میباشد.
در حالتیکه کلاسها به تعداد زیادی کلاس وابستگی داشته باشند و برای اجرا شدن، نیاز به تامین وابستگیهایشان داشته باشند، بهتر است یک کلاس اختصاصی، برای نمونه سازی این کلاسها با وابستگیهای مورد نیاز آنها، در سیستم وجود داشته باشد. این کلاس نمونه ساز را کانتینرIoC، یا کانتینر DI یا به طور خلاصه کانتینر مینامند ( Inversion of Control (IoC) ). کانتینر در اصل یک کارخانه میباشد که وظیفهی تامین نمونههایی از کلاسهایی را که از آن درخواست میشود، انجام میدهد. اگر یک کلاس تعریف شده، وابستگی به کلاسهای دیگر داشته باشد و کانتینر برای ارائه وابستگیهای کلاس تعریف شده تنظیم شده باشد، هر موقع نیاز به یک نمونه از این کلاس وجود داشته باشد، به عنوان بخشی از کار نمونه سازی از کلاس مورد نظر، کلاسهای وابستهی آن نیز ایجاد میشوند (همهی کارهای مربوط به نمونه سازی کلاس خاص و کلاسهای وابسته به آن توسط کانتینر انجام میگیرد). به این ترتیب، میتوان وابستگیهای بسیار پیچیده و تو در توی موجود در سیستم را بدون نیاز به هیچگونه نمونه سازی hard-code شده، برای کلاسها فراهم کرد. کانتینرها علاوه بر ایجاد اشیاء و وابستگیهای موجود در آنها، معمولا طول عمر اشیاء در اپلیکیشن را نیز مدیریت میکنند.
ASP.NET Core یک کانتینر بسیار ساده را به نام اینترفیس IServiceProvider ارائه داده است که به صورت پیش فرض از تزریق وابستگی در سازندهی کلاسها پشتیبانی میکند و همچنین ASP.NET برخی از سرویسهای خود را از طریق DI در دسترس قرار داده است. کانتینرASP.NET، یک اشارهگر به کلاسهایی است که به عنوان سرویس عمل میکنند. در ادامهی این مقاله، سرویسها به کلاسهایی گفته میشود که به وسیلهی کانتینر ASP.NET Core مدیریت میشوند. شما میتوانید سرویس ConfigureServices کانتینر را در داخل کلاس Startup پروژه خود پیکربندی کنید.
تزریق وابستگی از طریق متد سازندهی کلاس
تزریق وابستگی از طریق متد سازنده، مستلزم آن است که سازندهی کلاس مورد نظر عمومی باشد. در غیر این صورت، اپلیکیشن شما استثنای InvalidOperationException را با پیام زیر نشان میدهد:
A suitable constructor for type 'YourType' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.
تزریق از طریق متد سازنده مستلزم آن است که تنها یک سازندهی مناسب وجود داشته باشد. البته Overload سازنده امکان پذیر است؛ ولی باید تنها یک متد سازنده وجود داشته باشد که آرگومانهای آن توسط DI قابل ارائه باشند. اگر بیش از یکی وجود داشته باشد، سیستم استثنای InvalidOperationException را با پیام زیر نشان میدهد:
Multiple constructors accepting all given argument types have been found in type 'YourType'. There should only be one applicable constructor.
سازندگان میتوانند آرگومانهایی را از طریق DI دریافت کنند. برای این منظور آرگومانهای این سازندهها باید مقدار پیش فرضی را داشته باشند. به مثال زیر توجه نمایید:
// throws InvalidOperationException: Unable to resolve service for type 'System.String'... public CharactersController(ICharacterRepository characterRepository, string title) { _characterRepository = characterRepository; _title = title; } // runs without error public CharactersController(ICharacterRepository characterRepository, string title = "Characters") { _characterRepository = characterRepository; _title = title; }
استفاده از سرویس ارائه شده توسط فریم ورک
متد ConfigureServices در کلاس Startup، مسئول تعریف سرویسهایی است که سیستم از آن استفاده میکند. از جملهی این سرویسها میتوان به ویژگیهای پلتفرم مانند EF Core و ASP.NET Core MVC اشاره کرد. IServiceCollection که به ConfigureServices ارائه میشود، سرویسهای زیر را تعریف میکند (که البته بستگی به نوع پیکربندی هاست دارد):
نوع سرویس | طول زندگی |
Microsoft.AspNetCore.Hosting.IHostingEnvironment | Singleton |
Microsoft.AspNetCore.Hosting.IApplicationLifetime | Singleton |
Microsoft.AspNetCore.Hosting.IStartup | Singleton |
Microsoft.AspNetCore.Hosting.Server.IServer | Singleton |
Microsoft.Extensions.Options.IConfigureOptions | Transient |
Microsoft.Extensions.ObjectPool.ObjectPoolProvider | Singleton |
Microsoft.AspNetCore.Hosting.IStartupFilter | Transient |
System.Diagnostics.DiagnosticListener | Singleton |
System.Diagnostics.DiagnosticSource | Singleton |
Microsoft.Extensions.Options.IOptions | Singleton |
Microsoft.AspNetCore.Http.IHttpContextFactory | Transient |
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory | Transient |
Microsoft.Extensions.Logging.ILogger | Singleton |
Microsoft.Extensions.Logging.ILoggerFactory | Singleton |
در زیر نمونه ای از نحوهی اضافه کردن سرویسهای مختلف را به کانتینر، با استفاده از متدهای الحاقی مانند AddDbContext، AddIdentity و AddMvc، مشاهده میکنید:
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); services.AddMvc(); // Add application services. services.AddTransient<IEmailSender, AuthMessageSender>(); services.AddTransient<ISmsSender, AuthMessageSender>(); }
ثبت سرویسهای اختصاصی
شما میتوانید سرویسهای اپلیکیشن خودتان را به ترتیبی که در تکه کد زیر مشاهده میکنید، ثبت نمایید. اولین نوع جنریک، نوعی است که از کانتینر درخواست خواهد شد و معمولا به شکل اینترفیس میباشد. نوع دوم، نوع پیاده سازی شدهای است که به وسیلهی کانتینر، نمونه سازی خواهد شد و کانتینر برای درخواستهای از نوع اول، این نمونه از تایپ را ارائه خواهد کرد:
services.AddTransient<IEmailSender, AuthMessageSender>(); services.AddTransient<ISmsSender, AuthMessageSender>();
نکته:
هر متد الحاقی <services.Add<ServiceName، سرویسهایی را اضافه و پیکربندی میکند. به عنوان مثال services.AddMvc نیازمندیهای سرویس MVC را اضافه میکند. توصیه میشود شما هم با افزودن متدهای الحاقی در فضای نام Microsoft.Extensions.DependencyInjection این قرارداد را رعایت نمائید. این کار باعث کپسوله شدن ثبت گروهی سرویسها میشود.
متد AddTransient، برای نگاشت نوعهای انتزاعی به سرویسهای واقعی که نیاز به نمونه سازی به ازای هر درخواست دارند، استفاده میشود. در اصطلاح، طول عمر سرویسها در اینجا مشخص میشوند. در ادامه گزینههای دیگری هم برای طول عمر سرویسها تعریف خواهند شد. خیلی مهم است که برای هر یک از سرویسهای ثبت شده، طول عمر مناسبی را انتخاب نمایید. آیا برای هر کلاس که سرویسی را درخواست میکند، باید یک نمونهی جدید ساخته شود؟ آیا فقط یک نمونه در طول یک درخواست وب مورد استفاده قرار میگیرد؟ یا باید از یک نمونهی واحد برای طول عمر کل اپلیکیشن استفاده شود؟
در مثال ارائه شدهی در این مقاله، یک کنترلر ساده به نام CharactersController وجود دارد که نام کاراکتری را نشان میدهد. متد Index، لیست کنونی کاراکترهایی را که در اپلیکیشن ذخیره شدهاند، نشان میدهد. در صورتیکه این لیست خالی باشد، تعدادی به آن اضافه میکند. توجه داشته باشید، اگرچه این اپلیکیشن از Entity Framework Core و ClassDataContext برای دادههای مانا استفاده میکند، هیچیکدام از آنها در کنترلر ظاهر نمیشوند. در عوض، مکانیزم دسترسی به دادههای خاص، در پشت یک اینترفیس (ICharacterRepository) مخفی شده است (طبق الگوی طراحی ریپازیتوری). یک نمونه از ICharacterRepository از طریق سازنده درخواست میشود و به یک فیلد خصوصی اختصاص داده میشود، سپس برای دسترسی به کاراکترها در صورت لزوم استفاده میشود:
public class CharactersController : Controller { private readonly ICharacterRepository _characterRepository; public CharactersController(ICharacterRepository characterRepository) { _characterRepository = characterRepository; } // GET: /characters/ public IActionResult Index() { PopulateCharactersIfNoneExist(); var characters = _characterRepository.ListAll(); return View(characters); } private void PopulateCharactersIfNoneExist() { if (!_characterRepository.ListAll().Any()) { _characterRepository.Add(new Character("Darth Maul")); _characterRepository.Add(new Character("Darth Vader")); _characterRepository.Add(new Character("Yoda")); _characterRepository.Add(new Character("Mace Windu")); } } }
ICharacterRepository دو متد مورد نیاز کنترلر برای کار با نمونههای Character را تعریف میکند:
using System.Collections.Generic; using DependencyInjectionSample.Models; namespace DependencyInjectionSample.Interfaces { public interface ICharacterRepository { IEnumerable<Character> ListAll(); void Add(Character character); } }
using System.Collections.Generic; using System.Linq; using DependencyInjectionSample.Interfaces; namespace DependencyInjectionSample.Models { public class CharacterRepository : ICharacterRepository { private readonly ApplicationDbContext _dbContext; public CharacterRepository(ApplicationDbContext dbContext) { _dbContext = dbContext; } public IEnumerable<Character> ListAll() { return _dbContext.Characters.AsEnumerable(); } public void Add(Character character) { _dbContext.Characters.Add(character); _dbContext.SaveChanges(); } } }
نکته
ایجاد شیء درخواست شده و تمامی اشیاء مورد نیاز شیء درخواست شده را گراف شیء مینامند. به همین ترتیب مجموعهای از وابستگیهایی را که باید resolve شوند، به طور معمول، درخت وابستگی یا گراف وابستگی مینامند.
در مورد مثال مطرح شده، ICharacterRepository و به نوبه خود ApplicationDbContext باید با سرویسهای خود در کانتینر ConfigureServices و کلاس Startup ثبت شوند. ApplicationDbContext با فراخوانی متد <AddDbContext<T پیکربندی میشود. کد زیر ثبت کردن نوع CharacterRepository را نشان میدهد:
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<ApplicationDbContext>(options => options.UseInMemoryDatabase() ); // Add framework services. services.AddMvc(); // Register application services. services.AddScoped<ICharacterRepository, CharacterRepository>(); services.AddTransient<IOperationTransient, Operation>(); services.AddScoped<IOperationScoped, Operation>(); services.AddSingleton<IOperationSingleton, Operation>(); services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty)); services.AddTransient<OperationService, OperationService>(); }
هشدار
خطر بزرگی را که باید در نظر گرفت، resolve کردن سرویس Scoped از طول عمر singleton میباشد. در صورت انجام این کار، احتمال دارد که سرویسها وارد حالت نادرستی شوند.
سرویسهایی که وابستگیهای دیگری هم دارند، باید آنها را در کانتینر ثبت کنند. اگر سازندهی سرویس نیاز به یک primitive به عنوان ورودی داشته باشد، میتوان با استفاده از الگوی گزینهها و پیکربندی (options pattern and configuration)، ورودیهای مناسبی را به سازندهها منتقل کرد.
طول عمر سرویسها و گزینههای ثبت
سرویسهای ASP.NET را میتوان با طول عمرهای زیر پیکربندی کرد:
Transient: سرویسهایی با طول عمر Transient، در هر زمان که درخواست میشوند، مجددا ایجاد میشوند. این طول عمر برای سرویسهای سبک و بدون حالت مناسب میباشند.
Scoped: سرویسهایی با طول عمر Scoped، تنها یکبار در طی هر درخواست ایجاد میشوند.
Singleton: سرویسهایی با طول عمر Singleton، برای اولین باری که درخواست میشوند (یا اگر در ConfigureServices نمونهای را مشخص کرده باشید) ایجاد میشوند و درخواستهای آتی برای این سرویسها از همان نمونهی ایجاد شده استفاده میکنند. اگر اپلیکیشن شما درخواست رفتار singleton را داشته باشد، پیشنهاد میشود که سرویس کانتینر را برای مدیریت طول عمر سرویس مورد نیاز پیکربندی کنید و خودتان الگوی طراحی singleton را پیاده سازی نکنید.
سرویسها به چندین روش میتوانند در کانتینر ثبت شوند. چگونگی ثبت کردن یک سرویس پیاده سازی شده برای یک نوع، در بخشهای پیشین توضیح داده شده است. علاوه بر این، یک کارخانه را میتوان مشخص کرد، که برای ایجاد نمونه بر اساس تقاضا استفاده شود. رویکرد سوم، ایجاد مستقیم نمونهای از نوع مورد نظر است که در این حالت کانتینر اقدام به ایجاد یا نابود کردن نمونه نمیکند.
به منظور مشخص کردن تفاوت بین این طول عمرها و گزینههای ثبت کردن، یک اینترفیس ساده را در نظر بگیرید که نشان دهندهی یک یا چند operation است و یک شناسهی منحصر به فرد operation را از طریق OperationId نشان میدهد. برای مشخص شدن انواع طول عمرهای درخواست شده، بسته به نحوهی پیکربندی طول عمر سرویس مثال زده شده، کانتینر، نمونهی یکسان یا متفاوتی را از سرویس، به کلاس درخواست کننده ارائه میدهد. ما برای هر طول عمر، یک نوع را ایجاد میکنیم:
using System; namespace DependencyInjectionSample.Interfaces { public interface IOperation { Guid OperationId { get; } } public interface IOperationTransient : IOperation { } public interface IOperationScoped : IOperation { } public interface IOperationSingleton : IOperation { } public interface IOperationSingletonInstance : IOperation { } }
سپس در ConfigureServices، هر نوع با توجه به طول عمر مورد نظر، به کانتینر افزوده میشود:
services.AddScoped<ICharacterRepository, CharacterRepository>(); services.AddTransient<IOperationTransient, Operation>(); services.AddScoped<IOperationScoped, Operation>(); services.AddSingleton<IOperationSingleton, Operation>(); services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty)); services.AddTransient<OperationService, OperationService>();
using DependencyInjectionSample.Interfaces; namespace DependencyInjectionSample.Services { public class OperationService { public IOperationTransient TransientOperation { get; } public IOperationScoped ScopedOperation { get; } public IOperationSingleton SingletonOperation { get; } public IOperationSingletonInstance SingletonInstanceOperation { get; } public OperationService(IOperationTransient transientOperation, IOperationScoped scopedOperation, IOperationSingleton singletonOperation, IOperationSingletonInstance instanceOperation) { TransientOperation = transientOperation; ScopedOperation = scopedOperation; SingletonOperation = singletonOperation; SingletonInstanceOperation = instanceOperation; } } }
using DependencyInjectionSample.Interfaces; using DependencyInjectionSample.Services; using Microsoft.AspNetCore.Mvc; namespace DependencyInjectionSample.Controllers { public class OperationsController : Controller { private readonly OperationService _operationService; private readonly IOperationTransient _transientOperation; private readonly IOperationScoped _scopedOperation; private readonly IOperationSingleton _singletonOperation; private readonly IOperationSingletonInstance _singletonInstanceOperation; public OperationsController(OperationService operationService, IOperationTransient transientOperation, IOperationScoped scopedOperation, IOperationSingleton singletonOperation, IOperationSingletonInstance singletonInstanceOperation) { _operationService = operationService; _transientOperation = transientOperation; _scopedOperation = scopedOperation; _singletonOperation = singletonOperation; _singletonInstanceOperation = singletonInstanceOperation; } public IActionResult Index() { // viewbag contains controller-requested services ViewBag.Transient = _transientOperation; ViewBag.Scoped = _scopedOperation; ViewBag.Singleton = _singletonOperation; ViewBag.SingletonInstance = _singletonInstanceOperation; // operation service has its own requested services ViewBag.Service = _operationService; return View(); } } }
حالا دو درخواست جداگانه برای این کنترلر ساخته شده است:
به تفاوتهای موجود در مقادیر OperationId در یک درخواست و بین درخواستها توجه کنید:
- OperationId اشیاء Transient همیشه متفاوت میباشند. چون یک نمونه جدید برای هر کنترلر و هر سرویس ایجاد شدهاست.
- اشیاء Scoped در یک درخواست، یکسان هستند؛ اما در درخواستهای مختلف متفاوت میباشند.
- اشیاء Singleton برای هر شیء و هر درخواست (صرف نظر از اینکه یک نمونه در ConfigureServices ارائه شده است) یکسان میباشند.
درخواست سرویس
در ASP.NET سرویسهای موجود در یک درخواست HttpContext از طریق مجموعه RequestServices قابل مشاهده میباشد.
RequestServices نشان دهندهی سرویسهایی است که شما به عنوان بخشی از اپلیکیشن خود، آنها را پیکربندی و درخواست میکنید. هنگامیکه اشیاء اپلیکیشن شما وابستگیهای خود را مشخص میکنند، این وابستگیها با استفاده از نوعهای موجود در RequestServices برآورده میشوند و نوعهای موجود در ApplicationServices در این مرحله مورد استفاده قرار نمیگیرد.
به طور کلی، شما نباید مستقیما از این خواص استفاده کنید و بجای آن، نوعهای کلاس خود را توسط سازندهی کلاس، درخواست کنید و اجازه دهید فریم ورک این وابستگیها را تزریق کند. این کار باعث بهوجود آمدن کلاسهایی با قابلیت آزمونپذیری بالاتر و اتصالات شلتر بین آنها میشود.
نکته
درخواست وابستگیها با استفاده از پارامترهای کلاس سازنده، بر روش کار با مجموعهی RequestServices ارجحیت دارد.
طراحی سرویسها برای تزریق وابستگیها
شما باید سرویسهای خود را طوری طراحی کنید که از تزریق وابستگیها برای ارتباطات خود استفاده نمایند. این کار باعث کاهش استفاده از فراخوانیهای متدهای استاتیک (متدهای استاتیک، حالت دار میباشند و استفادهی زیاد از آنها باعث به وجود آمدن بوی بد کدی به نام static cling، میشود) و همچنین از بین رفتن نیاز به نمونه سازی مستقیم کلاسهای وابسته داخل سرویسها، میشود. هر موقع بخواهید بین new کردن یک کلاس، یا درخواست دادن آن از طریق تزریق وابستگی، یکی را انتخاب کنید، این اصطلاح را به یاد بیاورید، New is Glue. با پیروی از اصول SOLID طراحی شیء گرا، به طور طبیعی کلاسهای شما تمایل به کوچک بودن، کارا و قابل تست بودن را دارند.
اگر متوجه شدید که کلاسهای شما تمایل دارند تا تعداد وابستگیهای زیادی به آنها تزریق شود، چه باید بکنید؟ به طور کلی این مشکل نشانهای است از نقض Single Responsibility Principle یا SRP است و احتمالا کلاسهای شما وظایف بیش از اندازهای را دارند. در این گونه موارد تلاش کنید مقداری از وظایف کلاس را به یک کلاس جدید منتقل کنید. در نظر داشته باشید که کلاسهای کنترلر باید به مسائل UI تمرکز کنند و قوانین کسب و کار و جزئیات دسترسی به دادهها باید در کلاسهایی جداگانه و مرتبط با خود قرار داشته باشند.
به طور خاص برای دسترسی به داده ، شما میتوانید DbContext را به کنترلرهای خود تزریق کنید (با فرض اینکه شما EF را به کانتینر سرویس ConfigureServices اضافه کردهاید). بعضی از توسعه دهندگان به جای تزریق مستقیم DbContext از یک اینترفیس ریپازیتوری استفاده مینمایند. میتوانید با استفاده از یک اینترفیس برای کپسوله کردن منطق دسترسی به دادهها در یک مکان، تعداد تغییرات مورد نیاز را در صورت تغییر دیتابیس، به حداقل برسانید.
تخریب سرویس ها
سرویس کانتینر برای نوعهای IDisposable که خودش ایجاد کردهاست، متد Dispose را فراخوانی خواهد کرد. با این حال، اگر شما خودتان نمونهای را به صورت دستی نمونه سازی و به کانتینر اضافه کرده باشید، سرویس کانتینر آنرا dispose نخواهد کرد.
مثال:
// Services implement IDisposable: public class Service1 : IDisposable {} public class Service2 : IDisposable {} public class Service3 : IDisposable {} public void ConfigureServices(IServiceCollection services) { // container will create the instance(s) of these types and will dispose them services.AddScoped<Service1>(); services.AddSingleton<Service2>(); // container did not create instance so it will NOT dispose it services.AddSingleton<Service3>(new Service3()); services.AddSingleton(new Service3()); }
نکته:
در نسخه 1.0، کانتینر برای تمام اشیاء از نوع IDisposable از جمله اشیائی که خودش ایجاد نکرده بود، متد dispose را فراخوانی میکرد.
سرویسهای کانتینر جانشین
کانتینر موجود در net core. به منظور تامین نیازهای اساسی فریم ورک ایجاد شدهاست و تعداد زیادی از اپلیکیشنها از آن استفاده میکنند. با این حال، توسعه دهندگان میتوانند کانتینرهای مورد نظر خود را جایگزین آن کنند. متد ConfigureServices به طور معمول مقدار void را بر میگرداند. اما با تغییر امضای آن به نوع بازگشتیIServiceProvider، میتوان سرویس کانتینر متفاوتی را در اپلیکیشن پیکربندی کرد. سرویسهای کانتینر IOC مختلفی برای NET. وجود دارند؛ در مثال زیر، Autofac استفاده شده است.
در ابتدا بستههای زیر را نصب کنید:
Autofac Autofac.Extensions.DependencyInjection
public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddMvc(); // Add other framework services // Add Autofac var containerBuilder = new ContainerBuilder(); containerBuilder.RegisterModule<DefaultModule>(); containerBuilder.Populate(services); var container = containerBuilder.Build(); return new AutofacServiceProvider(container); }
توصیه ها
هنگام کار با تزریق وابستگیها، توصیههای ذیر را در نظر داشته باشید:
- DI برای اشیایی که دارای وابستگی پیچیده هستند، مناسب میباشد. کنترلرها، سرویسها، آداپتورها و ریپازیتوریها، نمونههایی از این اشیاء هستند که میتوانند به DI اضافه شوند.
- از ذخیرهی دادهها و پیکربندی مستقیم در DI اجتناب کنید. به عنوان مثال، معمولا سبد خرید کاربر نباید به سرویس کانتینر اضافه شود. پیکربندی باید از مدل گزینهها استفاده کند. همچنین از اشیاء "data holder"، که فقط برای دسترسی دادن به اشیاء دیگر ایجاد شدهاند، نیز اجتناب کنید. در صورت امکان بهتر است شیء واقعی مورد نیاز DI درخواست شود.
- از دسترسی استاتیک به سرویسها اجتناب شود.
- از نمونه سازی مستقیم سرویسها در کد برنامه خود اجتناب کنید.
- از دسترسی استاتیک به HttpContext اجتناب کنید.
توجه
مانند هر توصیهی دیگری، ممکن است شما با شرایطی مواجه شوید که مجبور به نقض هر یک از این توصیهها شوید. اما این موارد استثناء بسیار نادر میباشند و رعایت این نکات یک عادت برنامه نویسی خوب محسوب میشود.
مرجع: Introduction to Dependency Injection in ASP.NET Core
معرفی سرویسهای ارائه شده توسط شرکتهای گوگل، آمازون و مایکروسافت در قالب رایانش ابری - قسمت اول
رایانش ابری مفهوم نسبتا جدیدی در عرصهی فناوری اطلاعات است و در حال گسترش میباشد. به طور خلاصه رایانش ابری به همه چیز اعم از برنامه کاربردی( Application )، سکو ی ( Platform ) توسعه نرم افزار، سخت افزار و زیرساخت، به عنوان سرویس نگاه میکند. زیرساخت های موجود در مراکز داده( Data Center ) به انضمام نرمافزارهایی که در آن قرار دارند، مجموعههایی را تشکیل میدهند که ابر نامیده میشود. به عبارت سادهتر رایانش ابری یعنی استفاده اشتراکی از برنامهها و منابع یک محیط شبکهای برای انجام یک کار، بدون این که مالکیت، مدیریت منابع شبکه و سخت افزار و برنامهها، برای استفاده کننده مهم باشد. در رایانش ابری منابع کامپیوترها، برای انجام یک کار استفاده میشوند و دادههای مربوط به پردازش، در هیچ کدام از کامپیوترهای شخصی ذخیره نمیشوند، بلکه در جای دیگری در داخل همان منابع شبکه، ذخیره میشوند تا در زمان و مکان دیگری قابل دسترسی باشند.
بر همین اساس شرکت های پیشرو در زمینه فناوری اطلاعات به ارائه سرویس هایی تحت عنوان خدمات رایانش ابری پرداخته اند و هدف از این سری مطالب ارائه شده، شرح مختصری بر سرویسهای ارائه شده می باشد. در قسمت اول به معرفی سرویسهای شرکت گوگل پرداخته می شود و در قسمتهای بعدی، سرویسهای شرکتهای مایکروسافت و آمازون معرفی میگردد.
سرویسهای رایانش ابری گوگل، در زیر دو چتر قرار دارند. گروه اول شامل مجموعه گستردهای از برنامههای محبوب گوگل مانند Google Doc ، Google Health ، Google Mail ، Google Earth هستند که با کلیک بر روی گزینه More و Even More که در بالای صفحه اصلی گوگل قرار دارند، میتوان به آنها دسترسی پیدا کرد.
دومین محصول مبتنی بر ابر گوگل، ابزار توسعه PaaS گوگل است. این سکو در سال 2008 برای توسعه برنامههای تحت وب، با استفاده از زیرساخت گوگل به نام موتور Google App معرفی شد. هدف از آن قادر ساختن توسعه دهندگان برای ساخت و استقرار برنامههای وب بدون نگرانی از زیرساختی است که برنامه بر رویش اجرا میشود. برنامههای این موتور، با زبانهای سطح بالا به ویژه جاوا و پایتون و در چارچوب GAE نوشته میشوند. گوگل به منظور گسترش این نوع برنامهها یک سطح رایگان مشخص از سرویس را ارائه میدهد و زمانی که برنامه از سطح مشخصی از بار پردازشی، ذخیرهسازی و پهنای باند شبکه فراتر رفت، آنگاه شارژها بر مبنای میزان استفاده محاسبه میشود.
برنامههای GAE را باید به گونهای نوشت که با زیرساخت گوگل وفق یابند. این مسئله، باعث محدودیت برنامههای قابل اجرا در GAE میگردد و علاوه بر آن، انتقال برنامهها به سکوی GAE و یا انتقال از این سکو به سایر سکوهای موجود دشوار میشود.
از میان سرویس های ابری رایگان ارائه شده از سوی گوگل، به معرفی سرویس آنالیز گوگل بسنده کرده و تمرکز اصلی بر روی سکوی توسعه نرمافزاری این شرکت ( GAE ) میباشد.
Google Analytics
به اختصار GA نامیده میشود و یک ابزار آماری است که تعداد و انواع بازدیدکنندگان وبسایت و نحوه استفاده از وبسایت را اندازهگیری میکند. این محصول بر روی بسته تحلیلی Urchin 5 که گوگل در سال 2006 آن را خریداری نمود، ساخته شده است. این سرویس رایگان عرضه میشود و فرآیند تحلیل را با استفاده از یک قطعه کد جاوا اسکریپت به نام Google Analytics Tracking Code با پیادهسازی در تگ صفحه وب انجام میشود.
این کد با اولین بارگذاری صفحه در سیستم کاربران، به جمع آوری اطلاعات مورد نیاز پرداخته و برای پردازش به سرورهای GA باز پس میفرستد. این کد با کمک Cookie مرورگر اطلاعات مورد نیاز را جمع آوری مینماید.
آشنایی با Google App Engine
GAE یک سکو به عنوان سرویس میباشد و مبتنی بر ابر گوگل است و بر روی زیر ساخت گوگل مستقر شده است.
این سرویس توسعه دهندگان را قادر میسازد تا برنامههای وب ایجاد کرده و بر روی سرورهای گوگل مستقر سازند و گوگل مدیریت زیرساخت را بر عهده گیرد و اعمالی مانند نظارت، برطرف کردن اشکالات احتمالی، خوشه بندی، مدیریت وهلهسازی ماشینهای مجازی و غیره را انجام دهد. برای اجرای یک برنامه در GAE ابتدا باید استانداردهای سکوی گوگل رعایت شود. این استانداردها دامنه برنامههایی که قابل اجرا میباشند را بسیار محدود مینماید و قابلیت حمل آنها را کاهش میدهد.
محدودیتهایی
که این سکو ایجاد میکند، با خود مزایایی را به همراه میآورد که در زیر به آنها
اشاره میگردد:
- وب سرویسهای پویا بر مبنای استانداردهای رایج
- توسعه خودکار و توازن بار بین ماشینهای وهلهسازی شده که
مورد استفاده وب سرویس است.
- اعتبارسنجی با استفاده از API
موجود در گوگل.
- فضای ذخیره سازی ماندگار با قابلیت جستجو، مرتب سازی و
مدیریت تراکنش.
- صف کاری و زمان بندی کاری
- محیط توسعه سمت مشتری( توسعه دهندگان ) برای شبیه سازی GAE
در سیستم محلی.
- پشتیبانی از محیط اجرا جاوا و پایتون.
هنگامی که یک برنامه در GAE مستقر گردید، با استفاده از نام دامنه دلخواه یا با استفاده از آدرس تجاری Google Apps قابل دستیابی است. موتور Google Apps در حال حاضر برنامههایی که در جاوا و پایتون نوشته شده است را پشتیبانی میکند و علاوه بر آن از زبانهای ماشین مجازی جاوا و چندین چارچوب تحت وب پایتون که WSGI و CGI را پشتیبانی میکنند نیز با محیط GAE سازگاری دارند.
برنامههایی که در GAE اجرا میشوند از سیستم عامل مستقل هستند یا به گفته گوگل بر روی Sand Box اجرا میشوند. این ویژگی GAE را قادر میسازد، سیستم را بهینه کند تا تقاضاهای وب، با بار ترافیکی فعلی منطبق شوند. همچنین برنامهها را قادر میسازد با امنیت بالاتری کار کنند، زیرا تنها میتوانند به کامپیوترهایی متصل شوند که آدرسهای مشخصی دارند و سرویسها را با استفاده از پروتکل Http و یا Https از پورتهای شناخته شده پاسخ دهند. از طرف دیگر برنامهها نیز به این میزان محدود شده که تنها فایلها را بخوانند. آنها حق نوشتن فایل به صورت مستقیم بر روی سیستمها را ندارند و برای دستیابی به داده، باید از ذخیره داده در Cache یا سرویس ماندگار دیگری استفاده نمایند.
GAE یک سیستم انبار داده توزیع شده دارد که از پرس و جوها و تراکنشها پشتیبانی مینماید. این انبار داده غیر رابطهای است، اما اشیاء داده یا موجودیتهایی که خصوصیات لازم را دارند، ذخیره مینماید. به همین علت در پرس و جوها میتوان از فیلتر نوع موجودیت بهره برد و آنها را به ترتیب خصوصیات مرتب نمود.
در نهایت توجه به مدل قیمتگذاری گوگل قابل توجه است. گوگل برای تشویق توسعه دهندگان در نوشتن برنامه با استفاده از GAE ، استقرار و توسعه برنامه را تا میزان مشخصی از منابع رایگان کرده است و با عبور از مقدار رایگان باید هزینه را به ازای مصرف پرداخت نمود. بر اساس جدول ارائه شده در سایت شرکت گوگل به ازای تجاوز از میزان مصرف رایگان، سیستم هزینه گذاری بر اساس تعرفههای زیر، اقدام به محاسبه حق شارژ مینماید و بدیهی است برای آگاهی از آخرین تعرفهها و کسب اطلاعات بیشتر، مراجعه به صفحه سایت شرکت گوگل توصیه میشود:
- مبلغ به ازای هر یک ساعت استفاده از CPU
معادل 0.08 دلار
- داده ذخیره شده به ازای هر گیگابایت در ماه
معادل 0.18 دلار
- پهنای باند خروجی به ازای هر گیگابایت معادل
0.12 دلار
- پهنای باند ورودی رایگان
- هزینه دریافت هر ایمیل معادل 0.0001 دلار
به منظور ذخیره اطلاعات در منبع داده پایدار، از API استفاده میگردد که به ازای تعداد تراکنشهایی که تبادل میگردد، هزینه پرداخت میشود. از آنجایی که بنا به تعداد تبادلات و نوع حافظه پایداری که استفاده میگردد، هزینه متغیر است، خواننده محترم برای رویت لیست مذکور به منبع ذکر شده، ارجاع داده میشود.
منبع سهمیه | سهمیه پیش فرض رایگان به ازای هر برنامه |
مصرف CPU | 28 ساعت به ازای هر برنامه در روز |
منبع ذخیره پایدار داده | 1 گیگابایت به ازای هر برنامه در ماه |
پهنای باند ورودی | 1 گیگابایت به ازای هر برنامه در روز |
پهنای باند خروجی | 1 گیگابایت به ازای هر برنامه در روز |
تراکنش با منبع داده Datastore | 50 هزار تراکنش برای خواندن و نوشتن به ازای هر برنامه در ماه |
تراکنش با منبع داده Blobstore | 5 گیگابایت به ازای هر برنامه در روز |
ایمیل دریافتی | 100 دریافت به ازای هر برنامه در روز |