Abp vNext 依赖注入

abp,vnext,依赖,注入 · 浏览次数 : 19

小编点评

# DefaultTessAppService 默认TessAppService 是一个实现接口 ITestAppService 的类,用于配置服务。 默认TessAppService 的构造方法接收服务,并配置默认服务。 默认TessAppService 的`AddType` 方法用于配置类型,并根据接口类型和实现类名称,添加实现类。 默认TessAppService 的`GetLifeTimeOrNull` 方法用于获取接口的默认生命周期,并根据接口类型和实现类名称,获取对应的生命周期。 默认TessAppService 的`GetExposedServiceTypes` 方法用于获取接口的 exposed 服务类型,并根据接口类型和实现类名称,添加对应的服务类型。 默认TessAppService 的`SetExposedServiceTypes` 方法用于设置接口的 exposed 服务类型。 默认TessAppService 的`ConfigureServicesAsync` 方法用于配置服务,并根据接口类型和实现类名称,添加对应的服务。 默认TessAppService 的`GetDefaultServices` 方法用于获取接口的默认服务类型,并根据接口类型和实现类名称,获取对应的服务类型。 默认TessAppService 的`SetDefaultServices` 方法用于设置接口的默认服务类型。 默认TessAppService 的`ConfigureServicesAsync` 方法用于配置服务,并根据接口类型和实现类名称,添加对应的服务。

正文

文章目录

介绍

ABP的依赖注入系统是基于Microsoft的依赖注入扩展库(Microsoft.Extensions.DependencyInjection nuget包)开发的。所以我们采用dotnet自带的注入方式也是支持的

  • 由于ABP是一个模块化框架,因此每个模块都定义它自己的服务并在它自己的单独模块类中通过依赖注入进行注册.例:
public class BlogModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        //在此处注入依赖项
        // dotnet自带依赖注入方式也是支持的
        context.services.AddTransient
        context.services.AddScoped
        context.services.AddSingleton
    }
}

Autofac

Autofac 是.Net世界中最常用的依赖注入框架之一. 相比.Net Core标准的依赖注入库, 它提供了更多高级特性, 比如动态代理和属性注入.

集成

1.安装 Volo.Abp.Autofac nuget 包到你的项目 (对于一个多项目应用程序, 建议安装到可执行项目或者Web项目中.)
2.模块添加 AbpAutofacModule 依赖:

    [DependsOn(typeof(AbpAutofacModule))]
    public class MyModule : AbpModule
    {
        //...
    }
}

3.配置 AbpApplicationCreationOptions 用 Autofac 替换默认的依赖注入服务. 根据应用程序类型, 情况有所不同

  • ASP.NET Core 应用程序
public class Program
{
    public static int Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    internal static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            })
            .UseAutofac(); //Integrate Autofac!
}
  • 控制台应用程序
namespace AbpConsoleDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var application = AbpApplicationFactory.Create<AppModule>(options =>
            {
                options.UseAutofac(); //Autofac integration
            }))
            {
                //...
            }
        }
    }
}

依照约定的注册

如果实现这些接口,则会自动将类注册到依赖注入:

  • ITransientDependency 注册为transient生命周期.
  • ISingletonDependency 注册为singleton生命周期.
  • IScopedDependency 注册为scoped生命周期.

默认特定类型

一些特定类型会默认注册到依赖注入.例子:

  • 模块类注册为singleton.
  • MVC控制器(继承Controller或AbpController)被注册为transient.
  • MVC页面模型(继承PageModel或AbpPageModel)被注册为transient.
  • MVC视图组件(继承ViewComponent或AbpViewComponent)被注册为transient.
  • 应用程序服务(实现IApplicationService接口或继承ApplicationService类)注册为transient.
  • 存储库(实现IRepository接口)注册为transient.
  • 域服务(实现IDomainService接口)注册为transient.

手动注册

在某些情况下,你可能需要向IServiceCollection手动注册服务,尤其是在需要使用自定义工厂方法或singleton实例时.在这种情况下,你可以像Microsoft文档描述的那样直接添加服务.

public class BlogModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        context.services.AddTransient<ITestMicrosoftManager, TestMicrosoftManager>();
    }
}

如何使用

构造函数注入

  • 构造方法注入是将依赖项注入类的首选方式

属性注入

  • Microsoft依赖注入库不支持属性注入.但是,ABP可以与第三方DI提供商(例如Autofac)集成,以实现属性注入。
  • 属性注入依赖项通常被视为可选依赖项.这意味着没有它们,服务也可以正常工作.Logger就是这样的依赖项,MyService可以继续工作而无需日志记录.
public class MyService : ITransientDependency
{
    public ILogger<MyService> Logger { get; set; }

    public MyService()
    {
        Logger = NullLogger<MyService>.Instance;
    }

    public void DoSomething()
    {
        //...使用 Logger 写日志...
    }
}

IServiceProvider

直接从IServiceProvider解析服务.在这种情况下,你可以将IServiceProvider注入到你的类并使用

public class MyService : ITransientDependency
{
    private readonly IServiceProvider _serviceProvider;

    public MyService(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void DoSomething()
    {
        var taxCalculator = _serviceProvider.GetService<ITaxCalculator>();
        //...
    }
}

服务替换

在某些情况下,需要替换某些接口的实现.

  • ITestManager有一个默认实现DefaultManager,但是我现在想替换成TestReplaceManager,该如何操作呢?

原生dotnet方式替换

services.Replace(ServiceDescriptor.Transient<ITestManager, TestReplaceManager>());

Abp支持

  • 加上Dependency特性标签
  • 加上ExposeServices特性标签
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(ITestManager))]
public class TestReplaceManager : ITestManager, ITransientDependency
{
	public void Print()
	{
		Console.WriteLine("TestReplaceManager");
	}
}

问题

  1. 有时候我们实现了ITransientDependency,ISingletonDependency,IScopedDependency但是再运行是还是提示依赖注入失败?
  • 实现类的名称拼写错误
  • 比如接口名称为ITestAppService,但是实现类为DefaultTessAppService,这个时候编译不会报错,但是运行报错,下面会基于源码分析。
public class DefaultTessAppService : ApplicationService, ITestAppService
{
   // ....
}
  1. 我通过[Dependency(ReplaceServices = true)]替换服务没有生效?
  • 请添加[ExposeServices(typeof(ITestManager))]显示暴露服务,下面会基于源码分析。

源码分析

  1. 进入到Startup.AddApplication源码
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddApplication<xxxManagementHttpApiHostModule>();
    }
}
  1. 进入到await app.ConfigureServicesAsync()源码
public async static Task<IAbpApplicationWithExternalServiceProvider> CreateAsync(
    [NotNull] Type startupModuleType,
    [NotNull] IServiceCollection services,
    Action<AbpApplicationCreationOptions>? optionsAction = null)
{
    var app = new AbpApplicationWithExternalServiceProvider(startupModuleType, services, options =>
    {
        options.SkipConfigureServices = true;
        optionsAction?.Invoke(options);
    });

    await app.ConfigureServicesAsync();
    return app;
}
  1. 主要查看ConfigureServices下的Services.AddAssembly(assembly)方法。
 public virtual async Task ConfigureServicesAsync()
    {
        
        // 省略...

        var assemblies = new HashSet<Assembly>();

        //ConfigureServices
        foreach (var module in Modules)
        {
            if (module.Instance is AbpModule abpModule)
            {
                if (!abpModule.SkipAutoServiceRegistration)
                {
                    var assembly = module.Type.Assembly;
                    if (!assemblies.Contains(assembly))
                    {
                        Services.AddAssembly(assembly);
                        assemblies.Add(assembly);
                    }
                }
            }

            try
            {
                await module.Instance.ConfigureServicesAsync(context);
            }
            catch (Exception ex)
            {
                throw new AbpInitializationException($"An error occurred during {nameof(IAbpModule.ConfigureServicesAsync)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);
            }
        }

        // 省略...
    }

4.进入下面AddAssembly下AddType的逻辑

public class DefaultConventionalRegistrar : ConventionalRegistrarBase
{
    public override void AddType(IServiceCollection services, Type type)
    {
        if (IsConventionalRegistrationDisabled(type))
        {
            return;
        }
        // 查看是否有DependencyAttribute特性标签
        var dependencyAttribute = GetDependencyAttributeOrNull(type);
        // 判断是否有实现接口,注入对于的类型。
        var lifeTime = GetLifeTimeOrNull(type, dependencyAttribute);

        if (lifeTime == null)
        {
            return;
        }

        var exposedServiceTypes = GetExposedServiceTypes(type);

        TriggerServiceExposing(services, type, exposedServiceTypes);

        foreach (var exposedServiceType in exposedServiceTypes)
        {
            var serviceDescriptor = CreateServiceDescriptor(
                type,
                exposedServiceType,
                exposedServiceTypes,
                lifeTime.Value
            );

            if (dependencyAttribute?.ReplaceServices == true)
            {
                services.Replace(serviceDescriptor);
            }
            else if (dependencyAttribute?.TryRegister == true)
            {
                services.TryAdd(serviceDescriptor);
            }
            else
            {
                services.Add(serviceDescriptor);
            }
        }
    }
}

    // GetLifeTimeOrNull

    protected virtual ServiceLifetime? GetLifeTimeOrNull(Type type, DependencyAttribute? dependencyAttribute)
    {
        return dependencyAttribute?.Lifetime ?? GetServiceLifetimeFromClassHierarchy(type) ?? GetDefaultLifeTimeOrNull(type);
    }
    // abp 三个生命周期
    protected virtual ServiceLifetime? GetServiceLifetimeFromClassHierarchy(Type type)
    {
        if (typeof(ITransientDependency).GetTypeInfo().IsAssignableFrom(type))
        {
            return ServiceLifetime.Transient;
        }

        if (typeof(ISingletonDependency).GetTypeInfo().IsAssignableFrom(type))
        {
            return ServiceLifetime.Singleton;
        }

        if (typeof(IScopedDependency).GetTypeInfo().IsAssignableFrom(type))
        {
            return ServiceLifetime.Scoped;
        }

        return null;
    }

5.重点到了,看下为什么名称错误为什么导致注入失败。

  • 通过接口的名称去获取实现。
  • 也能解释有时候不显示指定ExposeServices可能替换失败的问题
public class ExposeServicesAttribute : Attribute, IExposedServiceTypesProvider
{
    // 省略...

    private static List<Type> GetDefaultServices(Type type)
    {
        var serviceTypes = new List<Type>();

        foreach (var interfaceType in type.GetTypeInfo().GetInterfaces())
        {
            var interfaceName = interfaceType.Name;
            if (interfaceType.IsGenericType)
            {
                interfaceName = interfaceType.Name.Left(interfaceType.Name.IndexOf('`'));
            }

            // 查询到实现类的名称是否是移除I
            if (interfaceName.StartsWith("I"))
            {
                interfaceName = interfaceName.Right(interfaceName.Length - 1);
            }
            // 查询到实现类的名称是否以接口名结尾
            if (type.Name.EndsWith(interfaceName))
            {
                serviceTypes.Add(interfaceType);
            }
        }

        return serviceTypes;
    }
}

Abp vNext Pro

如果觉得可以,不要吝啬你的小星星哦

文章目录

与Abp vNext 依赖注入相似的内容:

Abp vNext 依赖注入

文章目录 介绍 ABP的依赖注入系统是基于Microsoft的依赖注入扩展库(Microsoft.Extensions.DependencyInjection nuget包)开发的。所以我们采用dotnet自带的注入方式也是支持的。 由于ABP是一个模块化框架,因此每个模块都定义它自己的服务并在它自

Abp vNext 模块加载机制

文章目录 生命周期 PreConfigureServices 添加依赖注入或者其它配置之前 ConfigureServices 添加依赖注入或者其它配置 PostConfigureServices 添加依赖注入或者其它配置之后 OnPreApplicationInitialization 初始化所有

Abp vNext 入门到放弃系列

文章目录 1.模块介绍 2.模块加载机制 3.依赖注入 4.AutoMapper-- 待定 5.本地化--待定 6.模型验证--待定 7.异常处理--待定 8.缓存 9.动态代理和拦截 10.分布式锁 11.领域驱动设计 Abp vNext Pro Abp Vnext Pro Github地址 的

ABP-VNext 用户权限管理系统实战06---实体的创建标准及迁移

在apb-vnext的实体的创建中可以确实字段的长度、说明、对应的表、表中给字段加的索引 以项目中的订单表为例,如下: [Comment("订单主表")] [Table("t_voucher_order")] [Index(nameof(VoucherCode))] public class Ord

迁移现有用户数据到ABP vNext

## 前言 使用 ABP vNext(下文简称 ABP)时,通常都是从 cli 开始新建模板,从一个空项目开始。对已经存续的项目来说,现有的数据,特别是用户等核心数据需要进行迁移。 老的项目,随着规模越来越大,每次修改都需要更改非常多地方,最重要的是,共用数据库使得维护起来需要小心翼翼。为了后续维护

OData WebAPI实践-与ABP vNext集成

本文属于 OData 系列文章 ABP 是一个流行的 ASP. NET 开发框架,旧版的的 ABP 已经能够非常好的支持了 OData ,并提供了对应的 OData 包。 ABP vNext 是一个重新设计的,面向微服务的框架,提供了一些非常有用的特性,包括分页查询等但是它并不能原生支持 OData

Skywalking APM监控系列(一丶.NET5.0+接入Skywalking监听)

前言 新项目采用的abp vnext的微服务模块化架构,所以把应用的服务拆成了很多独立模块 在初期,我们通过日志还能跟踪到问题, 后期服务越来越多(大约扩充到了十几个),随着调用链路越来越深 ,问题也越来越能排查了. 往往入口报错之后,要跟好几个服务的日志 才能找到最终节点. 所以考虑引入Skywa

abp 创建DDD项目

abp 创建DDD项目 我和我的伙伴在搭建框架的基础框架,找了很多框架,最后选择用abp作为DDD的规范标准。 创建项目 1.命令行中安装 ABP CLI: dotnet tool install -g Volo.Abp.Cli 2.查看abp 版本: abp -v 3.如果版本过低,更新版本,目前

aspnetcore插件开发dll热加载 二

这一篇文章应该是个总结。 投简历的时候是不是有人问我有没有abp的开发经历,汗颜! 在各位大神的尝试及自己的总结下,还是实现了业务和主机服务分离,通过dll动态的加载卸载,控制器动态的删除添加。 项目如下: 演示效果: 下面就是代码部分: 重点 1.IActionDescriptorChangePr