You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用RazorEngine Roslyn代码时,ViewBase<T>带参构造函数实例化问题

解决RazorEngine Roslyn模式下带构造参数ViewBase的实例化问题

嘿,我之前在项目里也碰到过一模一样的问题——RazorEngine默认会尝试用无参构造函数实例化模板的基类,但咱们的ViewBase<T>偏偏需要构造参数,直接跑就报错。折腾了好一阵,总结了几个可行的方案,你可以根据自己的项目情况选:

方案一:自定义IActivator接管实例创建

这个方法最简单,直接替换RazorEngine默认的实例激活器,让它在创建模板类的时候帮我们传入基类需要的构造参数。

首先实现自己的IActivator:

public class ViewBaseActivator : IActivator
{
    // 假设你用依赖注入容器来获取构造参数所需的服务,这里换成你实际的服务获取逻辑
    private readonly IServiceProvider _serviceProvider;

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

    public object CreateInstance(Type type, params object[] args)
    {
        // 检查当前类型是否继承自ViewBase<T>
        var baseType = type.BaseType;
        if (baseType != null && baseType.IsGenericType && 
            baseType.GetGenericTypeDefinition() == typeof(ViewBase<>))
        {
            // 获取基类的构造函数参数,然后从服务容器中获取对应的实例
            var ctorParameters = baseType.GetConstructors()[0].GetParameters()
                .Select(param => _serviceProvider.GetRequiredService(param.ParameterType))
                .ToArray();
            
            // 用带参数的构造函数创建模板实例
            return Activator.CreateInstance(type, ctorParameters);
        }

        // 非ViewBase<T>子类用默认逻辑创建
        return Activator.CreateInstance(type, args);
    }
}

然后在配置RazorEngine服务的时候注册这个激活器:

var config = new TemplateServiceConfiguration
{
    Activator = new ViewBaseActivator(yourServiceProvider), // 传入你的服务容器实例
    // 其他配置项,比如启用Roslyn:
    Language = Language.CSharp,
    CompilerServiceFactory = new RoslynCompilerServiceFactory()
};

using (var razorService = RazorEngineService.Create(config))
{
    // 正常解析模板
    var result = razorService.RunCompile(templateContent, "templateKey", modelType, modelData);
}

方案二:自定义模板代码生成器,修改构造函数逻辑

如果需要更精细地控制模板类的生成代码,可以重写RazorEngine的代码生成器,让它直接生成带构造参数的模板类,这样激活的时候就自然能传入参数了。

实现自定义代码生成器:

public class ViewBaseCodeGenerator : TemplateCSharpCodeGenerator
{
    public ViewBaseCodeGenerator(IRazorEngineConfiguration config, Type templateType, string className, string rootNamespace)
        : base(config, templateType, className, rootNamespace)
    {
    }

    protected override void GenerateConstructor(CodeGeneratorContext context)
    {
        // 获取ViewBase<T>的构造函数参数(假设你的基类只有一个构造函数)
        var baseConstructor = typeof(ViewBase<>).MakeGenericType(ModelType).GetConstructors()[0];
        var baseParams = baseConstructor.GetParameters();

        // 给模板类创建带参数的构造函数
        var constructor = new CodeConstructor
        {
            Attributes = MemberAttributes.Public
        };

        // 添加构造参数,并调用基类构造函数
        foreach (var param in baseParams)
        {
            constructor.Parameters.Add(new CodeParameterDeclarationExpression(param.ParameterType, param.Name));
            constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression(param.Name));
        }

        // 将构造函数添加到生成的模板类中
        context.GeneratedType.Members.Add(constructor);
    }
}

然后注册这个代码生成器到配置里:

var config = new TemplateServiceConfiguration
{
    CodeGeneratorFactory = (cfg, templateType, className, rootNamespace) => 
        new ViewBaseCodeGenerator(cfg, templateType, className, rootNamespace),
    Language = Language.CSharp,
    CompilerServiceFactory = new RoslynCompilerServiceFactory()
};

using (var razorService = RazorEngineService.Create(config))
{
    // 此时生成的模板类会带有对应构造参数,你在创建实例时需要传入这些参数
    // 或者结合上面的IActivator来自动注入
}

方案三:使用ITemplateFactory完全控制模板实例化

如果上面的方法都不满足需求,还可以实现ITemplateFactory,完全接管模板的创建过程:

public class ViewBaseTemplateFactory : ITemplateFactory
{
    private readonly IServiceProvider _serviceProvider;

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

    public ITemplate CreateTemplate(Type templateType, object model)
    {
        // 根据模板类型和模型,创建ViewBase<T>实例并传入构造参数
        var baseType = templateType.BaseType;
        if (baseType != null && baseType.IsGenericType && 
            baseType.GetGenericTypeDefinition() == typeof(ViewBase<>))
        {
            var ctorParams = baseType.GetConstructors()[0].GetParameters()
                .Select(p => _serviceProvider.GetRequiredService(p.ParameterType))
                .ToArray();
            
            return (ITemplate)Activator.CreateInstance(templateType, ctorParams);
        }

        return (ITemplate)Activator.CreateInstance(templateType);
    }
}

注册方式:

var config = new TemplateServiceConfiguration
{
    TemplateFactory = new ViewBaseTemplateFactory(yourServiceProvider),
    Language = Language.CSharp,
    CompilerServiceFactory = new RoslynCompilerServiceFactory()
};

这几个方案里,我个人最常用的是方案一,因为它侵入性最低,不需要修改模板生成的代码,只需要替换激活逻辑就行。如果你的ViewBase<T>构造参数比较复杂,或者需要和依赖注入深度集成,这个方案最省心。

内容的提问来源于stack exchange,提问作者Stuart.Sklinar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 09:19:33