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

我在这里使用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()? - java

Improve this question 我在类wait()中找到了方法Object。这是最终的,这意味着该方法不能被覆盖。有什么想法为什么是最终的? 参考方案 @Flavio-这实际上是一个很好的问题。当然,您不能覆盖它的原因是设计师将其“确定为最终”。做出此决定的一些潜在原因:您不希望人们弄乱基本类(“对象”类)上基本操作的语义。由于它是“最终的”,因…

如何在C#中的循环过程中多次更改控件的属性 - c#

我正在尝试做这样的事情:int i; while(true) { label1.Text = i.ToString(); i++; Thread.Sleep(500); } (实际上,我正在尝试做更复杂的事情,这更有意义,但这只是我的问题的一个简单示例)我期望标签的文本每1/2秒更改一次..但是它被卡住了。谢谢 参考方案 您不能让GUI线程进入睡眠状态(因为…

Web应用程序上的恶意用户是否可以操纵Web应用程序前端发送的输入(在表单数据旁边)? - java

Web应用程序上的恶意用户是否可以通过任何可能的方式来操纵Web应用程序前端发送的输入(当然,这不是在谈论FORM DATA),但是发送的请求例如当我允许他编辑他的个人资料或他的内容时,他可能会操纵ID(userId或contentId),从而可能恶意地对其他用户的内容进行邪恶?这些输入固定在网页上并且不可编辑,但用户仍然可以操纵它们吗?用户是否可能以这种方…

PHP或Java中的WCMS(适用于Java开发人员)? - java

在我公司,我们是一个Java开发人员团队。对于项目,我们需要首先选择一个Web内容管理系统(wcms)。我们可以选择一个php吗?我们是否将不得不学习php,因为我们的客户有一些特殊需求,所以我们可能不得不插入我们自己的功能...还是可以在Java中添加插件并找到连接它们的方法?这样做很普遍吗?考虑到我们的时间,我们负担不起学习php您认为我们最好转向Jav…