[英]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转换格式,随意改进它):
by Mike Wasson 作者:Mike Wasson
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也有效)
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. 但是,出于几个原因,以这种方式对依赖项进行硬编码是一个坏主意。
ProductRepository
with a different implementation, you also need to modify the controller class. ProductRepository
替换为其他实现,还需要修改控制器类。 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. 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依赖项解析器的用武之地。
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接口有两种方法:
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在控制器类上查找无参数构造函数。
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容器还允许您控制对象生命周期和范围等内容。
"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.
不要为未知类型抛出异常。
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. }
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.