SignalR 1.x Sürümünde Bağımlılık Ekleme
Patrick Fletcher tarafından
Uyarı
Bu belgeler SignalR'nin en son sürümüne yönelik değildir. SignalR ASP.NET Core göz atın.
Bağımlılık ekleme, nesneler arasındaki sabit kodlanmış bağımlılıkları kaldırmanın bir yoludur ve test etmek (sahte nesneler kullanarak) veya çalışma zamanı davranışını değiştirmek için bir nesnenin bağımlılıklarını değiştirmeyi kolaylaştırır. Bu öğreticide SignalR hub'larında bağımlılık ekleme işleminin nasıl gerçekleştirebileceğiniz gösterilmektedir. Ayrıca SignalR ile IoC kapsayıcılarının nasıl kullanılacağını da gösterir. IoC kapsayıcısı, bağımlılık eklemeye yönelik genel bir çerçevedir.
Bağımlılık Ekleme nedir?
Bağımlılık ekleme hakkında zaten bilginiz varsa bu bölümü atlayın.
Bağımlılık ekleme (DI), nesnelerin kendi bağımlılıklarını oluşturmakla sorumlu olmadığı bir desendir. AŞAĞıDA DI'yi motive etmek için basit bir örnek verilmiştir. İletileri günlüğe kaydetmesi gereken bir nesneniz olduğunu varsayalım. Bir günlük arabirimi tanımlayabilirsiniz:
interface ILogger
{
void LogMessage(string message);
}
Nesnenizde, iletileri günlüğe kaydetmek için bir ILogger
oluşturabilirsiniz:
// Without dependency injection.
class SomeComponent
{
ILogger _logger = new FileLogger(@"C:\logs\log.txt");
public void DoSomething()
{
_logger.LogMessage("DoSomething");
}
}
Bu işe yarar, ancak en iyi tasarım değildir. öğesini başka bir ILogger
uygulamayla değiştirmek FileLogger
istiyorsanız, öğesini değiştirmeniz SomeComponent
gerekir. Birçok başka nesnenin kullandığını FileLogger
varsayarak, bunların tümünü değiştirmeniz gerekir. Ya da tekil yapmaya FileLogger
karar verirseniz, uygulama genelinde de değişiklik yapmanız gerekir.
Daha iyi bir ILogger
yaklaşım, örneğin bir oluşturucu bağımsız değişkeni kullanarak nesnesine "eklemek"tir:
// With dependency injection.
class SomeComponent
{
ILogger _logger;
// Inject ILogger into the object.
public SomeComponent(ILogger logger)
{
if (logger == null)
{
throw new NullReferenceException("logger");
}
_logger = logger;
}
public void DoSomething()
{
_logger.LogMessage("DoSomething");
}
}
Artık nesne, hangilerinin ILogger
kullanılacağını seçmekle sorumlu değildir. Buna bağlı nesneleri değiştirmeden uygulamaları değiştirebilirsiniz ILogger
.
var logger = new TraceLogger(@"C:\logs\log.etl");
var someComponent = new SomeComponent(logger);
Bu desen oluşturucu ekleme olarak adlandırılır. Başka bir desen, bağımlılığı bir ayarlayıcı yöntemi veya özelliği aracılığıyla ayarladığınız ayarlayıcı ekleme işlemidir.
SignalR'de Basit Bağımlılık Ekleme
SignalR ile Çalışmaya Başlama öğreticisinde yer alan Sohbet uygulamasını göz önünde bulundurun. Bu uygulamadan hub sınıfı aşağıdadır:
public class ChatHub : Hub
{
public void Send(string name, string message)
{
Clients.All.addMessage(name, message);
}
}
Sohbet iletilerini göndermeden önce sunucuda depolamak istediğinizi varsayalım. Bu işlevi soyutlayan bir arabirim tanımlayabilir ve arabirimi sınıfına eklemek ChatHub
için DI kullanabilirsiniz.
public interface IChatRepository
{
void Add(string name, string message);
// Other methods not shown.
}
public class ChatHub : Hub
{
private IChatRepository _repository;
public ChatHub(IChatRepository repository)
{
_repository = repository;
}
public void Send(string name, string message)
{
_repository.Add(name, message);
Clients.All.addMessage(name, message);
}
Tek sorun bir SignalR uygulamasının hub'ları doğrudan oluşturmamasıdır; SignalR bunları sizin için oluşturur. SignalR varsayılan olarak bir hub sınıfının parametresiz oluşturucuya sahip olmasını bekler. Ancak, hub örnekleri oluşturmak için bir işlevi kolayca kaydedebilir ve bu işlevi KULLANARAK DI gerçekleştirebilirsiniz. GlobalHost.DependencyResolver.Register'ı çağırarak işlevi kaydedin.
protected void Application_Start()
{
GlobalHost.DependencyResolver.Register(
typeof(ChatHub),
() => new ChatHub(new ChatMessageRepository()));
RouteTable.Routes.MapHubs();
// ...
}
Şimdi SignalR, bir ChatHub
örnek oluşturması gerektiğinde bu anonim işlevi çağırır.
IoC Kapsayıcıları
Önceki kod basit durumlar için uygundur. Ama yine de şunu yazmanız gerekiyordu:
... new ChatHub(new ChatMessageRepository()) ...
Birçok bağımlılığı olan karmaşık bir uygulamada, bu "kablolama" kodunun çoğunu yazmanız gerekebilir. Özellikle bağımlılıklar iç içe yerleştirilmişse bu kodun bakımı zor olabilir. Birim testi de zordur.
Çözümlerden biri bir IoC kapsayıcısı kullanmaktır. IoC kapsayıcısı, bağımlılıkları yönetmekle sorumlu olan bir yazılım bileşenidir. Türleri kapsayıcıya kaydeder ve sonra nesneleri oluşturmak için kapsayıcıyı kullanırsınız. Kapsayıcı, bağımlılık ilişkilerini otomatik olarak anlar. Birçok IoC kapsayıcısı, nesne ömrü ve kapsam gibi öğeleri denetlemenize de olanak sağlar.
Not
"IoC", bir çerçevenin uygulama koduna çağırdığı genel bir desen olan "denetimin ters çevrilmesi" anlamına gelir. IoC kapsayıcısı nesnelerinizi sizin için oluşturur ve bu da normal denetim akışını "tersine çevirir".
SignalR'da IoC Kapsayıcılarını Kullanma
Sohbet uygulaması büyük olasılıkla IoC kapsayıcısından yararlanamayacak kadar basittir. Bunun yerine StockTicker örneğine göz atalım.
StockTicker örneği iki ana sınıfı tanımlar:
StockTickerHub
: İstemci bağlantılarını yöneten hub sınıfı.StockTicker
: Hisse senedi fiyatlarını tutan ve bunları düzenli aralıklarla güncelleştiren bir tekil.
StockTickerHub
tekil StockTicker
için bir başvuru tutarken StockTicker
, için IHubConnectionContext'e bir başvuru tutar StockTickerHub
. Örneklerle StockTickerHub
iletişim kurmak için bu arabirimi kullanır. (Daha fazla bilgi için bkz. ASP.NET SignalR ile Sunucu Yayını.)
Bu bağımlılıkları biraz çözmek için bir IoC kapsayıcısı kullanabiliriz. İlk olarak ve StockTicker
sınıflarını StockTickerHub
basitleştirelim. Aşağıdaki kodda ihtiyacımız olmayan bölümleri açıklama satırı yaptım.
parametresiz oluşturucuyu içinden StockTicker
kaldırın. Bunun yerine hub'ı oluşturmak için her zaman DI kullanacağız.
[HubName("stockTicker")]
public class StockTickerHub : Hub
{
private readonly StockTicker _stockTicker;
//public StockTickerHub() : this(StockTicker.Instance) { }
public StockTickerHub(StockTicker stockTicker)
{
if (stockTicker == null)
{
throw new ArgumentNullException("stockTicker");
}
_stockTicker = stockTicker;
}
// ...
StockTicker için tekil örneği kaldırın. Daha sonra StockTicker ömrünü denetlemek için IoC kapsayıcısını kullanacağız. Ayrıca oluşturucuyu genel yapın.
public class StockTicker
{
//private readonly static Lazy<StockTicker> _instance = new Lazy<StockTicker>(
// () => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients));
// Important! Make this constructor public.
public StockTicker(IHubConnectionContext clients)
{
if (clients == null)
{
throw new ArgumentNullException("clients");
}
Clients = clients;
LoadDefaultStocks();
}
//public static StockTicker Instance
//{
// get
// {
// return _instance.Value;
// }
//}
Ardından için bir arabirim StockTicker
oluşturarak kodu yeniden düzenleyebiliriz. sınıfını StockTicker
birbirinden StockTickerHub
ayırmak için bu arabirimi kullanacağız.
Visual Studio bu tür yeniden düzenlemeyi kolaylaştırır. StockTicker.cs dosyasını açın, sınıf bildirimine StockTicker
sağ tıklayın ve Yeniden düzenle ... öğesini seçin. Arabirimi Ayıkla...
Arabirimi Ayıkla iletişim kutusunda Tümünü Seç'e tıklayın. Diğer varsayılan değerleri bırakın. Tamam'a tıklayın.
Visual Studio adlı IStockTicker
yeni bir arabirim oluşturur ve ayrıca'dan IStockTicker
türetilen şekilde değişirStockTicker
.
IStockTicker.cs dosyasını açın ve arabirimini public olarak değiştirin.
public interface IStockTicker
{
void CloseMarket();
IEnumerable<Stock> GetAllStocks();
MarketState MarketState { get; }
void OpenMarket();
void Reset();
}
sınıfında öğesinin StockTickerHub
iki örneğini StockTicker
olarak IStockTicker
değiştirin:
[HubName("stockTicker")]
public class StockTickerHub : Hub
{
private readonly IStockTicker _stockTicker;
public StockTickerHub(IStockTicker stockTicker)
{
if (stockTicker == null)
{
throw new ArgumentNullException("stockTicker");
}
_stockTicker = stockTicker;
}
IStockTicker
Arabirim oluşturmak kesinlikle gerekli değildir, ancak DI'nin uygulamanızdaki bileşenler arasındaki bağlantının azaltılmasına nasıl yardımcı olabileceğini göstermek istedim.
Ninject Kitaplığı'nı ekleme
.NET için birçok açık kaynak IoC kapsayıcısı vardır. Bu öğreticide Ninject'i kullanacağım. (Diğer popüler kitaplıklar Castle Windsor, Spring.Net, Autofac, Unity ve StructureMap'tir.)
Ninject kitaplığını yüklemek için NuGet Paket Yöneticisi'ni kullanın. Visual Studio'da Araçlar menüsünde NuGet Paket Yöneticisi Paket Yöneticisi>Konsolu'nu seçin. Paket Yöneticisi Konsolu penceresinde aşağıdaki komutu girin:
Install-Package Ninject -Version 3.0.1.10
SignalR Bağımlılık Çözümleyicisi'ni değiştirme
SignalR içinde Ninject kullanmak için DefaultDependencyResolver'dan türetilen bir sınıf oluşturun.
internal class NinjectSignalRDependencyResolver : DefaultDependencyResolver
{
private readonly IKernel _kernel;
public NinjectSignalRDependencyResolver(IKernel kernel)
{
_kernel = kernel;
}
public override object GetService(Type serviceType)
{
return _kernel.TryGet(serviceType) ?? base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
return _kernel.GetAll(serviceType).Concat(base.GetServices(serviceType));
}
}
Bu sınıf, DefaultDependencyResolver'ınGetService ve GetServices yöntemlerini geçersiz kılar. SignalR, hub örnekleri ve SignalR tarafından dahili olarak kullanılan çeşitli hizmetler dahil olmak üzere çalışma zamanında çeşitli nesneler oluşturmak için bu yöntemleri çağırır.
- GetService yöntemi, bir türün tek bir örneğini oluşturur. Ninject çekirdeğinin TryGet yöntemini çağırmak için bu yöntemi geçersiz kılın. Bu yöntem null döndürürse varsayılan çözümleyiciye geri dönün.
- GetServices yöntemi, belirtilen türde bir nesne koleksiyonu oluşturur. Ninject'ten gelen sonuçları varsayılan çözümleyicinin sonuçlarıyla birleştirmek için bu yöntemi geçersiz kılın.
Ninject Bağlamalarını Yapılandırma
Şimdi tür bağlamalarını bildirmek için Ninject kullanacağız.
RegisterHubs.cs dosyasını açın. yönteminde RegisterHubs.Start
Ninject'in çekirdeği çağırdığı Ninject kapsayıcısını oluşturun.
var kernel = new StandardKernel();
Özel bağımlılık çözümleyicimizin bir örneğini oluşturun:
var resolver = new NinjectSignalRDependencyResolver(kernel);
için IStockTicker
aşağıdaki gibi bir bağlama oluşturun:
kernel.Bind<IStockTicker>()
.To<Microsoft.AspNet.SignalR.StockTicker.StockTicker>() // Bind to StockTicker.
.InSingletonScope(); // Make it a singleton object.
Bu kod iki şey söylüyor. İlk olarak, uygulamanın her IStockTicker
ihtiyacı olduğunda, çekirdeğin bir örneği oluşturması StockTicker
gerekir. İkinci olarak StockTicker
, sınıfı tekil bir nesne olarak oluşturulmalıdır. Ninject nesnenin bir örneğini oluşturur ve her istek için aynı örneği döndürür.
IHubConnectionContext için aşağıdaki gibi bir bağlama oluşturun:
kernel.Bind<IHubConnectionContext>().ToMethod(context =>
resolver.Resolve<IConnectionManager>().GetHubContext<StockTickerHub>().Clients
).WhenInjectedInto<IStockTicker>();
Bu kod, IHubConnection döndüren anonim bir işlev oluşturur. WhenInjectedInto yöntemi Ninject'e bu işlevi yalnızca örnek oluştururken IStockTicker
kullanmasını söyler. Bunun nedeni SignalR'nin IHubConnectionContext örneklerini dahili olarak oluşturması ve SignalR'nin bunları oluşturma biçimini geçersiz kılmak istememizdir. Bu işlev yalnızca sınıfımız StockTicker
için geçerlidir.
Bağımlılık çözümleyicisini MapHubs yöntemine geçirin:
RouteTable.Routes.MapHubs(config);
Artık SignalR, varsayılan çözümleyici yerine MapHubs'da belirtilen çözümleyiciyi kullanacaktır.
için tam kod listesi aşağıdadır RegisterHubs.Start
.
public static class RegisterHubs
{
public static void Start()
{
var kernel = new StandardKernel();
var resolver = new NinjectSignalRDependencyResolver(kernel);
kernel.Bind<IStockTicker>()
.To<Microsoft.AspNet.SignalR.StockTicker.StockTicker>()
.InSingletonScope();
kernel.Bind<IHubConnectionContext>().ToMethod(context =>
resolver.Resolve<IConnectionManager>().
GetHubContext<StockTickerHub>().Clients
).WhenInjectedInto<IStockTicker>();
var config = new HubConfiguration()
{
Resolver = resolver
};
// Register the default hubs route: ~/signalr/hubs
RouteTable.Routes.MapHubs(config);
}
}
StockTicker uygulamasını Visual Studio'da çalıştırmak için F5 tuşuna basın. Tarayıcı penceresinde adresine http://localhost:*port*/SignalR.Sample/StockTicker.html
gidin.
Uygulama, öncekiyle tam olarak aynı işlevselliğe sahiptir. (Açıklama için bkz. ASP.NET SignalR ile Sunucu Yayını.) Davranışı değiştirmedik; yalnızca kodu test etme, koruma ve geliştirmeyi kolaylaştırdı.
Geri Bildirim
https://aka.ms/ContentUserFeedback.
Çok yakında: 2024 boyunca, içerik için geri bildirim mekanizması olarak GitHub Sorunları’nı kullanımdan kaldıracak ve yeni bir geri bildirim sistemiyle değiştireceğiz. Daha fazla bilgi için bkz.Gönderin ve geri bildirimi görüntüleyin