ASP.NET Web API 2 ' de bağımlılık ekleme

, Mike te son

Tamamlanmış projeyi indir

Bu öğreticide, ASP.NET Web API denetleyicinize bağımlılık 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ımlanması yaygındır. Bir örnekle bakalım. İ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; }
}

İşte, Entity Framework kullanarak bir veritabanındaki öğeleri depolayan basit bir depo sınıfı.

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 GET isteklerini destekleyen bir Web API denetleyicisi tanımlayalim Product . (Basitlik için GÖNDERI ve diğer yöntemleri dışarıda bırakıyorum.) İlk deneme aşağıda verilmiştir:

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 bağlı olduğuna ProductRepository ve denetleyicinin örneği oluşturmasına izin verdiğine dikkat edin ProductRepository . Bununla birlikte, birkaç nedenden dolayı bağımlılığı bu şekilde sabit olarak kodladığı için kötü bir fikir olabilir.

  • ProductRepositoryFarklı bir uygulamayla değiştirmek istiyorsanız, denetleyici sınıfını da değiştirmeniz gerekir.
  • , ProductRepository Bağımlılıkları varsa, bunları denetleyicinin içinde yapılandırmanız gerekir. Birden çok denetleyici içeren büyük bir proje için yapılandırma kodunuz projenize dağılmış hale gelir.
  • Denetleyici, veritabanını sorgulamak için sabit kodlanmış olduğundan, birim testi zordur. Birim testi için, geçerli tasarımla mümkün olmayan bir sahte veya saplama deposu kullanmanız gerekir.

Bu sorunları, depoyu denetleyiciye ekleme göre ele alabilir. İlk olarak, ProductRepository sınıfı bir arabirim olarak yeniden düzenleyin:

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

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

Sonra da IProductRepository bir oluşturucu parametresi olarak belirtin:

public class ProductsController : ApiController
{
    private IProductRepository _repository;

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

    // Other controller methods not shown.
}

Bu örnek, Oluşturucu Eklemekullanı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, uygulamanız denetleyiciyi doğrudan oluşturmadığından bir sorun oluştu. Web API 'SI isteği yönlendirdiğinizde denetleyiciyi oluşturur ve Web API 'SI hakkında hiçbir şey bilmez IProductRepository . Bu, Web API bağımlılığı Çözümleyicisinin geldiği yerdir.

Web API bağımlılığı Çözümleyicisi

Web API 'SI, bağımlılıkları çözümlemek için ıdependencyresolver arabirimini tanımlar. Arabirimin tanımı şöyledir:

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

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

IDependencyScope arabirimi iki yönteme sahiptir:

  • GetService bir türün bir örneğini oluşturur.
  • GetServices , belirtilen türde nesnelerin bir koleksiyonunu oluşturur.

Idependencyresolver yöntemi IDependencyScope devralır ve BeginScope metodunu ekler. Bu öğreticide daha sonra kapsamlar hakkında konuşacağız.

Web API 'SI bir denetleyici örneği oluşturduğunda, önce denetleyici türünü geçirerek ıdependencyresolver. GetServiceöğesini çağırır. Bu genişletilebilirlik kancasını, denetleyiciyi oluşturmak ve tüm bağımlılıkları çözmek için 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ümlemesi

Sıfırdan tamamlanmış bir ıdependencyresolver uygulaması yazabilseniz de, arabirim aslında Web API 'si ve var olan IOC kapsayıcıları arasında köprü olarak çalışacak şekilde tasarlanmıştı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".

Bu öğretici için, Microsoft düzenleri uygulamalarından Unity kullanacağız & . (Diğer popüler kitaplıklar, role Spring.net, Autofac, neklemesineve StructureMapiçerir.) Unity 'yi yüklemek için NuGet paket yöneticisini kullanabilirsiniz. Visual Studio 'daki Araçlar menüsünde, NuGet Paket Yöneticisi' ni ve ardından Paket Yöneticisi konsolu' nu seçin. Paket Yöneticisi konsolu penceresinde aşağıdaki komutu yazın:

Install-Package Unity

Bir Unity kapsayıcısını sarmalayan ıdependencyresolver 'ın bir uygulaması aşağıda verilmiştir.

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ümleyici 'yi yapılandırma

Genel HttpConfiguration nesnesinin dependencyresolver özelliğinde bağımlılık çözümleyici 'yi ayarlayın.

Aşağıdaki kod, IProductRepository arabirimi Unity ile kaydeder ve sonra bir oluşturur UnityResolver .

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 ömrünü yönetmek için bir kapsamınkavramını kullanır.

HttpConfiguration nesnesine eklenen bağımlılık Çözümleyicisi genel kapsama sahip. Web API 'SI bir denetleyici oluşturduğunda, BeginScope'u çağırır. Bu yöntem, bir alt kapsamı temsil eden bir IDependencyScope döndürür.

Daha sonra Web API 'SI, denetleyiciyi oluşturmak için alt kapsamdaki GetService 'i çağırır. İstek tamamlandığında, Web API 'SI alt kapsamda Dispose çağırır. Denetleyicinin bağımlılıklarını atmak için Dispose yöntemini kullanın.

BeginScope 'ı nasıl uygulayacağınızı IOC kapsayıcısına göre değişir. 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ının benzer eşdeğerleri vardır.