Uygulamalarımızda bağımlılık enjeksiyonunu yönetmenin birçok yolu vardır ve farklı yaklaşımların faydalarını ve sınırlamalarını anlamanın önemli olduğunu düşünüyorum. Bağımlılıklarınızı yapılandırmanın birincil yolu olarak önerilen AutofacServiceProviderFactory
yerine ASP.NET Core'da bir Autofac ContainerBuilder
kullanmak keşfedebileceğimiz yollardan biridir!
Bu makalede ASP.NET Core uygulamanızda AutofacServiceProviderFactory yerine Autofac's ContainerBuilder'ı nasıl kullanacağınızı vurguluyorum. Bağımlılık enjeksiyonuyla erişebildiğimiz diğer yaklaşımlara kıyasla bu yaklaşımla neler yapabileceğinize ve yapamayacağınıza bakacağız.
Bu, ASP.NET Core içindeki Autofac ile bağımlılık çözümlemesini araştıracağım bir serinin parçası olacak. Sayılar yayınlandıkça aşağıdaki seriye de yer vereceğim:
Bu serinin sonunda ASP.NET Core ve Blazor içindeki eklenti mimarilerini daha güvenli bir şekilde keşfedebileceksiniz ve bu, keşfetmeniz için daha da fazla içerik sağlayacak.
Adil olmak gerekirse, bölüm başlığı *neredeyse* tıklama tuzağıdır. ASP.NET Core uygulamalarınızda Autofac'ı kurmanın önerilen yolu olarak AutofacServiceProviderFactory
kullanılmasının çoğu uygulama için harika olduğunu düşünüyorum. Autofac'ı bağımlılık enjeksiyon çerçevesi olarak kullanmak isteyen geliştiricilerin büyük çoğunluğu bu şekilde pek fazla sorunla karşılaşmayacaktır.
Bize aşağıdakileri yapma olanağı sağlar:
WebApplicationBuilder
(ve şu anda mevcut olan her şeye) erişinIConfiguration
örneğine erişim ( WebApplicationBuilder
örneğinde de mevcuttur)
Ancak benim için büyük sorun: WebApplication
örneğine erişemiyoruz. C#'ta eklenti mimarileri oluşturduğumda, özellikle ASP.NET Core uygulamaları oluştururken, rotaları kaydetmek için WebApplication
örneğine erişime sahip olmayı seviyorum. Bu, eklentilerimden minimum sayıda API'yi kolaylıkla kaydetmeme olanak tanıyor; kullanışlı sözdizimini elde etmek için teknik olarak yalnızca IEndpointRouteBuilder
uygulamasına erişim gerekiyor.
Minimal olmayan API'leri bu olmadan kaydedebilir miyim? Kesinlikle. Benzer bir sözdizimi sağlamanın ve WebApplication
örneğini gerektirmenin başka bir yolu var mı? Büyük ihtimalle. Ancak BU soruna geçici bir çözüm bulmaya çalışmak yerine, ilgilendiğim bağımlılığa erişip erişemeyeceğimi görmek istedim.
Bağımlılık kapsayıcımı nasıl kuracağıma dair planı değiştirmenin zamanı gelmişti!
Keşfedecek bazı ortak noktalara sahip olmak için örnek bir uygulamaya bakalım. Önceki makaleyi okuduysanız, bu da benzer görünecektir; örnek hava durumu uygulamasının bir varyasyonu:
using Autofac; using Microsoft.AspNetCore.Mvc; // personal opinion: // I absolutely love having the entry point of my // applications being essentially: // - make my dependencies // - give me the primary dependency // - use it // - ... nothing else :) ContainerBuilder containerBuilder = new(); containerBuilder.RegisterModule<MyModule>(); using var container = containerBuilder.Build(); using var scope = container.BeginLifetimeScope(); var app = scope.Resolve<WebApplication>(); app.Run(); internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) { public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); } internal sealed class MyModule : Module { protected override void Load(ContainerBuilder containerBuilder) { containerBuilder.RegisterType<DependencyA>().SingleInstance(); containerBuilder.RegisterType<DependencyB>().SingleInstance(); containerBuilder.RegisterType<DependencyC>().SingleInstance(); containerBuilder .Register(ctx => { var builder = WebApplication.CreateBuilder(Environment.GetCommandLineArgs()); return builder; }) .SingleInstance(); containerBuilder .Register(ctx => ctx.Resolve<WebApplicationBuilder>().Configuration) .As<IConfiguration>() .SingleInstance(); containerBuilder .Register(ctx => { var builder = ctx.Resolve<WebApplicationBuilder>(); var app = builder.Build(); app.UseHttpsRedirection(); // FIXME: the problem is that the Autofac ContainerBuilder // was used to put all of these pieces together, // but we never told the web stack to use Autofac as the // service provider. // this means that the minimal API will never be able to // find services off the container. we would need to resolve // them BEFORE the API is called, like in this registration // method itself, from the context that is passed in. //DependencyA dependencyA = ctx.Resolve<DependencyA>(); // FIXME: But... What happens if something wants to take a // dependency on the WebApplication instance itself? Once the // web application has been built, there's no more adding // dependencies to it! var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; app.MapGet( "/weatherforecast", ( [FromServices] DependencyA dependencyA , [FromServices] DependencyB dependencyB , [FromServices] DependencyC dependencyC ) => { var forecast = Enumerable .Range(1, 5) .Select(index => new WeatherForecast ( DateOnly.FromDateTime(DateTime.Now.AddDays(index)), Random.Shared.Next(-20, 55), summaries[Random.Shared.Next(summaries.Length)] )) .ToArray(); return forecast; }); return app; }) .SingleInstance(); } } internal sealed class DependencyA( WebApplicationBuilder _webApplicationBuilder); internal sealed class DependencyB( WebApplication _webApplication); internal sealed class DependencyC( IConfiguration _configuration);
Bu yaklaşımın vurguladığı noktalardan biri, Autofac ContainerBuilder
sınıfını birincil bağımlılık konteynerimiz olarak kullanmanın bize giriş noktamızı şu şekilde yapılandırma fırsatını vermesidir:
Bana göre bu ideal uygulama başlatma kodudur. Neden? Çünkü ona dokunmak için asla buraya gelmene gerek yok. Durmadan. İçine ne kadar şey eklerseniz ekleyin! Ve hepsi bu, çünkü daha fazla modül yüklemek için montajları tarayabilirsiniz.
Tekrar söylüyorum bu benim kişisel tercihimdir ve herkesin hedefinin bu olması gerektiğini iddia etmeye çalışmıyorum.
Elbette kurşun geçirmez olmayan başka bir yaklaşım. O halde Autofac'ın bu kurulumunda neyi elde edemediğimizi tartışalım:
WebApplication
, Autofac'ı bağımlılık enjeksiyon çerçevesi olarak kullanacak şekilde yapılandırılmadı!WebApplication
örneğini hala bağımlılık kapsayıcısına alamıyoruz… Bu nedenle, özellikle buna erişme konusunda herhangi bir ilerleme kaydetmedik.
Ama çoğunlukla bu kadar! Bu çok kötü bir dezavantaj listesi değil, ancak Autofac ContainerBuilder yaklaşımı bizim için sihirli bir çözüm değildi. Peki bundan ne çıkardık? aynı zamanda aşağıdakilerin açıklanmasına da yardımcı olacaktır:
Yaptığımız her şeyin artıları ve eksileri! Artık ASP.NET Core'da Autofac ContainerBuilder ile ilgili sorunları gördüğümüze göre, bundan ne gibi ilerlemeler elde ettiğimize bakmanın zamanı geldi:
WebApplicationBuilder
ve IConfiguration
örneklerine hâlâ erişebiliyoruz; dolayısıyla bu, AutofacServiceProviderFactory
yaklaşımını kullanmanın karşılaştırılabilir bir avantajıdır.Bir Autofac kayıt yöntemi içerisinde minimal API'leri kaydedebildiğimizi gördük, ancak ne yazık ki konteynerden gelen bağımlılıkları doğrudan minimal API çağrısının kendisinde çözemiyoruz. Bağımlılıkların otomatik olarak çözülmesiyle rota tanımlarını işleyen aşağıdaki gibi özel bir sınıf oluşturabiliriz:
internal sealed class WeatherForecastRoutes( DependencyA _dependencyA // FIXME: still can't depend on this because // we can't get the WebApplication //, DependencyB _dependencyB , DependencyC _dependencyC) { private static readonly string[] _summaries = new[] { "Freezing", "Bracing", // ... }; public WeatherForecast[] Forecast() { var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast ( DateOnly.FromDateTime(DateTime.Now.AddDays(index)), Random.Shared.Next(-20, 55), _summaries[Random.Shared.Next(_summaries.Length)] )) .ToArray(); return forecast; } }
Bu sınıfa VE bağımlılıkların tümü aynı kaptaysa, otomatik çözümleme gerçekleşir. O zaman bu kodu çağırmanız yeterli:
var weatherForecastRoutes = ctx.Resolve<WeatherForecastRoutes>(); app.MapGet("/weatherforecast2", weatherForecastRoutes.Forecast);
Eklenti açısından bakıldığında bu hala biraz berbat çünkü kayıt kodunu bu şekilde çağırmak için tüm rota sınıflarını manuel olarak çözmemiz gerekecek - bunların hepsi bu şeylerin WebApplication
kendi erişimlerini çözememeleri gerçeğinden kaynaklanıyor misal.
Ama durun… Peki ya işleri tersine çevirirsek? Ya IRegisterRoutes
gibi bir arayüzü çözüp WebApplication
örneğine aktarabilseydik?!
// NOTE: make sure to register WeatherForecastRouteRegistrar on the // autofac container as IRegisterRoutes! internal interface IRegisterRoutes { void RegisterRoutes(WebApplication app); } internal sealed class WeatherForecastRouteRegistrar( WeatherForecastRoutes _weatherForecastRoutes) : IRegisterRoutes { public void RegisterRoutes(WebApplication app) { app.MapGet("/weatherforecast2", _weatherForecastRoutes.Forecast); } } // TODO: add this to the autofac code where the // WebApplication instance is built: foreach (var registrar in ctx.Resolve<IEnumerable<IRegisterRoutes>>()) { registrar.RegisterRoutes(app); }
Artık WebApplication
örneğinin eklentilerimiz tarafından erişilebilir olup olmadığını umursamamıza bile gerek yok! Belki de ilk versiyon o kadar sınırlayıcı değildi. Belki burada bir şeyin peşindeyizdir… Ancak bir sonraki makale bunu daha ayrıntılı olarak açıklamalıdır.
Bu makalede, normalde önerildiği gibi AutofacServiceProviderFactory
kullanmak yerine açıkça bir Autofac ContainerBuilder
kullanmayı araştırdım. Bazı benzer fayda ve dezavantajların yanı sıra dikkate alınması gereken farklı hususları da gördük. Her yol, başvurunuzda neyin peşinde olduğunuza bağlı olarak artılar ve eksiler sunabilir.
İlginç olan şu ki, eğer eklentiler üzerinde çalışmaya çalışıyorsak, eklentilerimizden WebApplication
örneğine erişmemize bile gerek kalmayabilir! Minimal API'leri önemsiyorsak, bu yine de sınırlayıcı olabilir… ancak aksi halde ilginç bir düşünce tarzına sahibiz!
Bunu yararlı bulduysanız ve daha fazla öğrenme fırsatı arıyorsanız, haftalık ücretsiz yazılım mühendisliği bültenime abone olmayı ve YouTube'daki ücretsiz videolarıma göz atmayı düşünün!