我在这里使用Unity。但是也许我们只需要指出一个正确的方向。
我们知道如何注入接口:
public class AccountController:ApiController
{
private readonly IAccountRepository _repository;
public AccountController(IAccountRepository repository)
{
_repository = repository;
}
}
与RegisterType
var container = new UnityContainer();
container.RegisterType<IAccountRepository, AccountRepository>(new HierarchicalLifetimeManager());
但是在我的AccountRepository中,我们将一个类注入了构造函数。
private readonly ApplicationUserManager _userManager;
public AccountRepository(ApplicationUserManager userManager)
{
_userManager = userManager;
}
因此,在调用ApiController时,仍然出现此错误:
尝试创建“ AccountController”类型的控制器时发生错误。确保控制器具有无参数的公共构造函数。
堆栈跟踪:
在System.Linq.Expressions.Expression.New(类型类型)在System.Web.Http.Internal.TypeActivator.Create [TBase](类型instanceType)在System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage请求,类型controllerType,Func`1&activator)在System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage请求,HttpControllerDescriptor controllerDescriptor,类型controllerType)
由于我创建了其他一些工作正常的ApiControllers,我想一定是因为我们的ApplicationUserManager无法解决。
在这里ApplicationUserManager继承自UserManager而不是接口。我不能使用container.RegisterType<interface, derived_class>
。解决该问题的正确方法是什么?
这是ApplicationUserManager:
public class ApplicationUserManager : UserManager<User>
{
public ApplicationUserManager(IdentityContext identityContext)
: base(new UserStore<User>(identityContext))
{
}
}
如以下一些评论所建议。这是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);
设置ASP.NET Identity似乎需要一些特殊的工作。我在这里找到一个链接:
Configure Unity DI for ASP.NET Identity。但是到目前为止,我仍然无法使其正常工作。
参考方案
问题在于您的AccountController类没有无参数的构造函数,您在堆栈跟踪中看到的DefaultHttpControllerActivator不知道如何实例化它。
幸运的是,ASP.NET WebAPI具有将控制器实例化任务委派给DI容器的内置方式。本文完全涵盖了您的用例,甚至有源代码可供下载:https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection
为了防止链接腐烂,我将以下文章存档(尽我所能将html的格式转换为最大格式,请随时进行改进):
ASP.NET Web API 2中的依赖注入
由Mike Wasson
在这篇文章中
本教程中使用的软件版本
什么是依赖注入?
Web API依赖关系解析器
Unity容器的依赖性解析
配置依赖关系解析器
依赖范围和控制器寿命
Download Completed Project
本教程说明如何将依赖项注入ASP.NET Web API控制器。
本教程中使用的软件版本
Web API 2
Unity应用程序块
实体框架6(第5版也适用)
什么是依赖注入?
依赖项是另一个对象需要的任何对象。例如,定义一个处理数据访问的存储库是很常见的。让我们用一个例子来说明。首先,我们将定义一个领域模型:
C#
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
这是一个简单的存储库类,使用Entity Framework将项目存储在数据库中。
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);
}
}
现在,让我们定义一个Web API控制器,该控制器支持Product
实体的GET请求。 (为简单起见,我省略了POST和其他方法。)这是第一次尝试:
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);
}
}
请注意,控制器类取决于ProductRepository
,我们让控制器创建ProductRepository
实例。但是,出于多种原因,以这种方式对依赖进行硬编码是一个坏主意。
如果要用其他实现替换ProductRepository
,则还需要修改控制器类。
如果ProductRepository
具有依赖项,则必须在控制器内部进行配置。对于具有多个控制器的大型项目,您的配置代码分散在整个项目中。
很难对单元进行测试,因为对控制器进行了硬编码以查询数据库。对于单元测试,应该使用模拟或存根存储库,而当前设计是不可能的。
我们可以通过将存储库注入控制器来解决这些问题。首先,将ProductRepository
类重构为接口:
C#
public interface IProductRepository
{
IEnumerable<Product> GetAll();
Product GetById(int id);
void Add(Product product);
}
public class ProductRepository : IProductRepository
{
// Implementation not shown.
}
然后提供IProductRepository
作为构造函数参数:
C#
public class ProductsController : ApiController
{
private IProductRepository _repository;
public ProductsController(IProductRepository repository)
{
_repository = repository;
}
// Other controller methods not shown.
}
本示例使用构造函数注入。您还可以使用setter注入,通过setter方法或属性设置依赖项。
但是现在出现了一个问题,因为您的应用程序没有直接创建控制器。 Web API在路由请求时创建控制器,而Web API对IProductRepository
一无所知。这是Web API依赖关系解析器出现的地方。
Web API依赖关系解析器
Web API定义了IDependencyResolver接口来解决依赖关系。这是接口的定义:
C#
public interface IDependencyResolver : IDependencyScope, IDisposable
{
IDependencyScope BeginScope();
}
public interface IDependencyScope : IDisposable
{
object GetService(Type serviceType);
IEnumerable<object> GetServices(Type serviceType);
}
IDependencyScope接口有两种方法:
GetService创建一个类型的实例。
GetServices创建指定类型的对象的集合。
IDependencyResolver方法继承了IDependencyScope并添加了BeginScope方法。我将在本教程的后面部分讨论范围。
当Web API创建控制器实例时,它首先调用IDependencyResolver.GetService,并传入控制器类型。您可以使用此可扩展性挂钩创建控制器,以解决所有依赖关系。如果GetService返回null,则Web API将在控制器类上查找无参数的构造函数。
Unity容器的依赖性解析
尽管您可以从头开始编写完整的IDependencyResolver实现,但该接口实际上是为充当Web API和现有IoC容器之间的桥梁而设计的。
IoC容器是负责管理依赖项的软件组件。您向容器注册类型,然后使用该容器创建对象。容器自动找出依赖关系。许多IoC容器还允许您控制诸如对象生存期和范围之类的东西。
注意
“ IoC”代表“控制反转”,这是框架调用应用程序代码的通用模式。 IoC容器会为您构造对象,从而“反转”通常的控制流程。
在本教程中,我们将使用Microsoft Patterns&Practices中的Unity。 (其他流行的库包括Castle Windsor,Spring.Net,Autofac,Ninject和StructureMap。)您可以使用NuGet软件包管理器来安装Unity。从Visual Studio的“工具”菜单中,选择“库软件包管理器”,然后选择“软件包管理器控制台”。在“程序包管理器控制台”窗口中,键入以下命令:
安慰
Install-Package Unity
这是包装一个Unity容器的IDependencyResolver的实现。
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();
}
}
注意
如果GetService方法无法解析类型,则应返回null。如果GetServices方法无法解析类型,则应返回一个空的集合对象。不要为未知类型抛出异常。
配置依赖关系解析器
在全局HttpConfiguration对象的DependencyResolver属性上设置依赖项解析器。
以下代码向Unity注册IProductRepository
接口,然后创建一个UnityResolver
。
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.
}
依赖范围和控制器寿命
控制器是根据请求创建的。为了管理对象的生存期,IDependencyResolver使用范围的概念。
附加到HttpConfiguration对象的依赖项解析器具有全局作用域。 Web API创建控制器时,它将调用BeginScope。此方法返回代表子范围的IDependencyScope。
然后,Web API在子作用域上调用GetService来创建控制器。请求完成后,Web API会在子范围上调用Dispose。使用Dispose方法处置控制器的依赖项。
如何实现BeginScope取决于IoC容器。对于Unity,作用域对应于一个子容器:
C#
public IDependencyScope BeginScope()
{
var child = container.CreateChildContainer();
return new UnityResolver(child);
}
在Django中聚合save()? - python我正在使用带有sqlite后端的Django,并且写入性能是一个问题。在某个阶段,我可能会毕业于“适当的”数据库,但是目前我仍然坚持使用sqlite。我认为我的写入性能问题可能与以下事实有关:我创建了大量行,并且大概每次save()一个行时,它都会锁定,解锁和同步磁盘上的数据库。如何将大量save()调用聚合到一个数据库操作中? 参考方案 编辑:不建议使用c…
为什么我不能在Java中重写方法wait()? - javaImprove this question 我在类wait()中找到了方法Object。这是最终的,这意味着该方法不能被覆盖。有什么想法为什么是最终的? 参考方案 @Flavio-这实际上是一个很好的问题。当然,您不能覆盖它的原因是设计师将其“确定为最终”。做出此决定的一些潜在原因:您不希望人们弄乱基本类(“对象”类)上基本操作的语义。由于它是“最终的”,因…
如何在C#中的循环过程中多次更改控件的属性 - c#我正在尝试做这样的事情:int i; while(true) { label1.Text = i.ToString(); i++; Thread.Sleep(500); } (实际上,我正在尝试做更复杂的事情,这更有意义,但这只是我的问题的一个简单示例)我期望标签的文本每1/2秒更改一次..但是它被卡住了。谢谢 参考方案 您不能让GUI线程进入睡眠状态(因为…
Web应用程序上的恶意用户是否可以操纵Web应用程序前端发送的输入(在表单数据旁边)? - javaWeb应用程序上的恶意用户是否可以通过任何可能的方式来操纵Web应用程序前端发送的输入(当然,这不是在谈论FORM DATA),但是发送的请求例如当我允许他编辑他的个人资料或他的内容时,他可能会操纵ID(userId或contentId),从而可能恶意地对其他用户的内容进行邪恶?这些输入固定在网页上并且不可编辑,但用户仍然可以操纵它们吗?用户是否可能以这种方…
PHP或Java中的WCMS(适用于Java开发人员)? - java在我公司,我们是一个Java开发人员团队。对于项目,我们需要首先选择一个Web内容管理系统(wcms)。我们可以选择一个php吗?我们是否将不得不学习php,因为我们的客户有一些特殊需求,所以我们可能不得不插入我们自己的功能...还是可以在Java中添加插件并找到连接它们的方法?这样做很普遍吗?考虑到我们的时间,我们负担不起学习php您认为我们最好转向Jav…