ABP如何为现有的模块添加新的功能

106 阅读2分钟

背景

ABP 中有文本模板模块,但是只提供了 scriban 的实现,且需要先定义模板名称、内容,操作起来不够简单。

需要利用 ABP 的文本模板模块,提供简单的调用方法,提供更多文本模板引擎,如 liquid。

具体来讲,需要为ITemplateRenderingEngine 接口及实现类添加一个新的方法,最外侧调用这个新的方法

思路

利用继承,

定义一个新的接口,继承原来的接口。

定义一个新的实现类,继承原来的实现类及新接口。如下

public interface IMyExtendedService : IOriginalService
{
    void NewMethod();
}

public class MyExtendedService : OriginalServiceImplementation, IMyExtendedService
{
    public void NewMethod()
    {
        // 实现新的方法
    }
}

注意,module 类也要 DependOn 原来的类,如下

[DependsOn(
    typeof(AbpTextTemplatingCoreModule)
)]
public class TextTemplatingCoreModule : AbpModule
{
}

有时,在 Module 中需要替换掉原来的实现类(因为新定义了接口和实现类)

[DependsOn(
    typeof(TextTemplatingCoreModule),
    typeof(AbpTextTemplatingScribanModule)
)]
public class TextTemplatingScribanModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        Configure<AbpTextTemplatingOptions>(options =>
        {
            options.DefaultRenderingEngine = ScribanTemplateRenderingEngine.EngineName;
            //覆盖掉默认的实现类ScribanTemplateRenderingEngine
            options.RenderingEngines[ScribanTemplateRenderingEngine.EngineName] = typeof(ScribanCustomTemplateRenderingEngine);
        });
    }
}

具体代码

public interface ICustomTemplateRenderingEngine : ITemplateRenderingEngine, ITransientDependency
{
    /// <summary>
    /// 渲染一段模板体(新添加的方法)
    /// </summary>
    /// <param name="templateContent"></param>
    /// <param name="model"></param>
    /// <param name="cultureName"></param>
    /// <param name="globalContext"></param>
    /// <returns></returns>
    Task<string> RenderTemplateAsync(
        [NotNull] string templateContent,
        object? model = null,
        string? cultureName = null,
        Dictionary<string, object>? globalContext = null
    );
}

public abstract class CustomTemplateRenderingEngineBase : TemplateRenderingEngineBase, ICustomTemplateRenderingEngine
{
    protected CustomTemplateRenderingEngineBase(ITemplateDefinitionManager templateDefinitionManager, ITemplateContentProvider templateContentProvider, IStringLocalizerFactory stringLocalizerFactory) : base(templateDefinitionManager, templateContentProvider, stringLocalizerFactory)
    {
    }

    public abstract Task<string> RenderTemplateAsync([NotNull] string templatText, object? model = null, string? cultureName = null, Dictionary<string, object>? globalContext = null);
}

public class ScribanCustomTemplateRenderingEngine : ScribanTemplateRenderingEngine, ICustomTemplateRenderingEngine, ITransientDependency
{
    public ScribanCustomTemplateRenderingEngine(ITemplateDefinitionManager templateDefinitionManager, ITemplateContentProvider templateContentProvider, IStringLocalizerFactory stringLocalizerFactory) : base(templateDefinitionManager, templateContentProvider, stringLocalizerFactory)
    {
    }

    public async Task<string> RenderTemplateAsync([NotNull] string templateContent, object? model = null, string? cultureName = null, Dictionary<string, object>? globalContext = null)
    {
        //构造context
        var context = new TemplateContext();

        var scriptObject = new ScriptObject();

        scriptObject.Import(globalContext);
        //下面是字典对象的写法
        if (model != null && model is IDictionary<string, object> dictionary)
        {
            scriptObject.Import(model);
            //foreach (var kvp in dictionary)
            //{
            //    scriptObject[kvp.Key] = kvp.Value;
            //}
        }

        context.PushGlobal(scriptObject);

        //渲染
        return await Template
                .Parse(templateContent)
                .RenderAsync(context);
    }
}

相关知识

替换服务注册

替换服务注册,就是替换实现类

[DependsOn(
    typeof(OriginalAbpModule)
)]
public class MyCustomModule : AbpModule
{
    public override void PreInitialize()
    {
        // 替换服务实现
        Configuration.ReplaceService<IOriginalService, MyExtendedService>(DependencyLifeStyle.Transient);
    }
}

装饰器模式

使用装饰器模式,可以在调用原方法之前或者之后添加额外的行为

public class MyExtendedServiceDecorator : IOriginalService
{
    private readonly IOriginalService _originalService;

    public MyExtendedServiceDecorator(IOriginalService originalService)
    {
        _originalService = originalService;
    }

    public void OriginalMethod()
    {
        // 可以在调用原始方法前后添加自定义行为
        _originalService.OriginalMethod();
    }

    public void NewMethod()
    {
        // 新方法的实现
    }
}