Autofac ile ASP .NET MVC’de Dependency Injection
Selamlar,
Bugün Autofac üzerine örnekler yaparak Dependency Injection konusuna değineceğiz. Aslında Autofac en kaba tabiri ile .Net framework için geliştirilmiş olan bir IOC container’dır.
Peki IOC Container nedir? Oluşturulacak olan nesnelerin yaşam döngüsünün yönetilmesidir. Yani belirlenen koşullarda, herbir request için singelton(Tekil) şekilde ilgili nesne örneğinin bizim adımıza üretilmesidir. Bu bize kolaylık ve kodda gözle görülür bir sadelik getirir.
Boş bir Mvc proje yaratılıp, Nuget’den aşağıdaki paketler indirilerek, “Autofac” projeye dahil edilmiş olunur.
Model klasörü altına “Product” sınıfımızı oluşturalım:
Product.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace MvcAutofac.Models { public class Product { public int Id { get; set; } public string Name { get; set; } public string Category { get; set; } public decimal Price { get; set; } } } |
“Repository” adında bir klasör açalım. İçerisine aşağıdaki “IProduct” interfaceini ve bu interfaceden türeyen “ProductRepo” adlı sınıf aşağıdaki gibi oluşturalım.
Iproduct.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
using MvcAutofac.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MvcAutofac.Repository { public interface IProduct { IEnumerable<Product> GetAllProduct(); Product GetProductByID(int id); Product AddProduct(Product item); bool UpdateProduct(Product item); bool DeleteProduct(int id); } } |
ProductRepo.cs: Aşağıda size örnek olması amacı ile, ürünler ile ilgili CRUD işlemlerin yapılabileceği bir Repository oluşturulmuştur.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
using System; using System.Collections.Generic; using System.Linq; using System.Web; using MvcAutofac.Models; using System.Collections; namespace MvcAutofac.Repository { public class ProductRepo : IProduct { private List<Product> products = new List<Product>(); private int _nextId = 1; public ProductRepo() { // Örnek Datalar. AddProduct(new Product { Name = "Computer", Category = "Electronics", Price = 4000 }); AddProduct(new Product { Name = "XBOX ONE", Category = "Electronics", Price = 400 }); AddProduct(new Product { Name = "IPhone7", Category = "Phone", Price = 1000 }); } public IEnumerable<Product> GetAllProduct() { // TO DO : Tüm datayı listeleme amacı ile verir. return products; } public Product GetProductByID(int id) { // TO DO : ID'ye göre ilgili kayıt bulunur. return products.Find(p => p.Id == id); } public Product AddProduct(Product item) { if (item == null) { throw new ArgumentNullException("item"); } // TO DO : Var olan kayıt kümesine yeni bir kayıt eklenir. item.Id = _nextId++; products.Add(item); return item; } public bool UpdateProduct(Product item) { if (item == null) { throw new ArgumentNullException("item"); } // TO DO : ID'ye göre ilgili data güncellenir. int index = products.FindIndex(p => p.Id == item.Id); if (index == -1) { return false; } products.RemoveAt(index); products.Add(item); return true; } public bool DeleteProduct(int id) { // TO DO : ID'ye göre ilgili data çıkarılır. products.RemoveAll(p => p.Id == id); return true; } } } |
Şimdi örnek olması amacı ile bir Mail Validate Servisi oluşturalım:
IMailService:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MvcAutofac.Repository { public interface IMailService { string CheckdMail(string mail); } } |
MailService: Aşağıdaki MailService’inde “CheckMail()” methodu girilen email’in geçerli olup olmadığını “EmailAddressAttribute().IsValid()” ile kontrol etmektedir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.ComponentModel.DataAnnotations; namespace MvcAutofac.Repository { public class MailService:IMailService { public string CheckdMail(string mail) { var email = new EmailAddressAttribute(); return new EmailAddressAttribute().IsValid(mail)?"Geçerli":"Geçersiz"; } } } |
3. ve Son bir servis örneği olarak aşağıda girilen isimin ve soy ismin değiştirilerek, ismin baş harfinin büyük, soy ismin tamamının büyük olması sağlanmıştır.
IService.cs:
1 2 3 4 5 6 7 8 9 10 11 12 |
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace MvcAutofac.Repository { public interface IService { string Execute(string Name, string Surname); } } |
Service.cs: Girilen ismin büyük harfle başlayıp, soyadın tamamının büyük harf ile yazılması sağlanmıştır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace MvcAutofac.Repository { public class Service:IService { public string Execute(string Name, string Surname) { return Name.Substring(0, 1).ToString().ToUpper() + Name.Substring(1) + Surname.ToUpper(); } } } |
Bugün 2 farklı yol ile ilgili servisleri “Register” edeceğiz. Öncelikle gelin uzun yol ile aşağıdaki gibi ilgili servisleri register edelim.
Global.asax:
- var builder = new ContainerBuilder(): Autofac için, yeni bir container oluşturulur.
- builder.RegisterControllers(Assembly.GetExecutingAssembly()) :İlgili “builder” Controller’ların Constructer’ında Injection yapılabilmesi için “builder” Container register edilir.
- builder.RegisterType<Register_Edilecek_Sınıf>.As<Türetilen_Interface> İlgili tüm servisler bu şekilde Register edilir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using MvcAutofac.Repository; using Autofac; using Autofac.Integration.Mvc; using System.Reflection; namespace MvcAutofac { public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { var builder = new ContainerBuilder(); builder.RegisterControllers(Assembly.GetExecutingAssembly()); builder.RegisterType<Service>().As<IService>(); builder.RegisterType<MailService>().As<IMailService>(); builder.RegisterType<ProductRepo>().As<IProduct>(); IContainer container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); } } } |
Dependency Injection İle Kullanım:
Ekran çıktısı:
HomeController.cs: Aşağıda görüldüğü gibi tüm ilgili servisler “HomeController“‘ın constructer’ında çağrılmıştır. Ve Dependency Injection ile, ilgili Constructer içerisinde istenen servis Singleton olarak getirilmiştir. “Index()” methodunda “MailService()‘”inde girilen ilgili mail’in geçerli olup olunmadığına bakılır. “_Service”‘de ise girilen ismin büyük harf ile başlayıp soyadın tamamının büyük olması sağlanır. Son olarak “_ProductService” ile ilgili product ürünler listelenir. Böylece “HomeController” sınıfı içinde ilgili 3 servis yaratılmamış “Autofac” sayesinde singleton bir şekilde sisteme Constructer’da dahil edilmesi sağlanmıştır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
using MvcAutofac.Repository; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MvcAutofac.Controllers { public class HomeController : Controller { IService _Service; IMailService _MailService; IProduct _ProductService; public HomeController(IService Service,IMailService mailService,IProduct productService) { _Service = Service; _MailService = mailService; _ProductService = productService; } // GET: Home public ActionResult Index() { string isMailValid=_MailService.CheckdMail("bora@borakasmer.com"); ViewBag.Name = _Service.Execute("bora", "kaşmer"); ViewBag.IsMailValid = isMailValid; var model = _ProductService.GetAllProduct(); return View(model); } } } |
Index.cshtml: İlgili ürünlerin, mailin ve ismin gösterildiği ekran aşağıdaki gibidir. Çalıştırılan servislerin resultları bu ekrandan görülmektedir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
@model IEnumerable<MvcAutofac.Models.Product> @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <link href="~/Content/bootstrap.min.css" rel="stylesheet" /> <title>Index</title> </head> <body> <div class="container"> <div class="jumbotron"> <h2>Hoşgeldin @ViewBag.Name</h2> <div>Mail Geçerli mi:@Html.Raw(@ViewBag.IsMailValid)</div> </div> <div> <table class="table"> <thead> <tr> <th>Ad</th> <th>Kategory</th> <th>Fiyat</th> </tr> </thead> @foreach (var item in Model) { <tr class="success"> <td>@item.Name</td> <td>@item.Category</td> <td>@item.Price</td> </tr> } </table> </div> </div> </body> </html> |
Böylece Autofac ile Dependency Injection kavramına bir giriş yapmış olduk. Mvc projemizin loose coupling bir yapıda yani, geliştirmeye açık ama değiştirmiye kapalı bir hale gelmesi sağlanmıştır. Ayrıca ilgili servisler tekrar tekrar yaratılması yerine tek bir seferde “Application_Start()” methodunda singleton olarak oluşturulması sağlanmıştır. Bu da bize hem işlem maliyetinden kurtulmamıza, hem de daha sade bir kod elde etmemize olanak sağlamıştır.
Geldik bir makalenin daha sonuna. Yeni bir makalede görüşmek üzere hoşçakalın.
Source: https://autofaccn.readthedocs.io/en/latest/integration/mvc.html, https://www.codeproject.com/Articles/808894/IoC-in-ASP-NET-MVC-using-Autofac
Eline sağlık Bora..
Ben teşekkür ederim.
Yazınız için teşekkürler. Fakat ikinci yolu unuttunuz sanki :)
“Bugün 2 farklı yol ile ilgili servisleri ‘Register’ edeceğiz.”
Ben teşekkür ederim.
Evet bir yol ile eklemişim. 2.yolu atlamışım :)
“Öncelikle gelin uzun yol ile aşağıdaki gibi ilgili servisleri register edelim” dedin abi kısa yolu da bekliyoruz , emeğine sağlık , hayırlı işler
Selam Yusuf,
Öncelikle Teşekkürler.
2. Yol artık başka bir makalenin içinde geçicek. 2.yok farklı bir yapı ile yeni bir uygulama yazılarak yapılması gerektiğinden sanırım bu makaleyi daha fazla uzatmak istemedim.
İyi çalışmalar.
Merhaba hocam makale için teşekkürler.
Dediğiniz gibi uyguladım çalıştı ancak static classlarda constroctur methodu kullanmadan interfaceleri nasıl çağıracağımı bulamadım. Bu konu hakkında fikriniz varsa paylaşabilir misiniz?
Hocam proje büyüdükçe Application_Start() içerisinde çok uzun bir kod blogu olmasını engellemek için nasıl bir yapı kurabiliriz.Örneğin tek bir Interface den kalıtım alan sınıfların hepsini tek seferde resgister edebilir miyiz
Selam Hakan,
Benim bildiğim yok. O malesef büyüyor. Ama performance anlamında bir sorun olmaz. Yalnız istersen tahminimce partial sınıflar oluşturup bölümlendirebilirsin. Böylece sayfaları guruplara göre ayırmış olursun. Denemedim. Ama denemesi bedava :)
İyi akşamlar…
Merhaba,
Selam :)
Çok güzel anlatım teşekkürler.
Teşekkürler güzel faydalı bir makale olmuş.
Teşekkürler Cihan..
Teşekkürler faydalı bir makale.
Interface kullanmak zorunlu mu? Sadece Classlar ile register yapabilir miyiz?
Teşekkürler Ahmet,
Evet Interface, Autofac ve bildiğim tüm Dependency Injection araçlarda zorunlu.
İyi çalışmalar.
Bora hocam teşekkürler. Ancak anlamadığım birşey var. Burada Index.cshtml içinde Product class üyesini model olarak kullanıyorsunuz. Yeni bir IProduct sınıfı ile çalışacak olursak, bu sefer Index ‘deki modelin tipini değiştirmemiz gerekecek. Model olarak IProduct kullanabileceğimiz bir yöntem mevcut mu?
Selamlar Başar,
Bir Interface tabi ki bir model olarak kullanılamaz. Ama Beklenen Model: “class product where T :IProduct” şeklinde tanımlanabilir.
İyi çalışmalar.
Merhaba,
Autofac ile signalr I kullanınca sistemi yavaşlatıyor.
Signalr kullanan servisimin Classini ve interface ini registertype Ile birbirine bağladıktan sonra instanceperdepency i kullanıyorum ama rami sisiriyor ve sitenin yavaşlamasına sebep oluyor.sanirim connectioni kapatamadigi icin yavaslatiyor.her db islemlerinden sonra connection close yapmak istemiyirum.metodlarimi ado sorgularinada cevirmek istemiyirum.instancelifescope u denedim sorunu cozuyor ama baska hata veriyor. Bunun önüne gecebilmek için instanceperdepency den başka ne kullanabilirim.
Teşekkürler.
Teşekkürler
https://my.pcloud.com/publink/show?code=XZBWwK7ZoA8YQUw6so0UCOLxbQoeqQdedTO7
Projeyi yazmaya üşenenler için linki kullanabilirsiniz =) Saygılar
Mümemmel anlatım,teşekkürler hocam.Hocam mvc.core için olanı yok mu?
Birde mvc core da stratup a yazılan örn: services.AddSingleton(); den farkı ne bu kullanımdaki container ın.
Saygılarımla