ASP.NET Web API 2'de Bağımlılık Ekleme

Tamamlanan Projeyi İndir

Bu öğreticide, ASP.NET Web API denetleyicinize bağımlılıkları ekleme gösterilmektedir.

Öğreticide kullanılan yazılım sürümleri

Bağımlılık Ekleme nedir?

Bağımlılık, başka bir nesnenin gerektirdiği herhangi bir nesnedir. Örneğin, veri erişimini işleyen bir depo tanımlamak yaygın bir durum olabilir. Şimdi bir örnekle gösterelim. İlk olarak bir etki alanı modeli tanımlayacağız:

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

Entity Framework kullanarak bir veritabanında öğeleri depolayan basit bir depo sınıfı aşağıdadır.

public class ProductsContext : DbContext
{
    public ProductsContext()
        : base("name=ProductsContext")
    {
    }
    public DbSet<Product> Products { get; set; }
}

public class ProductRepository : IDisposable
{
    private ProductsContext db = new ProductsContext();

    public IEnumerable<Product> GetAll()
    {
        return db.Products;
    }
    public Product GetByID(int id)
    {
        return db.Products.FirstOrDefault(p => p.Id == id);
    }
    public void Add(Product product)
    {
        db.Products.Add(product);
        db.SaveChanges();
    }

    protected void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (db != null)
            {
                db.Dispose();
                db = null;
            }
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

Şimdi varlıklar için Product GET isteklerini destekleyen bir Web API denetleyicisi tanımlayalım. (Basitlik için POST ve diğer yöntemleri bırakıyorum.) İşte ilk deneme:

public class ProductsController : ApiController
{
    // This line of code is a problem!
    ProductRepository _repository = new ProductRepository();

    public IEnumerable<Product> Get()
    {
        return _repository.GetAll();
    }

    public IHttpActionResult Get(int id)
    {
        var product = _repository.GetByID(id);
        if (product == null)
        {
            return NotFound();
        }
        return Ok(product);
    }
}

Denetleyici sınıfının öğesine bağlı ProductRepositoryolduğuna ve denetleyicinin örneği oluşturmasına izin verdiğinize ProductRepository dikkat edin. Bununla birlikte, bağımlılığı çeşitli nedenlerle bu şekilde sabit kodla uygulamak kötü bir fikirdir.

  • Öğesini farklı bir uygulamayla değiştirmek ProductRepository istiyorsanız denetleyici sınıfını da değiştirmeniz gerekir.
  • ProductRepository bağımlılıkları varsa, bunları denetleyici içinde yapılandırmanız gerekir. Birden çok denetleyiciye sahip büyük bir projede yapılandırma kodunuz projenize dağılır.
  • Denetleyici veritabanını sorgulamak için sabit kodlanmış olduğundan birim testi yapmak zordur. Birim testi için, geçerli tasarımla mümkün olmayan bir sahte veya saplama deposu kullanmanız gerekir.

Depoyu denetleyiciye ekleyerek bu sorunları giderebiliriz. İlk olarak sınıfını bir arabirimde yeniden düzenleme ProductRepository :

public interface IProductRepository
{
    IEnumerable<Product> GetAll();
    Product GetById(int id);
    void Add(Product product);
}

public class ProductRepository : IProductRepository
{
    // Implementation not shown.
}

Ardından oluşturucu parametresi olarak değerini IProductRepository sağlayın:

public class ProductsController : ApiController
{
    private IProductRepository _repository;

    public ProductsController(IProductRepository repository)  
    {
        _repository = repository;
    }

    // Other controller methods not shown.
}

Bu örnekte oluşturucu ekleme kullanılır. Ayrıca, bir ayarlayıcı yöntemi veya özelliği aracılığıyla bağımlılığı ayarladığınız ayarlayıcı ekleme özelliğini de kullanabilirsiniz.

Ancak artık bir sorun var çünkü uygulamanız denetleyiciyi doğrudan oluşturmaz. Web API'si, isteği yönlendirdiğinde denetleyiciyi oluşturur ve Web API'sinin hakkında IProductRepositoryhiçbir bilgi yoktur. Web API bağımlılık çözümleyicisi burada devreye girer.

Web API Bağımlılık Çözümleyicisi

Web API,bağımlılıkları çözümlemek için IDependencyResolver arabirimini tanımlar. Arabirimin tanımı aşağıdadır:

public interface IDependencyResolver : IDependencyScope, IDisposable
{
    IDependencyScope BeginScope();
}

public interface IDependencyScope : IDisposable
{
    object GetService(Type serviceType);
    IEnumerable<object> GetServices(Type serviceType);
}

IDependencyScope arabiriminin iki yöntemi vardır:

  • GetService bir tür örneği oluşturur.
  • GetServices , belirtilen türde bir nesne koleksiyonu oluşturur.

IDependencyResolver yöntemi IDependencyScope'ı devralır ve BeginScope yöntemini ekler. Bu öğreticinin ilerleyen bölümlerinde kapsamlar hakkında konuşacağım.

Web API'si bir denetleyici örneği oluşturduğunda, ilk olarak denetleyici türünü geçirerek IDependencyResolver.GetService'i çağırır. Denetleyiciyi oluşturmak ve bağımlılıkları çözmek için bu genişletilebilirlik kancasını kullanabilirsiniz. GetService null döndürürse, Web API'si denetleyici sınıfında parametresiz bir oluşturucu arar.

Unity Kapsayıcısı ile Bağımlılık Çözümü

Sıfırdan eksiksiz bir IDependencyResolver uygulaması yazabilseniz de, arabirim gerçekten Web API'si ile mevcut IoC kapsayıcıları arasında köprü görevi görmesi için tasarlanmıştı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".

Bu öğreticide, Microsoft Desenleri ve Uygulamaları'ndan Unity'yi kullanacağız. (Diğer popüler kitaplıklar şunlardır:Castle Windsor, Spring.Net, Autofac, Ninject, Simple Injector ve StructureMap.) Unity'yi yüklemek için NuGet Paket Yöneticisi kullanabilirsiniz. Visual Studio'daki Araçlar menüsünde NuGet Paket Yöneticisi'ı ve ardından konsol Paket Yöneticisi'ı seçin. Paket Yöneticisi Konsolu penceresinde aşağıdaki komutu yazın:

Install-Package Unity

Unity kapsayıcısını sarmalayan IDependencyResolver uygulaması aşağıdadır.

using Microsoft.Practices.Unity;
using System;
using System.Collections.Generic;
using System.Web.Http.Dependencies;

public class UnityResolver : IDependencyResolver
{
    protected IUnityContainer container;

    public UnityResolver(IUnityContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException(nameof(container));
        }
        this.container = container;
    }

    public object GetService(Type serviceType)
    {
        try
        {
            return container.Resolve(serviceType);
        }
        catch (ResolutionFailedException exception)
        {
            throw new InvalidOperationException(
                $"Unable to resolve service for type {serviceType}.",
                exception)
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        try
        {
            return container.ResolveAll(serviceType);
        }
        catch (ResolutionFailedException exception)
        {
            throw new InvalidOperationException(
                $"Unable to resolve service for type {serviceType}.",
                exception)
        }
    }

    public IDependencyScope BeginScope()
    {
        var child = container.CreateChildContainer();
        return new UnityResolver(child);
    }

    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        container.Dispose();
    }
}

Bağımlılık Çözümleyicisi'ni yapılandırma

Genel HttpConfiguration nesnesinin DependencyResolver özelliğinde bağımlılık çözümleyicisini ayarlayın.

Aşağıdaki kod, arabirimi Unity'ye kaydeder IProductRepository ve ardından bir UnityResolveroluşturur.

public static void Register(HttpConfiguration config)
{
    var container = new UnityContainer();
    container.RegisterType<IProductRepository, ProductRepository>(new HierarchicalLifetimeManager());
    config.DependencyResolver = new UnityResolver(container);

    // Other Web API configuration not shown.
}

Bağımlılık Kapsamı ve Denetleyici Ömrü

Denetleyiciler istek başına oluşturulur. IDependencyResolver, nesne yaşam sürelerini yönetmek için kapsam kavramını kullanır.

HttpConfiguration nesnesine bağlı bağımlılık çözümleyicisinin genel kapsamı vardır. Web API'si bir denetleyici oluşturduğunda BeginScope'ı çağırır. Bu yöntem, alt kapsamı temsil eden bir IDependencyScope döndürür.

Web API'si daha sonra alt kapsamda GetService'i çağırarak denetleyiciyi oluşturur. İstek tamamlandığında Web API'si alt kapsamda Dispose öğesini çağırır. Denetleyicinin bağımlılıklarını atmak için Dispose yöntemini kullanın.

BeginScope'un nasıl uygulandığı IoC kapsayıcısını temel alır. Unity için kapsam bir alt kapsayıcıya karşılık gelir:

public IDependencyScope BeginScope()
{
    var child = container.CreateChildContainer();
    return new UnityResolver(child);
}

Çoğu IoC kapsayıcısı benzer eşdeğerlere sahiptir.