简体   繁体   English

如何在C#中注入一个类(不是接口)?

[英]How to inject a class (not an interface) in C#?

I am using Unity here. 我在这里使用Unity But probably we just need to be pointed to a right direction. 但可能我们只需指向一个正确的方向。

We know how to inject an interface: 我们知道如何注入接口:

public class AccountController:ApiController
{
    private readonly IAccountRepository _repository;

    public AccountController(IAccountRepository repository)
    {
        _repository = repository;
    }
}

with RegisterType 使用RegisterType

var container = new UnityContainer();
container.RegisterType<IAccountRepository, AccountRepository>(new HierarchicalLifetimeManager());

But in my AccountRepository , we have a class injected into the constructor. 但是在我的AccountRepository中 ,我们将一个类注入到构造函数中。

private readonly ApplicationUserManager _userManager;
public AccountRepository(ApplicationUserManager userManager)
{
    _userManager = userManager;
}

Therefore, when calling the ApiController, I still get this error: 因此,在调用ApiController时,我仍然会收到此错误:

An error occurred when trying to create a controller of type 'AccountController'. 尝试创建“AccountController”类型的控制器时发生错误。 Make sure that the controller has a parameterless public constructor. 确保控制器具有无参数的公共构造函数。

Stacktrace: 堆栈跟踪:

at System.Linq.Expressions.Expression.New(Type type) at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType) at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator) at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) System.Web.Http.Detpatcher.DefaultHttpControllerActivator.GetInstanceOrActivator上的System.Web.Http.Internal.TypeActivator.Create [TBase](Type instanceType)中的System.Linq.Expressions.Expression.New(Type type)(HttpRequestMessage请求,类型System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage请求,HttpControllerDescriptor controllerDescriptor,类型controllerType)中的controllerType,Func`1和activator)

Since I have created some other ApiControllers which worked fine, I guess it must be because our ApplicationUserManager failed to be resolved. 由于我已经创建了一些其他工作正常的ApiControllers,我想这一定是因为我们的ApplicationUserManager无法解析。

Here ApplicationUserManager is inherited from UserManager instead of an interface. 这里ApplicationUserManager继承自UserManager而不是接口。 I cannot use container.RegisterType<interface, derived_class> . 我不能使用container.RegisterType<interface, derived_class> What is the correct way to resolve it? 解决问题的正确方法是什么?


Here is the ApplicationUserManager : 这是ApplicationUserManager

public class ApplicationUserManager : UserManager<User>
{
    public ApplicationUserManager(IdentityContext identityContext)
        : base(new UserStore<User>(identityContext))
    {
    }
}

As suggested by some comments below. 正如下面的一些评论所示。 Here is the RegisterType statements: 这是RegisterType语句:

var container = new UnityContainer();
container.RegisterType<IAccountRepository, AccountRepository>(new HierarchicalLifetimeManager());
container.RegisterType<ApplicationUserManager, ApplicationUserManager>(new HierarchicalLifetimeManager());
container.RegisterType<IdentityContext, IdentityContext>(new HierarchicalLifetimeManager());

config.DependencyResolver = new UnityResolver(container);

Looks like it takes some special work to set up ASP.NET Identity . 看起来设置ASP.NET Identity需要一些特殊的工作。 I find a link here: Configure Unity DI for ASP.NET Identity . 我在这里找到一个链接: 为ASP.NET身份配置Unity DI But so far I still could not make it work. 但到目前为止,我仍然无法使其发挥作用。

The problem is that your AccountController class doesn't have a parameterless constructor , the DefaultHttpControllerActivator you see in the stack trace has no idea how to instantiate it . 问题是你的AccountController类没有无参数构造函数 ,你在堆栈跟踪中看到的DefaultHttpControllerActivator 不知道如何实例化它

Fortunately the ASP.NET WebAPI has a built-in way of delegating the controller instantiation task to a DI container. 幸运的是,ASP.NET WebAPI具有将控制器实例化任务委派给DI容器的内置方式。 This article covers your use case exactly, and even has source code to download: https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection 本文完全涵盖您的用例,甚至还有下载源代码: https//docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection

To prevent link rot I've archived the article below (converted the formatting from html as best I could, feel free to improve it): 为了防止链接腐烂我已经存档下面的文章(尽可能地从html转换格式,随意改进它):

Dependency Injection in ASP.NET Web API 2 ASP.NET Web API 2中的依赖注入

by Mike Wasson 作者:Mike Wasson

In this article 在这篇文章中

  1. Software versions used in the tutorial 本教程中使用的软件版本
  2. What is Dependency Injection? 什么是依赖注入?
  3. The Web API Dependency Resolver Web API依赖关系解析器
  4. Dependency Resolution with the Unity Container Unity容器的依赖性解析
  5. Configuring the Dependency Resolver 配置依赖关系解析器
  6. Dependency Scope and Controller Lifetime 依赖范围和控制器寿命

Download Completed Project 下载已完成的项目

This tutorial shows how to inject dependencies into your ASP.NET Web API controller. 本教程介绍如何将依赖项注入ASP.NET Web API控制器。

Software versions used in the tutorial 本教程中使用的软件版本

  • Web API 2 Web API 2
  • Unity Application Block Unity应用程序块
  • Entity Framework 6 (version 5 also works) 实体框架6(版本5也有效)

What is Dependency Injection? 什么是依赖注入?

A dependency is any object that another object requires. 依赖项是另一个对象所需的任何对象。 For example, it's common to define a repository that handles data access. 例如,定义处理数据访问的存储库是很常见的。 Let's illustrate with an example. 让我们用一个例子来说明。 First, we'll define a domain model: 首先,我们将定义一个域模型:

C# C#

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

Here is a simple repository class that stores items in a database, using Entity Framework. 这是一个简单的存储库类,使用Entity Framework将项目存储在数据库中。

C# C#

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);
    }
}

Now let's define a Web API controller that supports GET requests for Product entities. 现在让我们定义一个支持Product实体的GET请求的Web API控制器。 (I'm leaving out POST and other methods for simplicity.) Here is a first attempt: (为了简单起见,我要省略POST和其他方法。)这是第一次尝试:

C# C#

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);
    }
}

Notice that the controller class depends on ProductRepository , and we are letting the controller create the ProductRepository instance. 请注意,控制器类依赖于ProductRepository ,我们让控制器创建ProductRepository实例。 However, it's a bad idea to hard code the dependency in this way, for several reasons. 但是,出于几个原因,以这种方式对依赖项进行硬编码是一个坏主意。

  • If you want to replace ProductRepository with a different implementation, you also need to modify the controller class. 如果要将ProductRepository替换为其他实现,还需要修改控制器类。
  • If the ProductRepository has dependencies, you must configure these inside the controller. 如果ProductRepository具有依赖项,则必须在控制器内配置它们。 For a large project with multiple controllers, your configuration code becomes scattered across your project. 对于具有多个控制器的大型项目,您的配置代码将分散在整个项目中。
  • It is hard to unit test, because the controller is hard-coded to query the database. 单元测试很难,因为控制器是硬编码的,用于查询数据库。 For a unit test, you should use a mock or stub repository, which is not possible with the currect design. 对于单元测试,您应该使用模拟或存根存储库,这在当前设计中是不可能的。

We can address these problems by injecting the repository into the controller. 我们可以通过存储库注入控制器来解决这些问题。 First, refactor the ProductRepository class into an interface: 首先,将ProductRepository类重构为一个接口:

C# C#

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

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

Then provide the IProductRepository as a constructor parameter: 然后提供IProductRepository作为构造函数参数:

C# C#

public class ProductsController : ApiController
{
    private IProductRepository _repository;

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

    // Other controller methods not shown.
}

This example uses constructor injection. 此示例使用构造函数注入。 You can also use setter injection , where you set the dependency through a setter method or property. 您还可以使用setter注入 ,通过setter方法或属性设置依赖项。

But now there is a problem, because your application doesn't create the controller directly. 但现在有一个问题,因为您的应用程序不直接创建控制器。 Web API creates the controller when it routes the request, and Web API doesn't know anything about IProductRepository . Web API在路由请求时创建控制器,Web API对IProductRepository This is where the Web API dependency resolver comes in. 这是Web API依赖项解析器的用武之地。

The Web API Dependency Resolver Web API依赖关系解析器

Web API defines the IDependencyResolver interface for resolving dependencies. Web API定义了用于解析依赖关系的IDependencyResolver接口。 Here is the definition of the interface: 这是界面的定义:

C# C#

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

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

The IDependencyScope interface has two methods: IDependencyScope接口有两种方法:

  • GetService creates one instance of a type. GetService创建一个类型的实例。
  • GetServices creates a collection of objects of a specified type. GetServices创建指定类型的对象的集合。

The IDependencyResolver method inherits IDependencyScope and adds the BeginScope method. IDependencyResolver方法继承IDependencyScope并添加BeginScope方法。 I'll talk about scopes later in this tutorial. 我将在本教程后面讨论范围。

When Web API creates a controller instance, it first calls IDependencyResolver.GetService , passing in the controller type. 当Web API创建控制器实例时,它首先调用IDependencyResolver.GetService ,并传入控制器类型。 You can use this extensibility hook to create the controller, resolving any dependencies. 您可以使用此扩展性挂钩来创建控制器,解决所有依赖项。 If GetService returns null, Web API looks for a parameterless constructor on the controller class. 如果GetService返回null,则Web API在控制器类上查找无参数构造函数。

Dependency Resolution with the Unity Container Unity容器的依赖性解析

Although you could write a complete IDependencyResolver implementation from scratch, the interface is really designed to act as bridge between Web API and existing IoC containers. 虽然您可以从头开始编写完整的IDependencyResolver实现,但该接口实际上是作为Web API和现有IoC容器之间的桥梁而设计的。

An IoC container is a software component that is responsible for managing dependencies. IoC容器是负责管理依赖关系的软件组件。 You register types with the container, and then use the container to create objects. 您使用容器注册类型,然后使用容器创建对象。 The container automatically figures out the dependency relations. 容器自动计算出依赖关系。 Many IoC containers also allow you to control things like object lifetime and scope. 许多IoC容器还允许您控制对象生命周期和范围等内容。

Note 注意

"IoC" stands for "inversion of control", which is a general pattern where a framework calls into application code. “IoC”代表“控制反转”,这是框架调用应用程序代码的一般模式。 An IoC container constructs your objects for you, which "inverts" the usual flow of control. IoC容器为您构造对象,它“反转”通常的控制流。

For this tutorial, we'll use Unity from Microsoft Patterns & Practices. 在本教程中,我们将使用Microsoft模式和实践中的Unity。 (Other popular libraries include Castle Windsor, Spring.Net, Autofac, Ninject, and StructureMap.) You can use NuGet Package Manager to install Unity. (其他受欢迎的库包括Castle Windsor,Spring.Net,Autofac,Ninject和StructureMap。)您可以使用NuGet Package Manager来安装Unity。 From the Tools menu in Visual Studio, select Library Package Manager , then select Package Manager Console . 从Visual Studio的“ 工具”菜单中,选择“ 库包管理器” ,然后选择“ 包管理器控制台” In the Package Manager Console window, type the following command: 在“程序包管理器控制台”窗口中,键入以下命令:

console 安慰

 Install-Package Unity 

Here is an implementation of IDependencyResolver that wraps a Unity container. 以下是包装Unity容器的IDependencyResolver的实现。

C# C#

 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("container"); } this.container = container; } public object GetService(Type serviceType) { try { return container.Resolve(serviceType); } catch (ResolutionFailedException) { return null; } } public IEnumerable<object> GetServices(Type serviceType) { try { return container.ResolveAll(serviceType); } catch (ResolutionFailedException) { return new List<object>(); } } public IDependencyScope BeginScope() { var child = container.CreateChildContainer(); return new UnityResolver(child); } public void Dispose() { Dispose(true); } protected virtual void Dispose(bool disposing) { container.Dispose(); } } 
Note 注意

If the GetService method cannot resolve a type, it should return null . 如果GetService方法无法解析类型,则应返回null If the GetServices method cannot resolve a type, it should return an empty collection object. 如果GetServices方法无法解析类型,则应返回空集合对象。 Don't throw exceptions for unknown types. 不要为未知类型抛出异常。

Configuring the Dependency Resolver 配置依赖关系解析器

Set the dependency resolver on the DependencyResolver property of the global HttpConfiguration object. 在全局HttpConfiguration对象的DependencyResolver属性上设置依赖项解析程序。

The following code registers the IProductRepository interface with Unity and then creates a UnityResolver . 以下代码在Unity中注册IProductRepository接口,然后创建UnityResolver

C# C#

 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. } 

Dependency Scope and Controller Lifetime 依赖范围和控制器寿命

Controllers are created per request. 根据请求创建控制器。 To manage object lifetimes, IDependencyResolver uses the concept of a scope . 为了管理对象生命周期, IDependencyResolver使用范围的概念。

The dependency resolver attached to the HttpConfiguration object has global scope. 附加到HttpConfiguration对象的依赖项解析程序具有全局范围。 When Web API creates a controller, it calls BeginScope . 当Web API创建控制器时,它会调用BeginScope This method returns an IDependencyScope that represents a child scope. 此方法返回表示子范围的IDependencyScope

Web API then calls GetService on the child scope to create the controller. 然后,Web API在子作用域上调用GetService来创建控制器。 When request is complete, Web API calls Dispose on the child scope. 请求完成后,Web API将调用子作用域上的Dispose Use the Dispose method to dispose of the controller's dependencies. 使用Dispose方法处理控制器的依赖项。

How you implement BeginScope depends on the IoC container. 如何实现BeginScope取决于IoC容器。 For Unity, scope corresponds to a child container: 对于Unity,scope对应于子容器:

C# C#

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

You don't need these 2 lines since Unity will resolve it for you. 你不需要这两行,因为Unity会为你解决它。

container.RegisterType<ApplicationUserManager, ApplicationUserManager>(new HierarchicalLifetimeManager());
container.RegisterType<IdentityContext, IdentityContext>(new HierarchicalLifetimeManager());

What does the constructor for IdentityContext look like? IdentityContext的构造函数是什么样的? It most likely has a dependency you haven't registered yet. 它很可能具有您尚未注册的依赖项。 If it has multiple constructors Unity will pick the constructor with the highest number of parameters. 如果它有多个构造函数,Unity将选择具有最多参数的构造函数。

If you want a non-default lifetime manager, you'll still have to register the types with unity. 如果您想要一个非默认的生命周期管理器,您仍然需要使用unity注册这些类型。 However, Unity uses a different syntax with only one generic argument for registering a concrete type (as opposed to a type mapping): 但是,Unity使用不同的语法,只有一个通用参数来注册具体类型(而不是类型映射):

container.RegisterType<ApplicationUserManager>(new HierarchicalLifetimeManager());
container.RegisterType<IdentityContext>(new HierarchicalLifetimeManager());

Obviously I can't try out the code from here, but see if that solves your problem. 显然我不能从这里试用代码,但看看是否能解决你的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM