SignalR’da Bağımlılık Ekleme

, Mike Ison, Patrick fleti

Warning

Bu belge, SignalR 'nin en son sürümü için değildir. ASP.NET Core SignalR'ye göz atın.

Bu konuda kullanılan yazılım sürümleri

Bu konunun önceki sürümleri

SignalR 'nin önceki sürümleri hakkında daha fazla bilgi için bkz. SignalR daha eski sürümleri.

Sorular ve açıklamalar

Lütfen bu öğreticiyi nasıl beğentireceğiniz ve sayfanın en altındaki açıklamalarda İyileştiğimiz hakkında geri bildirimde bulunun. Öğreticiyle doğrudan ilgili olmayan sorularınız varsa, bunları ASP.NET SignalR forumuna veya StackOverflow.com'e gönderebilirsiniz.

Bağımlılık ekleme, nesneler arasında sabit kodlanmış bağımlılıkları kaldırmanın bir yoludur. bu sayede, bir nesnenin bağımlılıklarını test etmek için (sahte nesneleri kullanarak) veya çalışma zamanı davranışını değiştirmek kolaylaşır. Bu öğreticide, SignalR hub 'larda bağımlılık ekleme işlemlerinin nasıl gerçekleştirileceği gösterilmektedir. Ayrıca, SignalR ile IOC kapsayıcılarını nasıl kullanacağınızı gösterir. Bir IOC kapsayıcısı, bağımlılık ekleme için genel bir çerçevedir.

Bağımlılık ekleme nedir?

Bağımlılık ekleme konusunda zaten bilgi sahibiyseniz bu bölümü atlayın.

Bağımlılık ekleme (dı), nesnelerin kendi bağımlılıklarını oluşturmadan sorumlu olmayan bir modeldir. İşte, bu, bir değer olarak motive etmek için basit bir örnektir. İletileri günlüğe kaydetmek için gereken bir nesneniz olduğunu varsayalım. Günlüğe kaydetme arabirimi tanımlayabilirsiniz:

interface ILogger 
{
    void LogMessage(string message);
}

Nesneniz, 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. FileLogger başka bir ILogger uygulamasıyla değiştirmek istiyorsanız SomeComponentdeğiştirmeniz gerekir. Birçok farklı nesnenin FileLoggerkullanduymasını, bunların tümünü değiştirmeniz gerekir. Ya da tek bir FileLogger yapmaya karar verirseniz, uygulama genelinde de değişiklikler yapmanız gerekir.

Daha iyi bir yaklaşım, nesne içine bir ILogger eklemek — örneğin, bir Oluşturucu bağımsız değişkeni kullanarak:

// 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, hangi ILogger kullanacağınızı seçmeden nesne sorumlu değildir. Kendisine bağımlı nesneleri değiştirmeden ILogger uygulamaları geçebilirsiniz.

var logger = new TraceLogger(@"C:\logs\log.etl");
var someComponent = new SomeComponent(logger);

Bu düzene Oluşturucu Eklemeadı verilir. Başka bir model, bir ayarlayıcı yöntemi veya özelliği aracılığıyla bağımlılığı ayarladığınız ayarlayıcı ekleme işlemi olur.

SignalR 'de basit bağımlılık ekleme

SignalR Ile çalışmayabaşlama öğreticiden sohbet uygulamasını göz önünde bulundurun. Bu uygulamadaki Merkez sınıfı aşağıda verilmiştir:

public class ChatHub : Hub
{
    public void Send(string name, string message)
    {
        Clients.All.addMessage(name, message);
    }
}

Göndermeden önce sohbet iletilerini sunucuda depolamak istediğinizi varsayalım. Bu işlevi soyutlayan bir arabirim tanımlayabilir ve arabirimi ChatHub sınıfına eklemek 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 doğrudan hub oluşturmamalıdır; SignalR bunları sizin için oluşturur. Varsayılan olarak, SignalR bir hub sınıfının parametresiz bir oluşturucuya sahip olmasını bekler. Ancak, hub örnekleri oluşturmak için bir işlevi kolayca kaydedebilir ve bu işlevi, DI işlemini gerçekleştirmek için kullanabilirsiniz. Globalhost. DependencyResolver. Registerçağırarak işlevi kaydedin.

public void Configuration(IAppBuilder app)
{
    GlobalHost.DependencyResolver.Register(
        typeof(ChatHub), 
        () => new ChatHub(new ChatMessageRepository()));

    App.MapSignalR();

    // ...
}

Artık ChatHub bir örnek oluşturması gerektiğinde SignalR bu anonim işlevi çağıracaktır.

IoC kapsayıcıları

Önceki kod, basit durumlar için uygundur. Ancak 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 ise, bu kod bakımını yapmak zor olabilir. Ayrıca birim testi de zordur.

Bir çözüm bir IOC kapsayıcısı kullanmaktır. Bir IOC kapsayıcısı, bağımlılıkları yönetmekten sorumlu bir yazılım bileşenidir. Türleri kapsayıcıya kaydeder ve sonra nesneleri oluşturmak için kapsayıcıyı kullanın. Kapsayıcı, bağımlılık ilişkilerini otomatik olarak belirler. Birçok IOC kapsayıcısı ayrıca nesne ömrü ve kapsamı gibi şeyleri denetlemenize olanak tanır.

Note

"IoC", bir çerçevenin uygulama koduna çağırdığı genel bir model olan "denetimin INVERSION" anlamına gelir. Bir IOC kapsayıcısı, nesnelerinizi sizin için oluşturur, bu da normal denetim akışını "tersine çevirir".

SignalR 'de IOC kapsayıcıları kullanma

Sohbet uygulaması, bir IOC kapsayıcısından faydalanmak için büyük olasılıkla çok basittir. Bunun yerine StockTicker örneğine göz atalım.

StockTicker örneği iki ana sınıfı tanımlar:

  • StockTickerHub: istemci bağlantılarını yöneten hub sınıfı.
  • StockTicker: stok fiyatlarını tutan ve düzenli aralıklarla güncelleştiren bir tek.

StockTickerHub, StockTicker tekil öğesine bir başvuru tutar StockTicker ancak StockTickerHubiçin IHV Ubconnectioncontext 'e bir başvuru barındırır. StockTickerHub örneklerle 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ıklara bir bit eklemek için bir IOC kapsayıcısı kullanabiliriz. İlk olarak, StockTickerHub ve StockTicker sınıflarını basitleşelim. Aşağıdaki kodda, ihtiyaç duyduğumuz parçalar hakkında yorum yaptı.

Parametresiz oluşturucuyu StockTickerHubkaldırın. Bunun yerine, her zaman, hub 'ı oluşturmak için DI 'yi 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 Singleton örneğini kaldırın. Daha sonra, StockTicker ömrünü denetlemek için IoC kapsayıcısını kullanacağız. Ayrıca, oluşturucuyu genel hale getirin.

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<dynamic> clients)
    {
        if (clients == null)
        {
            throw new ArgumentNullException("clients");
        }

        Clients = clients;
        LoadDefaultStocks();
    }

    //public static StockTicker Instance
    //{
    //    get
    //    {
    //        return _instance.Value;
    //    }
    //}

Daha sonra, StockTickeriçin bir arabirim oluşturarak kodu yeniden düzenleyebilirsiniz. Bu arabirimi, StockTickerHub StockTicker sınıfından ayırmak için kullanacağız.

Visual Studio bu tür yeniden düzenleme kolaylaştırır. StockTicker.cs dosyasını açın, StockTicker sınıfı bildirimine sağ tıklayın ve yeniden Düzenle ... seçeneğini belirleyin. 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 IStockTickeradlı yeni bir arabirim oluşturur ve ayrıca IStockTickertüretmeye StockTicker değişir.

IStockTicker.cs dosyasını açın ve arabirimi genel' e değiştirin.

public interface IStockTicker
{
    void CloseMarket();
    IEnumerable<Stock> GetAllStocks();
    MarketState MarketState { get; }
    void OpenMarket();
    void Reset();
}

StockTickerHub sınıfında, iki StockTicker örneğini IStockTickerolarak 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 arabirimi oluşturmak kesinlikle gerekli değildir, ancak uygulamanızın uygulamanızdaki bileşenler arasında Eşleneni azaltmaya nasıl yardımcı olduğunu göstermek istiyorum.

Nekleme kitaplığını ekleme

.NET için çok sayıda açık kaynaklı IFC kapsayıcısı vardır. Bu öğretici için Nekleme'yi kullanacağız. (Diğer popüler kitaplıklar, role Spring.net, Autofac, Unityve StructureMapiçerir.)

Nekleme kitaplığınıyüklemek Için NuGet Paket Yöneticisi 'ni kullanın. Visual Studio 'da, Araçlar menüsünden 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ümleyicisini değiştirme

SignalR içinde Nekleme kullanmak için, Defaultdependencyresolver'dan türeyen 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'ın GetService ve GetServices yöntemlerini geçersiz kılar. SignalR, hub örnekleri ve ayrıca 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. Nekleme ç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 nesnelerin bir koleksiyonunu oluşturur. Neklemesine ait sonuçları varsayılan çözümleyiciyle sonuçlarla birleştirmek için bu yöntemi geçersiz kılın.

Nekleme bağlamalarını yapılandırma

Şimdi tür bağlamaları bildirmek için Nekleme kullanacağız.

Uygulamanızın Startup.cs sınıfını açın (readme.txt' deki paket yönergelerine göre el ile oluşturduğunuz veya projenize kimlik doğrulaması eklenerek oluşturulan). Startup.Configuration yönteminde Nekleme kapsayıcısını oluşturun ve Nekleme, çekirdeğiçağırır.

var kernel = new StandardKernel();

Özel bağımlılık çözümleyicimizin bir örneğini oluşturun:

var resolver = new NinjectSignalRDependencyResolver(kernel);

IStockTicker için aşağıdaki şekilde 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 şeyi söyleyerek. İlk olarak, uygulama bir IStockTickerihtiyaç duyduğunda, çekirdek bir StockTickerörneği oluşturacaktır. İkincisi, StockTicker sınıfı tek bir nesne olarak oluşturulmuş olmalıdır. Nekleme nesnenin bir örneğini oluşturur ve her istek için aynı örneği döndürür.

Iubconnectioncontext için aşağıdaki şekilde bir bağlama oluşturun:

kernel.Bind(typeof(IHubConnectionContext<dynamic>)).ToMethod(context =>
                    resolver.Resolve<IConnectionManager>().GetHubContext<StockTickerHub>().Clients
                     ).WhenInjectedInto<IStockTicker>();

Bu kod, bir Ihubconnectiondöndüren anonim bir işlev oluşturur. Whenınjectedinto yöntemi, neklemesine bu işlevi yalnızca IStockTicker örnekleri oluştururken kullanmasını söyler. Bunun nedeni, SignalR 'nin, dahili olarak IHV Ubconnectioncontext örnekleri oluşturduğunu ve SignalR 'nin bunları nasıl oluşturduğunu geçersiz kılmak istemedik. Bu işlev yalnızca StockTicker sınıfımız için geçerlidir.

Bir hub yapılandırması ekleyerek bağımlılık çözümleyicisini Mapsignalr yöntemine geçirin:

var config = new HubConfiguration();
config.Resolver = resolver;
Microsoft.AspNet.SignalR.StockTicker.Startup.ConfigureSignalR(app, config);

Örnek başlangıç sınıfındaki Startup. ConfigureSignalR yöntemini yeni parametresiyle güncelleştirin:

public static void ConfigureSignalR(IAppBuilder app, HubConfiguration config)
{
    app.MapSignalR(config);
}

Şimdi SignalR, varsayılan çözümleyici yerine Mapsignalriçinde belirtilen çözümleyici 'yi kullanacaktır.

Startup.Configurationiçin kod listesinin tamamı aşağıda verilmiştir.

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888

        var kernel = new StandardKernel();
        var resolver = new NinjectSignalRDependencyResolver(kernel);

        kernel.Bind<IStockTicker>()
            .To<Microsoft.AspNet.SignalR.StockTicker.StockTicker>()  // Bind to StockTicker.
            .InSingletonScope();  // Make it a singleton object.

        kernel.Bind(typeof(IHubConnectionContext<dynamic>)).ToMethod(context =>
                resolver.Resolve<IConnectionManager>().GetHubContext<StockTickerHub>().Clients
                    ).WhenInjectedInto<IStockTicker>();

        var config = new HubConfiguration();
        config.Resolver = resolver;
        Microsoft.AspNet.SignalR.StockTicker.Startup.ConfigureSignalR(app, config);
    }
}

Visual Studio 'da StockTicker uygulamasını çalıştırmak için F5 'e basın. Tarayıcı penceresinde http://localhost:*port*/SignalR.Sample/StockTicker.html' a gidin.

Uygulama daha önce olduğu gibi tamamen aynı işlevselliğe sahiptir. (Bir açıklama için bkz. ASP.NET SignalR Ile sunucu yayını.) Davranışı değiştirmedik; kodu test etmek, sürdürmek ve geliştirmek için daha kolay hale getirilir.